mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-03-30 11:46:55 +00:00
[Linux] Implemented joystick hotplugging
This commit is contained in:
parent
4ca8c78764
commit
fb917a6d89
|
@ -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,27 +78,28 @@ 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))
|
||||||
if (stick != null)
|
|
||||||
{
|
{
|
||||||
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
|
JoystickDevice<X11JoyDetails> stick = OpenJoystick(file);
|
||||||
//number, stick.Axis.Count, stick.Button.Count, JoystickPath);
|
if (stick != null)
|
||||||
sticks.Add(stick);
|
{
|
||||||
|
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
|
||||||
|
//number, stick.Axis.Count, stick.Button.Count, JoystickPath);
|
||||||
|
sticks.Add(stick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
number = 0;
|
foreach (string file in Directory.GetFiles(JoystickPathLegacy))
|
||||||
while (number < max_sticks)
|
|
||||||
{
|
|
||||||
JoystickDevice<X11JoyDetails> stick = OpenJoystick(JoystickPathLegacy, number++);
|
|
||||||
if (stick != null)
|
|
||||||
{
|
{
|
||||||
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
|
JoystickDevice<X11JoyDetails> stick = OpenJoystick(file);
|
||||||
//number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy);
|
if (stick != null)
|
||||||
sticks.Add(stick);
|
{
|
||||||
|
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
|
||||||
|
//number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy);
|
||||||
|
sticks.Add(stick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,84 +157,135 @@ namespace OpenTK.Platform.X11
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void Poll()
|
#region Private Members
|
||||||
|
|
||||||
|
JoystickDevice<X11JoyDetails> OpenJoystick(string path)
|
||||||
|
{
|
||||||
|
JoystickDevice<X11JoyDetails> stick = null;
|
||||||
|
|
||||||
|
int number = GetJoystickNumber(Path.GetFileName(path));
|
||||||
|
if (number >= 0)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock);
|
||||||
|
if (fd == -1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Check joystick driver version (must be 1.0+)
|
||||||
|
int driver_version = 0x00000800;
|
||||||
|
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
|
||||||
|
if (driver_version < 0x00010000)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Get number of joystick axes
|
||||||
|
int axes = 0;
|
||||||
|
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
|
||||||
|
|
||||||
|
// Get number of joystick buttons
|
||||||
|
int buttons = 0;
|
||||||
|
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
|
||||||
|
|
||||||
|
stick = new JoystickDevice<X11JoyDetails>(number, axes, buttons);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(128);
|
||||||
|
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb);
|
||||||
|
stick.Description = sb.ToString();
|
||||||
|
|
||||||
|
stick.Details.FileDescriptor = fd;
|
||||||
|
stick.Details.State.SetIsConnected(true);
|
||||||
|
//stick.Details.Guid =
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stick == null && fd != -1)
|
||||||
|
UnsafeNativeMethods.close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
JoystickEvent e;
|
||||||
|
|
||||||
foreach (JoystickDevice js in sticks)
|
unsafe
|
||||||
{
|
{
|
||||||
unsafe
|
while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
|
||||||
{
|
{
|
||||||
while ((long)UnsafeNativeMethods.read(js.Id, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
|
e.Type &= ~JoystickEventType.Init;
|
||||||
|
|
||||||
|
switch (e.Type)
|
||||||
{
|
{
|
||||||
e.Type &= ~JoystickEventType.Init;
|
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;
|
||||||
|
|
||||||
switch (e.Type)
|
case JoystickEventType.Button:
|
||||||
{
|
js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0);
|
||||||
case JoystickEventType.Axis:
|
break;
|
||||||
// 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
|
bool IsValid(int index)
|
||||||
|
|
||||||
JoystickDevice<X11JoyDetails> OpenJoystick(string base_path, int number)
|
|
||||||
{
|
{
|
||||||
string path = Path.Combine(base_path, "js" + number.ToString());
|
return index_to_stick.ContainsKey(index);
|
||||||
JoystickDevice<X11JoyDetails> stick = null;
|
|
||||||
|
|
||||||
int fd = -1;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock);
|
|
||||||
if (fd == -1)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Check joystick driver version (must be 1.0+)
|
|
||||||
int driver_version = 0x00000800;
|
|
||||||
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
|
|
||||||
if (driver_version < 0x00010000)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Get number of joystick axes
|
|
||||||
int axes = 0;
|
|
||||||
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
|
|
||||||
|
|
||||||
// Get number of joystick buttons
|
|
||||||
int buttons = 0;
|
|
||||||
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
|
|
||||||
|
|
||||||
stick = new JoystickDevice<X11JoyDetails>(fd, axes, buttons);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(128);
|
|
||||||
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb);
|
|
||||||
stick.Description = sb.ToString();
|
|
||||||
|
|
||||||
stick.Id = number;
|
|
||||||
|
|
||||||
stick.Details.IsConnected = true;
|
|
||||||
|
|
||||||
Debug.Print("Found joystick on path {0}", path);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (stick == null && fd != -1)
|
|
||||||
UnsafeNativeMethods.close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stick;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region UnsafeNativeMethods
|
#region UnsafeNativeMethods
|
||||||
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue