Skip to content

Commit 964a753

Browse files
committed
Outsource Windows API definitions (extern) to CsWin32
1 parent e85403a commit 964a753

40 files changed

+645
-1165
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />
1919
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
2020
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
21+
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
2122
<PackageVersion Include="MSTest" Version="3.8.3" />
2223
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
2324
<PackageVersion Include="Nullable" Version="1.3.1" />

src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs

Lines changed: 98 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,28 @@
77
using BizHawk.Common;
88
using BizHawk.Common.CollectionExtensions;
99

10+
using Windows.Win32;
11+
using Windows.Win32.Foundation;
12+
using Windows.Win32.UI.Input;
13+
using Windows.Win32.UI.Input.KeyboardAndMouse;
14+
using Windows.Win32.UI.WindowsAndMessaging;
15+
1016
using static BizHawk.Common.RawInputImports;
11-
using static BizHawk.Common.WmImports;
17+
using static BizHawk.Common.WmImports1;
18+
using static Windows.Win32.Win32Imports;
1219

1320
namespace BizHawk.Bizware.Input
1421
{
1522
/// <summary>
1623
/// Note: Only 1 window per device class (e.g. keyboards) is actually allowed to use RAWINPUT (last one to call RegisterRawInputDevices)
1724
/// So only one instance can actually be used at the same time
1825
/// </summary>
19-
internal sealed class RawKeyMouseInput : IKeyMouseInput
26+
internal sealed unsafe class RawKeyMouseInput : IKeyMouseInput
2027
{
2128
private const int WM_CLOSE = 0x0010;
2229
private const int WM_INPUT = 0x00FF;
2330

24-
private IntPtr RawInputWindow;
31+
private HWND RawInputWindow;
2532
private bool _handleAltKbLayouts;
2633
private List<KeyEvent> _keyEvents = [ ];
2734
private (int X, int Y) _mouseDelta;
@@ -33,27 +40,29 @@ internal sealed class RawKeyMouseInput : IKeyMouseInput
3340
private int RawInputBufferSize;
3441
private readonly int RawInputBufferDataOffset;
3542

36-
private static readonly WNDPROC _wndProc = WndProc;
37-
38-
private static readonly Lazy<IntPtr> _rawInputWindowAtom = new(() =>
43+
private static unsafe readonly Lazy<PCWSTR> _rawInputWindowAtom = new(() =>
3944
{
40-
var wc = default(WNDCLASSW);
41-
wc.lpfnWndProc = _wndProc;
42-
wc.hInstance = LoaderApiImports.GetModuleHandleW(null);
43-
wc.lpszClassName = "RawKeyMouseInputClass";
44-
45-
var atom = RegisterClassW(ref wc);
46-
if (atom == IntPtr.Zero)
45+
WNDCLASSW wc = default;
46+
wc.lpfnWndProc = WndProc;
47+
wc.hInstance = GetModuleHandleW(default(PCWSTR));
48+
var lpszClassNameStr = "RawKeyMouseInputClass";
49+
PCWSTR atom;
50+
fixed (char* lpszClassName = lpszClassNameStr)
51+
{
52+
wc.lpszClassName = lpszClassName;
53+
atom = MAKEINTATOM(RegisterClassW(in wc));
54+
}
55+
if (atom.Value is null)
4756
{
4857
throw new InvalidOperationException("Failed to register RAWINPUT window class");
4958
}
5059

5160
return atom;
5261
});
5362

54-
private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
63+
private static LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam)
5564
{
56-
var ud = GetWindowLongPtrW(hWnd, GWLP_USERDATA);
65+
var ud = GetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA);
5766
if (ud == IntPtr.Zero)
5867
{
5968
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
@@ -65,7 +74,7 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
6574
{
6675
if (uMsg == WM_CLOSE)
6776
{
68-
SetWindowLongPtrW(hWnd, GWLP_USERDATA, IntPtr.Zero);
77+
SetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, IntPtr.Zero);
6978
handle = GCHandle.FromIntPtr(ud);
7079
rawKeyMouseInput = (RawKeyMouseInput)handle.Target;
7180
Marshal.FreeCoTaskMem(rawKeyMouseInput.RawInputBuffer);
@@ -75,36 +84,48 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
7584
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
7685
}
7786

78-
if (GetRawInputData(lParam, RID.INPUT, IntPtr.Zero,
79-
out var size, sizeof(RAWINPUTHEADER)) == -1)
87+
uint size = default;
88+
if (GetRawInputData(
89+
new(lParam),
90+
RAW_INPUT_DATA_COMMAND_FLAGS.RID_INPUT,
91+
pData: default,
92+
ref size,
93+
cbSizeHeader: unchecked((uint) sizeof(RAWINPUTHEADER)))
94+
is uint.MaxValue/*-1*/)
8095
{
8196
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
8297
}
8398

8499
// don't think size should ever be this big, but just in case
100+
var allocOnHeap = size > 1024;
85101
// also, make sure to align the buffer to a pointer boundary
86-
var buffer = size > 1024
87-
? new IntPtr[(size + sizeof(IntPtr) - 1) / sizeof(IntPtr)]
88-
: stackalloc IntPtr[(size + sizeof(IntPtr) - 1) / sizeof(IntPtr)];
102+
var roundedSize = unchecked((int) (size / sizeof(IntPtr)));
103+
if ((size & 0b11U) is not 0U) roundedSize++;
104+
var buffer = allocOnHeap ? new IntPtr[roundedSize] : stackalloc IntPtr[roundedSize];
89105

90106
handle = GCHandle.FromIntPtr(ud);
91107
rawKeyMouseInput = (RawKeyMouseInput)handle.Target;
92108

93109
fixed (IntPtr* p = buffer)
94110
{
95111
var input = (RAWINPUT*)p;
96-
if (GetRawInputData(lParam, RID.INPUT, input,
97-
ref size, sizeof(RAWINPUTHEADER)) == -1)
112+
if (GetRawInputData(
113+
new(lParam),
114+
RAW_INPUT_DATA_COMMAND_FLAGS.RID_INPUT,
115+
pData: input,
116+
ref size,
117+
cbSizeHeader: unchecked((uint) sizeof(RAWINPUTHEADER)))
118+
is uint.MaxValue/*-1*/)
98119
{
99120
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
100121
}
101122

102-
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD)
123+
if (input->header.dwType == RIM_TYPE.KEYBOARD)
103124
{
104125
rawKeyMouseInput.AddKeyInput(&input->data.keyboard);
105126
}
106127

107-
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
128+
if (input->header.dwType == RIM_TYPE.MOUSE)
108129
{
109130
rawKeyMouseInput.AddMouseInput(&input->data.mouse);
110131
}
@@ -113,14 +134,14 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
113134
while (true)
114135
{
115136
var rawInputBuffer = (RAWINPUT*)rawKeyMouseInput.RawInputBuffer;
116-
size = rawKeyMouseInput.RawInputBufferSize;
117-
var count = GetRawInputBuffer(rawInputBuffer, ref size, sizeof(RAWINPUTHEADER));
137+
size = unchecked((uint) rawKeyMouseInput.RawInputBufferSize);
138+
var count = GetRawInputBuffer(rawInputBuffer, ref size, unchecked((uint) sizeof(RAWINPUTHEADER)));
118139
if (count == 0)
119140
{
120141
break;
121142
}
122143

123-
if (count == -1)
144+
if (count is uint.MaxValue/*-1*/)
124145
{
125146
// From testing, it appears this never actually occurs in practice
126147
// As GetRawInputBuffer will succeed as long as the buffer has room for at least 1 packet
@@ -136,15 +157,15 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
136157
break;
137158
}
138159

139-
for (var i = 0u; i < (uint)count; i++)
160+
for (var i = 0U; i < count; i++)
140161
{
141-
if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD)
162+
if (rawInputBuffer->header.dwType == RIM_TYPE.KEYBOARD)
142163
{
143164
var keyboard = (RAWKEYBOARD*)((byte*)&rawInputBuffer->data.keyboard + rawKeyMouseInput.RawInputBufferDataOffset);
144165
rawKeyMouseInput.AddKeyInput(keyboard);
145166
}
146167

147-
if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
168+
if (rawInputBuffer->header.dwType == RIM_TYPE.MOUSE)
148169
{
149170
var mouse = (RAWMOUSE*)((byte*)&rawInputBuffer->data.mouse + rawKeyMouseInput.RawInputBufferDataOffset);
150171
rawKeyMouseInput.AddMouseInput(mouse);
@@ -157,20 +178,20 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
157178
}
158179
}
159180

160-
return IntPtr.Zero;
181+
return default;
161182
}
162183

163184
private unsafe void AddKeyInput(RAWKEYBOARD* keyboard)
164185
{
165-
if ((keyboard->Flags & ~(RAWKEYBOARD.RI_KEY.E0 | RAWKEYBOARD.RI_KEY.BREAK)) == 0)
186+
if ((keyboard->Flags & ~(RI_KEY.E0 | RI_KEY.BREAK)) == 0)
166187
{
167-
var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RAWKEYBOARD.RI_KEY.E0) != 0 ? 0x80 : 0));
188+
var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RI_KEY.E0) != 0 ? 0x80 : 0));
168189

