diff --git a/src/BizHawk.Bizware.Input/BizHawk.Bizware.Input.csproj b/src/BizHawk.Bizware.Input/BizHawk.Bizware.Input.csproj
index ffb3ee4b93..fe910a5fe1 100644
--- a/src/BizHawk.Bizware.Input/BizHawk.Bizware.Input.csproj
+++ b/src/BizHawk.Bizware.Input/BizHawk.Bizware.Input.csproj
@@ -9,6 +9,6 @@
-
+
diff --git a/src/BizHawk.Bizware.Input/HostInputFocus.cs b/src/BizHawk.Bizware.Input/HostInputFocus.cs
new file mode 100644
index 0000000000..57a8bff423
--- /dev/null
+++ b/src/BizHawk.Bizware.Input/HostInputFocus.cs
@@ -0,0 +1,13 @@
+#nullable enable
+
+namespace BizHawk.Bizware.Input
+{
+ [Flags]
+ public enum HostInputFocus
+ {
+ None = 0,
+ Mouse = 1,
+ Keyboard = 2,
+ Pad = 4
+ }
+}
diff --git a/src/BizHawk.Client.Common/input/HostInputAdapter.cs b/src/BizHawk.Bizware.Input/IHostInputAdapter.cs
similarity index 76%
rename from src/BizHawk.Client.Common/input/HostInputAdapter.cs
rename to src/BizHawk.Bizware.Input/IHostInputAdapter.cs
index 4198544a25..8947a7ed02 100644
--- a/src/BizHawk.Client.Common/input/HostInputAdapter.cs
+++ b/src/BizHawk.Bizware.Input/IHostInputAdapter.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
-namespace BizHawk.Client.Common
+namespace BizHawk.Bizware.Input
{
/// this was easier than trying to make static classes instantiable...
/// TODO: Reconsider if we want to hand over the main form handle
@@ -18,17 +18,17 @@ namespace BizHawk.Client.Common
/// keys are pad prefixes "X# "/"J# " (with the trailing space)
IReadOnlyDictionary> GetHapticsChannels();
- void ReInitGamepads(IntPtr mainFormHandle);
-
void PreprocessHostGamepads();
- void ProcessHostGamepads(Action handleButton, Action handleAxis);
+ void ProcessHostGamepads(Action handleButton, Action handleAxis);
IEnumerable ProcessHostKeyboards();
+ (int DeltaX, int DeltaY) ProcessHostMice();
+
/// implementors may store this for use during the next call
void SetHaptics(IReadOnlyCollection<(string Name, int Strength)> hapticsSnapshot);
- void UpdateConfig(Config config);
+ void SetAlternateKeyboardLayoutEnableCallback(Func getHandleAlternateKeyboardLayouts);
}
}
diff --git a/src/BizHawk.Bizware.Input/IPC/IPCKeyInput.cs b/src/BizHawk.Bizware.Input/IPC/IPCKeyInput.cs
index f8b5d3a14b..7d51686f0c 100644
--- a/src/BizHawk.Bizware.Input/IPC/IPCKeyInput.cs
+++ b/src/BizHawk.Bizware.Input/IPC/IPCKeyInput.cs
@@ -4,8 +4,6 @@ using System.IO;
using System.IO.Pipes;
using System.Threading;
-using BizHawk.Client.Common;
-
// this is not a very safe or pretty protocol, I'm not proud of it
namespace BizHawk.Bizware.Input
{
@@ -21,7 +19,7 @@ namespace BizHawk.Bizware.Input
}
}
- private static readonly List PendingKeyEvents = new();
+ private static readonly List PendingKeyEvents = [ ];
private static bool IPCActive;
private static void IPCThread()
diff --git a/src/BizHawk.Bizware.Input/KeyInput/IKeyInput.cs b/src/BizHawk.Bizware.Input/KeyInput/IKeyInput.cs
deleted file mode 100644
index b261394adf..0000000000
--- a/src/BizHawk.Bizware.Input/KeyInput/IKeyInput.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-#nullable enable
-
-using System.Collections.Generic;
-
-using BizHawk.Client.Common;
-
-namespace BizHawk.Bizware.Input
-{
- internal interface IKeyInput : IDisposable
- {
- IEnumerable Update(bool handleAltKbLayouts);
- }
-}
diff --git a/src/BizHawk.Bizware.Input/KeyInput/KeyInputFactory.cs b/src/BizHawk.Bizware.Input/KeyInput/KeyInputFactory.cs
deleted file mode 100644
index 881f7ae201..0000000000
--- a/src/BizHawk.Bizware.Input/KeyInput/KeyInputFactory.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-#nullable enable
-
-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"),
- };
- }
-}
diff --git a/src/BizHawk.Client.Common/input/DistinctKey.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/DistinctKey.cs
similarity index 99%
rename from src/BizHawk.Client.Common/input/DistinctKey.cs
rename to src/BizHawk.Bizware.Input/KeyMouseInput/DistinctKey.cs
index 6b6dc1f6d4..363bd8dc1c 100644
--- a/src/BizHawk.Client.Common/input/DistinctKey.cs
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/DistinctKey.cs
@@ -3,7 +3,7 @@
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable UnusedMember.Global
-namespace BizHawk.Client.Common
+namespace BizHawk.Bizware.Input
{
/// values are one-to-one with System.Windows.Input.Key except and which were added for this project
/// copied from MIT-licensed WPF source: https://github.com/dotnet/wpf/blob/49bb41ad83abeb5ae22e4c59d0f43c1287acac00/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/Key.cs
diff --git a/src/BizHawk.Bizware.Input/KeyMouseInput/IKeyMouseInput.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/IKeyMouseInput.cs
new file mode 100644
index 0000000000..de4151fd70
--- /dev/null
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/IKeyMouseInput.cs
@@ -0,0 +1,13 @@
+#nullable enable
+
+using System.Collections.Generic;
+
+namespace BizHawk.Bizware.Input
+{
+ internal interface IKeyMouseInput : IDisposable
+ {
+ IEnumerable UpdateKeyInputs(bool handleAltKbLayouts);
+
+ (int DeltaX, int DeltaY) UpdateMouseInput();
+ }
+}
diff --git a/src/BizHawk.Bizware.Input/KeyMouseInput/KeyEvent.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/KeyEvent.cs
new file mode 100644
index 0000000000..36475fec3c
--- /dev/null
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/KeyEvent.cs
@@ -0,0 +1,6 @@
+#nullable enable
+
+namespace BizHawk.Bizware.Input
+{
+ public readonly record struct KeyEvent(DistinctKey Key, bool Pressed);
+}
diff --git a/src/BizHawk.Bizware.Input/KeyMouseInput/KeyMouseInputFactory.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/KeyMouseInputFactory.cs
new file mode 100644
index 0000000000..c7739d8749
--- /dev/null
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/KeyMouseInputFactory.cs
@@ -0,0 +1,17 @@
+#nullable enable
+
+using BizHawk.Common;
+
+namespace BizHawk.Bizware.Input
+{
+ internal static class KeyMouseInputFactory
+ {
+ public static IKeyMouseInput CreateKeyMouseInput() => OSTailoredCode.CurrentOS switch
+ {
+ OSTailoredCode.DistinctOS.Linux => new X11KeyMouseInput(),
+ OSTailoredCode.DistinctOS.macOS => new QuartzKeyMouseInput(),
+ OSTailoredCode.DistinctOS.Windows => new RawKeyMouseInput(),
+ _ => throw new InvalidOperationException("Unknown OS"),
+ };
+ }
+}
diff --git a/src/BizHawk.Bizware.Input/KeyInput/QuartzKeyInput.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/QuartzKeyMouseInput.cs
similarity index 91%
rename from src/BizHawk.Bizware.Input/KeyInput/QuartzKeyInput.cs
rename to src/BizHawk.Bizware.Input/KeyMouseInput/QuartzKeyMouseInput.cs
index cb7191eb6d..88313d64c0 100644
--- a/src/BizHawk.Bizware.Input/KeyInput/QuartzKeyInput.cs
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/QuartzKeyMouseInput.cs
@@ -2,13 +2,11 @@
using System.Collections.Generic;
-using BizHawk.Client.Common;
-
using static BizHawk.Common.QuartzImports;
namespace BizHawk.Bizware.Input
{
- internal sealed class QuartzKeyInput : IKeyInput
+ internal sealed class QuartzKeyMouseInput : IKeyMouseInput
{
private readonly bool[] LastKeyState = new bool[0x7F];
@@ -16,7 +14,7 @@ namespace BizHawk.Bizware.Input
{
}
- public IEnumerable Update(bool handleAltKbLayouts)
+ public IEnumerable UpdateKeyInputs(bool handleAltKbLayouts)
{
var keyEvents = new List();
for (var keycode = 0; keycode < 0x7F; keycode++)
@@ -28,7 +26,7 @@ namespace BizHawk.Bizware.Input
{
if (KeyEnumMap.TryGetValue((CGKeyCode)keycode, out var key))
{
- keyEvents.Add(new(key, pressed: keystate));
+ keyEvents.Add(new(key, Pressed: keystate));
LastKeyState[keycode] = keystate;
}
}
@@ -37,6 +35,13 @@ namespace BizHawk.Bizware.Input
return keyEvents;
}
+ public (int DeltaX, int DeltaY) UpdateMouseInput()
+ {
+ // probably wrong, need to recheck when we actually get macos support
+ CGGetLastMouseDelta(out var deltaX, out var deltaY);
+ return (deltaX, deltaY);
+ }
+
private static readonly IReadOnlyDictionary KeyEnumMap = new Dictionary
{
[CGKeyCode.kVK_ANSI_A] = DistinctKey.A,
diff --git a/src/BizHawk.Bizware.Input/KeyInput/RawKeyInput.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs
similarity index 82%
rename from src/BizHawk.Bizware.Input/KeyInput/RawKeyInput.cs
rename to src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs
index 175f782b21..3968cbc45b 100644
--- a/src/BizHawk.Bizware.Input/KeyInput/RawKeyInput.cs
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs
@@ -1,9 +1,9 @@
#nullable enable
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using BizHawk.Client.Common;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
@@ -13,10 +13,10 @@ using static BizHawk.Common.WmImports;
namespace BizHawk.Bizware.Input
{
///
- /// Note: Only 1 window per device class (i.e. keyboards) is actually allowed to RAWINPUT (last one to call RegisterRawInputDevices)
+ /// Note: Only 1 window per device class (e.g. keyboards) is actually allowed to use RAWINPUT (last one to call RegisterRawInputDevices)
/// So only one instance can actually be used at the same time
///
- internal sealed class RawKeyInput : IKeyInput
+ internal sealed class RawKeyMouseInput : IKeyMouseInput
{
private const int WM_CLOSE = 0x0010;
private const int WM_INPUT = 0x00FF;
@@ -24,6 +24,8 @@ namespace BizHawk.Bizware.Input
private IntPtr RawInputWindow;
private bool _handleAltKbLayouts;
private List _keyEvents = [ ];
+ private (int X, int Y) _mouseDelta;
+ private (int X, int Y) _lastMouseAbsPos;
private readonly object _lockObj = new();
private bool _disposed;
@@ -38,7 +40,7 @@ namespace BizHawk.Bizware.Input
var wc = default(WNDCLASSW);
wc.lpfnWndProc = _wndProc;
wc.hInstance = LoaderApiImports.GetModuleHandleW(null);
- wc.lpszClassName = "RawKeyInputClass";
+ wc.lpszClassName = "RawKeyMouseInputClass";
var atom = RegisterClassW(ref wc);
if (atom == IntPtr.Zero)
@@ -58,15 +60,15 @@ namespace BizHawk.Bizware.Input
}
GCHandle handle;
- RawKeyInput rawKeyInput;
+ RawKeyMouseInput rawKeyMouseInput;
if (uMsg != WM_INPUT)
{
if (uMsg == WM_CLOSE)
{
SetWindowLongPtrW(hWnd, GWLP_USERDATA, IntPtr.Zero);
handle = GCHandle.FromIntPtr(ud);
- rawKeyInput = (RawKeyInput)handle.Target;
- Marshal.FreeCoTaskMem(rawKeyInput.RawInputBuffer);
+ rawKeyMouseInput = (RawKeyMouseInput)handle.Target;
+ Marshal.FreeCoTaskMem(rawKeyMouseInput.RawInputBuffer);
handle.Free();
}
@@ -86,7 +88,7 @@ namespace BizHawk.Bizware.Input
: stackalloc IntPtr[(size + sizeof(IntPtr) - 1) / sizeof(IntPtr)];
handle = GCHandle.FromIntPtr(ud);
- rawKeyInput = (RawKeyInput)handle.Target;
+ rawKeyMouseInput = (RawKeyMouseInput)handle.Target;
fixed (IntPtr* p = buffer)
{
@@ -99,14 +101,19 @@ namespace BizHawk.Bizware.Input
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD)
{
- rawKeyInput.AddKeyInput(&input->data.keyboard);
+ rawKeyMouseInput.AddKeyInput(&input->data.keyboard);
+ }
+
+ if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
+ {
+ rawKeyMouseInput.AddMouseInput(&input->data.mouse);
}
}
while (true)
{
- var rawInputBuffer = (RAWINPUT*)rawKeyInput.RawInputBuffer;
- size = rawKeyInput.RawInputBufferSize;
+ var rawInputBuffer = (RAWINPUT*)rawKeyMouseInput.RawInputBuffer;
+ size = rawKeyMouseInput.RawInputBufferSize;
var count = GetRawInputBuffer(rawInputBuffer, ref size, sizeof(RAWINPUTHEADER));
if (count == 0)
{
@@ -121,8 +128,8 @@ namespace BizHawk.Bizware.Input
const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
- rawKeyInput.RawInputBufferSize *= 2;
- rawKeyInput.RawInputBuffer = Marshal.ReAllocCoTaskMem(rawKeyInput.RawInputBuffer, rawKeyInput.RawInputBufferSize);
+ rawKeyMouseInput.RawInputBufferSize *= 2;
+ rawKeyMouseInput.RawInputBuffer = Marshal.ReAllocCoTaskMem(rawKeyMouseInput.RawInputBuffer, rawKeyMouseInput.RawInputBufferSize);
continue;
}
@@ -133,8 +140,14 @@ namespace BizHawk.Bizware.Input
{
if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD)
{
- var keyboard = (RAWKEYBOARD*)((byte*)&rawInputBuffer->data.keyboard + rawKeyInput.RawInputBufferDataOffset);
- rawKeyInput.AddKeyInput(keyboard);
+ var keyboard = (RAWKEYBOARD*)((byte*)&rawInputBuffer->data.keyboard + rawKeyMouseInput.RawInputBufferDataOffset);
+ rawKeyMouseInput.AddKeyInput(keyboard);
+ }
+
+ if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
+ {
+ var mouse = (RAWMOUSE*)((byte*)&rawInputBuffer->data.mouse + rawKeyMouseInput.RawInputBufferDataOffset);
+ rawKeyMouseInput.AddMouseInput(mouse);
}
var packetSize = rawInputBuffer->header.dwSize;
@@ -174,6 +187,22 @@ namespace BizHawk.Bizware.Input
}
}
+ private unsafe void AddMouseInput(RAWMOUSE* mouse)
+ {
+ // raw input usually doesn't report absolute inputs, only in odd cases with e.g. touchscreen and rdp screen
+ if ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE)
+ {
+ _mouseDelta.X += mouse->lLastX - _lastMouseAbsPos.X;
+ _mouseDelta.Y += mouse->lLastY - _lastMouseAbsPos.Y;
+ _lastMouseAbsPos = (mouse->lLastX, mouse->lLastY);
+ }
+ else // ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_RELATIVE)
+ {
+ _mouseDelta.X += mouse->lLastX;
+ _mouseDelta.Y += mouse->lLastY;
+ }
+ }
+
private static IntPtr CreateRawInputWindow()
{
const int WS_CHILD = 0x40000000;
@@ -196,22 +225,29 @@ namespace BizHawk.Bizware.Input
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()))
+ unsafe
{
- DestroyWindow(window);
- throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
+ var rid = stackalloc RAWINPUTDEVICE[2];
+ rid[0].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
+ rid[0].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD;
+ rid[0].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
+ rid[0].hwndTarget = window;
+ rid[1].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
+ rid[1].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_MOUSE;
+ rid[1].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
+ rid[1].hwndTarget = window;
+
+ if (!RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE)))
+ {
+ DestroyWindow(window);
+ throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
+ }
}
return window;
}
- public RawKeyInput()
+ public RawKeyMouseInput()
{
if (OSTailoredCode.IsUnixHost)
{
@@ -236,7 +272,7 @@ namespace BizHawk.Bizware.Input
RawInputBufferDataOffset = isWow64 ? 8 : 0;
}
- RawInputBufferSize = (Marshal.SizeOf() + RawInputBufferDataOffset) * 16;
+ RawInputBufferSize = (Unsafe.SizeOf() + RawInputBufferDataOffset) * 16;
RawInputBuffer = Marshal.AllocCoTaskMem(RawInputBufferSize);
}
@@ -261,7 +297,7 @@ namespace BizHawk.Bizware.Input
}
}
- public IEnumerable Update(bool handleAltKbLayouts)
+ public IEnumerable UpdateKeyInputs(bool handleAltKbLayouts)
{
lock (_lockObj)
{
@@ -291,6 +327,34 @@ namespace BizHawk.Bizware.Input
}
}
+ public (int DeltaX, int DeltaY) UpdateMouseInput()
+ {
+ lock (_lockObj)
+ {
+ if (_disposed)
+ {
+ return default;
+ }
+
+ if (RawInputWindow == IntPtr.Zero)
+ {
+ RawInputWindow = CreateRawInputWindow();
+ var handle = GCHandle.Alloc(this, GCHandleType.Normal);
+ SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
+ }
+
+ while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(ref msg);
+ DispatchMessageW(ref msg);
+ }
+
+ var ret = _mouseDelta;
+ _mouseDelta = default;
+ return ret;
+ }
+ }
+
private static readonly RawKey[] _rawKeysNoTranslation =
[
RawKey.NUMPAD0,
diff --git a/src/BizHawk.Bizware.Input/KeyInput/X11KeyInput.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/X11KeyMouseInput.cs
similarity index 74%
rename from src/BizHawk.Bizware.Input/KeyInput/X11KeyInput.cs
rename to src/BizHawk.Bizware.Input/KeyMouseInput/X11KeyMouseInput.cs
index a57f33f1e9..f7667eae73 100644
--- a/src/BizHawk.Bizware.Input/KeyInput/X11KeyInput.cs
+++ b/src/BizHawk.Bizware.Input/KeyMouseInput/X11KeyMouseInput.cs
@@ -1,26 +1,28 @@
#nullable enable
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.XInput2Imports;
using static BizHawk.Common.XlibImports;
// a lot of this code is taken from OpenTK
namespace BizHawk.Bizware.Input
{
- internal sealed class X11KeyInput : IKeyInput
+ internal sealed class X11KeyMouseInput : IKeyMouseInput
{
private IntPtr Display;
private readonly bool[] LastKeyState = new bool[256];
private readonly object LockObj = new();
private readonly DistinctKey[] KeyEnumMap = new DistinctKey[256];
+ private readonly bool _supportsXInput2;
+ private readonly int _xi2Opcode;
- public X11KeyInput()
+ public X11KeyMouseInput()
{
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Linux)
{
@@ -32,24 +34,58 @@ namespace BizHawk.Bizware.Input
if (Display == IntPtr.Zero)
{
// There doesn't seem to be a convention for what exception type to throw in these situations. Can't use NRE. Well...
-// _ = Unsafe.AsRef()!; // hmm
+ // _ = Unsafe.AsRef()!; // hmm
// InvalidOperationException doesn't match. Exception it is. --yoshi
throw new Exception("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);
+ // check if we can use XKb
+ int major = 1, minor = 0;
+ var supportsXkb = XkbQueryExtension(Display, out _, out _, out _, ref major, ref minor);
- if (supportsXkb)
+ if (supportsXkb)
+ {
+ // we generally want this behavior
+ XkbSetDetectableAutoRepeat(Display, true, out _);
+ }
+
+ CreateKeyMap(supportsXkb);
+
+ _supportsXInput2 = XQueryExtension(Display, "XInputExtension", out _xi2Opcode, out _, out _);
+ if (_supportsXInput2)
+ {
+ try
{
- // we generally want this behavior
- XkbSetDetectableAutoRepeat(Display, true, out _);
+ (major, minor) = (2, 1);
+ if (XIQueryVersion(Display, ref major, ref minor) != 0
+ || major * 100 + minor < 201)
+ {
+ _supportsXInput2 = false;
+ }
+ }
+ catch
+ {
+ // libXi.so.6 might not be present
+ _supportsXInput2 = false;
}
- CreateKeyMap(supportsXkb);
+ if (_supportsXInput2)
+ {
+ Span maskBuf = stackalloc byte[((int)XIEvents.XI_LASTEVENT + 7) / 8];
+ maskBuf.Clear();
+ XISetMask(maskBuf, (int)XIEvents.XI_RawMotion);
+ unsafe
+ {
+ fixed (byte* maskBufPtr = maskBuf)
+ {
+ XIEventMask eventMask;
+ eventMask.deviceid = XIAllMasterDevices;
+ eventMask.mask = (IntPtr)maskBufPtr;
+ eventMask.mask_len = maskBuf.Length;
+ _ = XISelectEvents(Display, XDefaultRootWindow(Display), ref eventMask, 1);
+ }
+ }
+ }
}
}
@@ -65,23 +101,19 @@ namespace BizHawk.Bizware.Input
}
}
- public unsafe IEnumerable Update(bool handleAltKbLayouts)
+ public unsafe IEnumerable UpdateKeyInputs(bool handleAltKbLayouts)
{
lock (LockObj)
{
// Can't update without a display connection
if (Display == IntPtr.Zero)
{
- return Enumerable.Empty();
+ return [ ];
}
var keys = stackalloc byte[32];
-
- using (new XLock(Display))
- {
- // this apparently always returns 1 no matter what?
- _ = XQueryKeymap(Display, keys);
- }
+ // this apparently always returns 1 no matter what?
+ _ = XQueryKeymap(Display, keys);
var keyEvents = new List();
for (var keycode = 0; keycode < 256; keycode++)
@@ -92,7 +124,7 @@ namespace BizHawk.Bizware.Input
var keystate = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0;
if (LastKeyState[keycode] != keystate)
{
- keyEvents.Add(new(key, pressed: keystate));
+ keyEvents.Add(new(key, Pressed: keystate));
LastKeyState[keycode] = keystate;
}
}
@@ -102,6 +134,86 @@ namespace BizHawk.Bizware.Input
}
}
+ public (int DeltaX, int DeltaY) UpdateMouseInput()
+ {
+ lock (LockObj)
+ {
+ // Can't update without a display connection
+ if (Display == IntPtr.Zero)
+ {
+ return default;
+ }
+
+ // need XInput2 support for this
+ if (!_supportsXInput2)
+ {
+ return default;
+ }
+
+ (double mouseDeltaX, double mouseDeltaY) = (0, 0);
+ while (XPending(Display) > 0)
+ {
+ _ = XNextEvent(Display, out var evt);
+ if (evt.xcookie.type != XEventTypes.GenericEvent
+ || evt.xcookie.extension != _xi2Opcode)
+ {
+ continue;
+ }
+
+ if (!XGetEventData(Display, ref evt.xcookie))
+ {
+ continue;
+ }
+
+ if ((XIEvents)evt.xcookie.evtype == XIEvents.XI_RawMotion)
+ {
+ unsafe
+ {
+ var xiRawEvent = (XIRawEvent*)evt.xcookie.data;
+ var valuatorsMask = new Span(xiRawEvent->valuators.mask, xiRawEvent->valuators.mask_len);
+ if (!valuatorsMask.IsEmpty)
+ {
+ var rawValueIndex = 0;
+
+ // not implemented until netcore / netstandard2.1
+ // copied from modern runtime
+ static bool IsNormal(double d)
+ {
+ var bits = BitConverter.DoubleToInt64Bits(d);
+ bits &= 0x7FFFFFFFFFFFFFFF;
+ return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) == 0);
+ }
+
+ if (XIMaskIsSet(valuatorsMask, 0))
+ {
+ var deltaX = xiRawEvent->raw_values[rawValueIndex];
+ if (IsNormal(deltaX))
+ {
+ mouseDeltaX += deltaX;
+ }
+
+ rawValueIndex++;
+ }
+
+ if (XIMaskIsSet(valuatorsMask, 1))
+ {
+ var deltaY = xiRawEvent->raw_values[rawValueIndex];
+ if (IsNormal(deltaY))
+ {
+ mouseDeltaY += deltaY;
+ }
+ }
+ }
+ }
+ }
+
+ _ = XFreeEventData(Display, ref evt.xcookie);
+ }
+
+ return ((int)mouseDeltaX, (int)mouseDeltaY);
+ }
+ }
+
private unsafe void CreateKeyMap(bool supportsXkb)
{
for (var i = 0; i < KeyEnumMap.Length; i++)
diff --git a/src/BizHawk.Bizware.Input/OSTailoredKeyInputAdapter.cs b/src/BizHawk.Bizware.Input/OSTailoredKeyInputAdapter.cs
index 33a601f592..b9fa23ae79 100644
--- a/src/BizHawk.Bizware.Input/OSTailoredKeyInputAdapter.cs
+++ b/src/BizHawk.Bizware.Input/OSTailoredKeyInputAdapter.cs
@@ -3,54 +3,53 @@
using System.Collections.Generic;
using System.Linq;
-using BizHawk.Client.Common;
-
namespace BizHawk.Bizware.Input
{
///
- /// Abstract class which only handles keyboard input
+ /// Abstract class which only handles keyboard and mouse 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 (must use XWayland, which Wayland users need to use anyways for BizHawk)
///
- public abstract class OSTailoredKeyInputAdapter : IHostInputAdapter
+ public abstract class OSTailoredKeyMouseInputAdapter : IHostInputAdapter
{
- private IKeyInput? _keyInput;
- protected Config? _config;
+ private IKeyMouseInput? _keyMouseInput;
+ protected Func? _getHandleAlternateKeyboardLayouts;
public abstract string Desc { get; }
public virtual void DeInitAll()
- => _keyInput!.Dispose();
+ => _keyMouseInput!.Dispose();
public virtual void FirstInitAll(IntPtr mainFormHandle)
{
- _keyInput = KeyInputFactory.CreateKeyInput();
+ _keyMouseInput = KeyMouseInputFactory.CreateKeyMouseInput();
IPCKeyInput.Initialize(); // why not? this isn't necessarily OS specific
}
public abstract IReadOnlyDictionary> GetHapticsChannels();
- public abstract void ReInitGamepads(IntPtr mainFormHandle);
-
public abstract void PreprocessHostGamepads();
- public abstract void ProcessHostGamepads(Action handleButton, Action handleAxis);
+ public abstract void ProcessHostGamepads(Action handleButton, Action handleAxis);
public virtual IEnumerable ProcessHostKeyboards()
{
- if (_config is null)
+ if (_getHandleAlternateKeyboardLayouts is null)
{
- throw new InvalidOperationException(nameof(ProcessHostKeyboards) + " called before the global config was passed");
+ throw new InvalidOperationException(nameof(ProcessHostKeyboards) + " called before alternate keyboard layout enable callback was set");
}
- var ret = _keyInput!.Update(_config.HandleAlternateKeyboardLayouts);
+ var ret = _keyMouseInput!.UpdateKeyInputs(_getHandleAlternateKeyboardLayouts());
return ret.Concat(IPCKeyInput.Update());
}
+ public virtual (int DeltaX, int DeltaY) ProcessHostMice()
+ => _keyMouseInput!.UpdateMouseInput();
+
public abstract void SetHaptics(IReadOnlyCollection<(string Name, int Strength)> hapticsSnapshot);
- public virtual void UpdateConfig(Config config)
- => _config = config;
+ public virtual void SetAlternateKeyboardLayoutEnableCallback(Func getHandleAlternateKeyboardLayouts)
+ => _getHandleAlternateKeyboardLayouts = getHandleAlternateKeyboardLayouts;
}
}
diff --git a/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs b/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs
index 5697a234d5..06a7bbfc6f 100644
--- a/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs
+++ b/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using BizHawk.Client.Common;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
#if BIZHAWKBUILD_DEBUG_RUMBLE
@@ -16,9 +15,9 @@ using static SDL2.SDL;
namespace BizHawk.Bizware.Input
{
- public sealed class SDL2InputAdapter : OSTailoredKeyInputAdapter
+ public sealed class SDL2InputAdapter : OSTailoredKeyMouseInputAdapter
{
- private static readonly IReadOnlyCollection SDL2_HAPTIC_CHANNEL_NAMES = new[] { "Left", "Right" };
+ private static readonly IReadOnlyCollection SDL2_HAPTIC_CHANNEL_NAMES = [ "Left", "Right" ];
private IReadOnlyDictionary _lastHapticsSnapshot = new Dictionary();
@@ -130,11 +129,7 @@ namespace BizHawk.Bizware.Input
.Where(pad => pad.HasRumble)
.Select(pad => pad.InputNamePrefix)
.ToDictionary(s => s, _ => SDL2_HAPTIC_CHANNEL_NAMES)
- : new();
- }
-
- public override void ReInitGamepads(IntPtr mainFormHandle)
- {
+ : [ ];
}
public override void PreprocessHostGamepads()
@@ -153,15 +148,15 @@ namespace BizHawk.Bizware.Input
DoSDLEventLoop();
}
- public override void ProcessHostGamepads(Action handleButton, Action handleAxis)
+ public override void ProcessHostGamepads(Action handleButton, Action handleAxis)
{
if (!_isInit) return;
foreach (var pad in SDL2Gamepad.EnumerateDevices())
{
- foreach (var but in pad.ButtonGetters)
+ foreach (var (ButtonName, GetIsPressed) in pad.ButtonGetters)
{
- handleButton(pad.InputNamePrefix + but.ButtonName, but.GetIsPressed(), ClientInputFocus.Pad);
+ handleButton(pad.InputNamePrefix + ButtonName, GetIsPressed(), HostInputFocus.Pad);
}
foreach (var (axisID, f) in pad.GetAxes())
@@ -188,7 +183,7 @@ namespace BizHawk.Bizware.Input
{
return _isInit
? base.ProcessHostKeyboards()
- : Enumerable.Empty();
+ : [ ];
}
public override void SetHaptics(IReadOnlyCollection<(string Name, int Strength)> hapticsSnapshot)
diff --git a/src/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/src/BizHawk.Client.Common/BizHawk.Client.Common.csproj
index 8a878ebfde..4eacc8bd3a 100644
--- a/src/BizHawk.Client.Common/BizHawk.Client.Common.csproj
+++ b/src/BizHawk.Client.Common/BizHawk.Client.Common.csproj
@@ -16,6 +16,7 @@
+
diff --git a/src/BizHawk.Client.Common/input/DistinctKeyNameOverrides.cs b/src/BizHawk.Client.Common/input/DistinctKeyNameOverrides.cs
index ce04224e53..da85486454 100644
--- a/src/BizHawk.Client.Common/input/DistinctKeyNameOverrides.cs
+++ b/src/BizHawk.Client.Common/input/DistinctKeyNameOverrides.cs
@@ -1,8 +1,10 @@
+using BizHawk.Bizware.Input;
+
namespace BizHawk.Client.Common
{
public static class DistinctKeyNameOverrides
{
- public static string GetName(in DistinctKey k)
+ public static string GetName(DistinctKey k)
=> k switch
{
DistinctKey.Back => "Backspace",
diff --git a/src/BizHawk.Client.Common/input/InputEvent.cs b/src/BizHawk.Client.Common/input/InputEvent.cs
index bfb2017f92..fcbd07e07f 100644
--- a/src/BizHawk.Client.Common/input/InputEvent.cs
+++ b/src/BizHawk.Client.Common/input/InputEvent.cs
@@ -3,24 +3,17 @@
using System.Collections.Generic;
using System.Text;
+using BizHawk.Bizware.Input;
+
namespace BizHawk.Client.Common
{
- [Flags]
- public enum ClientInputFocus
- {
- None = 0,
- Mouse = 1,
- Keyboard = 2,
- Pad = 4
- }
-
public class InputEvent
{
public InputEventType EventType;
public LogicalButton LogicalButton;
- public ClientInputFocus Source;
+ public HostInputFocus Source;
public override string ToString() => $"{EventType}:{LogicalButton}";
}
diff --git a/src/BizHawk.Client.Common/input/KeyEvent.cs b/src/BizHawk.Client.Common/input/KeyEvent.cs
deleted file mode 100644
index 89feec15a8..0000000000
--- a/src/BizHawk.Client.Common/input/KeyEvent.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-#nullable enable
-
-namespace BizHawk.Client.Common
-{
- public readonly struct KeyEvent
- {
- public readonly DistinctKey Key;
-
- public readonly bool Pressed;
-
- public KeyEvent(DistinctKey key, bool pressed)
- {
- Key = key;
- Pressed = pressed;
- }
- }
-}
diff --git a/src/BizHawk.Client.EmuHawk/Input/Input.cs b/src/BizHawk.Client.EmuHawk/Input/Input.cs
index 8dfa91707b..e11770f939 100644
--- a/src/BizHawk.Client.EmuHawk/Input/Input.cs
+++ b/src/BizHawk.Client.EmuHawk/Input/Input.cs
@@ -18,10 +18,10 @@ namespace BizHawk.Client.EmuHawk
/// Why is this receiving a control, but actually using it as a Form (where the WantingMouseFocus is checked?)
/// Because later we might change it to work off the control, specifically, if a control is supplied (normally actually a Form will be supplied)
///
- public void ControlInputFocus(Control c, ClientInputFocus types, bool wants)
+ public void ControlInputFocus(Control c, HostInputFocus types, bool wants)
{
- if (types.HasFlag(ClientInputFocus.Mouse) && wants) _wantingMouseFocus.Add(c);
- if (types.HasFlag(ClientInputFocus.Mouse) && !wants) _wantingMouseFocus.Remove(c);
+ if (types.HasFlag(HostInputFocus.Mouse) && wants) _wantingMouseFocus.Add(c);
+ if (types.HasFlag(HostInputFocus.Mouse) && !wants) _wantingMouseFocus.Remove(c);
}
private readonly HashSet _wantingMouseFocus = new HashSet();
@@ -48,7 +48,7 @@ namespace BizHawk.Client.EmuHawk
Adapter = new SDL2InputAdapter();
Console.WriteLine($"Using {Adapter.Desc} for host input (keyboard + gamepads)");
- Adapter.UpdateConfig(_currentConfig);
+ Adapter.SetAlternateKeyboardLayoutEnableCallback(() => _currentConfig.HandleAlternateKeyboardLayouts);
Adapter.FirstInitAll(mainFormHandle);
_updateThread = new Thread(UpdateThreadProc)
{
@@ -96,7 +96,7 @@ namespace BizHawk.Client.EmuHawk
["Shift"] = "LeftShift",
};
- private void HandleButton(string button, bool newState, ClientInputFocus source)
+ private void HandleButton(string button, bool newState, HostInputFocus source)
{
if (!(_currentConfig.MergeLAndRModifierKeys && ModifierKeyPreMap.TryGetValue(button, out var button1))) button1 = button;
var modIndex = _currentConfig.ModifierKeysEffective.IndexOf(button1);
@@ -135,7 +135,7 @@ namespace BizHawk.Client.EmuHawk
private void HandleAxis(string axis, int newValue)
{
- if (ShouldSwallow(MainFormInputAllowedCallback(false), ClientInputFocus.Pad))
+ if (ShouldSwallow(MainFormInputAllowedCallback(false), HostInputFocus.Pad))
return;
if (_trackDeltas)
@@ -195,9 +195,9 @@ namespace BizHawk.Client.EmuHawk
{
_currentConfig = _getConfigCallback();
UpdateModifierKeysEffective();
- Adapter.UpdateConfig(_currentConfig);
var keyEvents = Adapter.ProcessHostKeyboards();
+ var (mouseDeltaX, mouseDeltaY) = Adapter.ProcessHostMice();
Adapter.PreprocessHostGamepads();
//this block is going to massively modify data structures that the binding method uses, so we have to lock it all
@@ -208,7 +208,7 @@ namespace BizHawk.Client.EmuHawk
//analyze keys
foreach (var ke in keyEvents)
{
- HandleButton(DistinctKeyNameOverrides.GetName(in ke.Key), ke.Pressed, ClientInputFocus.Keyboard);
+ HandleButton(DistinctKeyNameOverrides.GetName(ke.Key), ke.Pressed, HostInputFocus.Keyboard);
}
lock (_axisValues)
@@ -217,7 +217,6 @@ namespace BizHawk.Client.EmuHawk
Adapter.ProcessHostGamepads(HandleButton, HandleAxis);
// analyze moose
- // other sorts of mouse api (raw input) could easily be added as a separate listing under a different class
if (_wantingMouseFocus.Contains(Form.ActiveForm))
{
var mousePos = Control.MousePosition;
@@ -235,11 +234,15 @@ namespace BizHawk.Client.EmuHawk
_axisValues["WMouse Y"] = mousePos.Y;
var mouseBtns = Control.MouseButtons;
- HandleButton("WMouse L", (mouseBtns & MouseButtons.Left) != 0, ClientInputFocus.Mouse);
- HandleButton("WMouse M", (mouseBtns & MouseButtons.Middle) != 0, ClientInputFocus.Mouse);
- HandleButton("WMouse R", (mouseBtns & MouseButtons.Right) != 0, ClientInputFocus.Mouse);
- HandleButton("WMouse 1", (mouseBtns & MouseButtons.XButton1) != 0, ClientInputFocus.Mouse);
- HandleButton("WMouse 2", (mouseBtns & MouseButtons.XButton2) != 0, ClientInputFocus.Mouse);
+ HandleButton("WMouse L", (mouseBtns & MouseButtons.Left) != 0, HostInputFocus.Mouse);
+ HandleButton("WMouse M", (mouseBtns & MouseButtons.Middle) != 0, HostInputFocus.Mouse);
+ HandleButton("WMouse R", (mouseBtns & MouseButtons.Right) != 0, HostInputFocus.Mouse);
+ HandleButton("WMouse 1", (mouseBtns & MouseButtons.XButton1) != 0, HostInputFocus.Mouse);
+ HandleButton("WMouse 2", (mouseBtns & MouseButtons.XButton2) != 0, HostInputFocus.Mouse);
+
+ // raw (relative) mouse input
+ _axisValues["RMouse X"] = mouseDeltaX;
+ _axisValues["RMouse Y"] = mouseDeltaY;
}
else
{
@@ -279,9 +282,9 @@ namespace BizHawk.Client.EmuHawk
}
}
- private static bool ShouldSwallow(AllowInput allowInput, ClientInputFocus inputFocus)
+ private static bool ShouldSwallow(AllowInput allowInput, HostInputFocus inputFocus)
{
- return allowInput == AllowInput.None || (allowInput == AllowInput.OnlyController && inputFocus != ClientInputFocus.Pad);
+ return allowInput == AllowInput.None || (allowInput == AllowInput.OnlyController && inputFocus != HostInputFocus.Pad);
}
public void StartListeningForAxisEvents()
diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs
index c8ea9768bf..092712f143 100644
--- a/src/BizHawk.Client.EmuHawk/MainForm.cs
+++ b/src/BizHawk.Client.EmuHawk/MainForm.cs
@@ -15,6 +15,7 @@ using System.Security.Principal;
using System.IO.Pipes;
using BizHawk.Bizware.Graphics;
+using BizHawk.Bizware.Input;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
@@ -58,9 +59,9 @@ namespace BizHawk.Client.EmuHawk
private readonly ToolStripMenuItemEx NullHawkVSysSubmenu = new() { Enabled = false, Text = "—" };
private void MainForm_Load(object sender, EventArgs e)
- {
+ {
UpdateWindowTitle();
-
+
{
for (int i = 1; i <= WINDOW_SCALE_MAX; i++)
{
@@ -1163,12 +1164,12 @@ namespace BizHawk.Client.EmuHawk
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
- Input.Instance.ControlInputFocus(this, ClientInputFocus.Mouse, true);
+ Input.Instance.ControlInputFocus(this, HostInputFocus.Mouse, true);
}
protected override void OnDeactivate(EventArgs e)
{
- Input.Instance.ControlInputFocus(this, ClientInputFocus.Mouse, false);
+ Input.Instance.ControlInputFocus(this, HostInputFocus.Mouse, false);
base.OnDeactivate(e);
}
@@ -2801,8 +2802,6 @@ namespace BizHawk.Client.EmuHawk
// Alt key hacks
protected override void WndProc(ref Message m)
{
- if (m.Msg == WmDeviceChange) Input.Instance.Adapter.ReInitGamepads(Handle);
-
// this is necessary to trap plain alt keypresses so that only our hotkey system gets them
if (m.Msg == 0x0112) // WM_SYSCOMMAND
{
diff --git a/src/BizHawk.Common/LSB/XInput2Imports.cs b/src/BizHawk.Common/LSB/XInput2Imports.cs
new file mode 100644
index 0000000000..85696f1f56
--- /dev/null
+++ b/src/BizHawk.Common/LSB/XInput2Imports.cs
@@ -0,0 +1,96 @@
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Common
+{
+ public static class XInput2Imports
+ {
+ private const string XI2 = "libXi.so.6";
+
+ [DllImport(XI2)]
+ public static extern int XIQueryVersion(IntPtr display, ref int major_version_inout, ref int minor_version_inout);
+
+ public enum XIEvents
+ {
+ XI_DeviceChanged = 1,
+ XI_KeyPress = 2,
+ XI_KeyRelease = 3,
+ XI_ButtonPress = 4,
+ XI_ButtonRelease = 5,
+ XI_Motion = 6,
+ XI_Enter = 7,
+ XI_Leave = 8,
+ XI_FocusIn = 9,
+ XI_FocusOut = 10,
+ XI_HierarchyChanged = 11,
+ XI_PropertyEvent = 12,
+ XI_RawKeyPress = 13,
+ XI_RawKeyRelease = 14,
+ XI_RawButtonPress = 15,
+ XI_RawButtonRelease = 16,
+ XI_RawMotion = 17,
+ XI_TouchBegin = 18, // XI 2.2
+ XI_TouchUpdate = 19,
+ XI_TouchEnd = 20,
+ XI_TouchOwnership = 21,
+ XI_RawTouchBegin = 22,
+ XI_RawTouchUpdate = 23,
+ XI_RawTouchEnd = 24,
+ XI_BarrierHit = 25, // XI 2.3
+ XI_BarrierLeave = 26,
+ XI_GesturePinchBegin = 27, // XI 2.4
+ XI_GesturePinchUpdate = 28,
+ XI_GesturePinchEnd = 29,
+ XI_GestureSwipeBegin = 30,
+ XI_GestureSwipeUpdate = 31,
+ XI_GestureSwipeEnd = 32,
+ XI_LASTEVENT = XI_GestureSwipeEnd
+ }
+
+ // these are normally macros in XI2.h
+ public static void XISetMask(Span maskBuf, int evt)
+ => maskBuf[evt >> 3] |= (byte)(1 << (evt & 7));
+
+ public static bool XIMaskIsSet(Span maskBuf, int evt)
+ => (maskBuf[evt >> 3] & (byte)(1 << (evt & 7))) != 0;
+
+ public const int XIAllDevices = 0;
+ public const int XIAllMasterDevices = 1;
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct XIEventMask
+ {
+ public int deviceid;
+ public int mask_len;
+ public IntPtr mask;
+ }
+
+ [DllImport(XI2)]
+ public static extern int XISelectEvents(IntPtr display, IntPtr win, ref XIEventMask masks, int num_masks);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct XIValuatorState
+ {
+ public int mask_len;
+ public byte* mask;
+ public double* values;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct XIRawEvent
+ {
+ public int type;
+ public nuint serial;
+ public int send_event;
+ public IntPtr display;
+ public int extension;
+ public int evtype;
+ public nuint time;
+ public int deviceid;
+ public int sourceid;
+ public int detail;
+ public int flags;
+ public XIValuatorState valuators;
+ public unsafe double* raw_values;
+ }
+ }
+}
diff --git a/src/BizHawk.Common/LSB/XlibImports.cs b/src/BizHawk.Common/LSB/XlibImports.cs
index 96d504ae55..f760c3196e 100644
--- a/src/BizHawk.Common/LSB/XlibImports.cs
+++ b/src/BizHawk.Common/LSB/XlibImports.cs
@@ -6,18 +6,18 @@ namespace BizHawk.Common
{
public static class XlibImports
{
- private const string LIB = "libX11.so.6";
+ private const string XLIB = "libX11.so.6";
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern IntPtr XOpenDisplay(string? display_name);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern int XCloseDisplay(IntPtr display);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern void XLockDisplay(IntPtr display);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern void XUnlockDisplay(IntPtr display);
// helper struct for XLockDisplay/XUnlockDisplay
@@ -47,7 +47,98 @@ namespace BizHawk.Common
}
}
- [DllImport(LIB)]
+ [DllImport(XLIB)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool XQueryExtension(IntPtr display, string name, out int major_opcode_return, out int first_event_return, out int first_error_return);
+
+ [DllImport(XLIB)]
+ public static extern IntPtr XDefaultRootWindow(IntPtr display);
+
+ [DllImport(XLIB)]
+ public static extern int XPending(IntPtr display);
+
+ public enum XEventTypes : int
+ {
+ KeyPress = 2,
+ KeyRelease = 3,
+ ButtonPress = 4,
+ ButtonRelease = 5,
+ MotionNotify = 6,
+ EnterNotify = 7,
+ LeaveNotify = 8,
+ FocusIn = 9,
+ FocusOut = 10,
+ KeymapNotify = 11,
+ Expose = 12,
+ GraphicsExpose = 13,
+ NoExpose = 14,
+ VisibilityNotify = 15,
+ CreateNotify = 16,
+ DestroyNotify = 17,
+ UnmapNotify = 18,
+ MapNotify = 19,
+ MapRequest = 20,
+ ReparentNotify = 21,
+ ConfigureNotify = 22,
+ ConfigureRequest = 23,
+ GravityNotify = 24,
+ ResizeRequest = 25,
+ CirculateNotify = 26,
+ CirculateRequest = 27,
+ PropertyNotify = 28,
+ SelectionClear = 29,
+ SelectionRequest = 30,
+ SelectionNotify = 31,
+ ColormapNotify = 32,
+ ClientMessage = 33,
+ MappingNotify = 34,
+ GenericEvent = 35,
+ LASTEvent = 36
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct XGenericEventCookie
+ {
+ public XEventTypes type;
+ public nuint serial;
+ public int send_event;
+ public IntPtr display;
+ public int extension;
+ public int evtype;
+ public uint cookie;
+ public IntPtr data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct XEventPadding
+ {
+ public nint pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+ public nint pad8, pad9, pad10, pad11, pad12, pad13, pad14, pad15;
+ public nint pad16, pad17, pad18, pad19, pad20, pad21, pad22, pad23;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct XEvent
+ {
+ [FieldOffset(0)]
+ public XEventTypes type;
+ [FieldOffset(0)]
+ public XGenericEventCookie xcookie;
+ [FieldOffset(0)]
+ public XEventPadding pad;
+ }
+
+ [DllImport(XLIB)]
+ public static extern int XNextEvent(IntPtr display, out XEvent event_return);
+
+ [DllImport(XLIB)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool XGetEventData(IntPtr display, ref XGenericEventCookie cookie);
+
+ [DllImport(XLIB)]
+ public static extern int XFreeEventData(IntPtr display, ref XGenericEventCookie cookie);
+
+ [DllImport(XLIB)]
public static extern unsafe int XQueryKeymap(IntPtr display, byte* keys_return);
// copied from OpenTK
@@ -464,15 +555,15 @@ namespace BizHawk.Common
public bool same_screen;
}
- [DllImport(LIB)]
+ [DllImport(XLIB)]
[return: MarshalAs(UnmanagedType.SysUInt)]
public static extern Keysym XLookupKeysym(ref XKeyEvent key_event, int index);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool XkbQueryExtension(IntPtr display, out int opcode_rtrn, out int event_rtrn, out int error_rtrn, ref int major_in_out, ref int minor_in_out);
-
- [DllImport(LIB)]
+
+ [DllImport(XLIB)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool XkbSetDetectableAutoRepeat(IntPtr display, [MarshalAs(UnmanagedType.Bool)] bool detectable, [MarshalAs(UnmanagedType.Bool)] out bool supported_rtrn);
@@ -531,16 +622,16 @@ namespace BizHawk.Common
public IntPtr geom;
}
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern unsafe XkbDescRec* XkbAllocKeyboard(IntPtr display);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern unsafe void XkbFreeKeyboard(XkbDescRec* xkb, int which, [MarshalAs(UnmanagedType.Bool)] bool free_all);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern unsafe int XkbGetNames(IntPtr display, uint which, XkbDescRec* xkb);
- [DllImport(LIB)]
+ [DllImport(XLIB)]
public static extern Keysym XkbKeycodeToKeysym(IntPtr display, int keycode, int group, int level);
}
}
diff --git a/src/BizHawk.Common/Win32/RawInputImports.cs b/src/BizHawk.Common/Win32/RawInputImports.cs
index d060fb2bcf..52175dc7d3 100644
--- a/src/BizHawk.Common/Win32/RawInputImports.cs
+++ b/src/BizHawk.Common/Win32/RawInputImports.cs
@@ -427,12 +427,22 @@ namespace BizHawk.Common
[StructLayout(LayoutKind.Sequential)]
public struct RAWMOUSE
{
- public ushort usFlags;
+ public MOUSE_FLAGS usFlags;
public uint ulButtons;
public uint ulRawButtons;
public int lLastX;
public int lLastY;
public uint ulExtraInformation;
+
+ [Flags]
+ public enum MOUSE_FLAGS : ushort
+ {
+ MOVE_RELATIVE = 0,
+ MOVE_ABSOLUTE = 1,
+ VIRTUAL_DESKTOP = 2,
+ ATTRIBUTES_CHANGED = 4,
+ MOVE_NOCOALESCE = 8,
+ }
}
[StructLayout(LayoutKind.Sequential)]
@@ -492,7 +502,7 @@ namespace BizHawk.Common
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool RegisterRawInputDevices(ref RAWINPUTDEVICE pRawInputDevice, uint uiNumDevices, int cbSize);
+ public static extern unsafe bool RegisterRawInputDevices(RAWINPUTDEVICE* pRawInputDevice, uint uiNumDevices, int cbSize);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
diff --git a/src/BizHawk.Common/macOS/QuartzImports.cs b/src/BizHawk.Common/macOS/QuartzImports.cs
index 808a0b5c5b..cd590638ec 100644
--- a/src/BizHawk.Common/macOS/QuartzImports.cs
+++ b/src/BizHawk.Common/macOS/QuartzImports.cs
@@ -130,5 +130,8 @@ namespace BizHawk.Common
[DllImport("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics")]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool CGEventSourceKeyState(CGEventSourceStateID stateID, CGKeyCode key);
+
+ [DllImport("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics")]
+ public static extern void CGGetLastMouseDelta(out int deltaX, out int deltaY);
}
}