66
77using 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+
915using static BizHawk . Common . RawInputImports ;
10- using static BizHawk . Common . WmImports ;
16+ using static BizHawk . Common . WmImports1 ;
17+ using static Windows . Win32 . Win32Imports ;
1118
1219namespace 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
0 commit comments