user32: Implement GetMouseMovePointsEx().
[wine.git] / dlls / user32 / tests / input.c
blobce272ed73f486efc5bb9bf41a8ce8620bed5e339
1 /* Test Key event to Key message translation
3 * Copyright 2003 Rein Klazes
4 * Copyright 2019 Remi Bernon for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 /* test whether the right type of messages:
22 * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN are sent in case of combined
23 * keystrokes.
25 * For instance <ALT>-X can be accomplished by
26 * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP
27 * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP
28 * Whether a KEY or a SYSKEY message is sent is not always clear, it is
29 * also not the same in WINNT as in WIN9X */
31 /* NOTE that there will be test failures under WIN9X
32 * No applications are known to me that rely on this
33 * so I don't fix it */
35 /* TODO:
36 * 1. extend it to the wm_command and wm_syscommand notifications
37 * 2. add some more tests with special cases like dead keys or right (alt) key
38 * 3. there is some adapted code from input.c in here. Should really
39 * make that code exactly the same.
40 * 4. resolve the win9x case when there is a need or the testing frame work
41 * offers a nice way.
42 * 5. The test app creates a window, the user should not take the focus
43 * away during its short existence. I could do something to prevent that
44 * if it is a problem.
48 #include <stdarg.h>
49 #include <assert.h>
51 #include "windef.h"
52 #include "winbase.h"
53 #include "wingdi.h"
54 #include "winuser.h"
55 #include "winnls.h"
56 #include "ddk/hidsdi.h"
58 #include "wine/test.h"
60 /* globals */
61 static HWND hWndTest;
62 static LONG timetag = 0x10000000;
64 #define DESKTOP_ALL_ACCESS 0x01ff
66 static struct {
67 LONG last_key_down;
68 LONG last_key_up;
69 LONG last_syskey_down;
70 LONG last_syskey_up;
71 LONG last_char;
72 LONG last_syschar;
73 LONG last_hook_down;
74 LONG last_hook_up;
75 LONG last_hook_syskey_down;
76 LONG last_hook_syskey_up;
77 WORD vk;
78 BOOL expect_alt;
79 BOOL sendinput_broken;
80 } key_status;
82 static BOOL (WINAPI *pGetCurrentInputMessageSource)( INPUT_MESSAGE_SOURCE *source );
83 static BOOL (WINAPI *pGetPointerType)(UINT32, POINTER_INPUT_TYPE*);
84 static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD);
85 static UINT (WINAPI *pGetRawInputDeviceList) (PRAWINPUTDEVICELIST, PUINT, UINT);
86 static UINT (WINAPI *pGetRawInputDeviceInfoW) (HANDLE, UINT, void *, UINT *);
87 static UINT (WINAPI *pGetRawInputDeviceInfoA) (HANDLE, UINT, void *, UINT *);
88 static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
90 #define MAXKEYEVENTS 12
91 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
92 and only one message */
94 /* keyboard message names, sorted as their value */
95 static const char *MSGNAME[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR",
96 "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"};
98 /* keyevents, add more as needed */
99 typedef enum KEVtag
100 { ALTDOWN = 1, ALTUP, XDOWN, XUP, SHIFTDOWN, SHIFTUP, CTRLDOWN, CTRLUP } KEV;
101 /* matching VK's */
102 static const int GETVKEY[]={0, VK_MENU, VK_MENU, 'X', 'X', VK_SHIFT, VK_SHIFT, VK_CONTROL, VK_CONTROL};
103 /* matching scan codes */
104 static const int GETSCAN[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D };
105 /* matching updown events */
106 static const int GETFLAGS[]={0, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP};
107 /* matching descriptions */
108 static const char *getdesc[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"};
110 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */
111 typedef struct
113 DWORD type;
114 union
116 MOUSEINPUT mi;
117 KEYBDINPUT ki;
118 HARDWAREINPUT hi;
119 } u;
120 } TEST_INPUT;
122 typedef struct {
123 UINT message;
124 WPARAM wParam;
125 LPARAM lParam;
126 } KMSG;
128 /*******************************************
129 * add new test sets here
130 * the software will make all combinations of the
131 * keyevent defined here
133 static const struct {
134 int nrkev;
135 KEV keydwn[MAXKEYEVENTS];
136 KEV keyup[MAXKEYEVENTS];
137 } testkeyset[]= {
138 { 2, { ALTDOWN, XDOWN }, { ALTUP, XUP}},
139 { 3, { ALTDOWN, XDOWN , SHIFTDOWN}, { ALTUP, XUP, SHIFTUP}},
140 { 3, { ALTDOWN, XDOWN , CTRLDOWN}, { ALTUP, XUP, CTRLUP}},
141 { 3, { SHIFTDOWN, XDOWN , CTRLDOWN}, { SHIFTUP, XUP, CTRLUP}},
142 { 0 } /* mark the end */
145 /**********************adapted from input.c **********************************/
147 static BYTE InputKeyStateTable[256];
148 static BYTE AsyncKeyStateTable[256];
149 static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
150 or a WM_KEYUP message */
151 static BOOL is_wow64;
153 static void init_function_pointers(void)
155 HMODULE hdll = GetModuleHandleA("user32");
157 #define GET_PROC(func) \
158 if (!(p ## func = (void*)GetProcAddress(hdll, #func))) \
159 trace("GetProcAddress(%s) failed\n", #func)
161 GET_PROC(GetCurrentInputMessageSource);
162 GET_PROC(GetMouseMovePointsEx);
163 GET_PROC(GetPointerType);
164 GET_PROC(GetRawInputDeviceList);
165 GET_PROC(GetRawInputDeviceInfoW);
166 GET_PROC(GetRawInputDeviceInfoA);
168 hdll = GetModuleHandleA("kernel32");
169 GET_PROC(IsWow64Process);
170 #undef GET_PROC
172 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 ))
173 is_wow64 = FALSE;
176 static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam )
178 UINT message;
179 int VKey = GETVKEY[kev];
180 WORD flags;
182 flags = LOBYTE(GETSCAN[kev]);
183 if (GETFLAGS[kev] & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED;
185 if (GETFLAGS[kev] & KEYEVENTF_KEYUP )
187 message = WM_KEYUP;
188 if( (InputKeyStateTable[VK_MENU] & 0x80) && (
189 (VKey == VK_MENU) || (VKey == VK_CONTROL) ||
190 !(InputKeyStateTable[VK_CONTROL] & 0x80))) {
191 if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
192 (VKey != VK_MENU)) /* <ALT>-down...<something else>-up */
193 message = WM_SYSKEYUP;
194 TrackSysKey = 0;
196 InputKeyStateTable[VKey] &= ~0x80;
197 flags |= KF_REPEAT | KF_UP;
199 else
201 if (InputKeyStateTable[VKey] & 0x80) flags |= KF_REPEAT;
202 if (!(InputKeyStateTable[VKey] & 0x80)) InputKeyStateTable[VKey] ^= 0x01;
203 InputKeyStateTable[VKey] |= 0x80;
204 AsyncKeyStateTable[VKey] |= 0x80;
206 message = WM_KEYDOWN;
207 if( (InputKeyStateTable[VK_MENU] & 0x80) &&
208 !(InputKeyStateTable[VK_CONTROL] & 0x80)) {
209 message = WM_SYSKEYDOWN;
210 TrackSysKey = VKey;
214 if (InputKeyStateTable[VK_MENU] & 0x80) flags |= KF_ALTDOWN;
216 if( plParam) *plParam = MAKELPARAM( 1, flags );
217 if( pwParam) *pwParam = VKey;
218 return message;
221 /****************************** end copy input.c ****************************/
224 * . prepare the keyevents for SendInputs
225 * . calculate the "expected" messages
226 * . Send the events to our window
227 * . retrieve the messages from the input queue
228 * . verify
230 static BOOL do_test( HWND hwnd, int seqnr, const KEV td[] )
232 TEST_INPUT inputs[MAXKEYEVENTS];
233 KMSG expmsg[MAXKEYEVENTS];
234 MSG msg;
235 char buf[100];
236 UINT evtctr=0, ret;
237 int kmctr, i;
239 buf[0]='\0';
240 TrackSysKey=0; /* see input.c */
241 for (i = 0; i < MAXKEYEVENTS; i++)
243 inputs[evtctr].type = INPUT_KEYBOARD;
244 inputs[evtctr].u.ki.wVk = GETVKEY[td[i]];
245 inputs[evtctr].u.ki.wScan = GETSCAN[td[i]];
246 inputs[evtctr].u.ki.dwFlags = GETFLAGS[td[i]];
247 inputs[evtctr].u.ki.dwExtraInfo = 0;
248 inputs[evtctr].u.ki.time = ++timetag;
249 if (td[i]) evtctr++;
251 strcat(buf, getdesc[td[i]]);
252 if(td[i])
253 expmsg[i].message = KbdMessage(td[i], &(expmsg[i].wParam), &(expmsg[i].lParam));
254 else
255 expmsg[i].message = 0;
257 for( kmctr = 0; kmctr < MAXKEYEVENTS && expmsg[kmctr].message; kmctr++)
259 ok( evtctr <= MAXKEYEVENTS, "evtctr is above MAXKEYEVENTS\n" );
260 ret = SendInput(evtctr, (INPUT *)inputs, sizeof(INPUT));
261 ok(ret == evtctr, "SendInput failed to send some events\n");
262 i = 0;
263 if (winetest_debug > 1)
264 trace("======== key stroke sequence #%d: %s =============\n",
265 seqnr + 1, buf);
266 while( PeekMessageA(&msg,hwnd,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE) ) {
267 if (winetest_debug > 1)
268 trace("message[%d] %-15s wParam %04lx lParam %08lx time %x\n", i,
269 MSGNAME[msg.message - WM_KEYFIRST], msg.wParam, msg.lParam, msg.time);
270 if( i < kmctr ) {
271 ok( msg.message == expmsg[i].message &&
272 msg.wParam == expmsg[i].wParam &&
273 msg.lParam == expmsg[i].lParam,
274 "%u/%u: wrong message %x/%08lx/%08lx expected %s/%08lx/%08lx\n",
275 seqnr, i, msg.message, msg.wParam, msg.lParam,
276 MSGNAME[(expmsg[i]).message - WM_KEYFIRST], expmsg[i].wParam, expmsg[i].lParam );
278 i++;
280 if (winetest_debug > 1)
281 trace("%d messages retrieved\n", i);
282 if (!i && kmctr)
284 skip( "simulated keyboard input doesn't work\n" );
285 return FALSE;
287 ok( i == kmctr, "message count is wrong: got %d expected: %d\n", i, kmctr);
288 return TRUE;
291 /* test all combinations of the specified key events */
292 static BOOL TestASet( HWND hWnd, int nrkev, const KEV kevdwn[], const KEV kevup[] )
294 int i,j,k,l,m,n;
295 static int count=0;
296 KEV kbuf[MAXKEYEVENTS];
297 BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409);
298 if (!us_kbd)
300 skip( "skipping test with inconsistent results on non-us keyboard\n" );
301 return TRUE;
304 assert( nrkev==2 || nrkev==3);
305 for(i=0;i<MAXKEYEVENTS;i++) kbuf[i]=0;
306 /* two keys involved gives 4 test cases */
307 if(nrkev==2) {
308 for(i=0;i<nrkev;i++) {
309 for(j=0;j<nrkev;j++) {
310 kbuf[0] = kevdwn[i];
311 kbuf[1] = kevdwn[1-i];
312 kbuf[2] = kevup[j];
313 kbuf[3] = kevup[1-j];
314 if (!do_test( hWnd, count++, kbuf)) return FALSE;
318 /* three keys involved gives 36 test cases */
319 if(nrkev==3){
320 for(i=0;i<nrkev;i++){
321 for(j=0;j<nrkev;j++){
322 if(j==i) continue;
323 for(k=0;k<nrkev;k++){
324 if(k==i || k==j) continue;
325 for(l=0;l<nrkev;l++){
326 for(m=0;m<nrkev;m++){
327 if(m==l) continue;
328 for(n=0;n<nrkev;n++){
329 if(n==l ||n==m) continue;
330 kbuf[0] = kevdwn[i];
331 kbuf[1] = kevdwn[j];
332 kbuf[2] = kevdwn[k];
333 kbuf[3] = kevup[l];
334 kbuf[4] = kevup[m];
335 kbuf[5] = kevup[n];
336 if (!do_test( hWnd, count++, kbuf)) return FALSE;
344 return TRUE;
347 /* test each set specified in the global testkeyset array */
348 static void TestSysKeys( HWND hWnd)
350 int i;
351 for(i=0; testkeyset[i].nrkev;i++)
352 if (!TestASet( hWnd, testkeyset[i].nrkev, testkeyset[i].keydwn, testkeyset[i].keyup)) break;
355 static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam,
356 LPARAM lParam )
358 return DefWindowProcA( hWnd, msg, wParam, lParam );
361 static void test_Input_whitebox(void)
363 MSG msg;
364 WNDCLASSA wclass;
365 HANDLE hInstance = GetModuleHandleA( NULL );
367 wclass.lpszClassName = "InputSysKeyTestClass";
368 wclass.style = CS_HREDRAW | CS_VREDRAW;
369 wclass.lpfnWndProc = WndProc;
370 wclass.hInstance = hInstance;
371 wclass.hIcon = LoadIconA( 0, (LPCSTR)IDI_APPLICATION );
372 wclass.hCursor = LoadCursorA( NULL, (LPCSTR)IDC_ARROW );
373 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
374 wclass.lpszMenuName = 0;
375 wclass.cbClsExtra = 0;
376 wclass.cbWndExtra = 0;
377 RegisterClassA( &wclass );
378 /* create the test window that will receive the keystrokes */
379 hWndTest = CreateWindowA( wclass.lpszClassName, "InputSysKeyTest",
380 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
381 NULL, NULL, hInstance, NULL);
382 assert( hWndTest );
383 ShowWindow( hWndTest, SW_SHOW);
384 SetWindowPos( hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE );
385 SetForegroundWindow( hWndTest );
386 UpdateWindow( hWndTest);
388 /* flush pending messages */
389 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
391 SetFocus( hWndTest );
392 TestSysKeys( hWndTest );
393 DestroyWindow(hWndTest);
396 static inline BOOL is_keyboard_message( UINT message )
398 return (message >= WM_KEYFIRST && message <= WM_KEYLAST);
401 static inline BOOL is_mouse_message( UINT message )
403 return (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST);
406 /* try to make sure pending X events have been processed before continuing */
407 static void empty_message_queue(void)
409 MSG msg;
410 int diff = 200;
411 int min_timeout = 50;
412 DWORD time = GetTickCount() + diff;
414 while (diff > 0)
416 if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT) break;
417 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
419 if (is_keyboard_message(msg.message) || is_mouse_message(msg.message))
420 ok(msg.time != 0, "message %#x has time set to 0\n", msg.message);
422 TranslateMessage(&msg);
423 DispatchMessageA(&msg);
425 diff = time - GetTickCount();
429 struct transition_s {
430 WORD wVk;
431 BYTE before_state;
432 BYTE optional;
435 typedef enum {
436 sent=0x1,
437 posted=0x2,
438 parent=0x4,
439 wparam=0x8,
440 lparam=0x10,
441 defwinproc=0x20,
442 beginpaint=0x40,
443 optional=0x80,
444 hook=0x100,
445 winevent_hook=0x200
446 } msg_flags_t;
448 struct message {
449 UINT message; /* the WM_* code */
450 msg_flags_t flags; /* message props */
451 WPARAM wParam; /* expected value of wParam */
452 LPARAM lParam; /* expected value of lParam */
455 static const struct sendinput_test_s {
456 WORD wVk;
457 DWORD dwFlags;
458 BOOL _todo_wine;
459 struct transition_s expected_transitions[MAXKEYEVENTS+1];
460 struct message expected_messages[MAXKEYMESSAGES+1];
461 } sendinput_test[] = {
462 /* test ALT+F */
463 /* 0 */
464 {VK_LMENU, 0, FALSE, {{VK_MENU, 0x00}, {VK_LMENU, 0x00}, {0}},
465 {{WM_SYSKEYDOWN, hook|wparam, VK_LMENU}, {WM_SYSKEYDOWN}, {0}}},
466 {'F', 0, FALSE, {{'F', 0x00}, {0}},
467 {{WM_SYSKEYDOWN, hook}, {WM_SYSKEYDOWN},
468 {WM_SYSCHAR},
469 {WM_SYSCOMMAND}, {0}}},
470 {'F', KEYEVENTF_KEYUP, FALSE, {{'F', 0x80}, {0}},
471 {{WM_SYSKEYUP, hook}, {WM_SYSKEYUP}, {0}}},
472 {VK_LMENU, KEYEVENTF_KEYUP, FALSE, {{VK_MENU, 0x80}, {VK_LMENU, 0x80}, {0}},
473 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
475 /* test CTRL+O */
476 /* 4 */
477 {VK_LCONTROL, 0, FALSE, {{VK_CONTROL, 0x00}, {VK_LCONTROL, 0x00}, {0}},
478 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {0}}},
479 {'O', 0, FALSE, {{'O', 0x00}, {0}},
480 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {WM_CHAR}, {0}}},
481 {'O', KEYEVENTF_KEYUP, FALSE, {{'O', 0x80}, {0}},
482 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
483 {VK_LCONTROL, KEYEVENTF_KEYUP, FALSE, {{VK_CONTROL, 0x80}, {VK_LCONTROL, 0x80}, {0}},
484 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
486 /* test ALT+CTRL+X */
487 /* 8 */
488 {VK_LMENU, 0, FALSE, {{VK_MENU, 0x00}, {VK_LMENU, 0x00}, {0}},
489 {{WM_SYSKEYDOWN, hook}, {WM_SYSKEYDOWN}, {0}}},
490 {VK_LCONTROL, 0, FALSE, {{VK_CONTROL, 0x00}, {VK_LCONTROL, 0x00}, {0}},
491 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {0}}},
492 {'X', 0, FALSE, {{'X', 0x00}, {0}},
493 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {0}}},
494 {'X', KEYEVENTF_KEYUP, FALSE, {{'X', 0x80}, {0}},
495 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
496 {VK_LCONTROL, KEYEVENTF_KEYUP, FALSE, {{VK_CONTROL, 0x80}, {VK_LCONTROL, 0x80}, {0}},
497 {{WM_SYSKEYUP, hook}, {WM_SYSKEYUP}, {0}}},
498 {VK_LMENU, KEYEVENTF_KEYUP, FALSE, {{VK_MENU, 0x80}, {VK_LMENU, 0x80}, {0}},
499 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
501 /* test SHIFT+A */
502 /* 14 */
503 {VK_LSHIFT, 0, FALSE, {{VK_SHIFT, 0x00}, {VK_LSHIFT, 0x00}, {0}},
504 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {0}}},
505 {'A', 0, FALSE, {{'A', 0x00}, {0}},
506 {{WM_KEYDOWN, hook}, {WM_KEYDOWN}, {WM_CHAR}, {0}}},
507 {'A', KEYEVENTF_KEYUP, FALSE, {{'A', 0x80}, {0}},
508 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
509 {VK_LSHIFT, KEYEVENTF_KEYUP, FALSE, {{VK_SHIFT, 0x80}, {VK_LSHIFT, 0x80}, {0}},
510 {{WM_KEYUP, hook}, {WM_KEYUP}, {0}}},
511 /* test L-SHIFT & R-SHIFT: */
512 /* RSHIFT == LSHIFT */
513 /* 18 */
514 {VK_RSHIFT, 0, FALSE,
515 /* recent windows versions (>= w2k3) correctly report an RSHIFT transition */
516 {{VK_SHIFT, 0x00}, {VK_LSHIFT, 0x00, TRUE}, {VK_RSHIFT, 0x00, TRUE}, {0}},
517 {{WM_KEYDOWN, hook|wparam, VK_RSHIFT},
518 {WM_KEYDOWN}, {0}}},
519 {VK_RSHIFT, KEYEVENTF_KEYUP, FALSE,
520 {{VK_SHIFT, 0x80}, {VK_LSHIFT, 0x80, TRUE}, {VK_RSHIFT, 0x80, TRUE}, {0}},
521 {{WM_KEYUP, hook, hook|wparam, VK_RSHIFT},
522 {WM_KEYUP}, {0}}},
524 /* LSHIFT | KEYEVENTF_EXTENDEDKEY == RSHIFT */
525 /* 20 */
526 {VK_LSHIFT, KEYEVENTF_EXTENDEDKEY, FALSE,
527 {{VK_SHIFT, 0x00}, {VK_RSHIFT, 0x00}, {0}},
528 {{WM_KEYDOWN, hook|wparam|lparam, VK_LSHIFT, LLKHF_EXTENDED},
529 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
530 {VK_LSHIFT, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
531 {{VK_SHIFT, 0x80}, {VK_RSHIFT, 0x80}, {0}},
532 {{WM_KEYUP, hook|wparam|lparam, VK_LSHIFT, LLKHF_UP|LLKHF_EXTENDED},
533 {WM_KEYUP, wparam|lparam, VK_SHIFT, KF_UP}, {0}}},
534 /* RSHIFT | KEYEVENTF_EXTENDEDKEY == RSHIFT */
535 /* 22 */
536 {VK_RSHIFT, KEYEVENTF_EXTENDEDKEY, FALSE,
537 {{VK_SHIFT, 0x00}, {VK_RSHIFT, 0x00}, {0}},
538 {{WM_KEYDOWN, hook|wparam|lparam, VK_RSHIFT, LLKHF_EXTENDED},
539 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
540 {VK_RSHIFT, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
541 {{VK_SHIFT, 0x80}, {VK_RSHIFT, 0x80}, {0}},
542 {{WM_KEYUP, hook|wparam|lparam, VK_RSHIFT, LLKHF_UP|LLKHF_EXTENDED},
543 {WM_KEYUP, wparam|lparam, VK_SHIFT, KF_UP}, {0}}},
545 /* Note about wparam for hook with generic key (VK_SHIFT, VK_CONTROL, VK_MENU):
546 win2k - sends to hook whatever we generated here
547 winXP+ - Attempts to convert key to L/R key but not always correct
549 /* SHIFT == LSHIFT */
550 /* 24 */
551 {VK_SHIFT, 0, FALSE,
552 {{VK_SHIFT, 0x00}, {VK_LSHIFT, 0x00}, {0}},
553 {{WM_KEYDOWN, hook/* |wparam */|lparam, VK_SHIFT, 0},
554 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
555 {VK_SHIFT, KEYEVENTF_KEYUP, FALSE,
556 {{VK_SHIFT, 0x80}, {VK_LSHIFT, 0x80}, {0}},
557 {{WM_KEYUP, hook/*|wparam*/|lparam, VK_SHIFT, LLKHF_UP},
558 {WM_KEYUP, wparam|lparam, VK_SHIFT, KF_UP}, {0}}},
559 /* SHIFT | KEYEVENTF_EXTENDEDKEY == RSHIFT */
560 /* 26 */
561 {VK_SHIFT, KEYEVENTF_EXTENDEDKEY, FALSE,
562 {{VK_SHIFT, 0x00}, {VK_RSHIFT, 0x00}, {0}},
563 {{WM_KEYDOWN, hook/*|wparam*/|lparam, VK_SHIFT, LLKHF_EXTENDED},
564 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
565 {VK_SHIFT, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
566 {{VK_SHIFT, 0x80}, {VK_RSHIFT, 0x80}, {0}},
567 {{WM_KEYUP, hook/*|wparam*/|lparam, VK_SHIFT, LLKHF_UP|LLKHF_EXTENDED},
568 {WM_KEYUP, wparam|lparam, VK_SHIFT, KF_UP}, {0}}},
570 /* test L-CONTROL & R-CONTROL: */
571 /* RCONTROL == LCONTROL */
572 /* 28 */
573 {VK_RCONTROL, 0, FALSE,
574 {{VK_CONTROL, 0x00}, {VK_LCONTROL, 0x00}, {0}},
575 {{WM_KEYDOWN, hook|wparam, VK_RCONTROL},
576 {WM_KEYDOWN, wparam|lparam, VK_CONTROL, 0}, {0}}},
577 {VK_RCONTROL, KEYEVENTF_KEYUP, FALSE,
578 {{VK_CONTROL, 0x80}, {VK_LCONTROL, 0x80}, {0}},
579 {{WM_KEYUP, hook|wparam, VK_RCONTROL},
580 {WM_KEYUP, wparam|lparam, VK_CONTROL, KF_UP}, {0}}},
581 /* LCONTROL | KEYEVENTF_EXTENDEDKEY == RCONTROL */
582 /* 30 */
583 {VK_LCONTROL, KEYEVENTF_EXTENDEDKEY, FALSE,
584 {{VK_CONTROL, 0x00}, {VK_RCONTROL, 0x00}, {0}},
585 {{WM_KEYDOWN, hook|wparam|lparam, VK_LCONTROL, LLKHF_EXTENDED},
586 {WM_KEYDOWN, wparam|lparam, VK_CONTROL, KF_EXTENDED}, {0}}},
587 {VK_LCONTROL, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
588 {{VK_CONTROL, 0x80}, {VK_RCONTROL, 0x80}, {0}},
589 {{WM_KEYUP, hook|wparam|lparam, VK_LCONTROL, LLKHF_UP|LLKHF_EXTENDED},
590 {WM_KEYUP, wparam|lparam, VK_CONTROL, KF_UP|KF_EXTENDED}, {0}}},
591 /* RCONTROL | KEYEVENTF_EXTENDEDKEY == RCONTROL */
592 /* 32 */
593 {VK_RCONTROL, KEYEVENTF_EXTENDEDKEY, FALSE,
594 {{VK_CONTROL, 0x00}, {VK_RCONTROL, 0x00}, {0}},
595 {{WM_KEYDOWN, hook|wparam|lparam, VK_RCONTROL, LLKHF_EXTENDED},
596 {WM_KEYDOWN, wparam|lparam, VK_CONTROL, KF_EXTENDED}, {0}}},
597 {VK_RCONTROL, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
598 {{VK_CONTROL, 0x80}, {VK_RCONTROL, 0x80}, {0}},
599 {{WM_KEYUP, hook|wparam|lparam, VK_RCONTROL, LLKHF_UP|LLKHF_EXTENDED},
600 {WM_KEYUP, wparam|lparam, VK_CONTROL, KF_UP|KF_EXTENDED}, {0}}},
601 /* CONTROL == LCONTROL */
602 /* 34 */
603 {VK_CONTROL, 0, FALSE,
604 {{VK_CONTROL, 0x00}, {VK_LCONTROL, 0x00}, {0}},
605 {{WM_KEYDOWN, hook/*|wparam, VK_CONTROL*/},
606 {WM_KEYDOWN, wparam|lparam, VK_CONTROL, 0}, {0}}},
607 {VK_CONTROL, KEYEVENTF_KEYUP, FALSE,
608 {{VK_CONTROL, 0x80}, {VK_LCONTROL, 0x80}, {0}},
609 {{WM_KEYUP, hook/*|wparam, VK_CONTROL*/},
610 {WM_KEYUP, wparam|lparam, VK_CONTROL, KF_UP}, {0}}},
611 /* CONTROL | KEYEVENTF_EXTENDEDKEY == RCONTROL */
612 /* 36 */
613 {VK_CONTROL, KEYEVENTF_EXTENDEDKEY, FALSE,
614 {{VK_CONTROL, 0x00}, {VK_RCONTROL, 0x00}, {0}},
615 {{WM_KEYDOWN, hook/*|wparam*/|lparam, VK_CONTROL, LLKHF_EXTENDED},
616 {WM_KEYDOWN, wparam|lparam, VK_CONTROL, KF_EXTENDED}, {0}}},
617 {VK_CONTROL, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
618 {{VK_CONTROL, 0x80}, {VK_RCONTROL, 0x80}, {0}},
619 {{WM_KEYUP, hook/*|wparam*/|lparam, VK_CONTROL, LLKHF_UP|LLKHF_EXTENDED},
620 {WM_KEYUP, wparam|lparam, VK_CONTROL, KF_UP|KF_EXTENDED}, {0}}},
622 /* test L-MENU & R-MENU: */
623 /* RMENU == LMENU */
624 /* 38 */
625 {VK_RMENU, 0, FALSE,
626 {{VK_MENU, 0x00}, {VK_LMENU, 0x00}, {VK_CONTROL, 0x00, 1}, {VK_LCONTROL, 0x01, 1}, {0}},
627 {{WM_SYSKEYDOWN, hook|wparam|optional, VK_LCONTROL},
628 {WM_SYSKEYDOWN, hook|wparam, VK_RMENU},
629 {WM_KEYDOWN, wparam|lparam|optional, VK_CONTROL, 0},
630 {WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0}, {0}}},
631 {VK_RMENU, KEYEVENTF_KEYUP, TRUE,
632 {{VK_MENU, 0x80}, {VK_LMENU, 0x80}, {VK_CONTROL, 0x81, 1}, {VK_LCONTROL, 0x80, 1}, {0}},
633 {{WM_KEYUP, hook|wparam|optional, VK_LCONTROL},
634 {WM_KEYUP, hook|wparam, VK_RMENU},
635 {WM_SYSKEYUP, wparam|lparam|optional, VK_CONTROL, KF_UP},
636 {WM_SYSKEYUP, wparam|lparam, VK_MENU, KF_UP},
637 {WM_SYSCOMMAND, optional}, {0}}},
638 /* LMENU | KEYEVENTF_EXTENDEDKEY == RMENU */
639 /* 40 */
640 {VK_LMENU, KEYEVENTF_EXTENDEDKEY, FALSE,
641 {{VK_MENU, 0x00}, {VK_RMENU, 0x00}, {0}},
642 {{WM_SYSKEYDOWN, hook|wparam|lparam, VK_LMENU, LLKHF_EXTENDED},
643 {WM_SYSKEYDOWN, wparam|lparam, VK_MENU, KF_EXTENDED}, {0}}},
644 {VK_LMENU, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, TRUE,
645 {{VK_MENU, 0x80}, {VK_RMENU, 0x80}, {0}},
646 {{WM_KEYUP, hook|wparam|lparam, VK_LMENU, LLKHF_UP|LLKHF_EXTENDED},
647 {WM_SYSKEYUP, wparam|lparam, VK_MENU, KF_UP|KF_EXTENDED},
648 {WM_SYSCOMMAND}, {0}}},
649 /* RMENU | KEYEVENTF_EXTENDEDKEY == RMENU */
650 /* 42 */
651 {VK_RMENU, KEYEVENTF_EXTENDEDKEY, FALSE,
652 {{VK_MENU, 0x00}, {VK_RMENU, 0x00}, {VK_CONTROL, 0x00, 1}, {VK_LCONTROL, 0x01, 1}, {0}},
653 {{WM_SYSKEYDOWN, hook|wparam|lparam|optional, VK_LCONTROL, 0},
654 {WM_SYSKEYDOWN, hook|wparam|lparam, VK_RMENU, LLKHF_EXTENDED},
655 {WM_KEYDOWN, wparam|lparam|optional, VK_CONTROL, 0},
656 {WM_SYSKEYDOWN, wparam|lparam, VK_MENU, KF_EXTENDED}, {0}}},
657 {VK_RMENU, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, TRUE,
658 {{VK_MENU, 0x80}, {VK_RMENU, 0x80}, {VK_CONTROL, 0x81, 1}, {VK_LCONTROL, 0x80, 1}, {0}},
659 {{WM_KEYUP, hook|wparam|lparam|optional, VK_LCONTROL, LLKHF_UP},
660 {WM_KEYUP, hook|wparam|lparam, VK_RMENU, LLKHF_UP|LLKHF_EXTENDED},
661 {WM_SYSKEYUP, wparam|lparam|optional, VK_CONTROL, KF_UP},
662 {WM_SYSKEYUP, wparam|lparam, VK_MENU, KF_UP|KF_EXTENDED},
663 {WM_SYSCOMMAND, optional}, {0}}},
664 /* MENU == LMENU */
665 /* 44 */
666 {VK_MENU, 0, FALSE,
667 {{VK_MENU, 0x00}, {VK_LMENU, 0x00}, {0}},
668 {{WM_SYSKEYDOWN, hook/*|wparam, VK_MENU*/},
669 {WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0}, {0}}},
670 {VK_MENU, KEYEVENTF_KEYUP, TRUE,
671 {{VK_MENU, 0x80}, {VK_LMENU, 0x80}, {0}},
672 {{WM_KEYUP, hook/*|wparam, VK_MENU*/},
673 {WM_SYSKEYUP, wparam|lparam, VK_MENU, KF_UP},
674 {WM_SYSCOMMAND}, {0}}},
675 /* MENU | KEYEVENTF_EXTENDEDKEY == RMENU */
676 /* 46 */
677 {VK_MENU, KEYEVENTF_EXTENDEDKEY, FALSE,
678 {{VK_MENU, 0x00}, {VK_RMENU, 0x00}, {VK_CONTROL, 0x00, 1}, {VK_LCONTROL, 0x01, 1}, {0}},
679 {{WM_SYSKEYDOWN, hook|wparam|lparam|optional, VK_CONTROL, 0},
680 {WM_SYSKEYDOWN, hook/*|wparam*/|lparam, VK_MENU, LLKHF_EXTENDED},
681 {WM_SYSKEYDOWN, wparam|lparam, VK_MENU, KF_EXTENDED}, {0}}},
682 {VK_MENU, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, TRUE,
683 {{VK_MENU, 0x80}, {VK_RMENU, 0x80}, {VK_CONTROL, 0x81, 1}, {VK_LCONTROL, 0x80, 1}, {0}},
684 {{WM_KEYUP, hook|wparam|lparam|optional, VK_CONTROL, LLKHF_UP},
685 {WM_KEYUP, hook/*|wparam*/|lparam, VK_MENU, LLKHF_UP|LLKHF_EXTENDED},
686 {WM_SYSKEYUP, wparam|lparam, VK_MENU, KF_UP|KF_EXTENDED},
687 {WM_SYSCOMMAND}, {0}}},
689 /* test LSHIFT & RSHIFT */
690 /* 48 */
691 {VK_LSHIFT, 0, FALSE,
692 {{VK_SHIFT, 0x00}, {VK_LSHIFT, 0x00}, {0}},
693 {{WM_KEYDOWN, hook|wparam|lparam, VK_LSHIFT, 0},
694 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
695 {VK_RSHIFT, KEYEVENTF_EXTENDEDKEY, FALSE,
696 {{VK_RSHIFT, 0x00}, {0}},
697 {{WM_KEYDOWN, hook|wparam|lparam, VK_RSHIFT, LLKHF_EXTENDED},
698 {WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0}, {0}}},
699 {VK_RSHIFT, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, FALSE,
700 {{VK_RSHIFT, 0x80}, {0}},
701 {{WM_KEYUP, hook|wparam|lparam, VK_RSHIFT, LLKHF_UP|LLKHF_EXTENDED},
702 {WM_KEYUP, optional}, {0}}},
703 {VK_LSHIFT, KEYEVENTF_KEYUP, FALSE,
704 {{VK_SHIFT, 0x80}, {VK_LSHIFT, 0x80}, {0}},
705 {{WM_KEYUP, hook|wparam, VK_LSHIFT},
706 {WM_KEYUP, wparam|lparam, VK_SHIFT, KF_UP}, {0}}},
708 {0, 0, FALSE, {{0}}, {{0}}} /* end */
711 static struct message sent_messages[MAXKEYMESSAGES];
712 static UINT sent_messages_cnt;
714 /* Verify that only specified key state transitions occur */
715 static void compare_and_check(int id, BYTE *ks1, BYTE *ks2,
716 const struct sendinput_test_s *test, BOOL foreground)
718 int i, failcount = 0;
719 const struct transition_s *t = test->expected_transitions;
720 UINT actual_cnt = 0;
721 const struct message *expected = test->expected_messages;
723 while (t->wVk && foreground) {
724 /* We won't receive any information from GetKeyboardState() if we're
725 * not the foreground window. */
726 BOOL matched = ((ks1[t->wVk]&0x80) == (t->before_state&0x80)
727 && (ks2[t->wVk]&0x80) == (~t->before_state&0x80));
729 if (!matched && !t->optional && test->_todo_wine)
731 failcount++;
732 todo_wine {
733 ok(matched, "%2d (%x/%x): %02x from %02x -> %02x "
734 "instead of %02x -> %02x\n", id, test->wVk, test->dwFlags,
735 t->wVk, ks1[t->wVk]&0x80, ks2[t->wVk]&0x80, t->before_state,
736 ~t->before_state&0x80);
738 } else {
739 ok(matched || t->optional, "%2d (%x/%x): %02x from %02x -> %02x "
740 "instead of %02x -> %02x\n", id, test->wVk, test->dwFlags,
741 t->wVk, ks1[t->wVk]&0x80, ks2[t->wVk]&0x80, t->before_state,
742 ~t->before_state&0x80);
744 ks2[t->wVk] = ks1[t->wVk]; /* clear the match */
745 t++;
747 for (i = 0; i < 256; i++)
748 if (ks2[i] != ks1[i] && test->_todo_wine)
750 failcount++;
751 todo_wine
752 ok(FALSE, "%2d (%x/%x): %02x from %02x -> %02x unexpected\n",
753 id, test->wVk, test->dwFlags, i, ks1[i], ks2[i]);
755 else
756 ok(ks2[i] == ks1[i], "%2d (%x/%x): %02x from %02x -> %02x unexpected\n",
757 id, test->wVk, test->dwFlags, i, ks1[i], ks2[i]);
759 while (expected->message && actual_cnt < sent_messages_cnt)
761 const struct message *actual = &sent_messages[actual_cnt];
763 if (expected->message == actual->message)
765 if (expected->flags & wparam)
767 if ((expected->flags & optional) && (expected->wParam != actual->wParam))
769 expected++;
770 continue;
772 if (expected->wParam != actual->wParam && test->_todo_wine)
774 failcount++;
775 todo_wine
776 ok(FALSE, "%2d (%x/%x): in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
777 id, test->wVk, test->dwFlags, expected->message, expected->wParam, actual->wParam);
779 else
780 ok(expected->wParam == actual->wParam,
781 "%2d (%x/%x): in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
782 id, test->wVk, test->dwFlags, expected->message, expected->wParam, actual->wParam);
784 if (expected->flags & lparam)
786 if (expected->lParam != actual->lParam && test->_todo_wine)
788 failcount++;
789 todo_wine
790 ok(FALSE, "%2d (%x/%x): in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
791 id, test->wVk, test->dwFlags, expected->message, expected->lParam, actual->lParam);
793 else
794 ok(expected->lParam == actual->lParam,
795 "%2d (%x/%x): in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
796 id, test->wVk, test->dwFlags, expected->message, expected->lParam, actual->lParam);
798 ok((expected->flags & hook) == (actual->flags & hook),
799 "%2d (%x/%x): the msg 0x%04x should have been sent by a hook\n",
800 id, test->wVk, test->dwFlags, expected->message);
803 else if (expected->flags & optional)
805 expected++;
806 continue;
808 else if (!(expected->flags & hook) && !foreground)
810 /* If we weren't able to receive foreground status, we won't get
811 * any window messages. */
812 expected++;
813 continue;
815 /* NT4 doesn't send SYSKEYDOWN/UP to hooks, only KEYDOWN/UP */
816 else if ((expected->flags & hook) &&
817 (expected->message == WM_SYSKEYDOWN || expected->message == WM_SYSKEYUP) &&
818 (actual->message == expected->message - 4))
820 ok((expected->flags & hook) == (actual->flags & hook),
821 "%2d (%x/%x): the msg 0x%04x should have been sent by a hook\n",
822 id, test->wVk, test->dwFlags, expected->message);
824 /* For VK_RMENU, at least localized Win2k/XP sends KEYDOWN/UP
825 * instead of SYSKEYDOWN/UP to the WNDPROC */
826 else if (test->wVk == VK_RMENU && !(expected->flags & hook) &&
827 (expected->message == WM_SYSKEYDOWN || expected->message == WM_SYSKEYUP) &&
828 (actual->message == expected->message - 4))
830 ok(expected->wParam == actual->wParam && expected->lParam == actual->lParam,
831 "%2d (%x/%x): the msg 0x%04x was expected, but got msg 0x%04x instead\n",
832 id, test->wVk, test->dwFlags, expected->message, actual->message);
834 else if (test->_todo_wine)
836 failcount++;
837 todo_wine
838 ok(FALSE,
839 "%2d (%x/%x): the msg 0x%04x was expected, but got msg 0x%04x instead\n",
840 id, test->wVk, test->dwFlags, expected->message, actual->message);
842 else
843 ok(FALSE,
844 "%2d (%x/%x): the msg 0x%04x was expected, but got msg 0x%04x instead\n",
845 id, test->wVk, test->dwFlags, expected->message, actual->message);
847 actual_cnt++;
848 expected++;
850 /* skip all optional trailing messages */
851 while (expected->message && ((expected->flags & optional) || (!(expected->flags & hook) && !foreground)))
852 expected++;
855 if (expected->message || actual_cnt < sent_messages_cnt)
857 if (test->_todo_wine)
859 failcount++;
860 todo_wine
861 ok(FALSE, "%2d (%x/%x): the msg sequence is not complete: expected %04x - actual %04x\n",
862 id, test->wVk, test->dwFlags, expected->message, sent_messages[actual_cnt].message);
864 else
865 ok(FALSE, "%2d (%x/%x): the msg sequence is not complete: expected %04x - actual %04x\n",
866 id, test->wVk, test->dwFlags, expected->message, sent_messages[actual_cnt].message);
869 if( test->_todo_wine && !failcount) /* succeeded yet marked todo */
870 todo_wine
871 ok(TRUE, "%2d (%x/%x): marked \"todo_wine\" but succeeds\n", id, test->wVk, test->dwFlags);
873 sent_messages_cnt = 0;
876 /* WndProc2 checks that we get at least the messages specified */
877 static LRESULT CALLBACK WndProc2(HWND hWnd, UINT Msg, WPARAM wParam,
878 LPARAM lParam)
880 if (winetest_debug > 1) trace("MSG: %8x W:%8lx L:%8lx\n", Msg, wParam, lParam);
882 if ((Msg >= WM_KEYFIRST && Msg <= WM_KEYLAST) || Msg == WM_SYSCOMMAND)
884 ok(sent_messages_cnt < MAXKEYMESSAGES, "Too many messages\n");
885 if (sent_messages_cnt < MAXKEYMESSAGES)
887 sent_messages[sent_messages_cnt].message = Msg;
888 sent_messages[sent_messages_cnt].flags = 0;
889 sent_messages[sent_messages_cnt].wParam = wParam;
890 sent_messages[sent_messages_cnt++].lParam = HIWORD(lParam) & (KF_UP|KF_EXTENDED);
893 return DefWindowProcA(hWnd, Msg, wParam, lParam);
896 static LRESULT CALLBACK hook_proc(int code, WPARAM wparam, LPARAM lparam)
898 KBDLLHOOKSTRUCT *hook_info = (KBDLLHOOKSTRUCT *)lparam;
900 if (code == HC_ACTION)
902 ok(sent_messages_cnt < MAXKEYMESSAGES, "Too many messages\n");
903 if (sent_messages_cnt < MAXKEYMESSAGES)
905 sent_messages[sent_messages_cnt].message = wparam;
906 sent_messages[sent_messages_cnt].flags = hook;
907 sent_messages[sent_messages_cnt].wParam = hook_info->vkCode;
908 sent_messages[sent_messages_cnt++].lParam = hook_info->flags & (LLKHF_UP|LLKHF_EXTENDED);
911 if(0) /* For some reason not stable on Wine */
913 if (wparam == WM_KEYDOWN || wparam == WM_SYSKEYDOWN)
914 ok(!(GetAsyncKeyState(hook_info->vkCode) & 0x8000), "key %x should be up\n", hook_info->vkCode);
915 else if (wparam == WM_KEYUP || wparam == WM_SYSKEYUP)
916 ok(GetAsyncKeyState(hook_info->vkCode) & 0x8000, "key %x should be down\n", hook_info->vkCode);
919 if (winetest_debug > 1)
920 trace("Hook: w=%lx vk:%8x sc:%8x fl:%8x %lx\n", wparam,
921 hook_info->vkCode, hook_info->scanCode, hook_info->flags, hook_info->dwExtraInfo);
923 return CallNextHookEx( 0, code, wparam, lparam );
925 static void test_Input_blackbox(void)
927 TEST_INPUT i;
928 int ii;
929 BYTE ks1[256], ks2[256];
930 LONG_PTR prevWndProc;
931 BOOL foreground;
932 HWND window;
933 HHOOK hook;
935 if (GetKeyboardLayout(0) != (HKL)(ULONG_PTR)0x04090409)
937 skip("Skipping Input_blackbox test on non-US keyboard\n");
938 return;
940 window = CreateWindowA("Static", NULL, WS_POPUP|WS_HSCROLL|WS_VSCROLL
941 |WS_VISIBLE, 0, 0, 200, 60, NULL, NULL,
942 NULL, NULL);
943 ok(window != NULL, "error: %d\n", (int) GetLastError());
944 SetWindowPos( window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE );
945 foreground = SetForegroundWindow( window );
946 if (!foreground)
947 skip("Failed to set foreground window; some tests will be skipped.\n");
949 if (!(hook = SetWindowsHookExA(WH_KEYBOARD_LL, hook_proc, GetModuleHandleA( NULL ), 0)))
951 DestroyWindow(window);
952 win_skip("WH_KEYBOARD_LL is not supported\n");
953 return;
956 /* must process all initial messages, otherwise X11DRV_KeymapNotify unsets
957 * key state set by SendInput(). */
958 empty_message_queue();
960 prevWndProc = SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR) WndProc2);
961 ok(prevWndProc != 0 || GetLastError() == 0, "error: %d\n", (int) GetLastError());
963 i.type = INPUT_KEYBOARD;
964 i.u.ki.time = 0;
965 i.u.ki.dwExtraInfo = 0;
967 for (ii = 0; ii < ARRAY_SIZE(sendinput_test)-1; ii++) {
968 GetKeyboardState(ks1);
969 i.u.ki.wScan = ii+1 /* useful for debugging */;
970 i.u.ki.dwFlags = sendinput_test[ii].dwFlags;
971 i.u.ki.wVk = sendinput_test[ii].wVk;
972 SendInput(1, (INPUT*)&i, sizeof(TEST_INPUT));
973 empty_message_queue();
974 GetKeyboardState(ks2);
975 compare_and_check(ii, ks1, ks2, &sendinput_test[ii], foreground);
978 empty_message_queue();
979 DestroyWindow(window);
980 UnhookWindowsHookEx(hook);
983 static void reset_key_status(WORD vk)
985 key_status.last_key_down = -1;
986 key_status.last_key_up = -1;
987 key_status.last_syskey_down = -1;
988 key_status.last_syskey_up = -1;
989 key_status.last_char = -1;
990 key_status.last_syschar = -1;
991 key_status.last_hook_down = -1;
992 key_status.last_hook_up = -1;
993 key_status.last_hook_syskey_down = -1;
994 key_status.last_hook_syskey_up = -1;
995 key_status.vk = vk;
996 key_status.expect_alt = FALSE;
997 key_status.sendinput_broken = FALSE;
1000 static void test_unicode_keys(HWND hwnd, HHOOK hook)
1002 TEST_INPUT inputs[2];
1003 MSG msg;
1005 /* init input data that never changes */
1006 inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
1007 inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
1008 inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
1010 /* pressing & releasing a single unicode character */
1011 inputs[0].u.ki.wVk = 0;
1012 inputs[0].u.ki.wScan = 0x3c0;
1013 inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
1015 reset_key_status(VK_PACKET);
1016 SendInput(1, (INPUT*)inputs, sizeof(INPUT));
1017 while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
1018 if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
1019 TranslateMessage(&msg);
1021 DispatchMessageW(&msg);
1023 if(!key_status.sendinput_broken){
1024 ok(key_status.last_key_down == VK_PACKET,
1025 "Last keydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_down);
1026 ok(key_status.last_char == 0x3c0,
1027 "Last char msg wparam should have been 0x3c0 (was: 0x%x)\n", key_status.last_char);
1028 if(hook)
1029 ok(key_status.last_hook_down == 0x3c0,
1030 "Last hookdown msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_down);
1033 inputs[1].u.ki.wVk = 0;
1034 inputs[1].u.ki.wScan = 0x3c0;
1035 inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1037 reset_key_status(VK_PACKET);
1038 SendInput(1, (INPUT*)(inputs+1), sizeof(INPUT));
1039 while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
1040 if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
1041 TranslateMessage(&msg);
1043 DispatchMessageW(&msg);
1045 if(!key_status.sendinput_broken){
1046 ok(key_status.last_key_up == VK_PACKET,
1047 "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
1048 if(hook)
1049 ok(key_status.last_hook_up == 0x3c0,
1050 "Last hookup msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_up);
1053 /* holding alt, pressing & releasing a unicode character, releasing alt */
1054 inputs[0].u.ki.wVk = VK_LMENU;
1055 inputs[0].u.ki.wScan = 0;
1056 inputs[0].u.ki.dwFlags = 0;
1058 inputs[1].u.ki.wVk = 0;
1059 inputs[1].u.ki.wScan = 0x3041;
1060 inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE;
1062 reset_key_status(VK_PACKET);
1063 key_status.expect_alt = TRUE;
1064 SendInput(2, (INPUT*)inputs, sizeof(INPUT));
1065 while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
1066 if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
1067 TranslateMessage(&msg);
1069 DispatchMessageW(&msg);
1071 if(!key_status.sendinput_broken){
1072 ok(key_status.last_syskey_down == VK_PACKET,
1073 "Last syskeydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_syskey_down);
1074 ok(key_status.last_syschar == 0x3041,
1075 "Last syschar msg should have been 0x3041 (was: 0x%x)\n", key_status.last_syschar);
1076 if(hook)
1077 ok(key_status.last_hook_syskey_down == 0x3041,
1078 "Last hooksysdown msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_syskey_down);
1081 inputs[1].u.ki.wVk = 0;
1082 inputs[1].u.ki.wScan = 0x3041;
1083 inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1085 inputs[0].u.ki.wVk = VK_LMENU;
1086 inputs[0].u.ki.wScan = 0;
1087 inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
1089 reset_key_status(VK_PACKET);
1090 key_status.expect_alt = TRUE;
1091 SendInput(2, (INPUT*)inputs, sizeof(INPUT));
1092 while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
1093 if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
1094 TranslateMessage(&msg);
1096 DispatchMessageW(&msg);
1098 if(!key_status.sendinput_broken){
1099 ok(key_status.last_key_up == VK_PACKET,
1100 "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
1101 if(hook)
1102 ok(key_status.last_hook_up == 0x3041,
1103 "Last hook up msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_up);
1106 /* Press and release, non-zero key code. */
1107 inputs[0].u.ki.wVk = 0x51;
1108 inputs[0].u.ki.wScan = 0x123;
1109 inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
1111 inputs[1].u.ki.wVk = 0x51;
1112 inputs[1].u.ki.wScan = 0x123;
1113 inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1115 reset_key_status(inputs[0].u.ki.wVk);
1116 SendInput(2, (INPUT*)inputs, sizeof(INPUT));
1117 while (PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE))
1119 TranslateMessage(&msg);
1120 DispatchMessageW(&msg);
1123 if (!key_status.sendinput_broken)
1125 ok(key_status.last_key_down == 0x51, "Unexpected key down %#x.\n", key_status.last_key_down);
1126 ok(key_status.last_key_up == 0x51, "Unexpected key up %#x.\n", key_status.last_key_up);
1127 if (hook)
1128 todo_wine
1129 ok(key_status.last_hook_up == 0x23, "Unexpected hook message %#x.\n", key_status.last_hook_up);
1133 static LRESULT CALLBACK unicode_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
1134 LPARAM lParam )
1136 switch(msg){
1137 case WM_KEYDOWN:
1138 key_status.last_key_down = wParam;
1139 break;
1140 case WM_SYSKEYDOWN:
1141 key_status.last_syskey_down = wParam;
1142 break;
1143 case WM_KEYUP:
1144 key_status.last_key_up = wParam;
1145 break;
1146 case WM_SYSKEYUP:
1147 key_status.last_syskey_up = wParam;
1148 break;
1149 case WM_CHAR:
1150 key_status.last_char = wParam;
1151 break;
1152 case WM_SYSCHAR:
1153 key_status.last_syschar = wParam;
1154 break;
1156 return DefWindowProcW(hWnd, msg, wParam, lParam);
1159 static LRESULT CALLBACK llkbd_unicode_hook(int nCode, WPARAM wParam, LPARAM lParam)
1161 if(nCode == HC_ACTION){
1162 LPKBDLLHOOKSTRUCT info = (LPKBDLLHOOKSTRUCT)lParam;
1163 if(!info->vkCode){
1164 key_status.sendinput_broken = TRUE;
1165 win_skip("SendInput doesn't support unicode on this platform\n");
1166 }else{
1167 if(key_status.expect_alt){
1168 ok(info->vkCode == VK_LMENU, "vkCode should have been VK_LMENU[0x%04x], was: 0x%x\n", VK_LMENU, info->vkCode);
1169 key_status.expect_alt = FALSE;
1170 }else
1171 todo_wine_if(key_status.vk != VK_PACKET)
1172 ok(info->vkCode == key_status.vk, "Unexpected vkCode %#x, expected %#x.\n", info->vkCode, key_status.vk);
1174 switch(wParam){
1175 case WM_KEYDOWN:
1176 key_status.last_hook_down = info->scanCode;
1177 break;
1178 case WM_KEYUP:
1179 key_status.last_hook_up = info->scanCode;
1180 break;
1181 case WM_SYSKEYDOWN:
1182 key_status.last_hook_syskey_down = info->scanCode;
1183 break;
1184 case WM_SYSKEYUP:
1185 key_status.last_hook_syskey_up = info->scanCode;
1186 break;
1189 return CallNextHookEx(NULL, nCode, wParam, lParam);
1192 static void test_Input_unicode(void)
1194 WCHAR classNameW[] = {'I','n','p','u','t','U','n','i','c','o','d','e',
1195 'K','e','y','T','e','s','t','C','l','a','s','s',0};
1196 WCHAR windowNameW[] = {'I','n','p','u','t','U','n','i','c','o','d','e',
1197 'K','e','y','T','e','s','t',0};
1198 MSG msg;
1199 WNDCLASSW wclass;
1200 HANDLE hInstance = GetModuleHandleW(NULL);
1201 HHOOK hook;
1202 HMODULE hModuleImm32;
1203 BOOL (WINAPI *pImmDisableIME)(DWORD);
1204 BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409);
1205 if (!us_kbd)
1207 skip( "skipping test with inconsistent results on non-us keyboard\n" );
1208 return;
1211 wclass.lpszClassName = classNameW;
1212 wclass.style = CS_HREDRAW | CS_VREDRAW;
1213 wclass.lpfnWndProc = unicode_wnd_proc;
1214 wclass.hInstance = hInstance;
1215 wclass.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
1216 wclass.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
1217 wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1218 wclass.lpszMenuName = 0;
1219 wclass.cbClsExtra = 0;
1220 wclass.cbWndExtra = 0;
1221 if(!RegisterClassW(&wclass)){
1222 win_skip("Unicode functions not supported\n");
1223 return;
1226 hModuleImm32 = LoadLibraryA("imm32.dll");
1227 if (hModuleImm32) {
1228 pImmDisableIME = (void *)GetProcAddress(hModuleImm32, "ImmDisableIME");
1229 if (pImmDisableIME)
1230 pImmDisableIME(0);
1232 pImmDisableIME = NULL;
1233 FreeLibrary(hModuleImm32);
1235 /* create the test window that will receive the keystrokes */
1236 hWndTest = CreateWindowW(wclass.lpszClassName, windowNameW,
1237 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
1238 NULL, NULL, hInstance, NULL);
1240 assert(hWndTest);
1241 assert(IsWindowUnicode(hWndTest));
1243 hook = SetWindowsHookExW(WH_KEYBOARD_LL, llkbd_unicode_hook, GetModuleHandleW(NULL), 0);
1244 if(!hook)
1245 win_skip("unable to set WH_KEYBOARD_LL hook\n");
1247 ShowWindow(hWndTest, SW_SHOW);
1248 SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
1249 SetForegroundWindow(hWndTest);
1250 UpdateWindow(hWndTest);
1252 /* flush pending messages */
1253 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
1255 SetFocus(hWndTest);
1257 test_unicode_keys(hWndTest, hook);
1259 if(hook)
1260 UnhookWindowsHookEx(hook);
1261 DestroyWindow(hWndTest);
1264 static void test_keynames(void)
1266 int i, len;
1267 char buff[256];
1269 for (i = 0; i < 512; i++)
1271 strcpy(buff, "----");
1272 len = GetKeyNameTextA(i << 16, buff, sizeof(buff));
1273 ok(len || !buff[0], "%d: Buffer is not zeroed\n", i);
1277 static POINT pt_old, pt_new;
1278 static BOOL clipped;
1279 #define STEP 3
1281 static LRESULT CALLBACK hook_proc1( int code, WPARAM wparam, LPARAM lparam )
1283 MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam;
1284 POINT pt, pt1;
1286 if (code == HC_ACTION)
1288 /* This is our new cursor position */
1289 pt_new = hook->pt;
1290 /* Should return previous position */
1291 GetCursorPos(&pt);
1292 ok(pt.x == pt_old.x && pt.y == pt_old.y, "GetCursorPos: (%d,%d)\n", pt.x, pt.y);
1294 /* Should set new position until hook chain is finished. */
1295 pt.x = pt_old.x + STEP;
1296 pt.y = pt_old.y + STEP;
1297 SetCursorPos(pt.x, pt.y);
1298 GetCursorPos(&pt1);
1299 if (clipped)
1300 ok(pt1.x == pt_old.x && pt1.y == pt_old.y, "Wrong set pos: (%d,%d)\n", pt1.x, pt1.y);
1301 else
1302 ok(pt1.x == pt.x && pt1.y == pt.y, "Wrong set pos: (%d,%d)\n", pt1.x, pt1.y);
1304 return CallNextHookEx( 0, code, wparam, lparam );
1307 static LRESULT CALLBACK hook_proc2( int code, WPARAM wparam, LPARAM lparam )
1309 MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam;
1310 POINT pt;
1312 if (code == HC_ACTION)
1314 ok(hook->pt.x == pt_new.x && hook->pt.y == pt_new.y,
1315 "Wrong hook coords: (%d %d) != (%d,%d)\n", hook->pt.x, hook->pt.y, pt_new.x, pt_new.y);
1317 /* Should match position set above */
1318 GetCursorPos(&pt);
1319 if (clipped)
1320 ok(pt.x == pt_old.x && pt.y == pt_old.y, "GetCursorPos: (%d,%d)\n", pt.x, pt.y);
1321 else
1322 ok(pt.x == pt_old.x +STEP && pt.y == pt_old.y +STEP, "GetCursorPos: (%d,%d)\n", pt.x, pt.y);
1324 return CallNextHookEx( 0, code, wparam, lparam );
1327 static LRESULT CALLBACK hook_proc3( int code, WPARAM wparam, LPARAM lparam )
1329 POINT pt;
1331 if (code == HC_ACTION)
1333 /* MSLLHOOKSTRUCT does not seem to be reliable and contains different data on each run. */
1334 GetCursorPos(&pt);
1335 ok(pt.x == pt_old.x && pt.y == pt_old.y, "GetCursorPos: (%d,%d)\n", pt.x, pt.y);
1337 return CallNextHookEx( 0, code, wparam, lparam );
1340 static void test_mouse_ll_hook(void)
1342 HWND hwnd;
1343 HHOOK hook1, hook2;
1344 POINT pt_org, pt;
1345 RECT rc;
1347 GetCursorPos(&pt_org);
1348 hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1349 10, 10, 200, 200, NULL, NULL, NULL, NULL);
1350 SetCursorPos(100, 100);
1352 if (!(hook2 = SetWindowsHookExA(WH_MOUSE_LL, hook_proc2, GetModuleHandleA(0), 0)))
1354 win_skip( "cannot set MOUSE_LL hook\n" );
1355 goto done;
1357 hook1 = SetWindowsHookExA(WH_MOUSE_LL, hook_proc1, GetModuleHandleA(0), 0);
1359 GetCursorPos(&pt_old);
1360 mouse_event(MOUSEEVENTF_MOVE, -STEP, 0, 0, 0);
1361 GetCursorPos(&pt_old);
1362 ok(pt_old.x == pt_new.x && pt_old.y == pt_new.y, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1363 mouse_event(MOUSEEVENTF_MOVE, +STEP, 0, 0, 0);
1364 GetCursorPos(&pt_old);
1365 ok(pt_old.x == pt_new.x && pt_old.y == pt_new.y, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1366 mouse_event(MOUSEEVENTF_MOVE, 0, -STEP, 0, 0);
1367 GetCursorPos(&pt_old);
1368 ok(pt_old.x == pt_new.x && pt_old.y == pt_new.y, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1369 mouse_event(MOUSEEVENTF_MOVE, 0, +STEP, 0, 0);
1370 GetCursorPos(&pt_old);
1371 ok(pt_old.x == pt_new.x && pt_old.y == pt_new.y, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1373 SetRect(&rc, 50, 50, 151, 151);
1374 ClipCursor(&rc);
1375 clipped = TRUE;
1377 SetCursorPos(40, 40);
1378 GetCursorPos(&pt_old);
1379 ok(pt_old.x == 50 && pt_old.y == 50, "Wrong new pos: (%d,%d)\n", pt_new.x, pt_new.y);
1380 SetCursorPos(160, 160);
1381 GetCursorPos(&pt_old);
1382 ok(pt_old.x == 150 && pt_old.y == 150, "Wrong new pos: (%d,%d)\n", pt_new.x, pt_new.y);
1383 mouse_event(MOUSEEVENTF_MOVE, +STEP, +STEP, 0, 0);
1384 GetCursorPos(&pt_old);
1385 ok(pt_old.x == 150 && pt_old.y == 150, "Wrong new pos: (%d,%d)\n", pt_new.x, pt_new.y);
1387 clipped = FALSE;
1388 pt_new.x = pt_new.y = 150;
1389 ClipCursor(NULL);
1390 UnhookWindowsHookEx(hook1);
1392 /* Now check that mouse buttons do not change mouse position
1393 if we don't have MOUSEEVENTF_MOVE flag specified. */
1395 /* We reusing the same hook callback, so make it happy */
1396 pt_old.x = pt_new.x - STEP;
1397 pt_old.y = pt_new.y - STEP;
1398 mouse_event(MOUSEEVENTF_LEFTUP, 123, 456, 0, 0);
1399 GetCursorPos(&pt);
1400 ok(pt.x == pt_new.x && pt.y == pt_new.y, "Position changed: (%d,%d)\n", pt.x, pt.y);
1401 mouse_event(MOUSEEVENTF_RIGHTUP, 456, 123, 0, 0);
1402 GetCursorPos(&pt);
1403 ok(pt.x == pt_new.x && pt.y == pt_new.y, "Position changed: (%d,%d)\n", pt.x, pt.y);
1405 mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE, 123, 456, 0, 0);
1406 GetCursorPos(&pt);
1407 ok(pt.x == pt_new.x && pt.y == pt_new.y, "Position changed: (%d,%d)\n", pt.x, pt.y);
1408 mouse_event(MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE, 456, 123, 0, 0);
1409 GetCursorPos(&pt);
1410 ok(pt.x == pt_new.x && pt.y == pt_new.y, "Position changed: (%d,%d)\n", pt.x, pt.y);
1412 UnhookWindowsHookEx(hook2);
1413 hook1 = SetWindowsHookExA(WH_MOUSE_LL, hook_proc3, GetModuleHandleA(0), 0);
1415 SetRect(&rc, 150, 150, 150, 150);
1416 ClipCursor(&rc);
1417 clipped = TRUE;
1419 SetCursorPos(140, 140);
1420 GetCursorPos(&pt_old);
1421 ok(pt_old.x == 150 && pt_old.y == 150, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1422 SetCursorPos(160, 160);
1423 GetCursorPos(&pt_old);
1424 todo_wine
1425 ok(pt_old.x == 149 && pt_old.y == 149, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1426 mouse_event(MOUSEEVENTF_MOVE, -STEP, -STEP, 0, 0);
1427 GetCursorPos(&pt_old);
1428 ok(pt_old.x == 150 && pt_old.y == 150, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1429 mouse_event(MOUSEEVENTF_MOVE, +STEP, +STEP, 0, 0);
1430 GetCursorPos(&pt_old);
1431 todo_wine
1432 ok(pt_old.x == 149 && pt_old.y == 149, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1433 mouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0);
1434 GetCursorPos(&pt_old);
1435 ok((pt_old.x == 150 && pt_old.y == 150) ||
1436 broken(pt_old.x == 149 && pt_old.y == 149) /* w1064v1809 */,
1437 "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1438 mouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0);
1439 GetCursorPos(&pt_old);
1440 todo_wine
1441 ok(pt_old.x == 149 && pt_old.y == 149, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1443 clipped = FALSE;
1444 ClipCursor(NULL);
1446 SetCursorPos(140, 140);
1447 SetRect(&rc, 150, 150, 150, 150);
1448 ClipCursor(&rc);
1449 GetCursorPos(&pt_old);
1450 ok(pt_old.x == 150 && pt_old.y == 150, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1451 ClipCursor(NULL);
1453 SetCursorPos(160, 160);
1454 SetRect(&rc, 150, 150, 150, 150);
1455 ClipCursor(&rc);
1456 GetCursorPos(&pt_old);
1457 todo_wine
1458 ok(pt_old.x == 149 && pt_old.y == 149, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1459 ClipCursor(NULL);
1461 SetCursorPos(150, 150);
1462 SetRect(&rc, 150, 150, 150, 150);
1463 ClipCursor(&rc);
1464 GetCursorPos(&pt_old);
1465 todo_wine
1466 ok(pt_old.x == 149 && pt_old.y == 149, "Wrong new pos: (%d,%d)\n", pt_old.x, pt_old.y);
1467 ClipCursor(NULL);
1469 UnhookWindowsHookEx(hook1);
1471 done:
1472 DestroyWindow(hwnd);
1473 SetCursorPos(pt_org.x, pt_org.y);
1476 static void test_GetMouseMovePointsEx(const char *argv0)
1478 #define BUFLIM 64
1479 #define MYERROR 0xdeadbeef
1480 PROCESS_INFORMATION process_info;
1481 STARTUPINFOA startup_info;
1482 char path[MAX_PATH];
1483 int i, count, retval;
1484 MOUSEMOVEPOINT in;
1485 MOUSEMOVEPOINT out[200];
1486 POINT point;
1487 TEST_INPUT input;
1489 /* Get a valid content for the input struct */
1490 if(!GetCursorPos(&point)) {
1491 win_skip("GetCursorPos() failed with error %u\n", GetLastError());
1492 return;
1494 memset(&in, 0, sizeof(MOUSEMOVEPOINT));
1495 in.x = point.x;
1496 in.y = point.y;
1498 /* test first parameter
1499 * everything different than sizeof(MOUSEMOVEPOINT)
1500 * is expected to fail with ERROR_INVALID_PARAMETER
1502 SetLastError(MYERROR);
1503 retval = pGetMouseMovePointsEx(0, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1504 if (retval == ERROR_INVALID_PARAMETER)
1506 win_skip( "GetMouseMovePointsEx broken on WinME\n" );
1507 return;
1509 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1510 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1511 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1513 SetLastError(MYERROR);
1514 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1515 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1516 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1517 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1519 SetLastError(MYERROR);
1520 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)+1, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1521 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1522 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1523 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1525 /* test second and third parameter
1527 SetLastError(MYERROR);
1528 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, out, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1529 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1530 ok(GetLastError() == ERROR_NOACCESS || GetLastError() == MYERROR,
1531 "expected error ERROR_NOACCESS, got %u\n", GetLastError());
1533 SetLastError(MYERROR);
1534 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1535 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1536 ok(ERROR_NOACCESS == GetLastError(),
1537 "expected error ERROR_NOACCESS, got %u\n", GetLastError());
1539 SetLastError(MYERROR);
1540 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1541 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1542 ok(ERROR_NOACCESS == GetLastError(),
1543 "expected error ERROR_NOACCESS, got %u\n", GetLastError());
1545 SetLastError(MYERROR);
1546 count = 0;
1547 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, count, GMMP_USE_DISPLAY_POINTS);
1548 if (retval == -1)
1549 ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %u\n", GetLastError());
1550 else
1551 ok(retval == count, "expected GetMouseMovePointsEx to succeed, got %d\n", retval);
1553 /* test fourth parameter
1554 * a value higher than 64 is expected to fail with ERROR_INVALID_PARAMETER
1556 SetLastError(MYERROR);
1557 count = -1;
1558 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS);
1559 ok(retval == count, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1560 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1561 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1563 SetLastError(MYERROR);
1564 count = 0;
1565 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS);
1566 if (retval == -1)
1567 ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %u\n", GetLastError());
1568 else
1569 ok(retval == count, "expected GetMouseMovePointsEx to succeed, got %d\n", retval);
1571 SetLastError(MYERROR);
1572 count = BUFLIM;
1573 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS);
1574 if (retval == -1)
1575 ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %u\n", GetLastError());
1576 else
1577 ok((0 <= retval) && (retval <= count), "expected GetMouseMovePointsEx to succeed, got %d\n", retval);
1579 SetLastError(MYERROR);
1580 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM+1, GMMP_USE_DISPLAY_POINTS);
1581 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1582 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1583 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1585 /* it was not possible to force an error with the fifth parameter on win2k */
1587 /* test combinations of wrong parameters to see which error wins */
1588 SetLastError(MYERROR);
1589 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, NULL, out, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1590 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1591 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1592 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1594 SetLastError(MYERROR);
1595 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, &in, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS);
1596 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1597 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1598 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1600 SetLastError(MYERROR);
1601 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, out, BUFLIM+1, GMMP_USE_DISPLAY_POINTS);
1602 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1603 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1604 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1606 SetLastError(MYERROR);
1607 retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, BUFLIM+1, GMMP_USE_DISPLAY_POINTS);
1608 ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval);
1609 ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR,
1610 "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
1612 /* more than 64 to be sure we wrap around */
1613 for (i = 0; i < 67; i++)
1615 in.x = i;
1616 in.y = i*2;
1617 SetCursorPos( in.x, in.y );
1620 SetLastError( MYERROR );
1621 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1622 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1623 ok( GetLastError() == MYERROR, "expected error to stay %x, got %x\n", MYERROR, GetLastError() );
1625 for (i = 0; i < retval; i++)
1627 ok( out[i].x == in.x && out[i].y == in.y, "wrong position %d, expected %dx%d got %dx%d\n", i, in.x, in.y, out[i].x, out[i].y );
1628 in.x--;
1629 in.y -= 2;
1632 in.x = 1500;
1633 in.y = 1500;
1634 SetLastError( MYERROR );
1635 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1636 ok( retval == -1, "expected to get -1 but got %d\n", retval );
1637 ok( GetLastError() == ERROR_POINT_NOT_FOUND, "expected error to be set to %x, got %x\n", ERROR_POINT_NOT_FOUND, GetLastError() );
1639 /* make sure there's no deduplication */
1640 in.x = 6;
1641 in.y = 6;
1642 SetCursorPos( in.x, in.y );
1643 SetCursorPos( in.x, in.y );
1644 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1645 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1646 ok( out[0].x == 6 && out[0].y == 6, "expected cursor position to be 6x6 but got %d %d\n", out[0].x, out[0].y );
1647 ok( out[1].x == 6 && out[1].y == 6, "expected cursor position to be 6x6 but got %d %d\n", out[1].x, out[1].y );
1649 /* make sure 2 events are distinguishable by their timestamps */
1650 in.x = 150;
1651 in.y = 75;
1652 SetCursorPos( 30, 30 );
1653 SetCursorPos( in.x, in.y );
1654 SetCursorPos( 150, 150 );
1655 Sleep( 3 );
1656 SetCursorPos( in.x, in.y );
1658 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1659 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1660 ok( out[0].x == 150 && out[0].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[0].x, out[0].y );
1661 ok( out[1].x == 150 && out[1].y == 150, "expected cursor position to be 150x150 but got %d %d\n", out[1].x, out[1].y );
1662 ok( out[2].x == 150 && out[2].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[2].x, out[2].y );
1664 in.time = out[2].time;
1665 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1666 ok( retval == 62, "expected to get 62 mouse move points but got %d\n", retval );
1667 ok( out[0].x == 150 && out[0].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[0].x, out[0].y );
1668 ok( out[1].x == 30 && out[1].y == 30, "expected cursor position to be 30x30 but got %d %d\n", out[1].x, out[1].y );
1670 /* events created through other means should also be on the list with correct extra info */
1671 mouse_event( MOUSEEVENTF_MOVE, -13, 17, 0, 0xcafecafe );
1672 ok( GetCursorPos( &point ), "failed to get cursor position\n" );
1673 ok( in.x != point.x && in.y != point.y, "cursor didn't change position after mouse_event()\n" );
1674 in.time = 0;
1675 in.x = point.x;
1676 in.y = point.y;
1677 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1678 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1679 ok( out[0].dwExtraInfo == 0xcafecafe, "wrong extra info, got 0x%lx expected 0xcafecafe\n", out[0].dwExtraInfo );
1681 input.type = INPUT_MOUSE;
1682 memset( &input, 0, sizeof(input) );
1683 input.u.mi.dwFlags = MOUSEEVENTF_MOVE;
1684 input.u.mi.dwExtraInfo = 0xdeadbeef;
1685 input.u.mi.dx = -17;
1686 input.u.mi.dy = 13;
1687 SendInput( 1, (INPUT *)&input, sizeof(INPUT) );
1688 ok( GetCursorPos( &point ), "failed to get cursor position\n" );
1689 ok( in.x != point.x && in.y != point.y, "cursor didn't change position after mouse_event()\n" );
1690 in.time = 0;
1691 in.x = point.x;
1692 in.y = point.y;
1693 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS );
1694 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1695 ok( out[0].dwExtraInfo == 0xdeadbeef, "wrong extra info, got 0x%lx expected 0xdeadbeef\n", out[0].dwExtraInfo );
1697 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_HIGH_RESOLUTION_POINTS );
1698 todo_wine ok( retval == 64, "expected to get 64 high resolution mouse move points but got %d\n", retval );
1700 sprintf(path, "%s input get_mouse_move_points_test", argv0);
1701 memset(&startup_info, 0, sizeof(startup_info));
1702 startup_info.cb = sizeof(startup_info);
1703 startup_info.dwFlags = STARTF_USESHOWWINDOW;
1704 startup_info.wShowWindow = SW_SHOWNORMAL;
1705 retval = CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info );
1706 ok(retval, "CreateProcess \"%s\" failed err %u.\n", path, GetLastError());
1707 winetest_wait_child_process(process_info.hProcess);
1708 CloseHandle(process_info.hProcess);
1709 CloseHandle(process_info.hThread);
1710 #undef BUFLIM
1711 #undef MYERROR
1714 static void test_GetMouseMovePointsEx_process(void)
1716 int retval;
1717 MOUSEMOVEPOINT in;
1718 MOUSEMOVEPOINT out[64], out2[64];
1719 POINT point;
1720 HDESK desk0, desk1;
1721 HWINSTA winstation0, winstation1;
1723 memset( out, 0, sizeof(out) );
1724 memset( out2, 0, sizeof(out2) );
1726 /* move point history is shared between desktops within the same windowstation */
1727 ok( GetCursorPos( &point ), "failed to get cursor position\n" );
1728 in.time = 0;
1729 in.x = point.x;
1730 in.y = point.y;
1731 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS );
1732 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1734 desk0 = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS );
1735 ok( desk0 != NULL, "OpenInputDesktop has failed with %d\n", GetLastError() );
1736 desk1 = CreateDesktopA( "getmousemovepointsex_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
1737 ok( desk1 != NULL, "CreateDesktopA failed with %d\n", GetLastError() );
1739 ok( SetThreadDesktop( desk1 ), "SetThreadDesktop failed!\n" );
1740 ok( SwitchDesktop( desk1 ), "SwitchDesktop failed\n" );
1742 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out2, ARRAY_SIZE(out2), GMMP_USE_DISPLAY_POINTS );
1743 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1745 ok( memcmp( out, out2, sizeof(out2) ) == 0, "expected to get exact same history on the new desktop\n" );
1747 in.time = 0;
1748 in.x = 38;
1749 in.y = 27;
1750 SetCursorPos( in.x, in.y );
1752 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out2, ARRAY_SIZE(out2), GMMP_USE_DISPLAY_POINTS );
1753 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1755 ok( SetThreadDesktop( desk0 ), "SetThreadDesktop failed!\n" );
1756 ok( SwitchDesktop( desk0 ), "SwitchDesktop failed\n" );
1758 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS );
1759 ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval );
1760 ok( memcmp( out, out2, sizeof( out2 ) ) == 0, "expected to get exact same history on the old desktop\n" );
1762 CloseDesktop( desk1 );
1763 CloseDesktop( desk0 );
1765 /* non-default windowstations are non-interactive */
1766 winstation0 = GetProcessWindowStation();
1767 ok( winstation0 != NULL, "GetProcessWindowStation has failed with %d\n", GetLastError() );
1768 desk0 = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS );
1769 ok( desk0 != NULL, "OpenInputDesktop has failed with %d\n", GetLastError() );
1770 winstation1 = CreateWindowStationA( "test_winstation", 0, WINSTA_ALL_ACCESS, NULL );
1772 if (winstation1 == NULL && GetLastError() == ERROR_ACCESS_DENIED)
1774 win_skip("not enough priviledges for CreateWindowStation\n");
1775 CloseDesktop( desk0 );
1776 CloseWindowStation( winstation0 );
1777 return;
1780 ok( winstation1 != NULL, "CreateWindowStationA has failed with %d\n", GetLastError() );
1781 ok( SetProcessWindowStation( winstation1 ), "SetProcessWindowStation has failed\n" );
1783 desk1 = CreateDesktopA( "getmousemovepointsex_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
1784 ok( desk1 != NULL, "CreateDesktopA failed with %d\n", GetLastError() );
1785 ok( SetThreadDesktop( desk1 ), "SetThreadDesktop failed!\n" );
1787 SetLastError( 0xDEADBEEF );
1788 retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS );
1789 todo_wine ok( retval == -1, "expected to get -1 mouse move points but got %d\n", retval );
1790 todo_wine ok( GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED got %d\n", GetLastError() );
1792 ok( SetProcessWindowStation( winstation0 ), "SetProcessWindowStation has failed\n" );
1793 ok( SetThreadDesktop( desk0 ), "SetThreadDesktop failed!\n" );
1794 CloseDesktop( desk1 );
1795 CloseWindowStation( winstation1 );
1796 CloseDesktop( desk0 );
1797 CloseWindowStation( winstation0 );
1800 static void test_GetRawInputDeviceList(void)
1802 RAWINPUTDEVICELIST devices[32];
1803 UINT ret, oret, devcount, odevcount, i;
1804 DWORD err;
1805 BOOLEAN br;
1807 SetLastError(0xdeadbeef);
1808 ret = pGetRawInputDeviceList(NULL, NULL, 0);
1809 err = GetLastError();
1810 ok(ret == -1, "expected -1, got %d\n", ret);
1811 ok(err == ERROR_INVALID_PARAMETER, "expected 87, got %d\n", err);
1813 SetLastError(0xdeadbeef);
1814 ret = pGetRawInputDeviceList(NULL, NULL, sizeof(devices[0]));
1815 err = GetLastError();
1816 ok(ret == -1, "expected -1, got %d\n", ret);
1817 ok(err == ERROR_NOACCESS, "expected 998, got %d\n", err);
1819 devcount = 0;
1820 ret = pGetRawInputDeviceList(NULL, &devcount, sizeof(devices[0]));
1821 ok(ret == 0, "expected 0, got %d\n", ret);
1822 ok(devcount > 0, "expected non-zero\n");
1824 SetLastError(0xdeadbeef);
1825 devcount = 0;
1826 ret = pGetRawInputDeviceList(devices, &devcount, sizeof(devices[0]));
1827 err = GetLastError();
1828 ok(ret == -1, "expected -1, got %d\n", ret);
1829 ok(err == ERROR_INSUFFICIENT_BUFFER, "expected 122, got %d\n", err);
1830 ok(devcount > 0, "expected non-zero\n");
1832 /* devcount contains now the correct number of devices */
1833 ret = pGetRawInputDeviceList(devices, &devcount, sizeof(devices[0]));
1834 ok(ret > 0, "expected non-zero\n");
1836 for(i = 0; i < devcount; ++i)
1838 WCHAR name[128];
1839 char nameA[128];
1840 UINT sz, len;
1841 RID_DEVICE_INFO info;
1842 HANDLE file;
1843 char *ppd;
1845 /* get required buffer size */
1846 name[0] = '\0';
1847 sz = 5;
1848 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz);
1849 ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %d\n", err);
1850 ok(sz > 5 && sz < ARRAY_SIZE(name), "Size should have been set and not too large (got: %u)\n", sz);
1852 /* buffer size for RIDI_DEVICENAME is in CHARs, not BYTEs */
1853 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz);
1854 ok(ret == sz, "GetRawInputDeviceInfo gave wrong return: %d\n", err);
1855 len = lstrlenW(name);
1856 ok(len + 1 == ret, "GetRawInputDeviceInfo returned wrong length (name: %u, ret: %u)\n", len + 1, ret);
1858 /* test A variant with same size */
1859 ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, nameA, &sz);
1860 ok(ret == sz, "GetRawInputDeviceInfoA gave wrong return: %d\n", err);
1861 len = strlen(nameA);
1862 ok(len + 1 == ret, "GetRawInputDeviceInfoA returned wrong length (name: %u, ret: %u)\n", len + 1, ret);
1864 /* buffer size for RIDI_DEVICEINFO is in BYTEs */
1865 memset(&info, 0, sizeof(info));
1866 info.cbSize = sizeof(info);
1867 sz = sizeof(info) - 1;
1868 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
1869 ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %d\n", err);
1870 ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
1872 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
1873 ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %d\n", err);
1874 ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
1875 ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%x\n", info.dwType);
1877 memset(&info, 0, sizeof(info));
1878 info.cbSize = sizeof(info);
1879 ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
1880 ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %d\n", err);
1881 ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
1882 ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%x\n", info.dwType);
1884 /* setupapi returns an NT device path, but CreateFile() < Vista can't
1885 * understand that; so use the \\?\ prefix instead */
1886 name[1] = '\\';
1887 file = CreateFileW(name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1888 todo_wine_if(i == 0 || i == 1)
1889 ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u\n", wine_dbgstr_w(name), GetLastError());
1891 sz = 0;
1892 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_PREPARSEDDATA, NULL, &sz);
1893 ok(ret == 0, "GetRawInputDeviceInfo gave wrong return: %u\n", ret);
1894 ok((info.dwType == RIM_TYPEHID && sz != 0) ||
1895 (info.dwType != RIM_TYPEHID && sz == 0),
1896 "Got wrong PPD size for type 0x%x: %u\n", info.dwType, sz);
1898 ppd = HeapAlloc(GetProcessHeap(), 0, sz);
1899 ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_PREPARSEDDATA, ppd, &sz);
1900 ok(ret == sz, "GetRawInputDeviceInfo gave wrong return: %u, should be %u\n", ret, sz);
1902 if (file != INVALID_HANDLE_VALUE && ret == sz)
1904 PHIDP_PREPARSED_DATA preparsed;
1906 if (info.dwType == RIM_TYPEHID)
1908 br = HidD_GetPreparsedData(file, &preparsed);
1909 ok(br == TRUE, "HidD_GetPreparsedData failed\n");
1911 if (br)
1912 ok(!memcmp(preparsed, ppd, sz), "Expected to get same preparsed data\n");
1914 else
1916 /* succeeds on hardware, fails in some VMs */
1917 br = HidD_GetPreparsedData(file, &preparsed);
1918 ok(br == TRUE || broken(br == FALSE), "HidD_GetPreparsedData failed\n");
1921 if (br)
1922 HidD_FreePreparsedData(preparsed);
1925 HeapFree(GetProcessHeap(), 0, ppd);
1927 CloseHandle(file);
1930 /* check if variable changes from larger to smaller value */
1931 devcount = odevcount = ARRAY_SIZE(devices);
1932 oret = ret = pGetRawInputDeviceList(devices, &odevcount, sizeof(devices[0]));
1933 ok(ret > 0, "expected non-zero\n");
1934 ok(devcount == odevcount, "expected %d, got %d\n", devcount, odevcount);
1935 devcount = odevcount;
1936 odevcount = ARRAY_SIZE(devices);
1937 ret = pGetRawInputDeviceList(NULL, &odevcount, sizeof(devices[0]));
1938 ok(ret == 0, "expected 0, got %d\n", ret);
1939 ok(odevcount == oret, "expected %d, got %d\n", oret, odevcount);
1942 static void test_GetRawInputData(void)
1944 UINT size;
1945 UINT ret;
1947 /* Null raw input handle */
1948 SetLastError(0xdeadbeef);
1949 ret = GetRawInputData(NULL, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
1950 ok(ret == ~0U, "Expect ret %u, got %u\n", ~0U, ret);
1951 ok(GetLastError() == ERROR_INVALID_HANDLE, "GetRawInputData returned %08x\n", GetLastError());
1954 static void test_RegisterRawInputDevices(void)
1956 HWND hwnd;
1957 RAWINPUTDEVICE raw_devices[2];
1958 UINT count, raw_devices_count;
1959 BOOL res;
1961 memset(raw_devices, 0, sizeof(raw_devices));
1962 raw_devices[0].usUsagePage = 0x01;
1963 raw_devices[0].usUsage = 0x05;
1964 raw_devices[1].usUsagePage = 0x01;
1965 raw_devices[1].usUsage = 0x04;
1967 hwnd = CreateWindowExA(WS_EX_TOPMOST, "static", "dinput", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
1968 ok(hwnd != NULL, "CreateWindowExA failed\n");
1971 res = RegisterRawInputDevices(NULL, 0, 0);
1972 ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
1975 SetLastError(0xdeadbeef);
1976 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), 0);
1977 ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
1978 ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
1980 SetLastError(0xdeadbeef);
1981 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
1982 ok(res == TRUE, "RegisterRawInputDevices failed\n");
1983 ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
1985 SetLastError(0xdeadbeef);
1986 count = GetRegisteredRawInputDevices(NULL, NULL, 0);
1987 ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count);
1988 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
1990 SetLastError(0xdeadbeef);
1991 raw_devices_count = 0;
1992 count = GetRegisteredRawInputDevices(NULL, &raw_devices_count, 0);
1993 ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count);
1994 ok(raw_devices_count == 0, "Unexpected registered devices count: %u\n", raw_devices_count);
1995 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
1997 SetLastError(0xdeadbeef);
1998 raw_devices_count = 0;
1999 count = GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE));
2000 ok(count == 0, "GetRegisteredRawInputDevices returned %u\n", count);
2001 ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count);
2002 ok(GetLastError() == 0xdeadbeef, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
2004 SetLastError(0xdeadbeef);
2005 raw_devices_count = 0;
2006 count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE));
2007 if (broken(count == 0) /* depends on windows versions */)
2008 win_skip("Ignoring GetRegisteredRawInputDevices success\n");
2009 else
2011 ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count);
2012 ok(raw_devices_count == 0, "Unexpected registered devices count: %u\n", raw_devices_count);
2013 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
2016 SetLastError(0xdeadbeef);
2017 raw_devices_count = 1;
2018 count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE));
2019 ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count);
2020 ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count);
2021 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
2023 SetLastError(0xdeadbeef);
2024 memset(raw_devices, 0, sizeof(raw_devices));
2025 raw_devices_count = ARRAY_SIZE(raw_devices);
2026 count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE));
2027 ok(count == 2, "GetRegisteredRawInputDevices returned %u\n", count);
2028 ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count);
2029 ok(GetLastError() == 0xdeadbeef, "GetRegisteredRawInputDevices unexpected error %08x\n", GetLastError());
2030 ok(raw_devices[0].usUsagePage == 0x01, "Unexpected usage page: %x\n", raw_devices[0].usUsagePage);
2031 ok(raw_devices[0].usUsage == 0x04, "Unexpected usage: %x\n", raw_devices[0].usUsage);
2032 ok(raw_devices[1].usUsagePage == 0x01, "Unexpected usage page: %x\n", raw_devices[1].usUsagePage);
2033 ok(raw_devices[1].usUsage == 0x05, "Unexpected usage: %x\n", raw_devices[1].usUsage);
2035 /* RIDEV_REMOVE requires hwndTarget == NULL */
2036 raw_devices[0].dwFlags = RIDEV_REMOVE;
2037 raw_devices[0].hwndTarget = hwnd;
2038 raw_devices[1].dwFlags = RIDEV_REMOVE;
2039 raw_devices[1].hwndTarget = hwnd;
2041 SetLastError(0xdeadbeef);
2042 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2043 ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
2044 ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
2046 raw_devices[0].hwndTarget = 0;
2047 raw_devices[1].hwndTarget = 0;
2049 SetLastError(0xdeadbeef);
2050 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2051 ok(res == TRUE, "RegisterRawInputDevices failed\n");
2052 ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
2055 /* RIDEV_INPUTSINK requires hwndTarget != NULL */
2056 raw_devices[0].dwFlags = RIDEV_INPUTSINK;
2057 raw_devices[0].hwndTarget = 0;
2058 raw_devices[1].dwFlags = RIDEV_INPUTSINK;
2059 raw_devices[1].hwndTarget = 0;
2061 SetLastError(0xdeadbeef);
2062 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2063 ok(res == FALSE, "RegisterRawInputDevices failed\n");
2064 ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
2066 raw_devices[0].hwndTarget = hwnd;
2067 raw_devices[1].hwndTarget = hwnd;
2069 SetLastError(0xdeadbeef);
2070 res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2071 ok(res == TRUE, "RegisterRawInputDevices succeeded\n");
2072 ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
2074 DestroyWindow(hwnd);
2077 static int rawinputbuffer_wndproc_count;
2079 #ifdef _WIN64
2080 typedef RAWINPUT RAWINPUT64;
2081 #else
2082 typedef struct
2084 RAWINPUTHEADER header;
2085 char pad[8];
2086 union {
2087 RAWMOUSE mouse;
2088 RAWKEYBOARD keyboard;
2089 RAWHID hid;
2090 } data;
2091 } RAWINPUT64;
2092 #endif
2094 static int rawinput_buffer_mouse_x(void *buffer, size_t index)
2096 if (is_wow64) return ((RAWINPUT64 *)buffer)[index].data.mouse.lLastX;
2097 return ((RAWINPUT *)buffer)[index].data.mouse.lLastX;
2100 static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
2102 RAWINPUT ri;
2103 char buffer[16 * sizeof(RAWINPUT64)];
2104 UINT size, count, rawinput_size, iteration = rawinputbuffer_wndproc_count++;
2105 MSG message;
2107 if (is_wow64) rawinput_size = sizeof(RAWINPUT64);
2108 else rawinput_size = sizeof(RAWINPUT);
2110 if (msg == WM_INPUT)
2112 count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER));
2113 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2115 size = sizeof(buffer);
2116 count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER));
2117 ok(count == 0, "GetRawInputBuffer returned %u\n", count);
2118 ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size);
2120 size = sizeof(buffer);
2121 memset(buffer, 0, sizeof(buffer));
2122 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2123 ok(count == 3, "GetRawInputBuffer returned %u\n", count);
2124 ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size);
2125 ok(rawinput_buffer_mouse_x(buffer, 0) == 2, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0));
2126 ok(rawinput_buffer_mouse_x(buffer, 1) == 3, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 1));
2127 ok(rawinput_buffer_mouse_x(buffer, 2) == 4, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 2));
2129 /* the first event should be removed by the next GetRawInputBuffer call
2130 * and the others should do another round through the message loop but not more */
2131 if (iteration == 0)
2133 mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2134 mouse_event(MOUSEEVENTF_MOVE, 6, 0, 0, 0);
2135 mouse_event(MOUSEEVENTF_MOVE, 2, 0, 0, 0);
2136 mouse_event(MOUSEEVENTF_MOVE, 3, 0, 0, 0);
2137 mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0);
2139 /* even though rawinput_size is the minimum required size,
2140 * it needs one more byte to return success */
2141 size = rawinput_size + 1;
2142 memset(buffer, 0, sizeof(buffer));
2143 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2144 ok(count == 1, "GetRawInputBuffer returned %u\n", count);
2145 ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0));
2147 /* peek the messages now, they should still arrive in the correct order */
2148 while (PeekMessageA(&message, 0, WM_INPUT, WM_INPUT, PM_REMOVE)) DispatchMessageA(&message);
2151 /* reading the message data now should fail on the second iteration, the data
2152 * from the first message has been overwritten. */
2153 size = sizeof(ri);
2154 memset(&ri, 0, sizeof(ri));
2155 SetLastError(0xdeadbeef);
2156 count = GetRawInputData((HRAWINPUT)lparam, RID_HEADER, &ri, &size, sizeof(RAWINPUTHEADER));
2157 if (iteration == 1)
2159 SetLastError(0xdeadbeef);
2160 count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, 0);
2161 ok(count == ~0U, "GetRawInputData succeeded\n");
2162 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08x\n", GetLastError());
2164 SetLastError(0xdeadbeef);
2165 size = 0;
2166 count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER));
2167 ok(count == ~0U, "GetRawInputData succeeded\n");
2168 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputData returned %08x\n", GetLastError());
2170 SetLastError(0xdeadbeef);
2171 size = sizeof(ri);
2172 count = GetRawInputData((HRAWINPUT)lparam, 0, &ri, &size, sizeof(RAWINPUTHEADER));
2173 ok(count == ~0U, "GetRawInputData succeeded\n");
2174 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08x\n", GetLastError());
2176 SetLastError(0xdeadbeef);
2177 size = sizeof(ri);
2178 count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER));
2179 ok(count == sizeof(ri), "GetRawInputData failed\n");
2180 ok(ri.data.mouse.lLastX == 6, "Unexpected rawinput data: %d\n", ri.data.mouse.lLastX);
2181 ok(GetLastError() == 0xdeadbeef, "GetRawInputData returned %08x\n", GetLastError());
2183 else
2185 ok(count == ~0U, "GetRawInputData succeeded\n");
2186 ok(GetLastError() == ERROR_INVALID_HANDLE, "GetRawInputData returned %08x\n", GetLastError());
2189 return 0;
2192 return DefWindowProcA(hwnd, msg, wparam, lparam);
2195 static void test_GetRawInputBuffer(void)
2197 RAWINPUTDEVICE raw_devices[1];
2198 char buffer[16 * sizeof(RAWINPUT64)];
2199 UINT size, count, rawinput_size;
2200 HWND hwnd;
2201 BOOL ret;
2203 if (is_wow64) rawinput_size = sizeof(RAWINPUT64);
2204 else rawinput_size = sizeof(RAWINPUT);
2206 hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
2207 100, 100, 100, 100, 0, NULL, NULL, NULL);
2208 SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinputbuffer_wndproc);
2209 ok(hwnd != 0, "CreateWindow failed\n");
2210 empty_message_queue();
2212 raw_devices[0].usUsagePage = 0x01;
2213 raw_devices[0].usUsage = 0x02;
2214 raw_devices[0].dwFlags = RIDEV_INPUTSINK;
2215 raw_devices[0].hwndTarget = hwnd;
2217 SetLastError(0xdeadbeef);
2218 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2219 ok(ret, "RegisterRawInputDevices failed\n");
2220 ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
2222 SetLastError(0xdeadbeef);
2223 count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER));
2224 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2225 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08x\n", GetLastError());
2227 size = sizeof(buffer);
2228 count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER));
2229 ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
2230 ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size);
2232 size = 0;
2233 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2234 ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
2235 ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size);
2237 SetLastError(0xdeadbeef);
2238 size = sizeof(buffer);
2239 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, 0);
2240 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2241 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08x\n", GetLastError());
2243 size = sizeof(buffer);
2244 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2245 ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
2246 ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size);
2248 mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2250 SetLastError(0xdeadbeef);
2251 size = 0;
2252 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2253 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2254 ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size);
2255 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputBuffer returned %08x\n", GetLastError());
2257 size = 0;
2258 count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER));
2259 ok(count == 0, "GetRawInputBuffer returned %u\n", count);
2260 ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size);
2262 SetLastError(0xdeadbeef);
2263 size = sizeof(buffer);
2264 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, 0);
2265 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2266 ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08x\n", GetLastError());
2268 size = sizeof(buffer);
2269 memset(buffer, 0, sizeof(buffer));
2270 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2271 ok(count == 1U, "GetRawInputBuffer returned %u\n", count);
2272 ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size);
2273 ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0));
2276 /* NOTE: calling with size == rawinput_size returns an error, */
2277 /* BUT it fills the buffer nonetheless and empties the internal buffer (!!) */
2278 mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2280 SetLastError(0xdeadbeef);
2281 size = rawinput_size;
2282 memset(buffer, 0, sizeof(buffer));
2283 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2284 ok(count == ~0U, "GetRawInputBuffer succeeded\n");
2285 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputBuffer returned %08x\n", GetLastError());
2286 ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0));
2288 size = sizeof(buffer);
2289 count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER));
2290 ok(count == 0U, "GetRawInputBuffer returned %u\n", count);
2293 rawinputbuffer_wndproc_count = 0;
2294 mouse_event(MOUSEEVENTF_MOVE, 1, 0, 0, 0);
2295 mouse_event(MOUSEEVENTF_MOVE, 2, 0, 0, 0);
2296 mouse_event(MOUSEEVENTF_MOVE, 3, 0, 0, 0);
2297 mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0);
2298 empty_message_queue();
2299 ok(rawinputbuffer_wndproc_count == 2, "Spurious WM_INPUT messages\n");
2301 raw_devices[0].dwFlags = RIDEV_REMOVE;
2302 raw_devices[0].hwndTarget = 0;
2304 SetLastError(0xdeadbeef);
2305 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2306 ok(ret, "RegisterRawInputDevices failed\n");
2307 ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
2309 DestroyWindow(hwnd);
2312 static BOOL rawinput_test_received_legacy;
2313 static BOOL rawinput_test_received_raw;
2314 static BOOL rawinput_test_received_rawfg;
2316 static LRESULT CALLBACK rawinput_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
2318 UINT ret, raw_size;
2319 RAWINPUT raw;
2321 if (msg == WM_INPUT)
2323 todo_wine_if(rawinput_test_received_raw)
2324 ok(!rawinput_test_received_raw, "Unexpected spurious WM_INPUT message.\n");
2325 ok(wparam == RIM_INPUT || wparam == RIM_INPUTSINK, "Unexpected wparam: %lu\n", wparam);
2327 rawinput_test_received_raw = TRUE;
2328 if (wparam == RIM_INPUT) rawinput_test_received_rawfg = TRUE;
2330 ret = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &raw_size, sizeof(RAWINPUTHEADER));
2331 ok(ret == 0, "GetRawInputData failed\n");
2332 ok(raw_size <= sizeof(raw), "Unexpected rawinput data size: %u", raw_size);
2334 raw_size = sizeof(raw);
2335 ret = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &raw, &raw_size, sizeof(RAWINPUTHEADER));
2336 ok(ret > 0 && ret != (UINT)-1, "GetRawInputData failed\n");
2337 ok(raw.header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %u\n", raw.header.dwType);
2339 ok(!(raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE), "Unexpected absolute rawinput motion\n");
2340 ok(!(raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP), "Unexpected virtual desktop rawinput motion\n");
2343 if (msg == WM_MOUSEMOVE) rawinput_test_received_legacy = TRUE;
2345 return DefWindowProcA(hwnd, msg, wparam, lparam);
2348 struct rawinput_test
2350 BOOL register_device;
2351 BOOL register_window;
2352 DWORD register_flags;
2353 BOOL expect_legacy;
2354 BOOL expect_raw;
2355 BOOL expect_rawfg;
2356 BOOL todo_legacy;
2357 BOOL todo_raw;
2358 BOOL todo_rawfg;
2361 struct rawinput_test rawinput_tests[] =
2363 { FALSE, FALSE, 0, TRUE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2364 { TRUE, FALSE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
2365 { TRUE, TRUE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
2366 { TRUE, TRUE, RIDEV_NOLEGACY, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
2368 /* same-process foreground tests */
2369 { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2370 { TRUE, TRUE, 0, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
2372 /* cross-process foreground tests */
2373 { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2374 { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2375 { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2377 /* multi-process rawinput tests */
2378 { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2379 { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2380 { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2382 { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2383 { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE },
2385 /* cross-desktop foreground tests */
2386 { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2387 { TRUE, TRUE, 0, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
2388 { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
2391 static void rawinput_test_process(void)
2393 RAWINPUTDEVICE raw_devices[1];
2394 HANDLE ready, start, done;
2395 DWORD ret;
2396 POINT pt;
2397 HWND hwnd = NULL;
2398 MSG msg;
2399 int i;
2401 ready = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_ready");
2402 ok(ready != 0, "OpenEventA failed, error: %u\n", GetLastError());
2404 start = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_start");
2405 ok(start != 0, "OpenEventA failed, error: %u\n", GetLastError());
2407 done = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_done");
2408 ok(done != 0, "OpenEventA failed, error: %u\n", GetLastError());
2410 for (i = 0; i < ARRAY_SIZE(rawinput_tests); ++i)
2412 WaitForSingleObject(ready, INFINITE);
2413 ResetEvent(ready);
2415 switch (i)
2417 case 6:
2418 case 7:
2419 case 8:
2420 case 9:
2421 case 10:
2422 case 11:
2423 case 12:
2424 case 13:
2425 case 16:
2426 GetCursorPos(&pt);
2428 hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
2429 pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL);
2430 SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc);
2431 ok(hwnd != 0, "CreateWindow failed\n");
2432 empty_message_queue();
2434 /* FIXME: Try to workaround X11/Win32 focus inconsistencies and
2435 * make the window visible and foreground as hard as possible. */
2436 ShowWindow(hwnd, SW_SHOW);
2437 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2438 SetForegroundWindow(hwnd);
2439 UpdateWindow(hwnd);
2440 empty_message_queue();
2442 if (i == 9 || i == 10 || i == 11 || i == 12)
2444 raw_devices[0].usUsagePage = 0x01;
2445 raw_devices[0].usUsage = 0x02;
2446 raw_devices[0].dwFlags = i == 11 ? RIDEV_INPUTSINK : 0;
2447 raw_devices[0].hwndTarget = i == 11 ? hwnd : 0;
2449 SetLastError(0xdeadbeef);
2450 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2451 ok(ret, "%d: RegisterRawInputDevices failed\n", i);
2452 ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08x\n", i, GetLastError());
2455 rawinput_test_received_legacy = FALSE;
2456 rawinput_test_received_raw = FALSE;
2457 rawinput_test_received_rawfg = FALSE;
2459 /* fallthrough */
2460 case 14:
2461 case 15:
2462 if (i != 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2463 empty_message_queue();
2464 break;
2467 SetEvent(start);
2469 while (MsgWaitForMultipleObjects(1, &done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0)
2470 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
2472 ResetEvent(done);
2474 if (i == 9 || i == 10 || i == 11 || i == 12)
2476 raw_devices[0].dwFlags = RIDEV_REMOVE;
2477 raw_devices[0].hwndTarget = 0;
2479 ok(rawinput_test_received_legacy, "%d: foreground process expected WM_MOUSEMOVE message\n", i);
2480 ok(rawinput_test_received_raw, "%d: foreground process expected WM_INPUT message\n", i);
2481 ok(rawinput_test_received_rawfg, "%d: foreground process expected RIM_INPUT message\n", i);
2483 SetLastError(0xdeadbeef);
2484 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2485 ok(ret, "%d: RegisterRawInputDevices failed\n", i);
2486 ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08x\n", i, GetLastError());
2489 if (hwnd) DestroyWindow(hwnd);
2492 WaitForSingleObject(ready, INFINITE);
2493 CloseHandle(done);
2494 CloseHandle(start);
2495 CloseHandle(ready);
2498 struct rawinput_test_thread_params
2500 HDESK desk;
2501 HANDLE ready;
2502 HANDLE start;
2503 HANDLE done;
2506 static DWORD WINAPI rawinput_test_desk_thread(void *arg)
2508 struct rawinput_test_thread_params *params = arg;
2509 RAWINPUTDEVICE raw_devices[1];
2510 DWORD ret;
2511 POINT pt;
2512 HWND hwnd = NULL;
2513 MSG msg;
2514 int i;
2516 ok( SetThreadDesktop( params->desk ), "SetThreadDesktop failed\n" );
2518 for (i = 14; i < ARRAY_SIZE(rawinput_tests); ++i)
2520 WaitForSingleObject(params->ready, INFINITE);
2521 ResetEvent(params->ready);
2523 switch (i)
2525 case 14:
2526 case 15:
2527 case 16:
2528 GetCursorPos(&pt);
2530 hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
2531 pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL);
2532 ok(hwnd != 0, "CreateWindow failed\n");
2533 SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc);
2534 empty_message_queue();
2536 /* FIXME: Try to workaround X11/Win32 focus inconsistencies and
2537 * make the window visible and foreground as hard as possible. */
2538 ShowWindow(hwnd, SW_SHOW);
2539 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2540 SetForegroundWindow(hwnd);
2541 UpdateWindow(hwnd);
2542 empty_message_queue();
2544 raw_devices[0].usUsagePage = 0x01;
2545 raw_devices[0].usUsage = 0x02;
2546 raw_devices[0].dwFlags = rawinput_tests[i].register_flags;
2547 raw_devices[0].hwndTarget = rawinput_tests[i].register_window ? hwnd : 0;
2549 rawinput_test_received_legacy = FALSE;
2550 rawinput_test_received_raw = FALSE;
2551 rawinput_test_received_rawfg = FALSE;
2553 SetLastError(0xdeadbeef);
2554 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2555 ok(ret, "%d: RegisterRawInputDevices failed\n", i);
2556 ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08x\n", i, GetLastError());
2557 break;
2560 SetEvent(params->start);
2562 while (MsgWaitForMultipleObjects(1, &params->done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0)
2563 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
2565 ResetEvent(params->done);
2566 if (hwnd) DestroyWindow(hwnd);
2569 return 0;
2572 static DWORD WINAPI rawinput_test_thread(void *arg)
2574 struct rawinput_test_thread_params *params = arg;
2575 HANDLE thread;
2576 POINT pt;
2577 HWND hwnd = NULL;
2578 int i;
2580 for (i = 0; i < 14; ++i)
2582 WaitForSingleObject(params->ready, INFINITE);
2583 ResetEvent(params->ready);
2585 switch (i)
2587 case 4:
2588 case 5:
2589 GetCursorPos(&pt);
2591 hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
2592 pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL);
2593 ok(hwnd != 0, "CreateWindow failed\n");
2594 empty_message_queue();
2596 /* FIXME: Try to workaround X11/Win32 focus inconsistencies and
2597 * make the window visible and foreground as hard as possible. */
2598 ShowWindow(hwnd, SW_SHOW);
2599 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2600 SetForegroundWindow(hwnd);
2601 UpdateWindow(hwnd);
2602 empty_message_queue();
2604 mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2605 empty_message_queue();
2606 break;
2609 SetEvent(params->start);
2611 WaitForSingleObject(params->done, INFINITE);
2612 ResetEvent(params->done);
2613 if (hwnd) DestroyWindow(hwnd);
2616 thread = CreateThread(NULL, 0, rawinput_test_desk_thread, params, 0, NULL);
2617 ok(thread != NULL, "CreateThread failed\n");
2618 WaitForSingleObject(thread, INFINITE);
2619 CloseHandle(thread);
2621 return 0;
2624 static void test_rawinput(const char* argv0)
2626 struct rawinput_test_thread_params params;
2627 PROCESS_INFORMATION process_info;
2628 RAWINPUTDEVICE raw_devices[1];
2629 STARTUPINFOA startup_info;
2630 HANDLE thread, process_ready, process_start, process_done;
2631 DWORD ret;
2632 POINT pt, newpt;
2633 HWND hwnd;
2634 BOOL skipped;
2635 char path[MAX_PATH];
2636 int i;
2638 params.desk = CreateDesktopA( "rawinput_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
2639 ok( params.desk != NULL, "CreateDesktopA failed, last error: %u\n", GetLastError() );
2641 params.ready = CreateEventA(NULL, FALSE, FALSE, NULL);
2642 ok(params.ready != NULL, "CreateEvent failed\n");
2644 params.start = CreateEventA(NULL, FALSE, FALSE, NULL);
2645 ok(params.start != NULL, "CreateEvent failed\n");
2647 params.done = CreateEventA(NULL, FALSE, FALSE, NULL);
2648 ok(params.done != NULL, "CreateEvent failed\n");
2650 thread = CreateThread(NULL, 0, rawinput_test_thread, &params, 0, NULL);
2651 ok(thread != NULL, "CreateThread failed\n");
2653 process_ready = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_ready");
2654 ok(process_ready != NULL, "CreateEventA failed\n");
2656 process_start = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_start");
2657 ok(process_start != NULL, "CreateEventA failed\n");
2659 process_done = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_done");
2660 ok(process_done != NULL, "CreateEventA failed\n");
2662 memset(&startup_info, 0, sizeof(startup_info));
2663 startup_info.cb = sizeof(startup_info);
2664 startup_info.dwFlags = STARTF_USESHOWWINDOW;
2665 startup_info.wShowWindow = SW_SHOWNORMAL;
2667 sprintf(path, "%s input rawinput_test", argv0);
2668 ret = CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info );
2669 ok(ret, "CreateProcess \"%s\" failed err %u.\n", path, GetLastError());
2671 SetCursorPos(100, 100);
2672 empty_message_queue();
2674 for (i = 0; i < ARRAY_SIZE(rawinput_tests); ++i)
2676 GetCursorPos(&pt);
2678 hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
2679 pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL);
2680 ok(hwnd != 0, "CreateWindow failed\n");
2681 if (i != 14 && i != 15 && i != 16)
2682 SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc);
2683 empty_message_queue();
2685 /* FIXME: Try to workaround X11/Win32 focus inconsistencies and
2686 * make the window visible and foreground as hard as possible. */
2687 ShowWindow(hwnd, SW_SHOW);
2688 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2689 SetForegroundWindow(hwnd);
2690 UpdateWindow(hwnd);
2691 empty_message_queue();
2693 rawinput_test_received_legacy = FALSE;
2694 rawinput_test_received_raw = FALSE;
2695 rawinput_test_received_rawfg = FALSE;
2697 raw_devices[0].usUsagePage = 0x01;
2698 raw_devices[0].usUsage = 0x02;
2699 raw_devices[0].dwFlags = rawinput_tests[i].register_flags;
2700 raw_devices[0].hwndTarget = rawinput_tests[i].register_window ? hwnd : 0;
2702 if (!rawinput_tests[i].register_device)
2703 skipped = FALSE;
2704 else
2706 SetLastError(0xdeadbeef);
2707 skipped = !RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2708 if (rawinput_tests[i].register_flags == RIDEV_EXINPUTSINK && skipped)
2709 win_skip("RIDEV_EXINPUTSINK not supported\n");
2710 else
2711 ok(!skipped, "%d: RegisterRawInputDevices failed: %u\n", i, GetLastError());
2714 SetEvent(params.ready);
2715 WaitForSingleObject(params.start, INFINITE);
2716 ResetEvent(params.start);
2718 /* we need the main window to be over the other thread window, as although
2719 * it is in another desktop, it will receive the messages directly otherwise */
2720 switch (i)
2722 case 14:
2723 case 15:
2724 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2725 SetForegroundWindow(hwnd);
2726 empty_message_queue();
2728 rawinput_test_received_legacy = FALSE;
2729 rawinput_test_received_raw = FALSE;
2730 rawinput_test_received_rawfg = FALSE;
2731 break;
2734 SetEvent(process_ready);
2735 WaitForSingleObject(process_start, INFINITE);
2736 ResetEvent(process_start);
2738 if (i <= 3 || i == 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0);
2739 empty_message_queue();
2741 SetEvent(process_done);
2742 SetEvent(params.done);
2744 if (!skipped)
2746 todo_wine_if(rawinput_tests[i].todo_legacy)
2747 ok(rawinput_test_received_legacy == rawinput_tests[i].expect_legacy,
2748 "%d: %sexpected WM_MOUSEMOVE message\n", i, rawinput_tests[i].expect_legacy ? "" : "un");
2749 todo_wine_if(rawinput_tests[i].todo_raw)
2750 ok(rawinput_test_received_raw == rawinput_tests[i].expect_raw,
2751 "%d: %sexpected WM_INPUT message\n", i, rawinput_tests[i].expect_raw ? "" : "un");
2752 todo_wine_if(rawinput_tests[i].todo_rawfg)
2753 ok(rawinput_test_received_rawfg == rawinput_tests[i].expect_rawfg,
2754 "%d: %sexpected RIM_INPUT message\n", i, rawinput_tests[i].expect_rawfg ? "" : "un");
2757 GetCursorPos(&newpt);
2758 ok((newpt.x - pt.x) == 5 || (newpt.x - pt.x) == 4, "%d: Unexpected cursor movement\n", i);
2760 if (rawinput_tests[i].register_device)
2762 raw_devices[0].dwFlags = RIDEV_REMOVE;
2763 raw_devices[0].hwndTarget = 0;
2765 SetLastError(0xdeadbeef);
2766 ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
2767 ok(ret, "%d: RegisterRawInputDevices failed: %u\n", i, GetLastError());
2770 DestroyWindow(hwnd);
2773 SetEvent(process_ready);
2774 winetest_wait_child_process(process_info.hProcess);
2775 CloseHandle(process_info.hProcess);
2776 CloseHandle(process_info.hThread);
2777 CloseHandle(process_done);
2778 CloseHandle(process_start);
2779 CloseHandle(process_ready);
2781 WaitForSingleObject(thread, INFINITE);
2783 CloseHandle(params.done);
2784 CloseHandle(params.start);
2785 CloseHandle(params.ready);
2786 CloseHandle(thread);
2788 CloseDesktop(params.desk);
2791 static void test_key_map(void)
2793 HKL kl = GetKeyboardLayout(0);
2794 UINT kL, kR, s, sL;
2795 int i;
2796 static const UINT numpad_collisions[][2] = {
2797 { VK_NUMPAD0, VK_INSERT },
2798 { VK_NUMPAD1, VK_END },
2799 { VK_NUMPAD2, VK_DOWN },
2800 { VK_NUMPAD3, VK_NEXT },
2801 { VK_NUMPAD4, VK_LEFT },
2802 { VK_NUMPAD6, VK_RIGHT },
2803 { VK_NUMPAD7, VK_HOME },
2804 { VK_NUMPAD8, VK_UP },
2805 { VK_NUMPAD9, VK_PRIOR },
2808 s = MapVirtualKeyExA(VK_SHIFT, MAPVK_VK_TO_VSC, kl);
2809 ok(s != 0, "MapVirtualKeyEx(VK_SHIFT) should return non-zero\n");
2810 sL = MapVirtualKeyExA(VK_LSHIFT, MAPVK_VK_TO_VSC, kl);
2811 ok(s == sL || broken(sL == 0), /* win9x */
2812 "%x != %x\n", s, sL);
2814 kL = MapVirtualKeyExA(0x2a, MAPVK_VSC_TO_VK, kl);
2815 ok(kL == VK_SHIFT, "Scan code -> vKey = %x (not VK_SHIFT)\n", kL);
2816 kR = MapVirtualKeyExA(0x36, MAPVK_VSC_TO_VK, kl);
2817 ok(kR == VK_SHIFT, "Scan code -> vKey = %x (not VK_SHIFT)\n", kR);
2819 kL = MapVirtualKeyExA(0x2a, MAPVK_VSC_TO_VK_EX, kl);
2820 ok(kL == VK_LSHIFT || broken(kL == 0), /* win9x */
2821 "Scan code -> vKey = %x (not VK_LSHIFT)\n", kL);
2822 kR = MapVirtualKeyExA(0x36, MAPVK_VSC_TO_VK_EX, kl);
2823 ok(kR == VK_RSHIFT || broken(kR == 0), /* win9x */
2824 "Scan code -> vKey = %x (not VK_RSHIFT)\n", kR);
2826 /* test that MAPVK_VSC_TO_VK prefers the non-numpad vkey if there's ambiguity */
2827 for (i = 0; i < ARRAY_SIZE(numpad_collisions); i++)
2829 UINT numpad_scan = MapVirtualKeyExA(numpad_collisions[i][0], MAPVK_VK_TO_VSC, kl);
2830 UINT other_scan = MapVirtualKeyExA(numpad_collisions[i][1], MAPVK_VK_TO_VSC, kl);
2832 /* do they really collide for this layout? */
2833 if (numpad_scan && other_scan == numpad_scan)
2835 UINT vkey = MapVirtualKeyExA(numpad_scan, MAPVK_VSC_TO_VK, kl);
2836 ok(vkey != numpad_collisions[i][0],
2837 "Got numpad vKey %x for scan code %x when there was another choice\n",
2838 vkey, numpad_scan);
2842 /* test the scan code prefixes of the right variant of a keys */
2843 s = MapVirtualKeyExA(VK_RCONTROL, MAPVK_VK_TO_VSC, kl);
2844 ok(s >> 8 == 0x00, "Scan code prefixes should not be returned when not using MAPVK_VK_TO_VSC_EX %#1x\n", s >> 8);
2845 s = MapVirtualKeyExA(VK_RCONTROL, MAPVK_VK_TO_VSC_EX, kl);
2846 ok(s >> 8 == 0xE0 || broken(s == 0), "Scan code prefix for VK_RCONTROL should be 0xE0 when MAPVK_VK_TO_VSC_EX is set, was %#1x\n", s >> 8);
2847 s = MapVirtualKeyExA(VK_RMENU, MAPVK_VK_TO_VSC_EX, kl);
2848 ok(s >> 8 == 0xE0 || broken(s == 0), "Scan code prefix for VK_RMENU should be 0xE0 when MAPVK_VK_TO_VSC_EX is set, was %#1x\n", s >> 8);
2849 s = MapVirtualKeyExA(VK_RSHIFT, MAPVK_VK_TO_VSC_EX, kl);
2850 ok(s >> 8 == 0x00 || broken(s == 0), "The scan code shouldn't have a prefix, got %#1x\n", s >> 8);
2853 #define shift 1
2854 #define ctrl 2
2856 static const struct tounicode_tests
2858 UINT vk;
2859 DWORD modifiers;
2860 WCHAR chr; /* if vk is 0, lookup vk using this char */
2861 int expect_ret;
2862 WCHAR expect_buf[4];
2863 } utests[] =
2865 { 0, 0, 'a', 1, {'a',0}},
2866 { 0, shift, 'a', 1, {'A',0}},
2867 { 0, ctrl, 'a', 1, {1, 0}},
2868 { 0, shift|ctrl, 'a', 1, {1, 0}},
2869 { VK_TAB, ctrl, 0, 0, {}},
2870 { VK_TAB, shift|ctrl, 0, 0, {}},
2871 { VK_RETURN, ctrl, 0, 1, {'\n', 0}},
2872 { VK_RETURN, shift|ctrl, 0, 0, {}},
2873 { 0, ctrl, '4', 0, {}},
2874 { 0, shift|ctrl, '4', 0, {}},
2875 { 0, ctrl, '!', 0, {}},
2876 { 0, ctrl, '\"', 0, {}},
2877 { 0, ctrl, '#', 0, {}},
2878 { 0, ctrl, '$', 0, {}},
2879 { 0, ctrl, '%', 0, {}},
2880 { 0, ctrl, '\'', 0, {}},
2881 { 0, ctrl, '(', 0, {}},
2882 { 0, ctrl, ')', 0, {}},
2883 { 0, ctrl, '*', 0, {}},
2884 { 0, ctrl, '+', 0, {}},
2885 { 0, ctrl, ',', 0, {}},
2886 { 0, ctrl, '-', 0, {}},
2887 { 0, ctrl, '.', 0, {}},
2888 { 0, ctrl, '/', 0, {}},
2889 { 0, ctrl, ':', 0, {}},
2890 { 0, ctrl, ';', 0, {}},
2891 { 0, ctrl, '<', 0, {}},
2892 { 0, ctrl, '=', 0, {}},
2893 { 0, ctrl, '>', 0, {}},
2894 { 0, ctrl, '?', 0, {}},
2895 { 0, ctrl, '@', 1, {0}},
2896 { 0, ctrl, '[', 1, {0x1b}},
2897 { 0, ctrl, '\\', 1, {0x1c}},
2898 { 0, ctrl, ']', 1, {0x1d}},
2899 { 0, ctrl, '^', 1, {0x1e}},
2900 { 0, ctrl, '_', 1, {0x1f}},
2901 { 0, ctrl, '`', 0, {}},
2902 { VK_SPACE, 0, 0, 1, {' ',0}},
2903 { VK_SPACE, shift, 0, 1, {' ',0}},
2904 { VK_SPACE, ctrl, 0, 1, {' ',0}},
2907 static void test_ToUnicode(void)
2909 WCHAR wStr[4];
2910 BYTE state[256];
2911 const BYTE SC_RETURN = 0x1c, SC_TAB = 0x0f, SC_A = 0x1e;
2912 const BYTE HIGHEST_BIT = 0x80;
2913 int i, ret;
2914 BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409);
2916 for(i=0; i<256; i++)
2917 state[i]=0;
2919 wStr[1] = 0xAA;
2920 SetLastError(0xdeadbeef);
2921 ret = ToUnicode(VK_RETURN, SC_RETURN, state, wStr, 4, 0);
2922 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2924 win_skip("ToUnicode is not implemented\n");
2925 return;
2928 ok(ret == 1, "ToUnicode for Return key didn't return 1 (was %i)\n", ret);
2929 if(ret == 1)
2931 ok(wStr[0]=='\r', "ToUnicode for CTRL + Return was %i (expected 13)\n", wStr[0]);
2932 ok(wStr[1]==0 || broken(wStr[1]!=0) /* nt4 */,
2933 "ToUnicode didn't null-terminate the buffer when there was room.\n");
2936 for (i = 0; i < ARRAY_SIZE(utests); i++)
2938 UINT vk = utests[i].vk, mod = utests[i].modifiers, scan;
2940 if(!vk)
2942 short vk_ret;
2944 if (!us_kbd) continue;
2945 vk_ret = VkKeyScanW(utests[i].chr);
2946 if (vk_ret == -1) continue;
2947 vk = vk_ret & 0xff;
2948 if (vk_ret & 0x100) mod |= shift;
2949 if (vk_ret & 0x200) mod |= ctrl;
2951 scan = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC);
2953 state[VK_SHIFT] = state[VK_LSHIFT] = (mod & shift) ? HIGHEST_BIT : 0;
2954 state[VK_CONTROL] = state[VK_LCONTROL] = (mod & ctrl) ? HIGHEST_BIT : 0;
2956 ret = ToUnicode(vk, scan, state, wStr, 4, 0);
2957 ok(ret == utests[i].expect_ret, "%d: got %d expected %d\n", i, ret, utests[i].expect_ret);
2958 if (ret)
2959 ok(!lstrcmpW(wStr, utests[i].expect_buf), "%d: got %s expected %s\n", i, wine_dbgstr_w(wStr),
2960 wine_dbgstr_w(utests[i].expect_buf));
2963 state[VK_SHIFT] = state[VK_LSHIFT] = 0;
2964 state[VK_CONTROL] = state[VK_LCONTROL] = 0;
2966 ret = ToUnicode(VK_TAB, SC_TAB, NULL, wStr, 4, 0);
2967 ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret);
2968 ret = ToUnicode(VK_RETURN, SC_RETURN, NULL, wStr, 4, 0);
2969 ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret);
2970 ret = ToUnicode('A', SC_A, NULL, wStr, 4, 0);
2971 ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret);
2972 ret = ToUnicodeEx(VK_TAB, SC_TAB, NULL, wStr, 4, 0, GetKeyboardLayout(0));
2973 ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret);
2974 ret = ToUnicodeEx(VK_RETURN, SC_RETURN, NULL, wStr, 4, 0, GetKeyboardLayout(0));
2975 ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret);
2976 ret = ToUnicodeEx('A', SC_A, NULL, wStr, 4, 0, GetKeyboardLayout(0));
2977 ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret);
2980 static void test_ToAscii(void)
2982 WORD character;
2983 BYTE state[256];
2984 const BYTE SC_RETURN = 0x1c, SC_A = 0x1e;
2985 const BYTE HIGHEST_BIT = 0x80;
2986 int ret;
2987 BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409);
2989 memset(state, 0, sizeof(state));
2991 character = 0;
2992 ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0);
2993 ok(ret == 1, "ToAscii for Return key didn't return 1 (was %i)\n", ret);
2994 ok(character == '\r', "ToAscii for Return was %i (expected 13)\n", character);
2996 character = 0;
2997 ret = ToAscii('A', SC_A, state, &character, 0);
2998 ok(ret == 1, "ToAscii for character 'A' didn't return 1 (was %i)\n", ret);
2999 if (us_kbd) ok(character == 'a', "ToAscii for character 'A' was %i (expected %i)\n", character, 'a');
3001 state[VK_CONTROL] |= HIGHEST_BIT;
3002 state[VK_LCONTROL] |= HIGHEST_BIT;
3003 character = 0;
3004 ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0);
3005 ok(ret == 1, "ToAscii for CTRL + Return key didn't return 1 (was %i)\n", ret);
3006 ok(character == '\n', "ToAscii for CTRL + Return was %i (expected 10)\n", character);
3008 character = 0;
3009 ret = ToAscii('A', SC_A, state, &character, 0);
3010 ok(ret == 1, "ToAscii for CTRL + character 'A' didn't return 1 (was %i)\n", ret);
3011 ok(character == 1, "ToAscii for CTRL + character 'A' was %i (expected 1)\n", character);
3013 state[VK_SHIFT] |= HIGHEST_BIT;
3014 state[VK_LSHIFT] |= HIGHEST_BIT;
3015 ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0);
3016 ok(ret == 0, "ToAscii for CTRL + Shift + Return key didn't return 0 (was %i)\n", ret);
3018 ret = ToAscii(VK_RETURN, SC_RETURN, NULL, &character, 0);
3019 ok(ret == 0, "ToAscii for NULL keystate didn't return 0 (was %i)\n", ret);
3020 ret = ToAscii('A', SC_A, NULL, &character, 0);
3021 ok(ret == 0, "ToAscii for NULL keystate didn't return 0 (was %i)\n", ret);
3022 ret = ToAsciiEx(VK_RETURN, SC_RETURN, NULL, &character, 0, GetKeyboardLayout(0));
3023 ok(ret == 0, "ToAsciiEx for NULL keystate didn't return 0 (was %i)\n", ret);
3024 ret = ToAsciiEx('A', SC_A, NULL, &character, 0, GetKeyboardLayout(0));
3025 ok(ret == 0, "ToAsciiEx for NULL keystate didn't return 0 (was %i)\n", ret);
3028 static void test_get_async_key_state(void)
3030 /* input value sanity checks */
3031 ok(0 == GetAsyncKeyState(1000000), "GetAsyncKeyState did not return 0\n");
3032 ok(0 == GetAsyncKeyState(-1000000), "GetAsyncKeyState did not return 0\n");
3035 static void test_keyboard_layout_name(void)
3037 BOOL ret;
3038 char klid[KL_NAMELENGTH];
3040 if (0) /* crashes on native system */
3041 ret = GetKeyboardLayoutNameA(NULL);
3043 SetLastError(0xdeadbeef);
3044 ret = GetKeyboardLayoutNameW(NULL);
3045 ok(!ret, "got %d\n", ret);
3046 ok(GetLastError() == ERROR_NOACCESS, "got %d\n", GetLastError());
3048 if (GetKeyboardLayout(0) != (HKL)(ULONG_PTR)0x04090409) return;
3050 klid[0] = 0;
3051 ret = GetKeyboardLayoutNameA(klid);
3052 ok(ret, "GetKeyboardLayoutNameA failed %u\n", GetLastError());
3053 ok(!strcmp(klid, "00000409"), "expected 00000409, got %s\n", klid);
3056 static void test_key_names(void)
3058 char buffer[40];
3059 WCHAR bufferW[40];
3060 int ret, prev;
3061 LONG lparam = 0x1d << 16;
3063 memset( buffer, 0xcc, sizeof(buffer) );
3064 ret = GetKeyNameTextA( lparam, buffer, sizeof(buffer) );
3065 ok( ret > 0, "wrong len %u for '%s'\n", ret, buffer );
3066 ok( ret == strlen(buffer), "wrong len %u for '%s'\n", ret, buffer );
3068 memset( buffer, 0xcc, sizeof(buffer) );
3069 prev = ret;
3070 ret = GetKeyNameTextA( lparam, buffer, prev );
3071 ok( ret == prev - 1, "wrong len %u for '%s'\n", ret, buffer );
3072 ok( ret == strlen(buffer), "wrong len %u for '%s'\n", ret, buffer );
3074 memset( buffer, 0xcc, sizeof(buffer) );
3075 ret = GetKeyNameTextA( lparam, buffer, 0 );
3076 ok( ret == 0, "wrong len %u for '%s'\n", ret, buffer );
3077 ok( buffer[0] == 0, "wrong string '%s'\n", buffer );
3079 memset( bufferW, 0xcc, sizeof(bufferW) );
3080 ret = GetKeyNameTextW( lparam, bufferW, ARRAY_SIZE(bufferW));
3081 ok( ret > 0, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
3082 ok( ret == lstrlenW(bufferW), "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
3084 memset( bufferW, 0xcc, sizeof(bufferW) );
3085 prev = ret;
3086 ret = GetKeyNameTextW( lparam, bufferW, prev );
3087 ok( ret == prev - 1, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
3088 ok( ret == lstrlenW(bufferW), "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
3090 memset( bufferW, 0xcc, sizeof(bufferW) );
3091 ret = GetKeyNameTextW( lparam, bufferW, 0 );
3092 ok( ret == 0, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
3093 ok( bufferW[0] == 0xcccc, "wrong string %s\n", wine_dbgstr_w(bufferW) );
3096 static void simulate_click(BOOL left, int x, int y)
3098 INPUT input[2];
3099 UINT events_no;
3101 SetCursorPos(x, y);
3102 memset(input, 0, sizeof(input));
3103 input[0].type = INPUT_MOUSE;
3104 U(input[0]).mi.dx = x;
3105 U(input[0]).mi.dy = y;
3106 U(input[0]).mi.dwFlags = left ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
3107 input[1].type = INPUT_MOUSE;
3108 U(input[1]).mi.dx = x;
3109 U(input[1]).mi.dy = y;
3110 U(input[1]).mi.dwFlags = left ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
3111 events_no = SendInput(2, input, sizeof(input[0]));
3112 ok(events_no == 2, "SendInput returned %d\n", events_no);
3115 static BOOL wait_for_message( MSG *msg )
3117 BOOL ret;
3119 for (;;)
3121 ret = PeekMessageA(msg, 0, 0, 0, PM_REMOVE);
3122 if (ret)
3124 if (msg->message == WM_PAINT) DispatchMessageA(msg);
3125 else break;
3127 else if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT) == WAIT_TIMEOUT) break;
3129 if (!ret) msg->message = 0;
3130 return ret;
3133 static BOOL wait_for_event(HANDLE event, int timeout)
3135 DWORD end_time = GetTickCount() + timeout;
3136 MSG msg;
3138 do {
3139 if(MsgWaitForMultipleObjects(1, &event, FALSE, timeout, QS_ALLINPUT) == WAIT_OBJECT_0)
3140 return TRUE;
3141 while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
3142 DispatchMessageA(&msg);
3143 timeout = end_time - GetTickCount();
3144 }while(timeout > 0);
3146 return FALSE;
3149 static WNDPROC def_static_proc;
3150 static DWORD hittest_no;
3151 static LRESULT WINAPI static_hook_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
3153 if (msg == WM_NCHITTEST)
3155 /* break infinite hittest loop */
3156 if(hittest_no > 50) return HTCLIENT;
3157 hittest_no++;
3160 return def_static_proc(hwnd, msg, wp, lp);
3163 struct thread_data
3165 HANDLE start_event;
3166 HANDLE end_event;
3167 HWND win;
3170 static DWORD WINAPI create_static_win(void *arg)
3172 struct thread_data *thread_data = arg;
3173 HWND win;
3174 MSG msg;
3176 win = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
3177 100, 100, 100, 100, 0, NULL, NULL, NULL);
3178 ok(win != 0, "CreateWindow failed\n");
3179 def_static_proc = (void*)SetWindowLongPtrA(win,
3180 GWLP_WNDPROC, (LONG_PTR)static_hook_proc);
3181 thread_data->win = win;
3183 while (wait_for_message(&msg)) DispatchMessageA(&msg);
3184 SetEvent(thread_data->start_event);
3185 wait_for_event(thread_data->end_event, 5000);
3186 return 0;
3189 static void test_Input_mouse(void)
3191 BOOL got_button_down, got_button_up;
3192 HWND hwnd, button_win, static_win;
3193 struct thread_data thread_data;
3194 HANDLE thread;
3195 DWORD thread_id;
3196 POINT pt, pt_org;
3197 MSG msg;
3198 BOOL ret;
3200 SetLastError(0xdeadbeef);
3201 ret = GetCursorPos(NULL);
3202 ok(!ret, "GetCursorPos succeed\n");
3203 ok(GetLastError() == 0xdeadbeef || GetLastError() == ERROR_NOACCESS, "error %u\n", GetLastError());
3205 SetLastError(0xdeadbeef);
3206 ret = GetCursorPos(&pt_org);
3207 ok(ret, "GetCursorPos failed\n");
3208 ok(GetLastError() == 0xdeadbeef, "error %u\n", GetLastError());
3210 button_win = CreateWindowA("button", "button", WS_VISIBLE | WS_POPUP,
3211 100, 100, 100, 100, 0, NULL, NULL, NULL);
3212 ok(button_win != 0, "CreateWindow failed\n");
3214 pt.x = pt.y = 150;
3215 hwnd = WindowFromPoint(pt);
3216 if (hwnd != button_win)
3218 skip("there's another window covering test window\n");
3219 DestroyWindow(button_win);
3220 return;
3223 /* simple button click test */
3224 simulate_click(TRUE, 150, 150);
3225 got_button_down = got_button_up = FALSE;
3226 while (wait_for_message(&msg))
3228 DispatchMessageA(&msg);
3230 if (msg.message == WM_LBUTTONDOWN)
3232 got_button_down = TRUE;
3234 else if (msg.message == WM_LBUTTONUP)
3236 got_button_up = TRUE;
3237 break;
3240 ok(got_button_down, "expected WM_LBUTTONDOWN message\n");
3241 ok(got_button_up, "expected WM_LBUTTONUP message\n");
3243 /* click through HTTRANSPARENT child window */
3244 static_win = CreateWindowA("static", "static", WS_VISIBLE | WS_CHILD,
3245 0, 0, 100, 100, button_win, NULL, NULL, NULL);
3246 ok(static_win != 0, "CreateWindow failed\n");
3247 def_static_proc = (void*)SetWindowLongPtrA(static_win,
3248 GWLP_WNDPROC, (LONG_PTR)static_hook_proc);
3249 simulate_click(FALSE, 150, 150);
3250 hittest_no = 0;
3251 got_button_down = got_button_up = FALSE;
3252 while (wait_for_message(&msg))
3254 DispatchMessageA(&msg);
3256 if (msg.message == WM_RBUTTONDOWN)
3258 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3259 got_button_down = TRUE;
3261 else if (msg.message == WM_RBUTTONUP)
3263 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3264 got_button_up = TRUE;
3265 break;
3268 ok(hittest_no && hittest_no<50, "expected WM_NCHITTEST message\n");
3269 ok(got_button_down, "expected WM_RBUTTONDOWN message\n");
3270 ok(got_button_up, "expected WM_RBUTTONUP message\n");
3271 DestroyWindow(static_win);
3273 /* click through HTTRANSPARENT top-level window */
3274 static_win = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP,
3275 100, 100, 100, 100, 0, NULL, NULL, NULL);
3276 ok(static_win != 0, "CreateWindow failed\n");
3277 def_static_proc = (void*)SetWindowLongPtrA(static_win,
3278 GWLP_WNDPROC, (LONG_PTR)static_hook_proc);
3279 simulate_click(TRUE, 150, 150);
3280 hittest_no = 0;
3281 got_button_down = got_button_up = FALSE;
3282 while (wait_for_message(&msg))
3284 DispatchMessageA(&msg);
3286 if (msg.message == WM_LBUTTONDOWN)
3288 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3289 got_button_down = TRUE;
3291 else if (msg.message == WM_LBUTTONUP)
3293 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3294 got_button_up = TRUE;
3295 break;
3298 ok(hittest_no && hittest_no<50, "expected WM_NCHITTEST message\n");
3299 todo_wine ok(got_button_down, "expected WM_LBUTTONDOWN message\n");
3300 todo_wine ok(got_button_up, "expected WM_LBUTTONUP message\n");
3301 DestroyWindow(static_win);
3303 /* click on HTTRANSPARENT top-level window that belongs to other thread */
3304 thread_data.start_event = CreateEventA(NULL, FALSE, FALSE, NULL);
3305 ok(thread_data.start_event != NULL, "CreateEvent failed\n");
3306 thread_data.end_event = CreateEventA(NULL, FALSE, FALSE, NULL);
3307 ok(thread_data.end_event != NULL, "CreateEvent failed\n");
3308 thread = CreateThread(NULL, 0, create_static_win, &thread_data, 0, NULL);
3309 ok(thread != NULL, "CreateThread failed\n");
3310 hittest_no = 0;
3311 got_button_down = got_button_up = FALSE;
3312 WaitForSingleObject(thread_data.start_event, INFINITE);
3313 simulate_click(FALSE, 150, 150);
3314 while (wait_for_message(&msg))
3316 DispatchMessageA(&msg);
3318 if (msg.message == WM_RBUTTONDOWN)
3319 got_button_down = TRUE;
3320 else if (msg.message == WM_RBUTTONUP)
3321 got_button_up = TRUE;
3323 SetEvent(thread_data.end_event);
3324 WaitForSingleObject(thread, INFINITE);
3325 CloseHandle(thread);
3326 ok(hittest_no && hittest_no<50, "expected WM_NCHITTEST message\n");
3327 ok(!got_button_down, "unexpected WM_RBUTTONDOWN message\n");
3328 ok(!got_button_up, "unexpected WM_RBUTTONUP message\n");
3330 /* click on HTTRANSPARENT top-level window that belongs to other thread,
3331 * thread input queues are attached */
3332 thread = CreateThread(NULL, 0, create_static_win, &thread_data, 0, &thread_id);
3333 ok(thread != NULL, "CreateThread failed\n");
3334 hittest_no = 0;
3335 got_button_down = got_button_up = FALSE;
3336 WaitForSingleObject(thread_data.start_event, INFINITE);
3337 ok(AttachThreadInput(thread_id, GetCurrentThreadId(), TRUE),
3338 "AttachThreadInput failed\n");
3339 while (wait_for_message(&msg)) DispatchMessageA(&msg);
3340 SetWindowPos(thread_data.win, button_win, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
3341 simulate_click(TRUE, 150, 150);
3342 while (wait_for_message(&msg))
3344 DispatchMessageA(&msg);
3346 if (msg.message == WM_LBUTTONDOWN)
3347 got_button_down = TRUE;
3348 else if (msg.message == WM_LBUTTONUP)
3349 got_button_up = TRUE;
3351 SetEvent(thread_data.end_event);
3352 WaitForSingleObject(thread, INFINITE);
3353 todo_wine ok(hittest_no > 50, "expected loop with WM_NCHITTEST messages\n");
3354 ok(!got_button_down, "unexpected WM_LBUTTONDOWN message\n");
3355 ok(!got_button_up, "unexpected WM_LBUTTONUP message\n");
3357 /* click after SetCapture call */
3358 hwnd = CreateWindowA("button", "button", WS_VISIBLE | WS_POPUP,
3359 0, 0, 100, 100, 0, NULL, NULL, NULL);
3360 ok(hwnd != 0, "CreateWindow failed\n");
3361 SetCapture(button_win);
3362 got_button_down = got_button_up = FALSE;
3363 simulate_click(FALSE, 50, 50);
3364 while (wait_for_message(&msg))
3366 DispatchMessageA(&msg);
3368 if (msg.message == WM_RBUTTONDOWN)
3370 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3371 got_button_down = TRUE;
3373 else if (msg.message == WM_RBUTTONUP)
3375 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3376 got_button_up = TRUE;
3377 break;
3380 ok(got_button_down, "expected WM_RBUTTONDOWN message\n");
3381 ok(got_button_up, "expected WM_RBUTTONUP message\n");
3382 DestroyWindow(hwnd);
3384 /* click on child window after SetCapture call */
3385 hwnd = CreateWindowA("button", "button2", WS_VISIBLE | WS_CHILD,
3386 0, 0, 100, 100, button_win, NULL, NULL, NULL);
3387 ok(hwnd != 0, "CreateWindow failed\n");
3388 got_button_down = got_button_up = FALSE;
3389 simulate_click(TRUE, 150, 150);
3390 while (wait_for_message(&msg))
3392 DispatchMessageA(&msg);
3394 if (msg.message == WM_LBUTTONDOWN)
3396 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3397 got_button_down = TRUE;
3399 else if (msg.message == WM_LBUTTONUP)
3401 ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
3402 got_button_up = TRUE;
3403 break;
3406 ok(got_button_down, "expected WM_LBUTTONDOWN message\n");
3407 ok(got_button_up, "expected WM_LBUTTONUP message\n");
3408 DestroyWindow(hwnd);
3409 ok(ReleaseCapture(), "ReleaseCapture failed\n");
3410 SetCursorPos(pt_org.x, pt_org.y);
3412 CloseHandle(thread_data.start_event);
3413 CloseHandle(thread_data.end_event);
3414 DestroyWindow(button_win);
3418 static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3420 if (message == WM_USER+1)
3422 HWND hwnd = (HWND)lParam;
3423 ok(GetFocus() == hwnd, "thread expected focus %p, got %p\n", hwnd, GetFocus());
3424 ok(GetActiveWindow() == hwnd, "thread expected active %p, got %p\n", hwnd, GetActiveWindow());
3426 return DefWindowProcA(hwnd, message, wParam, lParam);
3429 struct wnd_event
3431 HWND hwnd;
3432 HANDLE wait_event;
3433 HANDLE start_event;
3434 DWORD attach_from;
3435 DWORD attach_to;
3436 BOOL setWindows;
3439 static DWORD WINAPI thread_proc(void *param)
3441 MSG msg;
3442 struct wnd_event *wnd_event = param;
3443 BOOL ret;
3445 if (wnd_event->wait_event)
3447 ok(WaitForSingleObject(wnd_event->wait_event, INFINITE) == WAIT_OBJECT_0,
3448 "WaitForSingleObject failed\n");
3449 CloseHandle(wnd_event->wait_event);
3452 if (wnd_event->attach_from)
3454 ret = AttachThreadInput(wnd_event->attach_from, GetCurrentThreadId(), TRUE);
3455 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3458 if (wnd_event->attach_to)
3460 ret = AttachThreadInput(GetCurrentThreadId(), wnd_event->attach_to, TRUE);
3461 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3464 wnd_event->hwnd = CreateWindowExA(0, "TestWindowClass", "window caption text", WS_OVERLAPPEDWINDOW,
3465 100, 100, 200, 200, 0, 0, 0, NULL);
3466 ok(wnd_event->hwnd != 0, "Failed to create overlapped window\n");
3468 if (wnd_event->setWindows)
3470 SetFocus(wnd_event->hwnd);
3471 SetActiveWindow(wnd_event->hwnd);
3474 SetEvent(wnd_event->start_event);
3476 while (GetMessageA(&msg, 0, 0, 0))
3478 TranslateMessage(&msg);
3479 DispatchMessageA(&msg);
3482 return 0;
3485 static void test_attach_input(void)
3487 HANDLE hThread;
3488 HWND ourWnd, Wnd2;
3489 DWORD ret, tid;
3490 struct wnd_event wnd_event;
3491 WNDCLASSA cls;
3493 cls.style = 0;
3494 cls.lpfnWndProc = MsgCheckProcA;
3495 cls.cbClsExtra = 0;
3496 cls.cbWndExtra = 0;
3497 cls.hInstance = GetModuleHandleA(0);
3498 cls.hIcon = 0;
3499 cls.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
3500 cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
3501 cls.lpszMenuName = NULL;
3502 cls.lpszClassName = "TestWindowClass";
3503 if(!RegisterClassA(&cls)) return;
3505 wnd_event.wait_event = NULL;
3506 wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL);
3507 wnd_event.attach_from = 0;
3508 wnd_event.attach_to = 0;
3509 wnd_event.setWindows = FALSE;
3510 if (!wnd_event.start_event)
3512 win_skip("skipping interthread message test under win9x\n");
3513 return;
3516 hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
3517 ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError());
3519 ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3520 CloseHandle(wnd_event.start_event);
3522 ourWnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
3523 0, 0, 0, 0, 0, 0, 0, NULL);
3524 ok(ourWnd!= 0, "failed to create ourWnd window\n");
3526 Wnd2 = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
3527 0, 0, 0, 0, 0, 0, 0, NULL);
3528 ok(Wnd2!= 0, "failed to create Wnd2 window\n");
3530 SetFocus(ourWnd);
3531 SetActiveWindow(ourWnd);
3533 ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
3534 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3536 ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow());
3537 ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus());
3539 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)ourWnd);
3541 ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE);
3542 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3543 ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow());
3544 ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus());
3546 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0);
3548 ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
3549 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3551 ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow());
3552 ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus());
3553 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)ourWnd);
3555 SetActiveWindow(Wnd2);
3556 SetFocus(Wnd2);
3557 ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow());
3558 ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus());
3560 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)Wnd2);
3562 ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE);
3563 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3564 ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow());
3565 ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus());
3567 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0);
3569 ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
3570 ok(ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError());
3572 ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3573 CloseHandle(hThread);
3575 wnd_event.wait_event = NULL;
3576 wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL);
3577 wnd_event.attach_from = 0;
3578 wnd_event.attach_to = 0;
3579 wnd_event.setWindows = TRUE;
3581 hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
3582 ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError());
3584 ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3585 CloseHandle(wnd_event.start_event);
3587 SetFocus(ourWnd);
3588 SetActiveWindow(ourWnd);
3590 ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
3591 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3593 ok(GetActiveWindow() == wnd_event.hwnd, "expected active %p, got %p\n", wnd_event.hwnd, GetActiveWindow());
3594 ok(GetFocus() == wnd_event.hwnd, "expected focus %p, got %p\n", wnd_event.hwnd, GetFocus());
3596 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd);
3598 ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE);
3599 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3601 ok(GetActiveWindow() == 0, "expected active 0, got %p\n", GetActiveWindow());
3602 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
3604 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd);
3606 ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
3607 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3609 ok(GetActiveWindow() == wnd_event.hwnd, "expected active %p, got %p\n", wnd_event.hwnd, GetActiveWindow());
3610 ok(GetFocus() == wnd_event.hwnd, "expected focus %p, got %p\n", wnd_event.hwnd, GetFocus());
3612 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd);
3614 SetFocus(Wnd2);
3615 SetActiveWindow(Wnd2);
3616 ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow());
3617 ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus());
3619 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)Wnd2);
3621 ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE);
3622 ok(ret, "AttachThreadInput error %d\n", GetLastError());
3624 ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow());
3625 ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus());
3627 SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0);
3629 ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
3630 ok(ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError());
3632 ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3633 CloseHandle(hThread);
3635 wnd_event.wait_event = CreateEventW(NULL, 0, 0, NULL);
3636 wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL);
3637 wnd_event.attach_from = 0;
3638 wnd_event.attach_to = 0;
3639 wnd_event.setWindows = TRUE;
3641 hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
3642 ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError());
3644 SetLastError(0xdeadbeef);
3645 ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
3646 ok(!ret, "AttachThreadInput succeeded\n");
3647 ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == 0xdeadbeef) /* <= Win XP */,
3648 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3650 SetLastError(0xdeadbeef);
3651 ret = AttachThreadInput(tid, GetCurrentThreadId(), TRUE);
3652 ok(!ret, "AttachThreadInput succeeded\n");
3653 ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == 0xdeadbeef) /* <= Win XP */,
3654 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3656 SetEvent(wnd_event.wait_event);
3658 ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3659 CloseHandle(wnd_event.start_event);
3661 ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
3662 ok(ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError());
3664 ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3665 CloseHandle(hThread);
3667 wnd_event.wait_event = NULL;
3668 wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL);
3669 wnd_event.attach_from = GetCurrentThreadId();
3670 wnd_event.attach_to = 0;
3671 wnd_event.setWindows = FALSE;
3673 SetFocus(ourWnd);
3674 SetActiveWindow(ourWnd);
3676 hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
3677 ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError());
3679 ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3680 CloseHandle(wnd_event.start_event);
3682 ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow());
3683 ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus());
3685 ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
3686 ok(ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError());
3688 ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3689 CloseHandle(hThread);
3691 wnd_event.wait_event = NULL;
3692 wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL);
3693 wnd_event.attach_from = 0;
3694 wnd_event.attach_to = GetCurrentThreadId();
3695 wnd_event.setWindows = FALSE;
3697 SetFocus(ourWnd);
3698 SetActiveWindow(ourWnd);
3700 hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
3701 ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError());
3703 ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3704 CloseHandle(wnd_event.start_event);
3706 ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow());
3707 ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus());
3709 ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
3710 ok(ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError());
3712 ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
3713 CloseHandle(hThread);
3714 DestroyWindow(ourWnd);
3715 DestroyWindow(Wnd2);
3718 static DWORD WINAPI get_key_state_thread(void *arg)
3720 HANDLE *semaphores = arg;
3721 DWORD result;
3723 ReleaseSemaphore(semaphores[0], 1, NULL);
3724 result = WaitForSingleObject(semaphores[1], 1000);
3725 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
3727 result = GetKeyState('X');
3728 ok((result & 0x8000) || broken(!(result & 0x8000)), /* > Win 2003 */
3729 "expected that highest bit is set, got %x\n", result);
3731 ok((SHORT)(result & 0x007e) == 0,
3732 "expected that undefined bits are unset, got %x\n", result);
3734 ReleaseSemaphore(semaphores[0], 1, NULL);
3735 result = WaitForSingleObject(semaphores[1], 1000);
3736 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
3738 result = GetKeyState('X');
3739 ok(!(result & 0x8000), "expected that highest bit is unset, got %x\n", result);
3741 ok((SHORT)(result & 0x007e) == 0,
3742 "expected that undefined bits are unset, got %x\n", result);
3744 return 0;
3747 static void test_GetKeyState(void)
3749 HANDLE semaphores[2];
3750 HANDLE thread;
3751 DWORD result;
3752 HWND hwnd;
3754 semaphores[0] = CreateSemaphoreA(NULL, 0, 1, NULL);
3755 ok(semaphores[0] != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
3756 semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL);
3757 ok(semaphores[1] != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
3759 hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
3760 10, 10, 200, 200, NULL, NULL, NULL, NULL);
3761 ok(hwnd != NULL, "CreateWindowA failed %u\n", GetLastError());
3763 thread = CreateThread(NULL, 0, get_key_state_thread, semaphores, 0, NULL);
3764 ok(thread != NULL, "CreateThread failed %u\n", GetLastError());
3765 result = WaitForSingleObject(semaphores[0], 1000);
3766 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
3768 SetForegroundWindow(hwnd);
3769 SetFocus(hwnd);
3770 keybd_event('X', 0, 0, 0);
3772 ReleaseSemaphore(semaphores[1], 1, NULL);
3773 result = WaitForSingleObject(semaphores[0], 1000);
3774 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
3776 keybd_event('X', 0, KEYEVENTF_KEYUP, 0);
3778 ReleaseSemaphore(semaphores[1], 1, NULL);
3779 result = WaitForSingleObject(thread, 1000);
3780 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
3781 CloseHandle(thread);
3783 DestroyWindow(hwnd);
3784 CloseHandle(semaphores[0]);
3785 CloseHandle(semaphores[1]);
3788 static void test_OemKeyScan(void)
3790 DWORD ret, expect, vkey, scan;
3791 WCHAR oem, wchr;
3792 char oem_char;
3794 for (oem = 0; oem < 0x200; oem++)
3796 ret = OemKeyScan( oem );
3798 oem_char = LOBYTE( oem );
3799 /* OemKeyScan returns -1 for any character that cannot be mapped,
3800 * whereas OemToCharBuff changes unmappable characters to question
3801 * marks. The ASCII characters 0-127, including the real question mark
3802 * character, are all mappable and are the same in all OEM codepages. */
3803 if (!OemToCharBuffW( &oem_char, &wchr, 1 ) || (wchr == '?' && oem_char < 0))
3804 expect = -1;
3805 else
3807 vkey = VkKeyScanW( wchr );
3808 scan = MapVirtualKeyW( LOBYTE( vkey ), MAPVK_VK_TO_VSC );
3809 if (!scan)
3810 expect = -1;
3811 else
3813 vkey &= 0xff00;
3814 vkey <<= 8;
3815 expect = vkey | scan;
3818 ok( ret == expect, "%04x: got %08x expected %08x\n", oem, ret, expect );
3822 static INPUT_MESSAGE_SOURCE expect_src;
3824 static LRESULT WINAPI msg_source_proc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
3826 INPUT_MESSAGE_SOURCE source;
3827 MSG msg;
3829 ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
3830 switch (message)
3832 case WM_KEYDOWN:
3833 case WM_KEYUP:
3834 case WM_SYSKEYDOWN:
3835 case WM_SYSKEYUP:
3836 case WM_MOUSEMOVE:
3837 case WM_LBUTTONDOWN:
3838 case WM_LBUTTONUP:
3839 case WM_RBUTTONDOWN:
3840 case WM_RBUTTONUP:
3841 ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
3842 (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
3843 "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
3844 ok( source.originId == expect_src.originId ||
3845 (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
3846 "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
3847 SendMessageA( hwnd, WM_USER, 0, 0 );
3848 PostMessageA( hwnd, WM_USER, 0, 0 );
3849 if (PeekMessageW( &msg, hwnd, WM_USER, WM_USER, PM_REMOVE )) DispatchMessageW( &msg );
3850 ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
3851 (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
3852 "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
3853 ok( source.originId == expect_src.originId ||
3854 (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
3855 "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
3856 break;
3857 default:
3858 ok( source.deviceType == IMDT_UNAVAILABLE, "%x: wrong deviceType %x\n",
3859 message, source.deviceType );
3860 ok( source.originId == 0, "%x: wrong originId %x\n", message, source.originId );
3861 break;
3864 return DefWindowProcA( hwnd, message, wp, lp );
3867 static void test_input_message_source(void)
3869 WNDCLASSA cls;
3870 TEST_INPUT inputs[2];
3871 HWND hwnd;
3872 RECT rc;
3873 MSG msg;
3875 cls.style = 0;
3876 cls.lpfnWndProc = msg_source_proc;
3877 cls.cbClsExtra = 0;
3878 cls.cbWndExtra = 0;
3879 cls.hInstance = GetModuleHandleA(0);
3880 cls.hIcon = 0;
3881 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3882 cls.hbrBackground = 0;
3883 cls.lpszMenuName = NULL;
3884 cls.lpszClassName = "message source class";
3885 RegisterClassA(&cls);
3886 hwnd = CreateWindowA( cls.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 100, 100,
3887 0, 0, 0, 0 );
3888 ShowWindow( hwnd, SW_SHOWNORMAL );
3889 UpdateWindow( hwnd );
3890 SetForegroundWindow( hwnd );
3891 SetFocus( hwnd );
3893 inputs[0].type = INPUT_KEYBOARD;
3894 inputs[0].u.ki.dwExtraInfo = 0;
3895 inputs[0].u.ki.time = 0;
3896 inputs[0].u.ki.wVk = 0;
3897 inputs[0].u.ki.wScan = 0x3c0;
3898 inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
3899 inputs[1] = inputs[0];
3900 inputs[1].u.ki.dwFlags |= KEYEVENTF_KEYUP;
3902 expect_src.deviceType = IMDT_UNAVAILABLE;
3903 expect_src.originId = IMO_UNAVAILABLE;
3904 SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
3905 SendMessageA( hwnd, WM_MOUSEMOVE, 0, 0 );
3907 SendInput( 2, (INPUT *)inputs, sizeof(INPUT) );
3908 while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
3910 expect_src.deviceType = IMDT_KEYBOARD;
3911 expect_src.originId = IMO_INJECTED;
3912 TranslateMessage( &msg );
3913 DispatchMessageW( &msg );
3915 GetWindowRect( hwnd, &rc );
3916 simulate_click( TRUE, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 );
3917 simulate_click( FALSE, (rc.left + rc.right) / 2 + 1, (rc.top + rc.bottom) / 2 + 1 );
3918 while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
3920 expect_src.deviceType = IMDT_MOUSE;
3921 expect_src.originId = IMO_INJECTED;
3922 TranslateMessage( &msg );
3923 DispatchMessageW( &msg );
3926 expect_src.deviceType = IMDT_UNAVAILABLE;
3927 expect_src.originId = IMO_UNAVAILABLE;
3928 SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
3929 SendMessageA( hwnd, WM_LBUTTONDOWN, 0, 0 );
3930 PostMessageA( hwnd, WM_KEYUP, 0, 0 );
3931 PostMessageA( hwnd, WM_LBUTTONUP, 0, 0 );
3932 while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
3934 TranslateMessage( &msg );
3935 DispatchMessageW( &msg );
3938 expect_src.deviceType = IMDT_UNAVAILABLE;
3939 expect_src.originId = IMO_SYSTEM;
3940 SetCursorPos( (rc.left + rc.right) / 2 - 1, (rc.top + rc.bottom) / 2 - 1 );
3941 while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
3943 TranslateMessage( &msg );
3944 DispatchMessageW( &msg );
3947 DestroyWindow( hwnd );
3948 UnregisterClassA( cls.lpszClassName, GetModuleHandleA(0) );
3951 static void test_GetPointerType(void)
3953 BOOL ret;
3954 POINTER_INPUT_TYPE type = -1;
3955 UINT id = 0;
3957 SetLastError(0xdeadbeef);
3958 ret = pGetPointerType(id, NULL);
3959 ok(!ret, "GetPointerType should have failed.\n");
3960 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3961 "expected error ERROR_INVALID_PARAMETER, got %u.\n", GetLastError());
3963 SetLastError(0xdeadbeef);
3964 ret = pGetPointerType(id, &type);
3965 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3966 "expected error ERROR_INVALID_PARAMETER, got %u.\n", GetLastError());
3967 ok(!ret, "GetPointerType failed, got type %d for %u.\n", type, id );
3968 ok(type == -1, " type %d\n", type );
3970 id = 1;
3971 ret = pGetPointerType(id, &type);
3972 ok(ret, "GetPointerType failed, got type %d for %u.\n", type, id );
3973 ok(type == PT_MOUSE, " type %d\n", type );
3976 static void test_UnregisterDeviceNotification(void)
3978 BOOL ret = UnregisterDeviceNotification(NULL);
3979 ok(ret == FALSE, "Unregistering NULL Device Notification returned: %d\n", ret);
3982 START_TEST(input)
3984 char **argv;
3985 int argc;
3986 POINT pos;
3988 init_function_pointers();
3989 GetCursorPos( &pos );
3991 argc = winetest_get_mainargs(&argv);
3992 if (argc >= 3 && strcmp(argv[2], "rawinput_test") == 0)
3994 rawinput_test_process();
3995 return;
3998 if (argc >= 3 && strcmp(argv[2], "get_mouse_move_points_test") == 0)
4000 test_GetMouseMovePointsEx_process();
4001 return;
4004 test_Input_blackbox();
4005 test_Input_whitebox();
4006 test_Input_unicode();
4007 test_Input_mouse();
4008 test_keynames();
4009 test_mouse_ll_hook();
4010 test_key_map();
4011 test_ToUnicode();
4012 test_ToAscii();
4013 test_get_async_key_state();
4014 test_keyboard_layout_name();
4015 test_key_names();
4016 test_attach_input();
4017 test_GetKeyState();
4018 test_OemKeyScan();
4019 test_GetRawInputData();
4020 test_GetRawInputBuffer();
4021 test_RegisterRawInputDevices();
4022 test_rawinput(argv[0]);
4024 if(pGetMouseMovePointsEx)
4025 test_GetMouseMovePointsEx(argv[0]);
4026 else
4027 win_skip("GetMouseMovePointsEx is not available\n");
4029 if(pGetRawInputDeviceList)
4030 test_GetRawInputDeviceList();
4031 else
4032 win_skip("GetRawInputDeviceList is not available\n");
4034 if (pGetCurrentInputMessageSource)
4035 test_input_message_source();
4036 else
4037 win_skip("GetCurrentInputMessageSource is not available\n");
4039 SetCursorPos( pos.x, pos.y );
4041 if(pGetPointerType)
4042 test_GetPointerType();
4043 else
4044 win_skip("GetPointerType is not available\n");
4046 test_UnregisterDeviceNotification();