7
7
using BizHawk . Common ;
8
8
using BizHawk . Common . CollectionExtensions ;
9
9
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
+
10
16
using static BizHawk . Common . RawInputImports ;
11
- using static BizHawk . Common . WmImports ;
17
+ using static BizHawk . Common . WmImports1 ;
18
+ using static Windows . Win32 . Win32Imports ;
12
19
13
20
namespace BizHawk . Bizware . Input
14
21
{
15
22
/// <summary>
16
23
/// Note: Only 1 window per device class (e.g. keyboards) is actually allowed to use RAWINPUT (last one to call RegisterRawInputDevices)
17
24
/// So only one instance can actually be used at the same time
18
25
/// </summary>
19
- internal sealed class RawKeyMouseInput : IKeyMouseInput
26
+ internal sealed unsafe class RawKeyMouseInput : IKeyMouseInput
20
27
{
21
28
private const int WM_CLOSE = 0x0010 ;
22
29
private const int WM_INPUT = 0x00FF ;
23
30
24
- private IntPtr RawInputWindow ;
31
+ private HWND RawInputWindow ;
25
32
private bool _handleAltKbLayouts ;
26
33
private List < KeyEvent > _keyEvents = [ ] ;
27
34
private ( int X , int Y ) _mouseDelta ;
@@ -33,27 +40,29 @@ internal sealed class RawKeyMouseInput : IKeyMouseInput
33
40
private int RawInputBufferSize ;
34
41
private readonly int RawInputBufferDataOffset ;
35
42
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 ( ( ) =>
39
44
{
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 )
47
56
{
48
57
throw new InvalidOperationException ( "Failed to register RAWINPUT window class" ) ;
49
58
}
50
59
51
60
return atom ;
52
61
} ) ;
53
62
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 )
55
64
{
56
- var ud = GetWindowLongPtrW ( hWnd , GWLP_USERDATA ) ;
65
+ var ud = GetWindowLongPtrW ( hWnd , WINDOW_LONG_PTR_INDEX . GWLP_USERDATA ) ;
57
66
if ( ud == IntPtr . Zero )
58
67
{
59
68
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
@@ -65,7 +74,7 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
65
74
{
66
75
if ( uMsg == WM_CLOSE )
67
76
{
68
- SetWindowLongPtrW ( hWnd , GWLP_USERDATA , IntPtr . Zero ) ;
77
+ SetWindowLongPtrW ( hWnd , WINDOW_LONG_PTR_INDEX . GWLP_USERDATA , IntPtr . Zero ) ;
69
78
handle = GCHandle . FromIntPtr ( ud ) ;
70
79
rawKeyMouseInput = ( RawKeyMouseInput ) handle . Target ;
71
80
Marshal . FreeCoTaskMem ( rawKeyMouseInput . RawInputBuffer ) ;
@@ -75,36 +84,48 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
75
84
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
76
85
}
77
86
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*/ )
80
95
{
81
96
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
82
97
}
83
98
84
99
// don't think size should ever be this big, but just in case
100
+ var allocOnHeap = size > 1024 ;
85
101
// 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 ] ;
89
105
90
106
handle = GCHandle . FromIntPtr ( ud ) ;
91
107
rawKeyMouseInput = ( RawKeyMouseInput ) handle . Target ;
92
108
93
109
fixed ( IntPtr * p = buffer )
94
110
{
95
111
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*/ )
98
119
{
99
120
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
100
121
}
101
122
102
- if ( input ->header . dwType == RAWINPUTHEADER . RIM_TYPE . KEYBOARD )
123
+ if ( input ->header . dwType == RIM_TYPE . KEYBOARD )
103
124
{
104
125
rawKeyMouseInput . AddKeyInput ( & input ->data . keyboard ) ;
105
126
}
106
127
107
- if ( input ->header . dwType == RAWINPUTHEADER . RIM_TYPE . MOUSE )
128
+ if ( input ->header . dwType == RIM_TYPE . MOUSE )
108
129
{
109
130
rawKeyMouseInput . AddMouseInput ( & input ->data . mouse ) ;
110
131
}
@@ -113,14 +134,14 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
113
134
while ( true )
114
135
{
115
136
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 ) ) ) ;
118
139
if ( count == 0 )
119
140
{
120
141
break ;
121
142
}
122
143
123
- if ( count == - 1 )
144
+ if ( count is uint . MaxValue /*-1*/ )
124
145
{
125
146
// From testing, it appears this never actually occurs in practice
126
147
// 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
136
157
break ;
137
158
}
138
159
139
- for ( var i = 0u ; i < ( uint ) count ; i ++ )
160
+ for ( var i = 0U ; i < count ; i ++ )
140
161
{
141
- if ( rawInputBuffer ->header . dwType == RAWINPUTHEADER . RIM_TYPE . KEYBOARD )
162
+ if ( rawInputBuffer ->header . dwType == RIM_TYPE . KEYBOARD )
142
163
{
143
164
var keyboard = ( RAWKEYBOARD * ) ( ( byte * ) & rawInputBuffer ->data . keyboard + rawKeyMouseInput . RawInputBufferDataOffset ) ;
144
165
rawKeyMouseInput . AddKeyInput ( keyboard ) ;
145
166
}
146
167
147
- if ( rawInputBuffer ->header . dwType == RAWINPUTHEADER . RIM_TYPE . MOUSE )
168
+ if ( rawInputBuffer ->header . dwType == RIM_TYPE . MOUSE )
148
169
{
149
170
var mouse = ( RAWMOUSE * ) ( ( byte * ) & rawInputBuffer ->data . mouse + rawKeyMouseInput . RawInputBufferDataOffset ) ;
150
171
rawKeyMouseInput . AddMouseInput ( mouse ) ;
@@ -157,20 +178,20 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP
157
178
}
158
179
}
159
180
160
- return IntPtr . Zero ;
181
+ return default ;
161
182
}
162
183
163
184
private unsafe void AddKeyInput ( RAWKEYBOARD * keyboard )
164
185
{
165
- if ( ( keyboard ->Flags & ~ ( RAWKEYBOARD . RI_KEY . E0 | RAWKEYBOARD . RI_KEY . BREAK ) ) == 0 )
186
+ if ( ( keyboard ->Flags & ~ ( RI_KEY . E0 | RI_KEY . BREAK ) ) == 0 )
166
187
{
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 ) ) ;
168
189
169
190
// kind of a dumb hack, the Pause key is apparently special here
170
191
// keyboards would send scancode 0x1D with an E1 prefix, followed by 0x45 (which is NumLock!)
171
192
// 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
172
193
// (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 )
174
195
{
175
196
rawKey = RawKey . PAUSE ;
176
197
}
@@ -182,62 +203,64 @@ private unsafe void AddKeyInput(RAWKEYBOARD* keyboard)
182
203
183
204
if ( KeyEnumMap . TryGetValue ( rawKey , out var key ) )
184
205
{
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 ) ) ;
186
207
}
187
208
}
188
209
}
189
210
190
211
private unsafe void AddMouseInput ( RAWMOUSE * mouse )
191
212
{
192
213
// 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 )
194
215
{
195
216
_mouseDelta . X += mouse ->lLastX - _lastMouseAbsPos . X ;
196
217
_mouseDelta . Y += mouse ->lLastY - _lastMouseAbsPos . Y ;
197
218
_lastMouseAbsPos = ( mouse ->lLastX , mouse ->lLastY ) ;
198
219
}
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 )
200
221
{
201
222
_mouseDelta . X += mouse ->lLastX ;
202
223
_mouseDelta . Y += mouse ->lLastY ;
203
224
}
204
225
}
205
226
206
- private static IntPtr CreateRawInputWindow ( )
227
+ private static unsafe HWND CreateRawInputWindow ( )
207
228
{
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 (
210
234
dwExStyle : 0 ,
211
235
lpClassName : _rawInputWindowAtom . Value ,
212
- lpWindowName : "RawKeyInput" ,
213
- dwStyle : WS_CHILD ,
236
+ lpWindowName : lpWindowName ,
237
+ dwStyle : WINDOW_STYLE . WS_CHILD ,
214
238
X : 0 ,
215
239
Y : 0 ,
216
240
nWidth : 1 ,
217
241
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 )
224
248
{
225
249
throw new InvalidOperationException ( "Failed to create RAWINPUT window" ) ;
226
250
}
227
251
228
252
unsafe
229
253
{
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 ;
234
258
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 ;
238
262
rid [ 1 ] . hwndTarget = window ;
239
-
240
- if ( ! RegisterRawInputDevices ( rid , 2 , sizeof ( RAWINPUTDEVICE ) ) )
263
+ if ( ! RegisterRawInputDevices ( rid , unchecked ( ( uint ) sizeof ( RAWINPUTDEVICE ) ) ) )
241
264
{
242
265
DestroyWindow ( window ) ;
243
266
throw new InvalidOperationException ( "Failed to register RAWINPUTDEVICE" ) ;
@@ -280,11 +303,11 @@ public void Dispose()
280
303
{
281
304
lock ( _lockObj )
282
305
{
283
- if ( RawInputWindow != IntPtr . Zero )
306
+ if ( ! RawInputWindow . IsNull )
284
307
{
285
308
// 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 ;
288
311
}
289
312
else
290
313
{
@@ -310,15 +333,20 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
310
333
{
311
334
RawInputWindow = CreateRawInputWindow ( ) ;
312
335
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 ) ) ;
314
337
}
315
338
316
339
_handleAltKbLayouts = handleAltKbLayouts ;
317
340
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 ) )
319
347
{
320
- TranslateMessage ( ref msg ) ;
321
- DispatchMessageW ( ref msg ) ;
348
+ TranslateMessage ( in msg ) ;
349
+ DispatchMessageW ( in msg ) ;
322
350
}
323
351
324
352
var ret = _keyEvents ;
@@ -340,13 +368,13 @@ public IEnumerable<KeyEvent> UpdateKeyInputs(bool handleAltKbLayouts)
340
368
{
341
369
RawInputWindow = CreateRawInputWindow ( ) ;
342
370
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 ) ) ;
344
372
}
345
373
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 ) )
347
375
{
348
- TranslateMessage ( ref msg ) ;
349
- DispatchMessageW ( ref msg ) ;
376
+ TranslateMessage ( in msg ) ;
377
+ DispatchMessageW ( in msg ) ;
350
378
}
351
379
352
380
var ret = _mouseDelta ;
@@ -395,8 +423,7 @@ private static RawKey MapToRealKeyViaScanCode(RawKey key)
395
423
scanCode |= 0xE000 ;
396
424
}
397
425
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 ) ;
400
427
return VKeyToRawKeyMap . GetValueOrDefault ( virtualKey ) ;
401
428
}
402
429
0 commit comments