mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-11-06 08:25:13 +00:00
[Linux] Implemented evdev joystick device discovery
This commit is contained in:
parent
41f1f92cdf
commit
397bdda076
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Linux
|
namespace OpenTK.Platform.Linux
|
||||||
|
|
@ -36,6 +37,10 @@ namespace OpenTK.Platform.Linux
|
||||||
// Bindings for linux/input.h
|
// Bindings for linux/input.h
|
||||||
class Evdev
|
class Evdev
|
||||||
{
|
{
|
||||||
|
public const int KeyCount = 0x300;
|
||||||
|
public const int AxisCount = 0x40;
|
||||||
|
public const int EventCount = (int)EvdevType.CNT;
|
||||||
|
|
||||||
#region KeyMap
|
#region KeyMap
|
||||||
|
|
||||||
public static readonly Key[] KeyMap = new Key[]
|
public static readonly Key[] KeyMap = new Key[]
|
||||||
|
|
@ -365,9 +370,101 @@ namespace OpenTK.Platform.Linux
|
||||||
return MouseButton.Left;
|
return MouseButton.Left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint IOCreate(DirectionFlags dir, int number, int length)
|
||||||
|
{
|
||||||
|
long v =
|
||||||
|
((byte)dir << 30) |
|
||||||
|
((byte)'E' << 8) |
|
||||||
|
(number << 0) |
|
||||||
|
(length << 16);
|
||||||
|
return (uint)v;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EvdevButton : uint
|
public static int GetBit(int fd, EvdevType ev, int length, IntPtr data)
|
||||||
|
{
|
||||||
|
// EVIOCGBIT = _IOC(_IOC_READ, 'E', 0x20 + (ev), len)
|
||||||
|
uint ioctl = IOCreate(DirectionFlags.Read, (int)ev + 0x20, length);
|
||||||
|
int retval = Libc.ioctl(fd, ioctl, data);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetName(int fd, out string name)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
sbyte* pname = stackalloc sbyte[129];
|
||||||
|
int ret = Libc.ioctl(fd, EvdevIoctl.Name128, new IntPtr(pname));
|
||||||
|
name = new string(pname);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetId(int fd, out EvdevInputId id)
|
||||||
|
{
|
||||||
|
id = default(EvdevInputId);
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (EvdevInputId* pid = &id)
|
||||||
|
{
|
||||||
|
return Libc.ioctl(fd, EvdevIoctl.Id, new IntPtr(pid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EvdevAxis
|
||||||
|
{
|
||||||
|
X = 0x00,
|
||||||
|
Y = 0x01,
|
||||||
|
Z = 0x02,
|
||||||
|
RX = 0x03,
|
||||||
|
RY = 0x04,
|
||||||
|
RZ = 0x05,
|
||||||
|
THROTTLE = 0x06,
|
||||||
|
RUDDER = 0x07,
|
||||||
|
WHEEL = 0x08,
|
||||||
|
GAS = 0x09,
|
||||||
|
BRAKE = 0x0a,
|
||||||
|
HAT0X = 0x10,
|
||||||
|
HAT0Y = 0x11,
|
||||||
|
HAT1X = 0x12,
|
||||||
|
HAT1Y = 0x13,
|
||||||
|
HAT2X = 0x14,
|
||||||
|
HAT2Y = 0x15,
|
||||||
|
HAT3X = 0x16,
|
||||||
|
HAT3Y = 0x17,
|
||||||
|
PRESSURE = 0x18,
|
||||||
|
DISTANCE = 0x19,
|
||||||
|
TILT_X = 0x1a,
|
||||||
|
TILT_Y = 0x1b,
|
||||||
|
TOOL_WIDTH = 0x1c,
|
||||||
|
|
||||||
|
VOLUME = 0x20,
|
||||||
|
|
||||||
|
MISC = 0x28,
|
||||||
|
|
||||||
|
MT_SLOT = 0x2f, /* MT slot being modified */
|
||||||
|
MT_TOUCH_MAJOR = 0x30, /* Major axis of touching ellipse */
|
||||||
|
MT_TOUCH_MINOR = 0x31, /* Minor axis (omit if circular) */
|
||||||
|
MT_WIDTH_MAJOR = 0x32, /* Major axis of approaching ellipse */
|
||||||
|
MT_WIDTH_MINOR = 0x33, /* Minor axis (omit if circular) */
|
||||||
|
MT_ORIENTATION = 0x34, /* Ellipse orientation */
|
||||||
|
MT_POSITION_X = 0x35, /* Center X touch position */
|
||||||
|
MT_POSITION_Y = 0x36, /* Center Y touch position */
|
||||||
|
MT_TOOL_TYPE = 0x37, /* Type of touching device */
|
||||||
|
MT_BLOB_ID = 0x38, /* Group a set of packets as a blob */
|
||||||
|
MT_TRACKING_ID = 0x39, /* Unique ID of initiated contact */
|
||||||
|
MT_PRESSURE = 0x3a, /* Pressure on contact area */
|
||||||
|
MT_DISTANCE = 0x3b, /* Contact hover distance */
|
||||||
|
MT_TOOL_X = 0x3c, /* Center X tool position */
|
||||||
|
MT_TOOL_Y = 0x3d, /* Center Y tool position */
|
||||||
|
|
||||||
|
MAX = 0x3f,
|
||||||
|
CNT = (MAX+1),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EvdevButton
|
||||||
{
|
{
|
||||||
MISC = 0x100,
|
MISC = 0x100,
|
||||||
BTN0 = 0x100,
|
BTN0 = 0x100,
|
||||||
|
|
@ -447,6 +544,64 @@ namespace OpenTK.Platform.Linux
|
||||||
WHEEL = 0x150,
|
WHEEL = 0x150,
|
||||||
GEAR_DOWN = 0x150,
|
GEAR_DOWN = 0x150,
|
||||||
GEAR_UP = 0x151,
|
GEAR_UP = 0x151,
|
||||||
|
|
||||||
|
DPAD_UP = 0x220,
|
||||||
|
DPAD_DOWN = 0x221,
|
||||||
|
DPAD_LEFT = 0x222,
|
||||||
|
DPAD_RIGHT = 0x223,
|
||||||
|
|
||||||
|
Last = 0x300,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EvdevType : byte
|
||||||
|
{
|
||||||
|
SYN = 0x00,
|
||||||
|
KEY = 0x01,
|
||||||
|
REL = 0x02,
|
||||||
|
ABS = 0x03,
|
||||||
|
MSC = 0x04,
|
||||||
|
SW = 0x05,
|
||||||
|
LED = 0x11,
|
||||||
|
SND = 0x12,
|
||||||
|
REP = 0x14,
|
||||||
|
FF = 0x15,
|
||||||
|
PWR = 0x16,
|
||||||
|
FF_STATUS = 0x17,
|
||||||
|
MAX = 0x1f,
|
||||||
|
CNT = (MAX+1),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EvdevIoctl : uint
|
||||||
|
{
|
||||||
|
Id = (2u << 30) | ((byte)'E' << 8) | (0x02u << 0) | (8u << 16), //EVIOCGID = _IOR('E', 0x02, struct input_id)
|
||||||
|
Name128 = (2u << 30) | ((byte)'E' << 8) | (0x06u << 0) | (128u << 16), //EVIOCGNAME(len) = _IOC(_IOC_READ, 'E', 0x06, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct InputId
|
||||||
|
{
|
||||||
|
public ushort BusType;
|
||||||
|
public ushort Vendor;
|
||||||
|
public ushort Product;
|
||||||
|
public ushort Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct InputEvent
|
||||||
|
{
|
||||||
|
public TimeVal Time;
|
||||||
|
ushort type;
|
||||||
|
public ushort Code;
|
||||||
|
public int Value;
|
||||||
|
|
||||||
|
public EvdevType Type { get { return (EvdevType)type; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct TimeVal
|
||||||
|
{
|
||||||
|
public IntPtr Seconds;
|
||||||
|
public IntPtr MicroSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,10 @@ namespace OpenTK.Platform.Linux
|
||||||
public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data);
|
public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data);
|
||||||
|
|
||||||
[DllImport(lib)]
|
[DllImport(lib)]
|
||||||
public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data);
|
public static extern int ioctl(int d, EvdevIoctl request, [Out] IntPtr data);
|
||||||
|
|
||||||
|
[DllImport(lib)]
|
||||||
|
public static extern int ioctl(int d, uint request, [Out] IntPtr data);
|
||||||
|
|
||||||
[DllImport(lib)]
|
[DllImport(lib)]
|
||||||
public static extern int ioctl(int d, KeyboardIoctlCode request, ref IntPtr data);
|
public static extern int ioctl(int d, KeyboardIoctlCode request, ref IntPtr data);
|
||||||
|
|
@ -106,6 +109,14 @@ namespace OpenTK.Platform.Linux
|
||||||
InvalidValue = 22,
|
InvalidValue = 22,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum DirectionFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Write = 1,
|
||||||
|
Read = 2
|
||||||
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
enum OpenFlags
|
enum OpenFlags
|
||||||
{
|
{
|
||||||
|
|
@ -116,11 +127,6 @@ namespace OpenTK.Platform.Linux
|
||||||
CloseOnExec = 0x0080000
|
CloseOnExec = 0x0080000
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EvdevIoctlCode : uint
|
|
||||||
{
|
|
||||||
Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
enum JoystickEventType : byte
|
enum JoystickEventType : byte
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,23 @@ using OpenTK.Input;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Linux
|
namespace OpenTK.Platform.Linux
|
||||||
{
|
{
|
||||||
struct LinuxJoyDetails
|
class LinuxJoystickDetails
|
||||||
{
|
{
|
||||||
public Guid Guid;
|
public Guid Guid;
|
||||||
|
public string Name;
|
||||||
public int FileDescriptor;
|
public int FileDescriptor;
|
||||||
|
public int PathIndex; // e.g. "0" for "/dev/input/event0". Used as a hardware id
|
||||||
public JoystickState State;
|
public JoystickState State;
|
||||||
|
public JoystickCapabilities Caps;
|
||||||
|
|
||||||
|
public readonly Dictionary<EvdevAxis, JoystickAxis> AxisMap =
|
||||||
|
new Dictionary<EvdevAxis, JoystickAxis>();
|
||||||
|
public readonly Dictionary<EvdevButton, JoystickButton> ButtonMap =
|
||||||
|
new Dictionary<EvdevButton, JoystickButton>();
|
||||||
|
public readonly Dictionary<int, JoystickHat> HatMap =
|
||||||
|
new Dictionary<int, JoystickHat>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: despite what the name says, this class is Linux-specific.
|
|
||||||
sealed class LinuxJoystick : IJoystickDriver2
|
sealed class LinuxJoystick : IJoystickDriver2
|
||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
|
|
@ -51,8 +60,8 @@ namespace OpenTK.Platform.Linux
|
||||||
|
|
||||||
readonly FileSystemWatcher watcher = new FileSystemWatcher();
|
readonly FileSystemWatcher watcher = new FileSystemWatcher();
|
||||||
|
|
||||||
readonly Dictionary<int, int> index_to_stick = new Dictionary<int, int>();
|
readonly DeviceCollection<LinuxJoystickDetails> Sticks =
|
||||||
List<JoystickDevice<LinuxJoyDetails>> sticks = new List<JoystickDevice<LinuxJoyDetails>>();
|
new DeviceCollection<LinuxJoystickDetails>();
|
||||||
|
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
|
|
@ -89,12 +98,10 @@ namespace OpenTK.Platform.Linux
|
||||||
{
|
{
|
||||||
foreach (string file in Directory.GetFiles(path))
|
foreach (string file in Directory.GetFiles(path))
|
||||||
{
|
{
|
||||||
JoystickDevice<LinuxJoyDetails> stick = OpenJoystick(file);
|
LinuxJoystickDetails stick = OpenJoystick(file);
|
||||||
if (stick != null)
|
if (stick != null)
|
||||||
{
|
{
|
||||||
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
|
Sticks.Add(stick.PathIndex, stick);
|
||||||
//number, stick.Axis.Count, stick.Button.Count, path);
|
|
||||||
sticks.Add(stick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,10 +109,11 @@ namespace OpenTK.Platform.Linux
|
||||||
|
|
||||||
int GetJoystickNumber(string path)
|
int GetJoystickNumber(string path)
|
||||||
{
|
{
|
||||||
if (path.StartsWith("js"))
|
const string evdev = "event";
|
||||||
|
if (path.StartsWith(evdev))
|
||||||
{
|
{
|
||||||
int num;
|
int num;
|
||||||
if (Int32.TryParse(path.Substring(2), out num))
|
if (Int32.TryParse(path.Substring(evdev.Length), out num))
|
||||||
{
|
{
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
@ -129,23 +137,11 @@ namespace OpenTK.Platform.Linux
|
||||||
int number = GetJoystickNumber(file);
|
int number = GetJoystickNumber(file);
|
||||||
if (number != -1)
|
if (number != -1)
|
||||||
{
|
{
|
||||||
// Find which joystick id matches this number
|
var stick = Sticks.FromHardwareId(number);
|
||||||
int i;
|
if (stick != null)
|
||||||
for (i = 0; i < sticks.Count; i++)
|
|
||||||
{
|
{
|
||||||
if (sticks[i].Id == number)
|
CloseJoystick(stick);
|
||||||
{
|
Sticks.TryRemove(number);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == sticks.Count)
|
|
||||||
{
|
|
||||||
Debug.Print("[Evdev] Joystick id {0} does not exist.", number);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CloseJoystick(sticks[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,29 +151,9 @@ namespace OpenTK.Platform.Linux
|
||||||
|
|
||||||
#region Private Members
|
#region Private Members
|
||||||
|
|
||||||
Guid CreateGuid(JoystickDevice<LinuxJoyDetails> js, string path, int number)
|
Guid CreateGuid(EvdevInputId id, string name)
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[16];
|
byte[] bytes = new byte[16];
|
||||||
for (int i = 0; i < Math.Min(bytes.Length, js.Description.Length); i++)
|
|
||||||
{
|
|
||||||
bytes[i] = (byte)js.Description[i];
|
|
||||||
}
|
|
||||||
return new Guid(bytes);
|
|
||||||
|
|
||||||
#if false // Todo: move to /dev/input/event* from /dev/input/js*
|
|
||||||
string evdev_path = Path.Combine(Path.GetDirectoryName(path), "event" + number);
|
|
||||||
if (!File.Exists(evdev_path))
|
|
||||||
return new Guid();
|
|
||||||
|
|
||||||
int event_fd = UnsafeNativeMethods.open(evdev_path, OpenFlags.NonBlock);
|
|
||||||
if (event_fd < 0)
|
|
||||||
return new Guid();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
EventInputId id;
|
|
||||||
if (UnsafeNativeMethods.ioctl(event_fd, EvdevInputId.Id, out id) < 0)
|
|
||||||
return new Guid();
|
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
byte[] bus = BitConverter.GetBytes(id.BusType);
|
byte[] bus = BitConverter.GetBytes(id.BusType);
|
||||||
|
|
@ -206,24 +182,98 @@ namespace OpenTK.Platform.Linux
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (; i < bytes.Length; i++)
|
for (int j = 0; j < bytes.Length - i; j++)
|
||||||
{
|
{
|
||||||
bytes[i] = (byte)js.Description[i];
|
bytes[i + j] = (byte)name[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Guid(bytes);
|
return new Guid(bytes);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
|
unsafe static bool TestBit(byte* ptr, int bit)
|
||||||
{
|
{
|
||||||
UnsafeNativeMethods.close(event_fd);
|
int byte_offset = bit / 8;
|
||||||
}
|
int bit_offset = bit % 8;
|
||||||
#endif
|
return (*(ptr + byte_offset) & (1 << bit_offset)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
JoystickDevice<LinuxJoyDetails> OpenJoystick(string path)
|
unsafe static int AddAxes(LinuxJoystickDetails stick, byte* axisbit, int bytecount)
|
||||||
{
|
{
|
||||||
JoystickDevice<LinuxJoyDetails> stick = null;
|
JoystickAxis axes = 0;
|
||||||
|
JoystickHat hats = 0;
|
||||||
|
int bitcount = bytecount * 8;
|
||||||
|
for (EvdevAxis axis = 0; axis < EvdevAxis.CNT && (int)axis < bitcount; axis++)
|
||||||
|
{
|
||||||
|
if (axis >= EvdevAxis.HAT0X && axis <= EvdevAxis.HAT3Y)
|
||||||
|
{
|
||||||
|
// Axis is analogue hat - skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TestBit(axisbit, (int)axis))
|
||||||
|
{
|
||||||
|
stick.AxisMap.Add(axis, axes++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stick.AxisMap.Add(axis, (JoystickAxis)(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int)axes;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe static int AddButtons(LinuxJoystickDetails stick, byte* keybit, int bytecount)
|
||||||
|
{
|
||||||
|
JoystickButton buttons = 0;
|
||||||
|
int bitcount = bytecount * 8;
|
||||||
|
for (EvdevButton button = 0; button < EvdevButton.Last && (int)button < bitcount; button++)
|
||||||
|
{
|
||||||
|
if (button >= EvdevButton.DPAD_UP && button <= EvdevButton.DPAD_RIGHT)
|
||||||
|
{
|
||||||
|
// Button is dpad (hat) - skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TestBit(keybit, (int)button))
|
||||||
|
{
|
||||||
|
stick.ButtonMap.Add(button, buttons++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stick.ButtonMap.Add(button, (JoystickButton)(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int)buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe static int AddHats(LinuxJoystickDetails stick,
|
||||||
|
byte* axisbit, int axiscount,
|
||||||
|
byte* keybit, int keycount)
|
||||||
|
{
|
||||||
|
JoystickHat hats = 0;
|
||||||
|
for (EvdevAxis hat = EvdevAxis.HAT0X; hat < EvdevAxis.HAT3Y && (int)hat < axiscount * 8; hat++)
|
||||||
|
{
|
||||||
|
if (TestBit(axisbit, (int)hat))
|
||||||
|
{
|
||||||
|
stick.HatMap.Add((int)hat, hats++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EvdevButton dpad = EvdevButton.DPAD_UP; dpad < EvdevButton.DPAD_RIGHT && (int)dpad < keycount * 8; dpad++)
|
||||||
|
{
|
||||||
|
if (TestBit(axisbit, (int)dpad))
|
||||||
|
{
|
||||||
|
stick.HatMap.Add((int)dpad, hats++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)hats;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxJoystickDetails OpenJoystick(string path)
|
||||||
|
{
|
||||||
|
LinuxJoystickDetails stick = null;
|
||||||
|
|
||||||
int number = GetJoystickNumber(Path.GetFileName(path));
|
int number = GetJoystickNumber(Path.GetFileName(path));
|
||||||
if (number >= 0)
|
if (number >= 0)
|
||||||
|
|
@ -235,121 +285,112 @@ namespace OpenTK.Platform.Linux
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Check joystick driver version (must be 1.0+)
|
unsafe
|
||||||
int driver_version = 0x00000800;
|
|
||||||
Libc.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
|
|
||||||
if (driver_version < 0x00010000)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Get number of joystick axes
|
|
||||||
int axes = 0;
|
|
||||||
Libc.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
|
|
||||||
|
|
||||||
// Get number of joystick buttons
|
|
||||||
int buttons = 0;
|
|
||||||
Libc.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
|
|
||||||
|
|
||||||
stick = new JoystickDevice<LinuxJoyDetails>(number, axes, buttons);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(128);
|
|
||||||
Libc.ioctl(fd, JoystickIoctlCode.Name128, sb);
|
|
||||||
stick.Description = sb.ToString();
|
|
||||||
|
|
||||||
stick.Details.FileDescriptor = fd;
|
|
||||||
stick.Details.State.SetIsConnected(true);
|
|
||||||
stick.Details.Guid = CreateGuid(stick, path, number);
|
|
||||||
|
|
||||||
// Find the first disconnected joystick (if any)
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < sticks.Count; i++)
|
|
||||||
{
|
{
|
||||||
if (!sticks[i].Details.State.IsConnected)
|
const int evsize = Evdev.EventCount / 8;
|
||||||
|
const int axissize = Evdev.AxisCount / 8;
|
||||||
|
const int keysize = Evdev.KeyCount / 8;
|
||||||
|
byte* evbit = stackalloc byte[evsize];
|
||||||
|
byte* axisbit = stackalloc byte[axissize];
|
||||||
|
byte* keybit = stackalloc byte[keysize];
|
||||||
|
|
||||||
|
string name;
|
||||||
|
EvdevInputId id;
|
||||||
|
|
||||||
|
// Ensure this is a joystick device
|
||||||
|
bool is_valid = true;
|
||||||
|
|
||||||
|
is_valid &= Evdev.GetBit(fd, 0, evsize, new IntPtr(evbit)) >= 0;
|
||||||
|
is_valid &= Evdev.GetBit(fd, EvdevType.ABS, axissize, new IntPtr(axisbit)) >= 0;
|
||||||
|
is_valid &= Evdev.GetBit(fd, EvdevType.KEY, keysize, new IntPtr(keybit)) >= 0;
|
||||||
|
|
||||||
|
is_valid &= TestBit(evbit, (int)EvdevType.KEY);
|
||||||
|
is_valid &= TestBit(evbit, (int)EvdevType.ABS);
|
||||||
|
is_valid &= TestBit(axisbit, (int)EvdevAxis.X);
|
||||||
|
is_valid &= TestBit(axisbit, (int)EvdevAxis.Y);
|
||||||
|
|
||||||
|
is_valid &= Evdev.GetName(fd, out name) >= 0;
|
||||||
|
is_valid &= Evdev.GetId(fd, out id) >= 0;
|
||||||
|
|
||||||
|
if (is_valid)
|
||||||
{
|
{
|
||||||
break;
|
stick = new LinuxJoystickDetails
|
||||||
|
{
|
||||||
|
FileDescriptor = fd,
|
||||||
|
PathIndex = number,
|
||||||
|
State = new JoystickState(),
|
||||||
|
Name = name,
|
||||||
|
Guid = CreateGuid(id, name),
|
||||||
|
};
|
||||||
|
stick.Caps = new JoystickCapabilities(
|
||||||
|
AddAxes(stick, axisbit, axissize),
|
||||||
|
AddButtons(stick, keybit, keysize),
|
||||||
|
AddHats(stick, axisbit, axissize, keybit, keysize),
|
||||||
|
true);
|
||||||
|
stick.State.SetIsConnected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Print("Error opening joystick: {0}", e.ToString());
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (stick == null && fd != -1)
|
if (stick == null && fd != -1)
|
||||||
|
{
|
||||||
|
// Not a joystick
|
||||||
Libc.close(fd);
|
Libc.close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return stick;
|
return stick;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseJoystick(JoystickDevice<LinuxJoyDetails> js)
|
void CloseJoystick(LinuxJoystickDetails js)
|
||||||
{
|
{
|
||||||
Libc.close(js.Details.FileDescriptor);
|
Sticks.Remove(js.FileDescriptor);
|
||||||
js.Details.State = new JoystickState(); // clear joystick state
|
|
||||||
js.Details.FileDescriptor = -1;
|
|
||||||
|
|
||||||
// find and remove the joystick index from index_to_stick
|
Libc.close(js.FileDescriptor);
|
||||||
int key = -1;
|
js.FileDescriptor = -1;
|
||||||
foreach (int i in index_to_stick.Keys)
|
js.State = new JoystickState(); // clear joystick state
|
||||||
{
|
js.Caps = new JoystickCapabilities();
|
||||||
if (sticks[index_to_stick[i]] == js)
|
|
||||||
{
|
|
||||||
key = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index_to_stick.ContainsKey(key))
|
void PollJoystick(LinuxJoystickDetails js)
|
||||||
{
|
{
|
||||||
index_to_stick.Remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PollJoystick(JoystickDevice<LinuxJoyDetails> js)
|
|
||||||
{
|
|
||||||
JoystickEvent e;
|
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
while ((long)Libc.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
|
const int EventCount = 32;
|
||||||
{
|
InputEvent* events = stackalloc InputEvent[EventCount];
|
||||||
e.Type &= ~JoystickEventType.Init;
|
|
||||||
|
|
||||||
switch (e.Type)
|
long length = 0;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
case JoystickEventType.Axis:
|
length = (long)Libc.read(js.FileDescriptor, (void*)events, (UIntPtr)(sizeof(InputEvent) * EventCount));
|
||||||
// Flip vertical axes so that +1 point up.
|
if (length <= 0)
|
||||||
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;
|
break;
|
||||||
|
|
||||||
case JoystickEventType.Button:
|
length /= sizeof(InputEvent);
|
||||||
js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0);
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
InputEvent *e = events + i;
|
||||||
|
switch (e->Type)
|
||||||
|
{
|
||||||
|
case EvdevType.ABS:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EvdevType.KEY:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
js.Details.State.SetPacketNumber(unchecked((int)e.Time));
|
//js.State.SetPacketNumber(unchecked((int)e->Time.Seconds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValid(int index)
|
|
||||||
{
|
|
||||||
return index_to_stick.ContainsKey(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly string JoystickPath = "/dev/input";
|
static readonly string JoystickPath = "/dev/input";
|
||||||
|
|
@ -374,7 +415,7 @@ namespace OpenTK.Platform.Linux
|
||||||
}
|
}
|
||||||
|
|
||||||
watcher.Dispose();
|
watcher.Dispose();
|
||||||
foreach (JoystickDevice<LinuxJoyDetails> js in sticks)
|
foreach (LinuxJoystickDetails js in Sticks)
|
||||||
{
|
{
|
||||||
CloseJoystick(js);
|
CloseJoystick(js);
|
||||||
}
|
}
|
||||||
|
|
@ -394,39 +435,33 @@ namespace OpenTK.Platform.Linux
|
||||||
|
|
||||||
JoystickState IJoystickDriver2.GetState(int index)
|
JoystickState IJoystickDriver2.GetState(int index)
|
||||||
{
|
{
|
||||||
if (IsValid(index))
|
LinuxJoystickDetails js = Sticks.FromIndex(index);
|
||||||
|
if (js != null)
|
||||||
{
|
{
|
||||||
JoystickDevice<LinuxJoyDetails> js =
|
|
||||||
sticks[index_to_stick[index]];
|
|
||||||
PollJoystick(js);
|
PollJoystick(js);
|
||||||
return js.Details.State;
|
return js.State;
|
||||||
}
|
}
|
||||||
return new JoystickState();
|
return new JoystickState();
|
||||||
}
|
}
|
||||||
|
|
||||||
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
|
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
|
||||||
{
|
{
|
||||||
JoystickCapabilities caps = new JoystickCapabilities();
|
LinuxJoystickDetails js = Sticks.FromIndex(index);
|
||||||
if (IsValid(index))
|
if (js != null)
|
||||||
{
|
{
|
||||||
JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
|
return js.Caps;
|
||||||
caps = new JoystickCapabilities(
|
|
||||||
js.Axis.Count,
|
|
||||||
js.Button.Count,
|
|
||||||
0, // hats not supported by /dev/js
|
|
||||||
js.Details.State.IsConnected);
|
|
||||||
}
|
}
|
||||||
return caps;
|
return new JoystickCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
Guid IJoystickDriver2.GetGuid(int index)
|
Guid IJoystickDriver2.GetGuid(int index)
|
||||||
{
|
{
|
||||||
if (IsValid(index))
|
LinuxJoystickDetails js = Sticks.FromIndex(index);
|
||||||
|
if (js != null)
|
||||||
{
|
{
|
||||||
JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
|
return js.Guid;
|
||||||
return js.Details.Guid;
|
|
||||||
}
|
}
|
||||||
return new Guid();
|
return Guid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue