Skip to content

Commit 194fb9b

Browse files
committed
Outsource Windows API definitions (extern) to CsWin32
1 parent c33304b commit 194fb9b

39 files changed

+651
-1176
lines changed

Directory.Packages.props

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

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

Lines changed: 99 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,28 @@
66

77
using BizHawk.Common;
88

9+
using Windows.Win32;
10+
using Windows.Win32.Foundation;
11+
using Windows.Win32.UI.Input;
12+
using Windows.Win32.UI.Input.KeyboardAndMouse;
13+
using Windows.Win32.UI.WindowsAndMessaging;
14+
915
using static BizHawk.Common.RawInputImports;
10-
using static BizHawk.Common.WmImports;
16+
using static BizHawk.Common.WmImports1;
17+
using static Windows.Win32.Win32Imports;
1118

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

23-
private IntPtr RawInputWindow;
30+
private HWND RawInputWindow;
2431
private bool _handleAltKbLayouts;
2532
private List<KeyEvent> _keyEvents = [ ];
2633
private (int X, int Y) _mouseDelta;
@@ -32,27 +39,29 @@ internal sealed class RawKeyMouseInput : IKeyMouseInput
3239
private int RawInputBufferSize;
3340
private readonly int RawInputBufferDataOffset;
3441

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

5059
return atom;
5160
});
5261

53-
private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
62+
private static LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam)
5463
{
55-
var ud = GetWindowLongPtrW(hWnd, GWLP_USERDATA);
64+
var ud = GetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA);
5665
if (ud == IntPtr.Zero)
5766
{
5867
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
@@ -64,7 +73,7 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
6473
{
6574
if (uMsg == WM_CLOSE)
6675
{
67-
SetWindowLongPtrW(hWnd, GWLP_USERDATA, IntPtr.Zero);
76+
SetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, IntPtr.Zero);
6877
handle = GCHandle.FromIntPtr(ud);
6978
rawKeyMouseInput = (RawKeyMouseInput)handle.Target;
7079
Marshal.FreeCoTaskMem(rawKeyMouseInput.RawInputBuffer);
@@ -74,36 +83,48 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
7483
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
7584
}
7685

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

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

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

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

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

106-
if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
127+
if (input->header.dwType == RIM_TYPE.MOUSE)
107128
{
108129
rawKeyMouseInput.AddMouseInput(&input->data.mouse);
109130
}
@@ -112,14 +133,14 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
112133
while (true)
113134
{
114135
var rawInputBuffer = (RAWINPUT*)rawKeyMouseInput.RawInputBuffer;
115-
size = rawKeyMouseInput.RawInputBufferSize;
116-
var count = GetRawInputBuffer(rawInputBuffer, ref size, sizeof(RAWINPUTHEADER));
136+
size = unchecked((uint) rawKeyMouseInput.RawInputBufferSize);
137+
var count = GetRawInputBuffer(rawInputBuffer, ref size, unchecked((uint) sizeof(RAWINPUTHEADER)));
117138
if (count == 0)
118139
{
119140
break;
120141
}
121142

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

138-
for (var i = 0u; i < (uint)count; i++)
159+
for (var i = 0U; i < count; i++)
139160
{
140-
if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD)
161+
if (rawInputBuffer->header.dwType == RIM_TYPE.KEYBOARD)
141162
{
142163
var keyboard = (RAWKEYBOARD*)((byte*)&rawInputBuffer->data.keyboard + rawKeyMouseInput.RawInputBufferDataOffset);
143164
rawKeyMouseInput.AddKeyInput(keyboard);
144165
}
145166

146-
if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE)
167+
if (rawInputBuffer->header.dwType == RIM_TYPE.MOUSE)
147168
{
148169
var mouse = (RAWMOUSE*)((byte*)&rawInputBuffer->data.mouse + rawKeyMouseInput.RawInputBufferDataOffset);
149170
rawKeyMouseInput.AddMouseInput(mouse);
@@ -156,20 +177,20 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
156177
}
157178
}
158179