169190
// kind of a dumb hack, the Pause key is apparently special here
170191
// keyboards would send scancode 0x1D with an E1 prefix, followed by 0x45 (which is NumLock!)
171192
// this "NumLock" press will set the VKey to 255 (invalid VKey), so we can use that to know if this is actually a Pause press
172193
// (note that DIK_PAUSE is just 0x45 with an E0 prefix, although this is likely just a conversion DirectInput does internally)
173-
if (rawKey == RawKey.NUMLOCK && keyboard->VKey == VirtualKey.VK_NONE)
194+
if (rawKey is RawKey.NUMLOCK && (VirtualKey) keyboard->VKey is VirtualKey.VK_NONE)
174195
{
175196
rawKey = RawKey.PAUSE;
176197
}
@@ -182,62 +203,64 @@ private unsafe void AddKeyInput(RAWKEYBOARD* keyboard)
182203

183204
if (KeyEnumMap.TryGetValue(rawKey, out var key))
184205
{
185-
_keyEvents.Add(new(key, (keyboard->Flags & RAWKEYBOARD.RI_KEY.BREAK) == RAWKEYBOARD.RI_KEY.MAKE));
206+
_keyEvents.Add(new(key, (keyboard->Flags & RI_KEY.BREAK) == RI_KEY.MAKE));
186207
}
187208
}
188209
}
189210

