Remove EvDevKeyInput. Pretty much useless in practice as apparently you need higher permissions (e.g. root) to access keyboard evdev files, direct evdev is more reserved for gamepads (already handled by SDL2)
Turn these OS specific key input impls into instantiable classes instead of a ton of static classes, a lot nicer to work with as don't need to constantly check CurrentOS to know which class to use, only the factory needs to check that!
This commit is contained in:
parent
62501ad859
commit
220268809c
|
@ -82,7 +82,7 @@ namespace BizHawk.Bizware.Input
|
|||
return VKeyToDKeyMap.GetValueOrDefault(virtualKey, DInputKey.Unknown);
|
||||
}
|
||||
|
||||
// DInputKey is just a scancode so it's used with RAWKeyInput
|
||||
// DInputKey is just a scancode so it's used with RawKeyInput
|
||||
/*private*/ internal static readonly IReadOnlyDictionary<DInputKey, DistinctKey> KeyEnumMap = new Dictionary<DInputKey, DistinctKey>
|
||||
{
|
||||
[DInputKey.D0] = DistinctKey.D0,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.IO.Pipes;
|
||||
using System.Threading;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
|
||||
|
@ -21,8 +21,7 @@ namespace BizHawk.Bizware.Input
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly List<KeyEvent> PendingEventList = new();
|
||||
private static readonly List<KeyEvent> EventList = new();
|
||||
private static readonly List<KeyEvent> PendingKeyEvents = new();
|
||||
private static bool IPCActive;
|
||||
|
||||
private static void IPCThread()
|
||||
|
@ -42,9 +41,9 @@ namespace BizHawk.Bizware.Input
|
|||
{
|
||||
var e = br.ReadUInt32();
|
||||
var pressed = (e & 0x80000000) != 0;
|
||||
lock (PendingEventList)
|
||||
lock (PendingKeyEvents)
|
||||
{
|
||||
PendingEventList.Add(new((DistinctKey)(e & 0x7FFFFFFF), pressed));
|
||||
PendingKeyEvents.Add(new((DistinctKey)(e & 0x7FFFFFFF), pressed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,15 +58,15 @@ namespace BizHawk.Bizware.Input
|
|||
|
||||
public static IEnumerable<KeyEvent> Update()
|
||||
{
|
||||
EventList.Clear();
|
||||
var keyEvents = new List<KeyEvent>();
|
||||
|
||||
lock (PendingEventList)
|
||||
lock (PendingKeyEvents)
|
||||
{
|
||||
EventList.AddRange(PendingEventList);
|
||||
PendingEventList.Clear();
|
||||
keyEvents.AddRange(PendingKeyEvents);
|
||||
PendingKeyEvents.Clear();
|
||||
}
|
||||
|
||||
return EventList;
|
||||
return keyEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal interface IKeyInput : IDisposable
|
||||
{
|
||||
IEnumerable<KeyEvent> Update(bool handleAltKbLayouts);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal static class KeyInputFactory
|
||||
{
|
||||
public static IKeyInput CreateKeyInput() => OSTailoredCode.CurrentOS switch
|
||||
{
|
||||
OSTailoredCode.DistinctOS.Linux => new X11KeyInput(),
|
||||
OSTailoredCode.DistinctOS.macOS => new QuartzKeyInput(),
|
||||
OSTailoredCode.DistinctOS.Windows => new RawKeyInput(),
|
||||
_ => throw new InvalidOperationException("Unknown OS"),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -9,49 +9,33 @@ using static BizHawk.Common.QuartzImports;
|
|||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal static class QuartzKeyInput
|
||||
internal sealed class QuartzKeyInput : IKeyInput
|
||||
{
|
||||
private static readonly bool[] LastKeyState = new bool[0x7F];
|
||||
private static readonly object _syncObject = new();
|
||||
private readonly bool[] LastKeyState = new bool[0x7F];
|
||||
|
||||
public static void Initialize()
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_syncObject)
|
||||
{
|
||||
Deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Deinitialize()
|
||||
public IEnumerable<KeyEvent> Update(bool handleAltKbLayouts)
|
||||
{
|
||||
lock (_syncObject)
|
||||
var keyEvents = new List<KeyEvent>();
|
||||
for (var keycode = 0; keycode < 0x7F; keycode++)
|
||||
{
|
||||
Array.Clear(LastKeyState, 0, LastKeyState.Length);
|
||||
}
|
||||
}
|
||||
var keystate = CGEventSourceKeyState(
|
||||
CGEventSourceStateID.kCGEventSourceStateHIDSystemState, (CGKeyCode)keycode);
|
||||
|
||||
public static IEnumerable<KeyEvent> Update()
|
||||
{
|
||||
lock (_syncObject)
|
||||
{
|
||||
var eventList = new List<KeyEvent>();
|
||||
for (var keycode = 0; keycode < 0x7F; keycode++)
|
||||
if (LastKeyState[keycode] != keystate)
|
||||
{
|
||||
var keystate = CGEventSourceKeyState(
|
||||
CGEventSourceStateID.kCGEventSourceStateHIDSystemState, (CGKeyCode)keycode);
|
||||
|
||||
if (LastKeyState[keycode] != keystate)
|
||||
if (KeyEnumMap.TryGetValue((CGKeyCode)keycode, out var key))
|
||||
{
|
||||
if (KeyEnumMap.TryGetValue((CGKeyCode)keycode, out var key))
|
||||
{
|
||||
eventList.Add(new(key, pressed: keystate));
|
||||
LastKeyState[keycode] = keystate;
|
||||
}
|
||||
keyEvents.Add(new(key, pressed: keystate));
|
||||
LastKeyState[keycode] = keystate;
|
||||
}
|
||||
}
|
||||
|
||||
return eventList;
|
||||
}
|
||||
|
||||
return keyEvents;
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyDictionary<CGKeyCode, DistinctKey> KeyEnumMap = new Dictionary<CGKeyCode, DistinctKey>
|
|
@ -0,0 +1,202 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
using static BizHawk.Common.RawInputImports;
|
||||
using static BizHawk.Common.Win32Imports;
|
||||
|
||||
using RawKey = Vortice.DirectInput.Key;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Note: Only 1 window per device class (i.e. keyboards) is actually allowed to RAWINPUT (last one to call RegisterRawInputDevices)
|
||||
/// So only one instance can actually be used at the same time
|
||||
/// </summary>
|
||||
internal sealed class RawKeyInput : IKeyInput
|
||||
{
|
||||
private const int WM_CLOSE = 0x0010;
|
||||
private const int WM_INPUT = 0x00FF;
|
||||
|
||||
private IntPtr RawInputWindow;
|
||||
private bool HandleAltKbLayouts;
|
||||
private List<KeyEvent> KeyEvents = new();
|
||||
private readonly object LockObj = new();
|
||||
|
||||
private static readonly WNDPROC _wndProc = WndProc;
|
||||
|
||||
private static readonly Lazy<IntPtr> _rawInputWindowAtom = new(() =>
|
||||
{
|
||||
var wc = default(WNDCLASS);
|
||||
wc.lpfnWndProc = _wndProc;
|
||||
wc.hInstance = GetModuleHandle(null);
|
||||
wc.lpszClassName = "RawKeyInputClass";
|
||||
|
||||
var atom = RegisterClass(ref wc);
|
||||
if (atom == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to register RAWINPUT window class");
|
||||
}
|
||||
|
||||
return atom;
|
||||
});
|
||||
|
||||
private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
var ud = GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
if (ud == IntPtr.Zero)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (uMsg != WM_INPUT)
|
||||
{
|
||||
if (uMsg == WM_CLOSE)
|
||||
{
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA, IntPtr.Zero);
|
||||
GCHandle.FromIntPtr(ud).Free();
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (GetRawInputData(lParam, RID.INPUT, IntPtr.Zero,
|
||||
out var size, Marshal.SizeOf<RAWINPUTHEADER>()) == -1)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
// don't think size should ever be this big, but just in case
|
||||
var buffer = size > 1024
|
||||
? new byte[size]
|
||||
: stackalloc byte[size];
|
||||
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
var input = (RAWINPUT*)p;
|
||||
|
||||
if (GetRawInputData(lParam, RID.INPUT, input,
|
||||
ref size, Marshal.SizeOf<RAWINPUTHEADER>()) == -1)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD && input->data.keyboard.Flags <= RAWKEYBOARD.RIM_KEY.E1)
|
||||
{
|
||||
var handle = GCHandle.FromIntPtr(ud);
|
||||
var rawKeyInput = (RawKeyInput)handle.Target;
|
||||
|
||||
lock (rawKeyInput.LockObj)
|
||||
{
|
||||
// TODO: Make a separate enum map for RAWINPUT / VKeys and don't rely on DKeyInput's maps
|
||||
var rawKey = rawKeyInput.HandleAltKbLayouts
|
||||
? DKeyInput.VKeyToDKeyMap.GetValueOrDefault(input->data.keyboard.VKey, RawKey.Unknown)
|
||||
: (RawKey)(input->data.keyboard.MakeCode |
|
||||
(input->data.keyboard.Flags >= RAWKEYBOARD.RIM_KEY.E0 ? 0x80 : 0));
|
||||
|
||||
if (DKeyInput.KeyEnumMap.TryGetValue(rawKey, out var key) && key != DistinctKey.Unknown)
|
||||
{
|
||||
rawKeyInput.KeyEvents.Add(new(key, input->data.keyboard.Flags is RAWKEYBOARD.RIM_KEY.MAKE or RAWKEYBOARD.RIM_KEY.E0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefRawInputProc(input, 0, Marshal.SizeOf<RAWINPUTHEADER>());
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr CreateRawInputWindow()
|
||||
{
|
||||
const int WS_CHILD = 0x40000000;
|
||||
var window = CreateWindowEx(
|
||||
dwExStyle: 0,
|
||||
lpClassName: _rawInputWindowAtom.Value,
|
||||
lpWindowName: "RawKeyInput",
|
||||
dwStyle: WS_CHILD,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
nWidth: 1,
|
||||
nHeight: 1,
|
||||
hWndParent: HWND_MESSAGE,
|
||||
hMenu: IntPtr.Zero,
|
||||
hInstance: GetModuleHandle(null),
|
||||
lpParam: IntPtr.Zero);
|
||||
|
||||
if (window == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create RAWINPUT window");
|
||||
}
|
||||
|
||||
var rid = default(RAWINPUTDEVICE);
|
||||
rid.usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
|
||||
rid.usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD;
|
||||
rid.dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
|
||||
rid.hwndTarget = window;
|
||||
|
||||
if (!RegisterRawInputDevices(ref rid, 1, Marshal.SizeOf<RAWINPUTDEVICE>()))
|
||||
{
|
||||
DestroyWindow(window);
|
||||
throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public RawKeyInput()
|
||||
{
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
throw new NotSupportedException("RAWINPUT is Windows only");
|
||||
}
|
||||
|
||||
// we can't use a window created on this thread, as Update is called on a different thread
|
||||
// but we can still test window creation
|
||||
var testWindow = CreateRawInputWindow(); // this will throw if window creation or rawinput registering fails
|
||||
DestroyWindow(testWindow);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (LockObj)
|
||||
{
|
||||
if (RawInputWindow != IntPtr.Zero)
|
||||
{
|
||||
// Can't use DestroyWindow, that's only allowed in the thread that created the window!
|
||||
PostMessage(RawInputWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
|
||||
RawInputWindow = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<KeyEvent> Update(bool handleAltKbLayouts)
|
||||
{
|
||||
lock (LockObj)
|
||||
{
|
||||
if (RawInputWindow == IntPtr.Zero)
|
||||
{
|
||||
RawInputWindow = CreateRawInputWindow();
|
||||
var handle = GCHandle.Alloc(this, GCHandleType.Normal);
|
||||
SetWindowLongPtr(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
|
||||
}
|
||||
|
||||
HandleAltKbLayouts = handleAltKbLayouts;
|
||||
|
||||
while (PeekMessage(out var msg, RawInputWindow, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(ref msg);
|
||||
DispatchMessage(ref msg);
|
||||
}
|
||||
|
||||
var ret = KeyEvents;
|
||||
KeyEvents = new();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
using static BizHawk.Common.XlibImports;
|
||||
|
@ -12,47 +14,46 @@ using static BizHawk.Common.XlibImports;
|
|||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal static class X11KeyInput
|
||||
internal sealed class X11KeyInput : IKeyInput
|
||||
{
|
||||
private static IntPtr Display;
|
||||
private static readonly DistinctKey[] KeyEnumMap = new DistinctKey[256];
|
||||
private static readonly bool[] LastKeyState = new bool[256];
|
||||
private IntPtr Display;
|
||||
private readonly bool[] LastKeyState = new bool[256];
|
||||
private readonly object LockObj = new();
|
||||
private readonly DistinctKey[] KeyEnumMap = new DistinctKey[256];
|
||||
|
||||
private static readonly object _syncObject = new();
|
||||
|
||||
public static void Initialize()
|
||||
public X11KeyInput()
|
||||
{
|
||||
lock (_syncObject)
|
||||
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Linux)
|
||||
{
|
||||
Deinitialize();
|
||||
throw new NotSupportedException("X11 is Linux only");
|
||||
}
|
||||
|
||||
Display = XOpenDisplay(null);
|
||||
Display = XOpenDisplay(null);
|
||||
|
||||
if (Display == IntPtr.Zero)
|
||||
if (Display == IntPtr.Zero)
|
||||
{
|
||||
throw new("Could not open XDisplay");
|
||||
}
|
||||
|
||||
using (new XLock(Display))
|
||||
{
|
||||
// check if we can use XKb
|
||||
int major = 1, minor = 0;
|
||||
var supportsXkb = XkbQueryExtension(Display, out _, out _, out _, ref major, ref minor);
|
||||
|
||||
if (supportsXkb)
|
||||
{
|
||||
throw new("Could not open XDisplay");
|
||||
// we generally want this behavior
|
||||
XkbSetDetectableAutoRepeat(Display, true, out _);
|
||||
}
|
||||
|
||||
using (new XLock(Display))
|
||||
{
|
||||
// check if we can use XKb
|
||||
int major = 1, minor = 0;
|
||||
var supportsXkb = XkbQueryExtension(Display, out _, out _, out _, ref major, ref minor);
|
||||
|
||||
if (supportsXkb)
|
||||
{
|
||||
// we generally want this behavior
|
||||
XkbSetDetectableAutoRepeat(Display, true, out _);
|
||||
}
|
||||
|
||||
CreateKeyMap(supportsXkb);
|
||||
}
|
||||
CreateKeyMap(supportsXkb);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Deinitialize()
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_syncObject)
|
||||
lock (LockObj)
|
||||
{
|
||||
if (Display != IntPtr.Zero)
|
||||
{
|
||||
|
@ -62,9 +63,9 @@ namespace BizHawk.Bizware.Input
|
|||
}
|
||||
}
|
||||
|
||||
public static unsafe IEnumerable<KeyEvent> Update()
|
||||
public unsafe IEnumerable<KeyEvent> Update(bool handleAltKbLayouts)
|
||||
{
|
||||
lock (_syncObject)
|
||||
lock (LockObj)
|
||||
{
|
||||
// Can't update without a display connection
|
||||
if (Display == IntPtr.Zero)
|
||||
|
@ -80,23 +81,26 @@ namespace BizHawk.Bizware.Input
|
|||
_ = XQueryKeymap(Display, keys);
|
||||
}
|
||||
|
||||
var eventList = new List<KeyEvent>();
|
||||
var keyEvents = new List<KeyEvent>();
|
||||
for (var keycode = 0; keycode < 256; keycode++)
|
||||
{
|
||||
var keystate = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0;
|
||||
|
||||
if (LastKeyState[keycode] != keystate)
|
||||
var key = KeyEnumMap[keycode];
|
||||
if (key != DistinctKey.Unknown)
|
||||
{
|
||||
eventList.Add(new(KeyEnumMap[keycode], pressed: keystate));
|
||||
LastKeyState[keycode] = keystate;
|
||||
var keystate = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0;
|
||||
if (LastKeyState[keycode] != keystate)
|
||||
{
|
||||
keyEvents.Add(new(key, pressed: keystate));
|
||||
LastKeyState[keycode] = keystate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventList;
|
||||
return keyEvents;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void CreateKeyMap(bool supportsXkb)
|
||||
private unsafe void CreateKeyMap(bool supportsXkb)
|
||||
{
|
||||
for (var i = 0; i < KeyEnumMap.Length; i++)
|
||||
{
|
|
@ -1,74 +1,32 @@
|
|||
#nullable enable
|
||||
|
||||
// #define USE_EVDEV
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract class which only handles keyboard input
|
||||
/// Uses OS specific functionality, as there is no good cross platform way to do this
|
||||
/// (mostly as all the available cross-platform options require a focused window, arg!)
|
||||
/// TODO: Doesn't work for Wayland yet (maybe Linux should just use evdev here)
|
||||
/// TODO: Stop using a ton of static classes here, checking CurrentOS constantly is annoying...
|
||||
/// (Mostly as all the available cross-platform options require a focused window, arg!)
|
||||
/// TODO: Doesn't work for Wayland yet (well, XWayland probably works fine? Probably want libinput support regardless)
|
||||
/// </summary>
|
||||
public abstract class OSTailoredKeyInputAdapter : IHostInputAdapter
|
||||
{
|
||||
private IKeyInput? _keyInput;
|
||||
protected Config? _config;
|
||||
|
||||
public abstract string Desc { get; }
|
||||
|
||||
public virtual void DeInitAll()
|
||||
{
|
||||
switch (OSTailoredCode.CurrentOS)
|
||||
{
|
||||
case OSTailoredCode.DistinctOS.Linux:
|
||||
#if USE_EVDEV
|
||||
EvDevKeyInput.Deinitialize();
|
||||
#else
|
||||
X11KeyInput.Deinitialize();
|
||||
#endif
|
||||
break;
|
||||
case OSTailoredCode.DistinctOS.macOS:
|
||||
QuartzKeyInput.Deinitialize();
|
||||
break;
|
||||
case OSTailoredCode.DistinctOS.Windows:
|
||||
RAWKeyInput.Deinitialize();
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
=> _keyInput!.Dispose();
|
||||
|
||||
public virtual void FirstInitAll(IntPtr mainFormHandle)
|
||||
{
|
||||
switch (OSTailoredCode.CurrentOS)
|
||||
{
|
||||
case OSTailoredCode.DistinctOS.Linux:
|
||||
// TODO: probably need a libinput option for Wayland
|
||||
// (unless we just want to ditch this and always use evdev here?)
|
||||
#if USE_EVDEV
|
||||
EvDevKeyInput.Deinitialize();
|
||||
#else
|
||||
X11KeyInput.Initialize();
|
||||
#endif
|
||||
break;
|
||||
case OSTailoredCode.DistinctOS.macOS:
|
||||
QuartzKeyInput.Initialize();
|
||||
break;
|
||||
case OSTailoredCode.DistinctOS.Windows:
|
||||
RAWKeyInput.Initialize();
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_keyInput = KeyInputFactory.CreateKeyInput();
|
||||
IPCKeyInput.Initialize(); // why not? this isn't necessarily OS specific
|
||||
}
|
||||
|
||||
|
@ -82,18 +40,12 @@ namespace BizHawk.Bizware.Input
|
|||
|
||||
public virtual IEnumerable<KeyEvent> ProcessHostKeyboards()
|
||||
{
|
||||
var ret = OSTailoredCode.CurrentOS switch
|
||||
if (_config is null)
|
||||
{
|
||||
#if USE_EVDEV
|
||||
OSTailoredCode.DistinctOS.Linux => EvDevKeyInput.Update(),
|
||||
#else
|
||||
OSTailoredCode.DistinctOS.Linux => X11KeyInput.Update(),
|
||||
#endif
|
||||
OSTailoredCode.DistinctOS.macOS => QuartzKeyInput.Update(),
|
||||
OSTailoredCode.DistinctOS.Windows => RAWKeyInput.Update(_config ?? throw new(nameof(ProcessHostKeyboards) + " called before the global config was passed")),
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
throw new InvalidOperationException(nameof(ProcessHostKeyboards) + " called before the global config was passed");
|
||||
}
|
||||
|
||||
var ret = _keyInput!.Update(_config.HandleAlternateKeyboardLayouts);
|
||||
return ret.Concat(IPCKeyInput.Update());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
using static BizHawk.Common.RawInputImports;
|
||||
using static BizHawk.Common.Win32Imports;
|
||||
|
||||
using RAWKey = Vortice.DirectInput.Key;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal static class RAWKeyInput
|
||||
{
|
||||
private static volatile bool _isInit;
|
||||
private static IntPtr _rawInputWindowAtom;
|
||||
private static IntPtr _rawInputWindow;
|
||||
private static bool _handleAlternativeKeyboardLayouts;
|
||||
private static List<KeyEvent> _keyEvents = new();
|
||||
|
||||
private static readonly WNDPROC _wndProc = WndProc;
|
||||
private static readonly object _lockObj = new();
|
||||
|
||||
private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
const uint WM_INPUT = 0x00FF;
|
||||
|
||||
if (uMsg != WM_INPUT)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (GetRawInputData(lParam, RID.INPUT, IntPtr.Zero,
|
||||
out var size, Marshal.SizeOf<RAWINPUTHEADER>()) == -1)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
// don't think size should ever be this big, but just in case
|
||||
var buffer = size > 1024
|
||||
? new byte[size]
|
||||
: stackalloc byte[size];
|
||||
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
var input = (RAWINPUT*)p;
|
||||
|
||||
if (GetRawInputData(lParam, RID.INPUT, input,
|
||||
ref size, Marshal.SizeOf<RAWINPUTHEADER>()) == -1)
|
||||
{
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD && input->data.keyboard.Flags <= RAWKEYBOARD.RIM_KEY.E1)
|
||||
{
|
||||
var rawKey = _handleAlternativeKeyboardLayouts
|
||||
? DKeyInput.VKeyToDKeyMap.GetValueOrDefault(input->data.keyboard.VKey, RAWKey.Unknown)
|
||||
: (RAWKey)(input->data.keyboard.MakeCode |
|
||||
(input->data.keyboard.Flags >= RAWKEYBOARD.RIM_KEY.E0 ? 0x80 : 0));
|
||||
|
||||
if (DKeyInput.KeyEnumMap.TryGetValue(rawKey, out var key) && key != DistinctKey.Unknown)
|
||||
{
|
||||
_keyEvents.Add(new(key, input->data.keyboard.Flags is RAWKEYBOARD.RIM_KEY.MAKE or RAWKEYBOARD.RIM_KEY.E0));
|
||||
}
|
||||
}
|
||||
|
||||
return DefRawInputProc(input, 0, Marshal.SizeOf<RAWINPUTHEADER>());
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateRawInputWindow()
|
||||
{
|
||||
const int WS_CHILD = 0x40000000;
|
||||
var window = CreateWindowEx(
|
||||
dwExStyle: 0,
|
||||
lpClassName: _rawInputWindowAtom,
|
||||
lpWindowName: "RAWKeyInput",
|
||||
dwStyle: WS_CHILD,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
nWidth: 1,
|
||||
nHeight: 1,
|
||||
hWndParent: HWND_MESSAGE,
|
||||
hMenu: IntPtr.Zero,
|
||||
hInstance: GetModuleHandle(null),
|
||||
lpParam: IntPtr.Zero);
|
||||
|
||||
if (window == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create RAWINPUT window");
|
||||
}
|
||||
|
||||
var ri = new RAWINPUTDEVICE[1];
|
||||
ri[0].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
|
||||
ri[0].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD;
|
||||
ri[0].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
|
||||
ri[0].hwndTarget = window;
|
||||
|
||||
if (!RegisterRawInputDevices(ri, 1, Marshal.SizeOf<RAWINPUTDEVICE>()))
|
||||
{
|
||||
DestroyWindow(window);
|
||||
throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
|
||||
}
|
||||
|
||||
_rawInputWindow = window;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
throw new NotSupportedException("RAWINPUT is Windows only");
|
||||
}
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
Deinitialize();
|
||||
|
||||
if (_rawInputWindowAtom == IntPtr.Zero)
|
||||
{
|
||||
var wc = default(WNDCLASS);
|
||||
wc.lpfnWndProc = _wndProc;
|
||||
wc.hInstance = GetModuleHandle(null);
|
||||
wc.lpszClassName = "RAWKeyInputClass";
|
||||
|
||||
_rawInputWindowAtom = RegisterClass(ref wc);
|
||||
if (_rawInputWindowAtom == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to register RAWINPUT window class");
|
||||
}
|
||||
|
||||
// we can't use a window created on this thread, as Update is called on a different thread
|
||||
// but we can still test window creation
|
||||
CreateRawInputWindow(); // this will throw if window creation or rawinput registering fails
|
||||
DestroyWindow(_rawInputWindow);
|
||||
_rawInputWindow = IntPtr.Zero;
|
||||
|
||||
_isInit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Deinitialize()
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (_rawInputWindow != IntPtr.Zero)
|
||||
{
|
||||
// Can't use DestroyWindow, that's only allowed in the thread that created the window!
|
||||
const int WM_CLOSE = 0x0010;
|
||||
PostMessage(_rawInputWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
|
||||
_rawInputWindow = IntPtr.Zero;
|
||||
}
|
||||
|
||||
_keyEvents.Clear();
|
||||
_isInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyEvent> Update(Config config)
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (!_isInit)
|
||||
{
|
||||
return Enumerable.Empty<KeyEvent>();
|
||||
}
|
||||
|
||||
if (_rawInputWindow == IntPtr.Zero)
|
||||
{
|
||||
CreateRawInputWindow();
|
||||
}
|
||||
|
||||
_handleAlternativeKeyboardLayouts = config.HandleAlternateKeyboardLayouts;
|
||||
|
||||
while (PeekMessage(out var msg, _rawInputWindow, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(ref msg);
|
||||
DispatchMessage(ref msg);
|
||||
}
|
||||
|
||||
var ret = _keyEvents;
|
||||
_keyEvents = new();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,474 +0,0 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
using static BizHawk.Common.EvDevImports;
|
||||
using static BizHawk.Common.FildesImports;
|
||||
|
||||
namespace BizHawk.Bizware.Input
|
||||
{
|
||||
internal static class EvDevKeyInput
|
||||
{
|
||||
private sealed record EvDevKeyboard(
|
||||
uint DriverVersion,
|
||||
ushort IdBus,
|
||||
ushort IdVendor,
|
||||
ushort IdProduct,
|
||||
ushort IdVersion,
|
||||
string Name,
|
||||
int Fd,
|
||||
string Path) : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
=> _ = close(Fd);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var verMajor = DriverVersion >> 16;
|
||||
var verMinor = DriverVersion >> 8 & 0xFF;
|
||||
var varRev = DriverVersion & 0xFF;
|
||||
return $"{Name} ({verMajor}.{verMinor}.{varRev} {IdBus:X4}/{IdVendor:X4}/{IdProduct:X4}/{IdVersion:X4})";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool _isInit;
|
||||
private static FileSystemWatcher? _fileSystemWatcher;
|
||||
private static readonly Dictionary<string, EvDevKeyboard> _keyboards = new();
|
||||
|
||||
private static readonly object _lockObj = new();
|
||||
|
||||
private static List<int> DecodeBits(ReadOnlySpan<byte> bits)
|
||||
{
|
||||
var result = new List<int>(bits.Length * 8);
|
||||
for (var i = 0; i < bits.Length; i++)
|
||||
{
|
||||
var b = bits[i];
|
||||
var bitPos = i * 8;
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
if (b.Bit(j))
|
||||
{
|
||||
result.Add(bitPos + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static unsafe void MaybeAddKeyboard(string path)
|
||||
{
|
||||
if (_keyboards.ContainsKey(path))
|
||||
{
|
||||
// already have this, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
var fd = open(path, OpenFlags.O_RDONLY | OpenFlags.O_NONBLOCK | OpenFlags.O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var version = 0u;
|
||||
var id = stackalloc ushort[4];
|
||||
var str = stackalloc byte[256];
|
||||
new Span<ushort>(id, 4).Clear();
|
||||
new Span<byte>(str, 256).Clear();
|
||||
|
||||
// if any of these fail, the device was either removed or garbage
|
||||
if (ioctl(fd, new(EVIOCGVERSION), &version) == -1 ||
|
||||
ioctl(fd, new(EVIOCGID), id) == -1 ||
|
||||
ioctl(fd, new(EVIOCGNAME(256)), str) == -1)
|
||||
{
|
||||
_ = close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
str[255] = 0; // not trusting this remains nul terminated
|
||||
var name = Marshal.PtrToStringAnsi(new(str))!;
|
||||
|
||||
const int eventBitBufferSize = (int)EvDevEventType.EV_MAX / 8 + 1;
|
||||
var eventBits = stackalloc byte[eventBitBufferSize];
|
||||
new Span<byte>(eventBits, eventBitBufferSize).Clear();
|
||||
if (ioctl(fd, new(EVIOCGBIT(EvDevEventType.EV_SYN, (int)EvDevEventType.EV_MAX)), eventBits) == -1)
|
||||
{
|
||||
_ = close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
var supportedEvents = DecodeBits(new(eventBits, eventBitBufferSize));
|
||||
if (!supportedEvents.Contains((int)EvDevEventType.EV_KEY))
|
||||
{
|
||||
// we only care about keyboards
|
||||
_ = close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
const int keyBitBufferSize = (int)EvDevKeyCode.KEY_MAX / 8 + 1;
|
||||
var keyBits = stackalloc byte[keyBitBufferSize];
|
||||
new Span<byte>(keyBits, keyBitBufferSize).Clear();
|
||||
if (ioctl(fd, new(EVIOCGBIT(EvDevEventType.EV_KEY, (int)EvDevKeyCode.KEY_MAX)), keyBits) == -1)
|
||||
{
|
||||
_ = close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
var supportedKeys = DecodeBits(new(keyBits, keyBitBufferSize));
|
||||
if (supportedKeys.Count == 0)
|
||||
{
|
||||
// probably garbage
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.IndexOf("keyboard", StringComparison.InvariantCultureIgnoreCase) == -1)
|
||||
{
|
||||
// probably not be a keyboard
|
||||
// TODO: do some better heuristics here (maybe check if supportedKeys has A-Z?)
|
||||
return;
|
||||
}
|
||||
|
||||
var keyboard = new EvDevKeyboard(
|
||||
DriverVersion: version,
|
||||
IdBus: id[0],
|
||||
IdProduct: id[1],
|
||||
IdVendor: id[2],
|
||||
IdVersion: id[3],
|
||||
Name: name,
|
||||
Fd: fd,
|
||||
Path: path
|
||||
);
|
||||
|
||||
Console.WriteLine($"Added keyboard {keyboard}");
|
||||
_keyboards.Add(path, keyboard);
|
||||
}
|
||||
|
||||
private static void MaybeRemoveKeyboard(string path)
|
||||
{
|
||||
if (_keyboards.TryGetValue(path, out var keyboard))
|
||||
{
|
||||
Console.WriteLine($"Removed keyboard {keyboard}");
|
||||
keyboard.Dispose();
|
||||
_keyboards.Remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnWatcherEvent(object _, FileSystemEventArgs e)
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (!_isInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
|
||||
switch (e.ChangeType)
|
||||
{
|
||||
case WatcherChangeTypes.Created:
|
||||
case WatcherChangeTypes.Changed:
|
||||
MaybeAddKeyboard(e.FullPath);
|
||||
break;
|
||||
case WatcherChangeTypes.Deleted:
|
||||
MaybeRemoveKeyboard(e.FullPath);
|
||||
break;
|
||||
default:
|
||||
Debug.WriteLine($"Unexpected watcher event {e.ChangeType}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Linux)
|
||||
{
|
||||
// also supported in BSDs, I think OSTailoredCode.CurrentOS is Linux for BSDs?
|
||||
throw new NotSupportedException("evdev is Linux only");
|
||||
}
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
Deinitialize();
|
||||
|
||||
_fileSystemWatcher = new("/dev/input/", "event*")
|
||||
{
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.Attributes,
|
||||
};
|
||||
|
||||
_fileSystemWatcher.Created += OnWatcherEvent;
|
||||
_fileSystemWatcher.Changed += OnWatcherEvent;
|
||||
_fileSystemWatcher.Deleted += OnWatcherEvent;
|
||||
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||
|
||||
var evFns = Directory.GetFiles("/dev/input/", "event*");
|
||||
foreach (var fn in evFns)
|
||||
{
|
||||
MaybeAddKeyboard(fn);
|
||||
}
|
||||
|
||||
_isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Deinitialize()
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
_fileSystemWatcher?.Dispose();
|
||||
_fileSystemWatcher = null;
|
||||
|
||||
foreach (var keyboard in _keyboards.Values)
|
||||
{
|
||||
keyboard.Dispose();
|
||||
}
|
||||
|
||||
_keyboards.Clear();
|
||||
_isInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyEvent> Update()
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (!_isInit)
|
||||
{
|
||||
return Enumerable.Empty<KeyEvent>();
|
||||
}
|
||||
|
||||
var kbEvent = default(EvDevKeyboardEvent);
|
||||
var kbEventSize = (IntPtr)Marshal.SizeOf<EvDevKeyboardEvent>();
|
||||
var kbsToClose = new List<string>();
|
||||
var keyEvents = new List<KeyEvent>();
|
||||
|
||||
foreach (var keyboard in _keyboards.Values)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var res = read(keyboard.Fd, &kbEvent, kbEventSize);
|
||||
if (res == (IntPtr)(-1))
|
||||
{
|
||||
// this is actually errno, despite the Win32 name
|
||||
var errno = Marshal.GetLastWin32Error();
|
||||
|
||||
const int EAGAIN = 11;
|
||||
const int ENODEV = 19;
|
||||
|
||||
// EAGAIN means there's no more events left to read (generally expected)
|
||||
if (errno == EAGAIN)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// ENODEV means the device is gone
|
||||
if (errno == ENODEV)
|
||||
{
|
||||
// can't remove the kbs while iterating them!
|
||||
kbsToClose.Add(keyboard.Path);
|
||||
break;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Unexpected error reading keyboards: {errno}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (res != kbEventSize)
|
||||
{
|
||||
Debug.WriteLine("Unexpected incomplete read");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (kbEvent.type != EvDevEventType.EV_KEY)
|
||||
{
|
||||
// don't care for non-EV_KEY events
|
||||
continue;
|
||||
}
|
||||
|
||||
if (KeyEnumMap.TryGetValue(kbEvent.code, out var key))
|
||||
{
|
||||
switch (kbEvent.value)
|
||||
{
|
||||
case EvDevKeyValue.KeyUp:
|
||||
keyEvents.Add(new(key, pressed: false));
|
||||
break;
|
||||
case EvDevKeyValue.KeyDown:
|
||||
keyEvents.Add(new(key, pressed: true));
|
||||
break;
|
||||
case EvDevKeyValue.KeyRepeat:
|
||||
// should this be considered a press event?
|
||||
// probably not particularly useful to do so
|
||||
break;
|
||||
default:
|
||||
Debug.WriteLine($"Unexpected event value {kbEvent.value}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var path in kbsToClose)
|
||||
{
|
||||
MaybeRemoveKeyboard(path);
|
||||
}
|
||||
|
||||
return keyEvents;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<EvDevKeyCode, DistinctKey> KeyEnumMap = new()
|
||||
{
|
||||
[EvDevKeyCode.KEY_ESC] = DistinctKey.Escape,
|
||||
[EvDevKeyCode.KEY_1] = DistinctKey.D1,
|
||||
[EvDevKeyCode.KEY_2] = DistinctKey.D2,
|
||||
[EvDevKeyCode.KEY_3] = DistinctKey.D3,
|
||||
[EvDevKeyCode.KEY_4] = DistinctKey.D4,
|
||||
[EvDevKeyCode.KEY_5] = DistinctKey.D5,
|
||||
[EvDevKeyCode.KEY_6] = DistinctKey.D6,
|
||||
[EvDevKeyCode.KEY_7] = DistinctKey.D7,
|
||||
[EvDevKeyCode.KEY_8] = DistinctKey.D8,
|
||||
[EvDevKeyCode.KEY_9] = DistinctKey.D9,
|
||||
[EvDevKeyCode.KEY_0] = DistinctKey.D0,
|
||||
[EvDevKeyCode.KEY_MINUS] = DistinctKey.OemMinus,
|
||||
[EvDevKeyCode.KEY_EQUAL] = DistinctKey.OemPlus,
|
||||
[EvDevKeyCode.KEY_BACKSPACE] = DistinctKey.Back,
|
||||
[EvDevKeyCode.KEY_TAB] = DistinctKey.Tab,
|
||||
[EvDevKeyCode.KEY_Q] = DistinctKey.Q,
|
||||
[EvDevKeyCode.KEY_W] = DistinctKey.W,
|
||||
[EvDevKeyCode.KEY_E] = DistinctKey.E,
|
||||
[EvDevKeyCode.KEY_R] = DistinctKey.R,
|
||||
[EvDevKeyCode.KEY_T] = DistinctKey.T,
|
||||
[EvDevKeyCode.KEY_Y] = DistinctKey.Y,
|
||||
[EvDevKeyCode.KEY_U] = DistinctKey.U,
|
||||
[EvDevKeyCode.KEY_I] = DistinctKey.I,
|
||||
[EvDevKeyCode.KEY_O] = DistinctKey.O,
|
||||
[EvDevKeyCode.KEY_P] = DistinctKey.P,
|
||||
[EvDevKeyCode.KEY_LEFTBRACE] = DistinctKey.OemOpenBrackets,
|
||||
[EvDevKeyCode.KEY_RIGHTBRACE] = DistinctKey.OemCloseBrackets,
|
||||
[EvDevKeyCode.KEY_ENTER] = DistinctKey.Enter,
|
||||
[EvDevKeyCode.KEY_LEFTCTRL] = DistinctKey.LeftCtrl,
|
||||
[EvDevKeyCode.KEY_A] = DistinctKey.A,
|
||||
[EvDevKeyCode.KEY_S] = DistinctKey.S,
|
||||
[EvDevKeyCode.KEY_D] = DistinctKey.D,
|
||||
[EvDevKeyCode.KEY_F] = DistinctKey.F,
|
||||
[EvDevKeyCode.KEY_G] = DistinctKey.G,
|
||||
[EvDevKeyCode.KEY_H] = DistinctKey.H,
|
||||
[EvDevKeyCode.KEY_J] = DistinctKey.J,
|
||||
[EvDevKeyCode.KEY_K] = DistinctKey.K,
|
||||
[EvDevKeyCode.KEY_L] = DistinctKey.L,
|
||||
[EvDevKeyCode.KEY_SEMICOLON] = DistinctKey.OemSemicolon,
|
||||
[EvDevKeyCode.KEY_APOSTROPHE] = DistinctKey.OemQuotes,
|
||||
[EvDevKeyCode.KEY_GRAVE] = DistinctKey.OemTilde,
|
||||
[EvDevKeyCode.KEY_LEFTSHIFT] = DistinctKey.LeftShift,
|
||||
[EvDevKeyCode.KEY_BACKSLASH] = DistinctKey.OemBackslash,
|
||||
[EvDevKeyCode.KEY_Z] = DistinctKey.Z,
|
||||
[EvDevKeyCode.KEY_X] = DistinctKey.X,
|
||||
[EvDevKeyCode.KEY_C] = DistinctKey.C,
|
||||
[EvDevKeyCode.KEY_V] = DistinctKey.V,
|
||||
[EvDevKeyCode.KEY_B] = DistinctKey.B,
|
||||
[EvDevKeyCode.KEY_N] = DistinctKey.N,
|
||||
[EvDevKeyCode.KEY_M] = DistinctKey.M,
|
||||
[EvDevKeyCode.KEY_COMMA] = DistinctKey.OemComma,
|
||||
[EvDevKeyCode.KEY_DOT] = DistinctKey.OemPeriod,
|
||||
[EvDevKeyCode.KEY_SLASH] = DistinctKey.OemQuestion,
|
||||
[EvDevKeyCode.KEY_RIGHTSHIFT] = DistinctKey.RightShift,
|
||||
[EvDevKeyCode.KEY_KPASTERISK] = DistinctKey.Multiply,
|
||||
[EvDevKeyCode.KEY_LEFTALT] = DistinctKey.LeftAlt,
|
||||
[EvDevKeyCode.KEY_SPACE] = DistinctKey.Space,
|
||||
[EvDevKeyCode.KEY_CAPSLOCK] = DistinctKey.CapsLock,
|
||||
[EvDevKeyCode.KEY_F1] = DistinctKey.F1,
|
||||
[EvDevKeyCode.KEY_F2] = DistinctKey.F2,
|
||||
[EvDevKeyCode.KEY_F3] = DistinctKey.F3,
|
||||
[EvDevKeyCode.KEY_F4] = DistinctKey.F4,
|
||||
[EvDevKeyCode.KEY_F5] = DistinctKey.F5,
|
||||
[EvDevKeyCode.KEY_F6] = DistinctKey.F6,
|
||||
[EvDevKeyCode.KEY_F7] = DistinctKey.F7,
|
||||
[EvDevKeyCode.KEY_F8] = DistinctKey.F8,
|
||||
[EvDevKeyCode.KEY_F9] = DistinctKey.F9,
|
||||
[EvDevKeyCode.KEY_F10] = DistinctKey.F10,
|
||||
[EvDevKeyCode.KEY_NUMLOCK] = DistinctKey.NumLock,
|
||||
[EvDevKeyCode.KEY_SCROLLLOCK] = DistinctKey.Scroll,
|
||||
[EvDevKeyCode.KEY_KP7] = DistinctKey.NumPad7,
|
||||
[EvDevKeyCode.KEY_KP8] = DistinctKey.NumPad8,
|
||||
[EvDevKeyCode.KEY_KP9] = DistinctKey.NumPad9,
|
||||
[EvDevKeyCode.KEY_KPMINUS] = DistinctKey.Subtract,
|
||||
[EvDevKeyCode.KEY_KP4] = DistinctKey.NumPad4,
|
||||
[EvDevKeyCode.KEY_KP5] = DistinctKey.NumPad5,
|
||||
[EvDevKeyCode.KEY_KP6] = DistinctKey.NumPad6,
|
||||
[EvDevKeyCode.KEY_KPPLUS] = DistinctKey.Add,
|
||||
[EvDevKeyCode.KEY_KP1] = DistinctKey.NumPad1,
|
||||
[EvDevKeyCode.KEY_KP2] = DistinctKey.NumPad2,
|
||||
[EvDevKeyCode.KEY_KP3] = DistinctKey.NumPad3,
|
||||
[EvDevKeyCode.KEY_KPDOT] = DistinctKey.Decimal,
|
||||
[EvDevKeyCode.KEY_102ND] = DistinctKey.Oem102,
|
||||
[EvDevKeyCode.KEY_F11] = DistinctKey.F11,
|
||||
[EvDevKeyCode.KEY_F12] = DistinctKey.F12,
|
||||
[EvDevKeyCode.KEY_KPENTER] = DistinctKey.NumPadEnter,
|
||||
[EvDevKeyCode.KEY_RIGHTCTRL] = DistinctKey.RightCtrl,
|
||||
[EvDevKeyCode.KEY_KPSLASH] = DistinctKey.Divide,
|
||||
[EvDevKeyCode.KEY_RIGHTALT] = DistinctKey.RightAlt,
|
||||
[EvDevKeyCode.KEY_LINEFEED] = DistinctKey.LineFeed,
|
||||
[EvDevKeyCode.KEY_HOME] = DistinctKey.Home,
|
||||
[EvDevKeyCode.KEY_UP] = DistinctKey.Up,
|
||||
[EvDevKeyCode.KEY_PAGEUP] = DistinctKey.PageUp,
|
||||
[EvDevKeyCode.KEY_LEFT] = DistinctKey.Left,
|
||||
[EvDevKeyCode.KEY_RIGHT] = DistinctKey.Right,
|
||||
[EvDevKeyCode.KEY_END] = DistinctKey.End,
|
||||
[EvDevKeyCode.KEY_DOWN] = DistinctKey.Down,
|
||||
[EvDevKeyCode.KEY_PAGEDOWN] = DistinctKey.PageDown,
|
||||
[EvDevKeyCode.KEY_INSERT] = DistinctKey.Insert,
|
||||
[EvDevKeyCode.KEY_DELETE] = DistinctKey.Delete,
|
||||
[EvDevKeyCode.KEY_MUTE] = DistinctKey.VolumeMute,
|
||||
[EvDevKeyCode.KEY_VOLUMEDOWN] = DistinctKey.VolumeDown,
|
||||
[EvDevKeyCode.KEY_VOLUMEUP] = DistinctKey.VolumeUp,
|
||||
[EvDevKeyCode.KEY_LEFTMETA] = DistinctKey.LWin,
|
||||
[EvDevKeyCode.KEY_RIGHTMETA] = DistinctKey.RWin,
|
||||
[EvDevKeyCode.KEY_STOP] = DistinctKey.BrowserStop,
|
||||
[EvDevKeyCode.KEY_HELP] = DistinctKey.Help,
|
||||
[EvDevKeyCode.KEY_SLEEP] = DistinctKey.Sleep,
|
||||
[EvDevKeyCode.KEY_PROG1] = DistinctKey.LaunchApplication1,
|
||||
[EvDevKeyCode.KEY_PROG2] = DistinctKey.LaunchApplication2,
|
||||
[EvDevKeyCode.KEY_MAIL] = DistinctKey.LaunchMail,
|
||||
[EvDevKeyCode.KEY_BACK] = DistinctKey.BrowserBack,
|
||||
[EvDevKeyCode.KEY_FORWARD] = DistinctKey.BrowserForward,
|
||||
[EvDevKeyCode.KEY_NEXTSONG] = DistinctKey.MediaNextTrack,
|
||||
[EvDevKeyCode.KEY_PLAYPAUSE] = DistinctKey.MediaPlayPause,
|
||||
[EvDevKeyCode.KEY_PREVIOUSSONG] = DistinctKey.MediaPreviousTrack,
|
||||
[EvDevKeyCode.KEY_STOPCD] = DistinctKey.MediaStop,
|
||||
[EvDevKeyCode.KEY_HOMEPAGE] = DistinctKey.BrowserHome,
|
||||
[EvDevKeyCode.KEY_REFRESH] = DistinctKey.BrowserRefresh,
|
||||
[EvDevKeyCode.KEY_F13] = DistinctKey.F13,
|
||||
[EvDevKeyCode.KEY_F14] = DistinctKey.F14,
|
||||
[EvDevKeyCode.KEY_F15] = DistinctKey.F15,
|
||||
[EvDevKeyCode.KEY_F16] = DistinctKey.F16,
|
||||
[EvDevKeyCode.KEY_F17] = DistinctKey.F17,
|
||||
[EvDevKeyCode.KEY_F18] = DistinctKey.F18,
|
||||
[EvDevKeyCode.KEY_F19] = DistinctKey.F19,
|
||||
[EvDevKeyCode.KEY_F20] = DistinctKey.F20,
|
||||
[EvDevKeyCode.KEY_F21] = DistinctKey.F21,
|
||||
[EvDevKeyCode.KEY_F22] = DistinctKey.F22,
|
||||
[EvDevKeyCode.KEY_F23] = DistinctKey.F23,
|
||||
[EvDevKeyCode.KEY_F24] = DistinctKey.F24,
|
||||
[EvDevKeyCode.KEY_PLAY] = DistinctKey.Play,
|
||||
[EvDevKeyCode.KEY_PRINT] = DistinctKey.Print,
|
||||
[EvDevKeyCode.KEY_SEARCH] = DistinctKey.BrowserSearch,
|
||||
[EvDevKeyCode.KEY_CANCEL] = DistinctKey.Cancel,
|
||||
[EvDevKeyCode.KEY_MEDIA] = DistinctKey.SelectMedia,
|
||||
[EvDevKeyCode.KEY_SELECT] = DistinctKey.Select,
|
||||
[EvDevKeyCode.KEY_FAVORITES] = DistinctKey.BrowserFavorites,
|
||||
[EvDevKeyCode.KEY_CLEAR] = DistinctKey.Clear,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,629 +0,0 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// a lot of this is take from https://github.com/afshin-parsa/evdev-sharp
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class EvDevImports
|
||||
{
|
||||
public enum EvDevEventType : ushort
|
||||
{
|
||||
EV_SYN = 0x00,
|
||||
EV_KEY = 0x01,
|
||||
EV_REL = 0x02,
|
||||
EV_ABS = 0x03,
|
||||
EV_MSC = 0x04,
|
||||
EV_SW = 0x05,
|
||||
EV_LED = 0x11,
|
||||
EV_SND = 0x12,
|
||||
EV_REP = 0x14,
|
||||
EV_FF = 0x15,
|
||||
EV_PWR = 0x16,
|
||||
EV_FF_STATUS = 0x17,
|
||||
EV_MAX = 0x1f,
|
||||
EV_CNT = EV_MAX + 1,
|
||||
}
|
||||
|
||||
public enum EvDevKeyCode : ushort
|
||||
{
|
||||
KEY_RESERVED = 0,
|
||||
KEY_ESC = 1,
|
||||
KEY_1 = 2,
|
||||
KEY_2 = 3,
|
||||
KEY_3 = 4,
|
||||
KEY_4 = 5,
|
||||
KEY_5 = 6,
|
||||
KEY_6 = 7,
|
||||
KEY_7 = 8,
|
||||
KEY_8 = 9,
|
||||
KEY_9 = 10,
|
||||
KEY_0 = 11,
|
||||
KEY_MINUS = 12,
|
||||
KEY_EQUAL = 13,
|
||||
KEY_BACKSPACE = 14,
|
||||
KEY_TAB = 15,
|
||||
KEY_Q = 16,
|
||||
KEY_W = 17,
|
||||
KEY_E = 18,
|
||||
KEY_R = 19,
|
||||
KEY_T = 20,
|
||||
KEY_Y = 21,
|
||||
KEY_U = 22,
|
||||
KEY_I = 23,
|
||||
KEY_O = 24,
|
||||
KEY_P = 25,
|
||||
KEY_LEFTBRACE = 26,
|
||||
KEY_RIGHTBRACE = 27,
|
||||
KEY_ENTER = 28,
|
||||
KEY_LEFTCTRL = 29,
|
||||
KEY_A = 30,
|
||||
KEY_S = 31,
|
||||
KEY_D = 32,
|
||||
KEY_F = 33,
|
||||
KEY_G = 34,
|
||||
KEY_H = 35,
|
||||
KEY_J = 36,
|
||||
KEY_K = 37,
|
||||
KEY_L = 38,
|
||||
KEY_SEMICOLON = 39,
|
||||
KEY_APOSTROPHE = 40,
|
||||
KEY_GRAVE = 41,
|
||||
KEY_LEFTSHIFT = 42,
|
||||
KEY_BACKSLASH = 43,
|
||||
KEY_Z = 44,
|
||||
KEY_X = 45,
|
||||
KEY_C = 46,
|
||||
KEY_V = 47,
|
||||
KEY_B = 48,
|
||||
KEY_N = 49,
|
||||
KEY_M = 50,
|
||||
KEY_COMMA = 51,
|
||||
KEY_DOT = 52,
|
||||
KEY_SLASH = 53,
|
||||
KEY_RIGHTSHIFT = 54,
|
||||
KEY_KPASTERISK = 55,
|
||||
KEY_LEFTALT = 56,
|
||||
KEY_SPACE = 57,
|
||||
KEY_CAPSLOCK = 58,
|
||||
KEY_F1 = 59,
|
||||
KEY_F2 = 60,
|
||||
KEY_F3 = 61,
|
||||
KEY_F4 = 62,
|
||||
KEY_F5 = 63,
|
||||
KEY_F6 = 64,
|
||||
KEY_F7 = 65,
|
||||
KEY_F8 = 66,
|
||||
KEY_F9 = 67,
|
||||
KEY_F10 = 68,
|
||||
KEY_NUMLOCK = 69,
|
||||
KEY_SCROLLLOCK = 70,
|
||||
KEY_KP7 = 71,
|
||||
KEY_KP8 = 72,
|
||||
KEY_KP9 = 73,
|
||||
KEY_KPMINUS = 74,
|
||||
KEY_KP4 = 75,
|
||||
KEY_KP5 = 76,
|
||||
KEY_KP6 = 77,
|
||||
KEY_KPPLUS = 78,
|
||||
KEY_KP1 = 79,
|
||||
KEY_KP2 = 80,
|
||||
KEY_KP3 = 81,
|
||||
KEY_KP0 = 82,
|
||||
KEY_KPDOT = 83,
|
||||
KEY_ZENKAKUHANKAKU = 85,
|
||||
KEY_102ND = 86,
|
||||
KEY_F11 = 87,
|
||||
KEY_F12 = 88,
|
||||
KEY_RO = 89,
|
||||
KEY_KATAKANA = 90,
|
||||
KEY_HIRAGANA = 91,
|
||||
KEY_HENKAN = 92,
|
||||
KEY_KATAKANAHIRAGANA = 93,
|
||||
KEY_MUHENKAN = 94,
|
||||
KEY_KPJPCOMMA = 95,
|
||||
KEY_KPENTER = 96,
|
||||
KEY_RIGHTCTRL = 97,
|
||||
KEY_KPSLASH = 98,
|
||||
KEY_SYSRQ = 99,
|
||||
KEY_RIGHTALT = 100,
|
||||
KEY_LINEFEED = 101,
|
||||
KEY_HOME = 102,
|
||||
KEY_UP = 103,
|
||||
KEY_PAGEUP = 104,
|
||||
KEY_LEFT = 105,
|
||||
KEY_RIGHT = 106,
|
||||
KEY_END = 107,
|
||||
KEY_DOWN = 108,
|
||||
KEY_PAGEDOWN = 109,
|
||||
KEY_INSERT = 110,
|
||||
KEY_DELETE = 111,
|
||||
KEY_MACRO = 112,
|
||||
KEY_MUTE = 113,
|
||||
KEY_VOLUMEDOWN = 114,
|
||||
KEY_VOLUMEUP = 115,
|
||||
KEY_POWER = 116,
|
||||
KEY_KPEQUAL = 117,
|
||||
KEY_KPPLUSMINUS = 118,
|
||||
KEY_PAUSE = 119,
|
||||
KEY_SCALE = 120,
|
||||
KEY_KPCOMMA = 121,
|
||||
KEY_HANGEUL = 122,
|
||||
KEY_HANGUEL = KEY_HANGEUL,
|
||||
KEY_HANJA = 123,
|
||||
KEY_YEN = 124,
|
||||
KEY_LEFTMETA = 125,
|
||||
KEY_RIGHTMETA = 126,
|
||||
KEY_COMPOSE = 127,
|
||||
KEY_STOP = 128,
|
||||
KEY_AGAIN = 129,
|
||||
KEY_PROPS = 130,
|
||||
KEY_UNDO = 131,
|
||||
KEY_FRONT = 132,
|
||||
KEY_COPY = 133,
|
||||
KEY_OPEN = 134,
|
||||
KEY_PASTE = 135,
|
||||
KEY_FIND = 136,
|
||||
KEY_CUT = 137,
|
||||
KEY_HELP = 138,
|
||||
KEY_MENU = 139,
|
||||
KEY_CALC = 140,
|
||||
KEY_SETUP = 141,
|
||||
KEY_SLEEP = 142,
|
||||
KEY_WAKEUP = 143,
|
||||
KEY_FILE = 144,
|
||||
KEY_SENDFILE = 145,
|
||||
KEY_DELETEFILE = 146,
|
||||
KEY_XFER = 147,
|
||||
KEY_PROG1 = 148,
|
||||
KEY_PROG2 = 149,
|
||||
KEY_WWW = 150,
|
||||
KEY_MSDOS = 151,
|
||||
KEY_COFFEE = 152,
|
||||
KEY_SCREENLOCK = KEY_COFFEE,
|
||||
KEY_ROTATE_DISPLAY = 153,
|
||||
KEY_DIRECTION = KEY_ROTATE_DISPLAY,
|
||||
KEY_CYCLEWINDOWS = 154,
|
||||
KEY_MAIL = 155,
|
||||
KEY_BOOKMARKS = 156,
|
||||
KEY_COMPUTER = 157,
|
||||
KEY_BACK = 158,
|
||||
KEY_FORWARD = 159,
|
||||
KEY_CLOSECD = 160,
|
||||
KEY_EJECTCD = 161,
|
||||
KEY_EJECTCLOSECD = 162,
|
||||
KEY_NEXTSONG = 163,
|
||||
KEY_PLAYPAUSE = 164,
|
||||
KEY_PREVIOUSSONG = 165,
|
||||
KEY_STOPCD = 166,
|
||||
KEY_RECORD = 167,
|
||||
KEY_REWIND = 168,
|
||||
KEY_PHONE = 169,
|
||||
KEY_ISO = 170,
|
||||
KEY_CONFIG = 171,
|
||||
KEY_HOMEPAGE = 172,
|
||||
KEY_REFRESH = 173,
|
||||
KEY_EXIT = 174,
|
||||
KEY_MOVE = 175,
|
||||
KEY_EDIT = 176,
|
||||
KEY_SCROLLUP = 177,
|
||||
KEY_SCROLLDOWN = 178,
|
||||
KEY_KPLEFTPAREN = 179,
|
||||
KEY_KPRIGHTPAREN = 180,
|
||||
KEY_NEW = 181,
|
||||
KEY_REDO = 182,
|
||||
KEY_F13 = 183,
|
||||
KEY_F14 = 184,
|
||||
KEY_F15 = 185,
|
||||
KEY_F16 = 186,
|
||||
KEY_F17 = 187,
|
||||
KEY_F18 = 188,
|
||||
KEY_F19 = 189,
|
||||
KEY_F20 = 190,
|
||||
KEY_F21 = 191,
|
||||
KEY_F22 = 192,
|
||||
KEY_F23 = 193,
|
||||
KEY_F24 = 194,
|
||||
KEY_PLAYCD = 200,
|
||||
KEY_PAUSECD = 201,
|
||||
KEY_PROG3 = 202,
|
||||
KEY_PROG4 = 203,
|
||||
KEY_DASHBOARD = 204,
|
||||
KEY_SUSPEND = 205,
|
||||
KEY_CLOSE = 206,
|
||||
KEY_PLAY = 207,
|
||||
KEY_FASTFORWARD = 208,
|
||||
KEY_BASSBOOST = 209,
|
||||
KEY_PRINT = 210,
|
||||
KEY_HP = 211,
|
||||
KEY_CAMERA = 212,
|
||||
KEY_SOUND = 213,
|
||||
KEY_QUESTION = 214,
|
||||
KEY_EMAIL = 215,
|
||||
KEY_CHAT = 216,
|
||||
KEY_SEARCH = 217,
|
||||
KEY_CONNECT = 218,
|
||||
KEY_FINANCE = 219,
|
||||
KEY_SPORT = 220,
|
||||
KEY_SHOP = 221,
|
||||
KEY_ALTERASE = 222,
|
||||
KEY_CANCEL = 223,
|
||||
KEY_BRIGHTNESSDOWN = 224,
|
||||
KEY_BRIGHTNESSUP = 225,
|
||||
KEY_MEDIA = 226,
|
||||
KEY_SWITCHVIDEOMODE = 227,
|
||||
KEY_KBDILLUMTOGGLE = 228,
|
||||
KEY_KBDILLUMDOWN = 229,
|
||||
KEY_KBDILLUMUP = 230,
|
||||
KEY_SEND = 231,
|
||||
KEY_REPLY = 232,
|
||||
KEY_FORWARDMAIL = 233,
|
||||
KEY_SAVE = 234,
|
||||
KEY_DOCUMENTS = 235,
|
||||
KEY_BATTERY = 236,
|
||||
KEY_BLUETOOTH = 237,
|
||||
KEY_WLAN = 238,
|
||||
KEY_UWB = 239,
|
||||
KEY_UNKNOWN = 240,
|
||||
KEY_VIDEO_NEXT = 241,
|
||||
KEY_VIDEO_PREV = 242,
|
||||
KEY_BRIGHTNESS_CYCLE = 243,
|
||||
KEY_BRIGHTNESS_AUTO = 244,
|
||||
KEY_BRIGHTNESS_ZERO = KEY_BRIGHTNESS_AUTO,
|
||||
KEY_DISPLAY_OFF = 245,
|
||||
KEY_WWAN = 246,
|
||||
KEY_WIMAX = KEY_WWAN,
|
||||
KEY_RFKILL = 247,
|
||||
KEY_MICMUTE = 248,
|
||||
BTN_MISC = 0x100,
|
||||
BTN_0 = 0x100,
|
||||
BTN_1 = 0x101,
|
||||
BTN_2 = 0x102,
|
||||
BTN_3 = 0x103,
|
||||
BTN_4 = 0x104,
|
||||
BTN_5 = 0x105,
|
||||
BTN_6 = 0x106,
|
||||
BTN_7 = 0x107,
|
||||
BTN_8 = 0x108,
|
||||
BTN_9 = 0x109,
|
||||
BTN_MOUSE = 0x110,
|
||||
BTN_LEFT = 0x110,
|
||||
BTN_RIGHT = 0x111,
|
||||
BTN_MIDDLE = 0x112,
|
||||
BTN_SIDE = 0x113,
|
||||
BTN_EXTRA = 0x114,
|
||||
BTN_FORWARD = 0x115,
|
||||
BTN_BACK = 0x116,
|
||||
BTN_TASK = 0x117,
|
||||
BTN_JOYSTICK = 0x120,
|
||||
BTN_TRIGGER = 0x120,
|
||||
BTN_THUMB = 0x121,
|
||||
BTN_THUMB2 = 0x122,
|
||||
BTN_TOP = 0x123,
|
||||
BTN_TOP2 = 0x124,
|
||||
BTN_PINKIE = 0x125,
|
||||
BTN_BASE = 0x126,
|
||||
BTN_BASE2 = 0x127,
|
||||
BTN_BASE3 = 0x128,
|
||||
BTN_BASE4 = 0x129,
|
||||
BTN_BASE5 = 0x12a,
|
||||
BTN_BASE6 = 0x12b,
|
||||
BTN_DEAD = 0x12f,
|
||||
BTN_GAMEPAD = 0x130,
|
||||
BTN_SOUTH = 0x130,
|
||||
BTN_A = BTN_SOUTH,
|
||||
BTN_EAST = 0x131,
|
||||
BTN_B = BTN_EAST,
|
||||
BTN_C = 0x132,
|
||||
BTN_NORTH = 0x133,
|
||||
BTN_X = BTN_NORTH,
|
||||
BTN_WEST = 0x134,
|
||||
BTN_Y = BTN_WEST,
|
||||
BTN_Z = 0x135,
|
||||
BTN_TL = 0x136,
|
||||
BTN_TR = 0x137,
|
||||
BTN_TL2 = 0x138,
|
||||
BTN_TR2 = 0x139,
|
||||
BTN_SELECT = 0x13a,
|
||||
BTN_START = 0x13b,
|
||||
BTN_MODE = 0x13c,
|
||||
BTN_THUMBL = 0x13d,
|
||||
BTN_THUMBR = 0x13e,
|
||||
BTN_DIGI = 0x140,
|
||||
BTN_TOOL_PEN = 0x140,
|
||||
BTN_TOOL_RUBBER = 0x141,
|
||||
BTN_TOOL_BRUSH = 0x142,
|
||||
BTN_TOOL_PENCIL = 0x143,
|
||||
BTN_TOOL_AIRBRUSH = 0x144,
|
||||
BTN_TOOL_FINGER = 0x145,
|
||||
BTN_TOOL_MOUSE = 0x146,
|
||||
BTN_TOOL_LENS = 0x147,
|
||||
BTN_TOOL_QUINTTAP = 0x148,
|
||||
BTN_STYLUS3 = 0x149,
|
||||
BTN_TOUCH = 0x14a,
|
||||
BTN_STYLUS = 0x14b,
|
||||
BTN_STYLUS2 = 0x14c,
|
||||
BTN_TOOL_DOUBLETAP = 0x14d,
|
||||
BTN_TOOL_TRIPLETAP = 0x14e,
|
||||
BTN_TOOL_QUADTAP = 0x14f,
|
||||
BTN_WHEEL = 0x150,
|
||||
BTN_GEAR_DOWN = 0x150,
|
||||
BTN_GEAR_UP = 0x151,
|
||||
KEY_OK = 0x160,
|
||||
KEY_SELECT = 0x161,
|
||||
KEY_GOTO = 0x162,
|
||||
KEY_CLEAR = 0x163,
|
||||
KEY_POWER2 = 0x164,
|
||||
KEY_OPTION = 0x165,
|
||||
KEY_INFO = 0x166,
|
||||
KEY_TIME = 0x167,
|
||||
KEY_VENDOR = 0x168,
|
||||
KEY_ARCHIVE = 0x169,
|
||||
KEY_PROGRAM = 0x16a,
|
||||
KEY_CHANNEL = 0x16b,
|
||||
KEY_FAVORITES = 0x16c,
|
||||
KEY_EPG = 0x16d,
|
||||
KEY_PVR = 0x16e,
|
||||
KEY_MHP = 0x16f,
|
||||
KEY_LANGUAGE = 0x170,
|
||||
KEY_TITLE = 0x171,
|
||||
KEY_SUBTITLE = 0x172,
|
||||
KEY_ANGLE = 0x173,
|
||||
KEY_ZOOM = 0x174,
|
||||
KEY_MODE = 0x175,
|
||||
KEY_KEYBOARD = 0x176,
|
||||
KEY_SCREEN = 0x177,
|
||||
KEY_PC = 0x178,
|
||||
KEY_TV = 0x179,
|
||||
KEY_TV2 = 0x17a,
|
||||
KEY_VCR = 0x17b,
|
||||
KEY_VCR2 = 0x17c,
|
||||
KEY_SAT = 0x17d,
|
||||
KEY_SAT2 = 0x17e,
|
||||
KEY_CD = 0x17f,
|
||||
KEY_TAPE = 0x180,
|
||||
KEY_RADIO = 0x181,
|
||||
KEY_TUNER = 0x182,
|
||||
KEY_PLAYER = 0x183,
|
||||
KEY_TEXT = 0x184,
|
||||
KEY_DVD = 0x185,
|
||||
KEY_AUX = 0x186,
|
||||
KEY_MP3 = 0x187,
|
||||
KEY_AUDIO = 0x188,
|
||||
KEY_VIDEO = 0x189,
|
||||
KEY_DIRECTORY = 0x18a,
|
||||
KEY_LIST = 0x18b,
|
||||
KEY_MEMO = 0x18c,
|
||||
KEY_CALENDAR = 0x18d,
|
||||
KEY_RED = 0x18e,
|
||||
KEY_GREEN = 0x18f,
|
||||
KEY_YELLOW = 0x190,
|
||||
KEY_BLUE = 0x191,
|
||||
KEY_CHANNELUP = 0x192,
|
||||
KEY_CHANNELDOWN = 0x193,
|
||||
KEY_FIRST = 0x194,
|
||||
KEY_LAST = 0x195,
|
||||
KEY_AB = 0x196,
|
||||
KEY_NEXT = 0x197,
|
||||
KEY_RESTART = 0x198,
|
||||
KEY_SLOW = 0x199,
|
||||
KEY_SHUFFLE = 0x19a,
|
||||
KEY_BREAK = 0x19b,
|
||||
KEY_PREVIOUS = 0x19c,
|
||||
KEY_DIGITS = 0x19d,
|
||||
KEY_TEEN = 0x19e,
|
||||
KEY_TWEN = 0x19f,
|
||||
KEY_VIDEOPHONE = 0x1a0,
|
||||
KEY_GAMES = 0x1a1,
|
||||
KEY_ZOOMIN = 0x1a2,
|
||||
KEY_ZOOMOUT = 0x1a3,
|
||||
KEY_ZOOMRESET = 0x1a4,
|
||||
KEY_WORDPROCESSOR = 0x1a5,
|
||||
KEY_EDITOR = 0x1a6,
|
||||
KEY_SPREADSHEET = 0x1a7,
|
||||
KEY_GRAPHICSEDITOR = 0x1a8,
|
||||
KEY_PRESENTATION = 0x1a9,
|
||||
KEY_DATABASE = 0x1aa,
|
||||
KEY_NEWS = 0x1ab,
|
||||
KEY_VOICEMAIL = 0x1ac,
|
||||
KEY_ADDRESSBOOK = 0x1ad,
|
||||
KEY_MESSENGER = 0x1ae,
|
||||
KEY_DISPLAYTOGGLE = 0x1af,
|
||||
KEY_BRIGHTNESS_TOGGLE = KEY_DISPLAYTOGGLE,
|
||||
KEY_SPELLCHECK = 0x1b0,
|
||||
KEY_LOGOFF = 0x1b1,
|
||||
KEY_DOLLAR = 0x1b2,
|
||||
KEY_EURO = 0x1b3,
|
||||
KEY_FRAMEBACK = 0x1b4,
|
||||
KEY_FRAMEFORWARD = 0x1b5,
|
||||
KEY_CONTEXT_MENU = 0x1b6,
|
||||
KEY_MEDIA_REPEAT = 0x1b7,
|
||||
KEY_10CHANNELSUP = 0x1b8,
|
||||
KEY_10CHANNELSDOWN = 0x1b9,
|
||||
KEY_IMAGES = 0x1ba,
|
||||
KEY_DEL_EOL = 0x1c0,
|
||||
KEY_DEL_EOS = 0x1c1,
|
||||
KEY_INS_LINE = 0x1c2,
|
||||
KEY_DEL_LINE = 0x1c3,
|
||||
KEY_FN = 0x1d0,
|
||||
KEY_FN_ESC = 0x1d1,
|
||||
KEY_FN_F1 = 0x1d2,
|
||||
KEY_FN_F2 = 0x1d3,
|
||||
KEY_FN_F3 = 0x1d4,
|
||||
KEY_FN_F4 = 0x1d5,
|
||||
KEY_FN_F5 = 0x1d6,
|
||||
KEY_FN_F6 = 0x1d7,
|
||||
KEY_FN_F7 = 0x1d8,
|
||||
KEY_FN_F8 = 0x1d9,
|
||||
KEY_FN_F9 = 0x1da,
|
||||
KEY_FN_F10 = 0x1db,
|
||||
KEY_FN_F11 = 0x1dc,
|
||||
KEY_FN_F12 = 0x1dd,
|
||||
KEY_FN_1 = 0x1de,
|
||||
KEY_FN_2 = 0x1df,
|
||||
KEY_FN_D = 0x1e0,
|
||||
KEY_FN_E = 0x1e1,
|
||||
KEY_FN_F = 0x1e2,
|
||||
KEY_FN_S = 0x1e3,
|
||||
KEY_FN_B = 0x1e4,
|
||||
KEY_BRL_DOT1 = 0x1f1,
|
||||
KEY_BRL_DOT2 = 0x1f2,
|
||||
KEY_BRL_DOT3 = 0x1f3,
|
||||
KEY_BRL_DOT4 = 0x1f4,
|
||||
KEY_BRL_DOT5 = 0x1f5,
|
||||
KEY_BRL_DOT6 = 0x1f6,
|
||||
KEY_BRL_DOT7 = 0x1f7,
|
||||
KEY_BRL_DOT8 = 0x1f8,
|
||||
KEY_BRL_DOT9 = 0x1f9,
|
||||
KEY_BRL_DOT10 = 0x1fa,
|
||||
KEY_NUMERIC_0 = 0x200,
|
||||
KEY_NUMERIC_1 = 0x201,
|
||||
KEY_NUMERIC_2 = 0x202,
|
||||
KEY_NUMERIC_3 = 0x203,
|
||||
KEY_NUMERIC_4 = 0x204,
|
||||
KEY_NUMERIC_5 = 0x205,
|
||||
KEY_NUMERIC_6 = 0x206,
|
||||
KEY_NUMERIC_7 = 0x207,
|
||||
KEY_NUMERIC_8 = 0x208,
|
||||
KEY_NUMERIC_9 = 0x209,
|
||||
KEY_NUMERIC_STAR = 0x20a,
|
||||
KEY_NUMERIC_POUND = 0x20b,
|
||||
KEY_NUMERIC_A = 0x20c,
|
||||
KEY_NUMERIC_B = 0x20d,
|
||||
KEY_NUMERIC_C = 0x20e,
|
||||
KEY_NUMERIC_D = 0x20f,
|
||||
KEY_CAMERA_FOCUS = 0x210,
|
||||
KEY_WPS_BUTTON = 0x211,
|
||||
KEY_TOUCHPAD_TOGGLE = 0x212,
|
||||
KEY_TOUCHPAD_ON = 0x213,
|
||||
KEY_TOUCHPAD_OFF = 0x214,
|
||||
KEY_CAMERA_ZOOMIN = 0x215,
|
||||
KEY_CAMERA_ZOOMOUT = 0x216,
|
||||
KEY_CAMERA_UP = 0x217,
|
||||
KEY_CAMERA_DOWN = 0x218,
|
||||
KEY_CAMERA_LEFT = 0x219,
|
||||
KEY_CAMERA_RIGHT = 0x21a,
|
||||
KEY_ATTENDANT_ON = 0x21b,
|
||||
KEY_ATTENDANT_OFF = 0x21c,
|
||||
KEY_ATTENDANT_TOGGLE = 0x21d,
|
||||
KEY_LIGHTS_TOGGLE = 0x21e,
|
||||
BTN_DPAD_UP = 0x220,
|
||||
BTN_DPAD_DOWN = 0x221,
|
||||
BTN_DPAD_LEFT = 0x222,
|
||||
BTN_DPAD_RIGHT = 0x223,
|
||||
KEY_ALS_TOGGLE = 0x230,
|
||||
KEY_BUTTONCONFIG = 0x240,
|
||||
KEY_TASKMANAGER = 0x241,
|
||||
KEY_JOURNAL = 0x242,
|
||||
KEY_CONTROLPANEL = 0x243,
|
||||
KEY_APPSELECT = 0x244,
|
||||
KEY_SCREENSAVER = 0x245,
|
||||
KEY_VOICECOMMAND = 0x246,
|
||||
KEY_ASSISTANT = 0x247,
|
||||
KEY_BRIGHTNESS_MIN = 0x250,
|
||||
KEY_BRIGHTNESS_MAX = 0x251,
|
||||
KEY_KBDINPUTASSIST_PREV = 0x260,
|
||||
KEY_KBDINPUTASSIST_NEXT = 0x261,
|
||||
KEY_KBDINPUTASSIST_PREVGROUP = 0x262,
|
||||
KEY_KBDINPUTASSIST_NEXTGROUP = 0x263,
|
||||
KEY_KBDINPUTASSIST_ACCEPT = 0x264,
|
||||
KEY_KBDINPUTASSIST_CANCEL = 0x265,
|
||||
KEY_RIGHT_UP = 0x266,
|
||||
KEY_RIGHT_DOWN = 0x267,
|
||||
KEY_LEFT_UP = 0x268,
|
||||
KEY_LEFT_DOWN = 0x269,
|
||||
KEY_ROOT_MENU = 0x26a,
|
||||
KEY_MEDIA_TOP_MENU = 0x26b,
|
||||
KEY_NUMERIC_11 = 0x26c,
|
||||
KEY_NUMERIC_12 = 0x26d,
|
||||
KEY_AUDIO_DESC = 0x26e,
|
||||
KEY_3D_MODE = 0x26f,
|
||||
KEY_NEXT_FAVORITE = 0x270,
|
||||
KEY_STOP_RECORD = 0x271,
|
||||
KEY_PAUSE_RECORD = 0x272,
|
||||
KEY_VOD = 0x273,
|
||||
KEY_UNMUTE = 0x274,
|
||||
KEY_FASTREVERSE = 0x275,
|
||||
KEY_SLOWREVERSE = 0x276,
|
||||
KEY_DATA = 0x277,
|
||||
KEY_ONSCREEN_KEYBOARD = 0x278,
|
||||
BTN_TRIGGER_HAPPY = 0x2c0,
|
||||
BTN_TRIGGER_HAPPY1 = 0x2c0,
|
||||
BTN_TRIGGER_HAPPY2 = 0x2c1,
|
||||
BTN_TRIGGER_HAPPY3 = 0x2c2,
|
||||
BTN_TRIGGER_HAPPY4 = 0x2c3,
|
||||
BTN_TRIGGER_HAPPY5 = 0x2c4,
|
||||
BTN_TRIGGER_HAPPY6 = 0x2c5,
|
||||
BTN_TRIGGER_HAPPY7 = 0x2c6,
|
||||
BTN_TRIGGER_HAPPY8 = 0x2c7,
|
||||
BTN_TRIGGER_HAPPY9 = 0x2c8,
|
||||
BTN_TRIGGER_HAPPY10 = 0x2c9,
|
||||
BTN_TRIGGER_HAPPY11 = 0x2ca,
|
||||
BTN_TRIGGER_HAPPY12 = 0x2cb,
|
||||
BTN_TRIGGER_HAPPY13 = 0x2cc,
|
||||
BTN_TRIGGER_HAPPY14 = 0x2cd,
|
||||
BTN_TRIGGER_HAPPY15 = 0x2ce,
|
||||
BTN_TRIGGER_HAPPY16 = 0x2cf,
|
||||
BTN_TRIGGER_HAPPY17 = 0x2d0,
|
||||
BTN_TRIGGER_HAPPY18 = 0x2d1,
|
||||
BTN_TRIGGER_HAPPY19 = 0x2d2,
|
||||
BTN_TRIGGER_HAPPY20 = 0x2d3,
|
||||
BTN_TRIGGER_HAPPY21 = 0x2d4,
|
||||
BTN_TRIGGER_HAPPY22 = 0x2d5,
|
||||
BTN_TRIGGER_HAPPY23 = 0x2d6,
|
||||
BTN_TRIGGER_HAPPY24 = 0x2d7,
|
||||
BTN_TRIGGER_HAPPY25 = 0x2d8,
|
||||
BTN_TRIGGER_HAPPY26 = 0x2d9,
|
||||
BTN_TRIGGER_HAPPY27 = 0x2da,
|
||||
BTN_TRIGGER_HAPPY28 = 0x2db,
|
||||
BTN_TRIGGER_HAPPY29 = 0x2dc,
|
||||
BTN_TRIGGER_HAPPY30 = 0x2dd,
|
||||
BTN_TRIGGER_HAPPY31 = 0x2de,
|
||||
BTN_TRIGGER_HAPPY32 = 0x2df,
|
||||
BTN_TRIGGER_HAPPY33 = 0x2e0,
|
||||
BTN_TRIGGER_HAPPY34 = 0x2e1,
|
||||
BTN_TRIGGER_HAPPY35 = 0x2e2,
|
||||
BTN_TRIGGER_HAPPY36 = 0x2e3,
|
||||
BTN_TRIGGER_HAPPY37 = 0x2e4,
|
||||
BTN_TRIGGER_HAPPY38 = 0x2e5,
|
||||
BTN_TRIGGER_HAPPY39 = 0x2e6,
|
||||
BTN_TRIGGER_HAPPY40 = 0x2e7,
|
||||
KEY_MIN_INTERESTING = KEY_MUTE,
|
||||
KEY_MAX = 0x2ff,
|
||||
KEY_CNT = KEY_MAX + 1,
|
||||
}
|
||||
|
||||
public enum EvDevKeyValue : int
|
||||
{
|
||||
KeyUp = 0,
|
||||
KeyDown = 1,
|
||||
KeyRepeat = 2
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct EvDevKeyboardEvent
|
||||
{
|
||||
public IntPtr tv_sec;
|
||||
public IntPtr tv_usec;
|
||||
public EvDevEventType type;
|
||||
public EvDevKeyCode code;
|
||||
public EvDevKeyValue value;
|
||||
}
|
||||
|
||||
private const uint IOC_READ = 2;
|
||||
private const uint IOC_EV_TYPE = 'E';
|
||||
private static uint EVIOCG(long nr, long size)
|
||||
=> (uint)((IOC_READ << 30) | (size << 16) | (IOC_EV_TYPE << 8) | nr);
|
||||
|
||||
public static uint EVIOCGNAME(long len) => EVIOCG( 0x06, len);
|
||||
public static uint EVIOCGBIT(EvDevEventType ev, long len) => EVIOCG(0x20 + (long)ev, len);
|
||||
|
||||
public const uint EVIOCGVERSION = 0x80044501;
|
||||
public const uint EVIOCGID = 0x80084502;
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern unsafe int ioctl(int fd, UIntPtr request, void* data);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class FildesImports
|
||||
{
|
||||
[Flags]
|
||||
public enum OpenFlags : int
|
||||
{
|
||||
O_RDONLY = 0,
|
||||
O_WRONLY = 1,
|
||||
O_RDWR = 2,
|
||||
// O_LARGEFILE = 0,
|
||||
O_CREAT = 0x40,
|
||||
O_EXCL = 0x80,
|
||||
O_NOCTTY = 0x100,
|
||||
O_TRUNC = 0x200,
|
||||
O_APPEND = 0x400,
|
||||
O_NONBLOCK = 0x800,
|
||||
O_NDELAY = O_NONBLOCK,
|
||||
O_DSYNC = 0x1000,
|
||||
O_ASYNC = 0x2000,
|
||||
O_DIRECT = 0x4000,
|
||||
O_DIRECTORY = 0x10000,
|
||||
O_NOFOLLOW = 0x20000,
|
||||
O_NOATIME = 0x40000,
|
||||
O_CLOEXEC = 0x80000,
|
||||
O_SYNC = 0x100000 | O_DSYNC,
|
||||
O_PATH = 0x200000,
|
||||
O_TMPFILE = 0x400000 | O_DIRECTORY,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum OpenMode : int
|
||||
{
|
||||
S_IXOTH = 0x1,
|
||||
S_IWOTH = 0x2,
|
||||
S_IROTH = 0x4,
|
||||
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH,
|
||||
S_IXGRP = 0x8,
|
||||
S_IWGRP = 0x10,
|
||||
S_IRGRP = 0x20,
|
||||
S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP,
|
||||
S_IXUSR = 0x40,
|
||||
S_IWUSR = 0x80,
|
||||
S_IRUSR = 0x100,
|
||||
S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR,
|
||||
S_ISVTX = 0x200,
|
||||
S_ISGID = 0x400,
|
||||
S_ISUID = 0x800,
|
||||
}
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int open(string pathname, OpenFlags flags);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int open(string pathname, OpenFlags flags, OpenMode mode);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int close(int fd);
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
public static extern unsafe IntPtr read(int fd, void* buf, IntPtr count);
|
||||
}
|
||||
}
|
|
@ -140,6 +140,10 @@ namespace BizHawk.Common
|
|||
[DllImport("user32.dll")]
|
||||
public static extern unsafe int GetRawInputData(IntPtr hRawInput, RID uiCommand, RAWINPUT* pData, ref int bSize, int cbSizeHeader);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool RegisterRawInputDevices(ref RAWINPUTDEVICE pRawInputDevice, uint uiNumDevices, int cbSize);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, uint uiNumDevices, int cbSize);
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace BizHawk.Common
|
|||
public const int MAX_PATH = 260;
|
||||
public const uint PM_REMOVE = 0x0001U;
|
||||
public static readonly IntPtr HWND_MESSAGE = new(-3);
|
||||
public const int GWLP_USERDATA = -21;
|
||||
|
||||
public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
|
||||
|
||||
|
@ -163,6 +164,9 @@ namespace BizHawk.Common
|
|||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetProcessHeap();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = false)]
|
||||
public static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, int dwBytes);
|
||||
|
||||
|
@ -201,6 +205,9 @@ namespace BizHawk.Common
|
|||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
|
||||
|
||||
|
|
Loading…
Reference in New Issue