159-
return IntPtr.Zero;
180+
return default;
160181
}
161182

162183
private unsafe void AddKeyInput(RAWKEYBOARD* keyboard)
163184
{
164-
if ((keyboard->Flags & ~(RAWKEYBOARD.RI_KEY.E0 | RAWKEYBOARD.RI_KEY.BREAK)) == 0)
185+
if ((keyboard->Flags & ~(RI_KEY.E0 | RI_KEY.BREAK)) == 0)
165186
{
166-
var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RAWKEYBOARD.RI_KEY.E0) != 0 ? 0x80 : 0));
187+
var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RI_KEY.E0) != 0 ? 0x80 : 0));
167188

168189
// kind of a dumb hack, the Pause key is apparently special here
169190
// keyboards would send scancode 0x1D with an E1 prefix, followed by 0x45 (which is NumLock!)
170191
// 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
171192
// (note that DIK_PAUSE is just 0x45 with an E0 prefix, although this is likely just a conversion DirectInput does internally)
172-
if (rawKey == RawKey.NUMLOCK && keyboard->VKey == VirtualKey.VK_NONE)
193+
if (rawKey is RawKey.NUMLOCK && (VirtualKey) keyboard->VKey is VirtualKey.VK_NONE)
173194
{
174195
rawKey = RawKey.PAUSE;
175196
}
@@ -181,62 +202,65 @@ private unsafe void AddKeyInput(RAWKEYBOARD* keyboard)
181202

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

189210
private unsafe void AddMouseInput(RAWMOUSE* mouse)
190211
{
191212
// raw input usually doesn't report absolute inputs, only in odd cases with e.g. touchscreen and rdp screen
192-
if ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE)
213+
var relAbsBit = unchecked((MOUSE_STATE) ((ushort) mouse->usFlags & (ushort) MOUSE_STATE.MOUSE_MOVE_ABSOLUTE)); //TODO seems like it could be `[Flags]`, report to upstream?
214+
if (relAbsBit is MOUSE_STATE.MOUSE_MOVE_ABSOLUTE)
193215
{
194216
_mouseDelta.X += mouse->lLastX - _lastMouseAbsPos.X;
195217
_mouseDelta.Y += mouse->lLastY - _lastMouseAbsPos.Y;
196218
_lastMouseAbsPos = (mouse->lLastX, mouse->lLastY);
197219
}
198-
else // ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_RELATIVE)
220+
else // relAbsBit is MOUSE_STATE.MOUSE_MOVE_RELATIVE
199221
{
200222
_mouseDelta.X += mouse->lLastX;
201223
_mouseDelta.Y += mouse->lLastY;
202224
}
203225
}
204226