190211
private unsafe void AddMouseInput(RAWMOUSE* mouse)
191212
{
192213
// raw input usually doesn't report absolute inputs, only in odd cases with e.g. touchscreen and rdp screen
193-
if ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE)
214+
if ((mouse->usFlags & MOUSE_STATE.MOUSE_MOVE_ABSOLUTE) == MOUSE_STATE.MOUSE_MOVE_ABSOLUTE)
194215
{
195216
_mouseDelta.X += mouse->lLastX - _lastMouseAbsPos.X;
196217
_mouseDelta.Y += mouse->lLastY - _lastMouseAbsPos.Y;
197218
_lastMouseAbsPos = (mouse->lLastX, mouse->lLastY);
198219
}
199-
else // ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_RELATIVE)
220+
else // ((mouse->usFlags & MOUSE_STATE.MOUSE_MOVE_ABSOLUTE) == MOUSE_STATE.MOUSE_MOVE_RELATIVE)
200221
{
201222
_mouseDelta.X += mouse->lLastX;
202223
_mouseDelta.Y += mouse->lLastY;
203224
}
204225
}
205226

206-
private static IntPtr CreateRawInputWindow()
227+
private static unsafe HWND CreateRawInputWindow()
207228
{
208-
const int WS_CHILD = 0x40000000;
209-
var window = CreateWindowExW(
229+
var lpWindowNameStr = "RawKeyInput";
230+
HWND window;
231+
fixed (char* lpWindowName = lpWindowNameStr)
232+
{
233+
window = CreateWindowExW(
210234
dwExStyle: 0,
211235
lpClassName: _rawInputWindowAtom.Value,
212-
lpWindowName: "RawKeyInput",
213-
dwStyle: WS_CHILD,
236+
lpWindowName: lpWindowName,
237+
dwStyle: WINDOW_STYLE.WS_CHILD,
214238
X: 0,
215239
Y: 0,
216240
nWidth: 1,
217241
nHeight: 1,
218-
hWndParent: HWND_MESSAGE,
219-
hMenu: IntPtr.Zero,
220-
hInstance: LoaderApiImports.GetModuleHandleW(null),
221-
lpParam: IntPtr.Zero);
222-
223-
if (window == IntPtr.Zero)
242+
hWndParent: HWND.HWND_MESSAGE,
243+
hMenu: default,
244+
hInstance: GetModuleHandleW(default(PCWSTR)),
245+
lpParam: default);
246+
}
247+
if (window.IsNull)
224248
{
225249
throw new InvalidOperationException("Failed to create RAWINPUT window");
226250
}
227251

228252
unsafe
229253
{
230-
var rid = stackalloc RAWINPUTDEVICE[2];
231-
rid[0].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
232-
rid[0].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD;
233-
rid[0].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
254+
Span<RAWINPUTDEVICE> rid = stackalloc RAWINPUTDEVICE[2];
255+
rid[0].usUsagePage = HidUsagePage.GENERIC;
256+
rid[0].usUsage = HidUsageId.GENERIC_KEYBOARD;
257+
rid[0].dwFlags = RAWINPUTDEVICE_FLAGS.RIDEV_INPUTSINK;
234258
rid[0].hwndTarget = window;
235-
rid[1].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
236-
rid[1].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_MOUSE;
237-
rid[1].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK;
259+
rid[1].usUsagePage = HidUsagePage.GENERIC;
260+
rid[1].usUsage = HidUsageId.GENERIC_MOUSE;
261+
rid[1].dwFlags = RAWINPUTDEVICE_FLAGS.RIDEV_INPUTSINK;
238262
rid[1].hwndTarget = window;
239-
240-
if (!RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE)))
263+
if (!RegisterRawInputDevices(rid, unchecked((uint) sizeof(RAWINPUTDEVICE))))
241264
{
242265
DestroyWindow(window);
243266
throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
@@ -280,11 +303,11 @@ public void Dispose()
280303
{
281304
lock (_lockObj)
282305
{
283-
if (RawInputWindow != IntPtr.Zero)
306+
if (!RawInputWindow.IsNull)
284307
{
285308
// Can't use DestroyWindow, that's only allowed in the thread that created the window!
286-
PostMessageW(RawInputWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
287-
RawInputWindow = IntPtr.Zero;
309+
PostMessageW(RawInputWindow, WM_CLOSE, default, default);
310+
RawInputWindow = HWND.Null;
288311
}
289312
else
290313
{
@@ -310,15 +333,20 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
310333
{
311334
RawInputWindow = CreateRawInputWindow();
312335
var handle = GCHandle.Alloc(this, GCHandleType.Normal);
313-
SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
336+
SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle));
314337
}
315338

316339
_handleAltKbLayouts = handleAltKbLayouts;
317340

318-
while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PM_REMOVE))
341+
while (PeekMessageW(
342+
out var msg,
343+
RawInputWindow,
344+
wMsgFilterMin: 0,
345+
wMsgFilterMax: 0,
346+
PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
319347
{
320-
TranslateMessage(ref msg);
321-
DispatchMessageW(ref msg);
348+
TranslateMessage(in msg);
349+
DispatchMessageW(in msg);
322350
}
323351

324352
var ret = _keyEvents;
@@ -340,13 +368,13 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
340368
{
341369
RawInputWindow = CreateRawInputWindow();
342370
var handle = GCHandle.Alloc(this, GCHandleType.Normal);
343-
SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
371+
SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle));
344372
}
345373

346-
while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PM_REMOVE))
374+
while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
347375
{
348-
TranslateMessage(ref msg);
349-
DispatchMessageW(ref msg);
376+
TranslateMessage(in msg);
377+
DispatchMessageW(in msg);
350378
}
351379

