[Linux] Implemented joystick hotplugging

This commit is contained in:
thefiddler 2014-01-14 23:51:31 +01:00
parent 4ca8c78764
commit fb917a6d89

View file

@ -37,7 +37,8 @@ namespace OpenTK.Platform.X11
{ {
struct X11JoyDetails struct X11JoyDetails
{ {
public bool IsConnected; public int FileDescriptor;
public JoystickState State;
} }
sealed class X11Joystick : IJoystickDriver2 sealed class X11Joystick : IJoystickDriver2
@ -67,6 +68,8 @@ namespace OpenTK.Platform.X11
watcher_legacy.Created += JoystickAdded; watcher_legacy.Created += JoystickAdded;
watcher_legacy.Deleted += JoystickRemoved; watcher_legacy.Deleted += JoystickRemoved;
watcher_legacy.EnableRaisingEvents = true; watcher_legacy.EnableRaisingEvents = true;
OpenJoysticks();
} }
#endregion #endregion
@ -75,10 +78,11 @@ namespace OpenTK.Platform.X11
void OpenJoysticks() void OpenJoysticks()
{ {
int number = 0, max_sticks = 25; lock (sync)
while (number < max_sticks)
{ {
JoystickDevice<X11JoyDetails> stick = OpenJoystick(JoystickPath, number++); foreach (string file in Directory.GetFiles(JoystickPath))
{
JoystickDevice<X11JoyDetails> stick = OpenJoystick(file);
if (stick != null) if (stick != null)
{ {
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
@ -87,10 +91,9 @@ namespace OpenTK.Platform.X11
} }
} }
number = 0; foreach (string file in Directory.GetFiles(JoystickPathLegacy))
while (number < max_sticks)
{ {
JoystickDevice<X11JoyDetails> stick = OpenJoystick(JoystickPathLegacy, number++); JoystickDevice<X11JoyDetails> stick = OpenJoystick(file);
if (stick != null) if (stick != null)
{ {
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
@ -99,6 +102,7 @@ namespace OpenTK.Platform.X11
} }
} }
} }
}
int GetJoystickNumber(string path) int GetJoystickNumber(string path)
{ {
@ -117,35 +121,7 @@ namespace OpenTK.Platform.X11
{ {
lock (sync) lock (sync)
{ {
string file = Path.GetFileName(e.FullPath); OpenJoystick(e.FullPath);
int number = GetJoystickNumber(file);
if (number != -1)
{
JoystickDevice<X11JoyDetails> stick = OpenJoystick(e.FullPath, number);
// Find the first disconnected joystick (if any)
int i;
for (i = 0; i < sticks.Count; i++)
{
if (!stick.Details.IsConnected)
{
break;
}
}
// If no disconnected joystick exists, append a new slot
if (i == sticks.Count)
{
sticks.Add(stick);
}
else
{
sticks[i] = stick;
}
// Map player index to joystick
index_to_stick.Add(index_to_stick.Count, i);
}
} }
} }
@ -173,8 +149,7 @@ namespace OpenTK.Platform.X11
} }
else else
{ {
JoystickDevice<X11JoyDetails> stick = sticks[i]; CloseJoystick(sticks[i]);
stick.Details.IsConnected = false;
} }
} }
} }
@ -182,44 +157,15 @@ namespace OpenTK.Platform.X11
#endregion #endregion
public void Poll()
{
JoystickEvent e;
foreach (JoystickDevice js in sticks)
{
unsafe
{
while ((long)UnsafeNativeMethods.read(js.Id, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
{
e.Type &= ~JoystickEventType.Init;
switch (e.Type)
{
case JoystickEventType.Axis:
// Flip vertical axes so that +1 point up.
if (e.Number % 2 == 0)
js.SetAxis((JoystickAxis)e.Number, e.Value / 32767.0f);
else
js.SetAxis((JoystickAxis)e.Number, -e.Value / 32767.0f);
break;
case JoystickEventType.Button:
js.SetButton((JoystickButton)e.Number, e.Value != 0);
break;
}
}
}
}
}
#region Private Members #region Private Members
JoystickDevice<X11JoyDetails> OpenJoystick(string base_path, int number) JoystickDevice<X11JoyDetails> OpenJoystick(string path)
{ {
string path = Path.Combine(base_path, "js" + number.ToString());
JoystickDevice<X11JoyDetails> stick = null; JoystickDevice<X11JoyDetails> stick = null;
int number = GetJoystickNumber(Path.GetFileName(path));
if (number >= 0)
{
int fd = -1; int fd = -1;
try try
{ {
@ -241,15 +187,38 @@ namespace OpenTK.Platform.X11
int buttons = 0; int buttons = 0;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
stick = new JoystickDevice<X11JoyDetails>(fd, axes, buttons); stick = new JoystickDevice<X11JoyDetails>(number, axes, buttons);
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb);
stick.Description = sb.ToString(); stick.Description = sb.ToString();
stick.Id = number; stick.Details.FileDescriptor = fd;
stick.Details.State.SetIsConnected(true);
//stick.Details.Guid =
stick.Details.IsConnected = true; // Find the first disconnected joystick (if any)
int i;
for (i = 0; i < sticks.Count; i++)
{
if (!sticks[i].Details.State.IsConnected)
{
break;
}
}
// If no disconnected joystick exists, append a new slot
if (i == sticks.Count)
{
sticks.Add(stick);
}
else
{
sticks[i] = stick;
}
// Map player index to joystick
index_to_stick.Add(index_to_stick.Count, i);
Debug.Print("Found joystick on path {0}", path); Debug.Print("Found joystick on path {0}", path);
} }
@ -258,10 +227,67 @@ namespace OpenTK.Platform.X11
if (stick == null && fd != -1) if (stick == null && fd != -1)
UnsafeNativeMethods.close(fd); UnsafeNativeMethods.close(fd);
} }
}
return stick; return stick;
} }
void CloseJoystick(JoystickDevice<X11JoyDetails> js)
{
UnsafeNativeMethods.close(js.Details.FileDescriptor);
js.Details.State = new JoystickState(); // clear joystick state
js.Details.FileDescriptor = -1;
// find and remove the joystick index from index_to_stick
int key = -1;
foreach (int i in index_to_stick.Keys)
{
if (sticks[index_to_stick[i]] == js)
{
key = i;
break;
}
}
if (index_to_stick.ContainsKey(key))
{
index_to_stick.Remove(key);
}
}
void PollJoystick(JoystickDevice<X11JoyDetails> js)
{
JoystickEvent e;
unsafe
{
while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
{
e.Type &= ~JoystickEventType.Init;
switch (e.Type)
{
case JoystickEventType.Axis:
// Flip vertical axes so that +1 point up.
if (e.Number % 2 == 0)
js.Details.State.SetAxis((JoystickAxis)e.Number, e.Value);
else
js.Details.State.SetAxis((JoystickAxis)e.Number, unchecked((short)-e.Value));
break;
case JoystickEventType.Button:
js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0);
break;
}
}
}
}
bool IsValid(int index)
{
return index_to_stick.ContainsKey(index);
}
#region UnsafeNativeMethods #region UnsafeNativeMethods
struct JoystickEvent struct JoystickEvent
@ -335,9 +361,9 @@ namespace OpenTK.Platform.X11
{ {
} }
foreach (JoystickDevice js in sticks) foreach (JoystickDevice<X11JoyDetails> js in sticks)
{ {
UnsafeNativeMethods.close(js.Id); CloseJoystick(js);
} }
disposed = true; disposed = true;
@ -355,12 +381,26 @@ namespace OpenTK.Platform.X11
JoystickState IJoystickDriver2.GetState(int index) JoystickState IJoystickDriver2.GetState(int index)
{ {
if (IsValid(index))
{
JoystickDevice<X11JoyDetails> js =
sticks[index_to_stick[index]];
PollJoystick(js);
return js.Details.State;
}
return new JoystickState(); return new JoystickState();
} }
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
{ {
return new JoystickCapabilities(); JoystickCapabilities caps = new JoystickCapabilities();
if (IsValid(index))
{
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]];
caps = new JoystickCapabilities(
js.Axis.Count, js.Button.Count, js.Details.State.IsConnected);
}
return caps;
} }
Guid IJoystickDriver2.GetGuid(int index) Guid IJoystickDriver2.GetGuid(int index)