205-
private static IntPtr CreateRawInputWindow()
227+
private static unsafe HWND CreateRawInputWindow()
206228
{
207-
const int WS_CHILD = 0x40000000;
208-
var window = CreateWindowExW(
229+
var lpWindowNameStr = "RawKeyInput";
230+
HWND window;
231+
fixed (char* lpWindowName = lpWindowNameStr)
232+
{
233+
window = CreateWindowExW(
209234
dwExStyle: 0,
210235
lpClassName: _rawInputWindowAtom.Value,
211-
lpWindowName: "RawKeyInput",
212-
dwStyle: WS_CHILD,
236+
lpWindowName: lpWindowName,
237+
dwStyle: WINDOW_STYLE.WS_CHILD,
213238
X: 0,
214239
Y: 0,
215240
nWidth: 1,
216241
nHeight: 1,
217-
hWndParent: HWND_MESSAGE,
218-
hMenu: IntPtr.Zero,
219-
hInstance: LoaderApiImports.GetModuleHandleW(null),
220-
lpParam: IntPtr.Zero);
221-
222-
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)
223248
{
224249
throw new InvalidOperationException("Failed to create RAWINPUT window");
225250
}
226251

227252
unsafe
228253
{
229-
var rid = stackalloc RAWINPUTDEVICE[2];
230-
rid[0].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
231-
rid[0].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD;
232-
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;
233258
rid[0].hwndTarget = window;
234-
rid[1].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC;
235-
rid[1].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_MOUSE;
236-
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;
237262
rid[1].hwndTarget = window;
238-
239-
if (!RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE)))
263+
if (!RegisterRawInputDevices(rid, unchecked((uint) sizeof(RAWINPUTDEVICE))))
240264
{
241265
DestroyWindow(window);
242266
throw new InvalidOperationException("Failed to register RAWINPUTDEVICE");
@@ -279,11 +303,11 @@ public void Dispose()
279303
{
280304
lock (_lockObj)
281305
{
282-
if (RawInputWindow != IntPtr.Zero)
306+
if (!RawInputWindow.IsNull)
283307
{
284308
// Can't use DestroyWindow, that's only allowed in the thread that created the window!
285-
PostMessageW(RawInputWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
286-
RawInputWindow = IntPtr.Zero;
309+
PostMessageW(RawInputWindow, WM_CLOSE, default, default);
310+
RawInputWindow = HWND.Null;
287311
}
288312
else
289313
{
@@ -309,15 +333,20 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
309333
{
310334
RawInputWindow = CreateRawInputWindow();
311335
var handle = GCHandle.Alloc(this, GCHandleType.Normal);
312-
SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
336+
SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle));
313337
}
314338

315339
_handleAltKbLayouts = handleAltKbLayouts;
316340

317-
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))
318347
{
319-
TranslateMessage(ref msg);
320-
DispatchMessageW(ref msg);
348+
TranslateMessage(in msg);
349+
DispatchMessageW(in msg);
321350
}
322351

323352
var ret = _keyEvents;
@@ -339,13 +368,13 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
339368
{
340369
RawInputWindow = CreateRawInputWindow();
341370
var handle = GCHandle.Alloc(this, GCHandleType.Normal);
342-
SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle));
371+
SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle));
343372
}
344373

345-
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))
346375
{
347-
TranslateMessage(ref msg);
348-
DispatchMessageW(ref msg);
376+
TranslateMessage(in msg);
377+
DispatchMessageW(in msg);
349378
}
350379

351380
var ret = _mouseDelta;
@@ -394,8 +423,7 @@ private static RawKey MapToRealKeyViaScanCode(RawKey key)
394423
scanCode |= 0xE000;
395424
}
396425

397-
const uint MAPVK_VSC_TO_VK_EX = 0x03;
398-
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);
399427
return VKeyToRawKeyMap.GetValueOrDefault(virtualKey);
400428
}
401429

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

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

11+
using Windows.Win32.UI.WindowsAndMessaging;
12+
1113
using static SDL2.SDL;
1214

1315
namespace BizHawk.Bizware.Input
@@ -46,10 +48,15 @@ private static void DoSDLEventLoop()
4648
// similar code shouldn't be needed on other platforms (which have global message queues and not thread local message queues)
4749
if (!OSTailoredCode.IsUnixHost)
4850
{
49-
while (WmImports.PeekMessageW(out var msg, IntPtr.Zero, 0, 0, WmImports.PM_REMOVE))
51+
while (WmImports.PeekMessageW(
52+
out var msg,
53+
hWnd: default,
54+
wMsgFilterMin: 0,
55+
wMsgFilterMax: 0,
56+
PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
5057
{
51-
WmImports.TranslateMessage(ref msg);
52-
WmImports.DispatchMessageW(ref msg);
58+
WmImports.TranslateMessage(in msg);
59+
WmImports.DispatchMessageW(in msg);
5360
}
5461
}
5562

0 commit comments

Comments
 (0)