352380
var ret = _mouseDelta;
@@ -395,8 +423,7 @@ private static RawKey MapToRealKeyViaScanCode(RawKey key)
395423
scanCode |= 0xE000;
396424
}
397425

398-
const uint MAPVK_VSC_TO_VK_EX = 0x03;
399-
var virtualKey = (VirtualKey)MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX);
426+
var virtualKey = (VirtualKey) MapVirtualKeyW(scanCode, MAP_VIRTUAL_KEY_TYPE.MAPVK_VSC_TO_VK_EX);
400427
return VKeyToRawKeyMap.GetValueOrDefault(virtualKey);
401428
}
402429

src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using BizHawk.Common.NumberExtensions;
1010
#endif
1111

12+
using Windows.Win32.UI.WindowsAndMessaging;
13+
1214
using static SDL2.SDL;
1315

1416
#pragma warning disable BHI1007 // target-typed Exception TODO don't
@@ -49,10 +51,15 @@ private static void DoSDLEventLoop()
4951
// similar code shouldn't be needed on other platforms (which have global message queues and not thread local message queues)
5052
if (!OSTailoredCode.IsUnixHost)
5153
{
52-
while (WmImports.PeekMessageW(out var msg, IntPtr.Zero, 0, 0, WmImports.PM_REMOVE))
54+
while (WmImports.PeekMessageW(
55+
out var msg,
56+
hWnd: default,
57+
wMsgFilterMin: 0,
58+
wMsgFilterMax: 0,
59+
PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
5360
{
54-
WmImports.TranslateMessage(ref msg);
55-
WmImports.DispatchMessageW(ref msg);
61+
WmImports.TranslateMessage(in msg);
62+
WmImports.DispatchMessageW(in msg);
5663
}
5764
}
5865

0 commit comments

Comments
 (0)