/* Test Key event to Key message translation * * Copyright 2003 Rein Klazes * Copyright 2019 Remi Bernon for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /* test whether the right type of messages: * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN are sent in case of combined * keystrokes. * * For instance -X can be accomplished by * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP * Whether a KEY or a SYSKEY message is sent is not always clear, it is * also not the same in WINNT as in WIN9X */ /* NOTE that there will be test failures under WIN9X * No applications are known to me that rely on this * so I don't fix it */ /* TODO: * 1. extend it to the wm_command and wm_syscommand notifications * 2. add some more tests with special cases like dead keys or right (alt) key * 3. there is some adapted code from input.c in here. Should really * make that code exactly the same. * 4. resolve the win9x case when there is a need or the testing frame work * offers a nice way. * 5. The test app creates a window, the user should not take the focus * away during its short existence. I could do something to prevent that * if it is a problem. * */ #include #include #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "wingdi.h" #include "winnls.h" #include "winreg.h" #include "ddk/hidsdi.h" #include "imm.h" #include "kbd.h" #include "wine/test.h" #define check_member_( file, line, val, exp, fmt, member ) \ ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member ) #define check_member( val, exp, fmt, member ) \ check_member_( __FILE__, __LINE__, val, exp, fmt, member ) static const char *debugstr_ok( const char *cond ) { int c, n = 0; /* skip possible casts */ while ((c = *cond++)) { if (c == '(') n++; if (!n) break; if (c == ')') n--; } if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 ); return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 ); } #define ok_eq( e, r, t, f, ... ) \ do \ { \ t v = (r); \ ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ } while (0) #define ok_ne( e, r, t, f, ... ) \ do \ { \ t v = (r); \ ok( v != (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ } while (0) #define ok_rect( e, r ) \ do \ { \ RECT v = (r); \ ok( EqualRect( &v, &(e) ), "%s %s\n", debugstr_ok(#r), wine_dbgstr_rect(&v) ); \ } while (0) #define ok_point( e, r ) \ do \ { \ POINT v = (r); \ ok( !memcmp( &v, &(e), sizeof(v) ), "%s %s\n", debugstr_ok(#r), wine_dbgstr_point(&v) ); \ } while (0) #define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() ) enum user_function { MSG_TEST_WIN = 1, LL_HOOK_KEYBD, LL_HOOK_MOUSE, }; struct user_call { enum user_function func; union { struct { HWND hwnd; UINT msg; WPARAM wparam; LPARAM lparam; } message; struct { UINT msg; UINT scan; UINT vkey; UINT flags; UINT_PTR extra; } ll_hook_kbd; struct { UINT msg; POINT point; UINT data; UINT flags; UINT time; UINT_PTR extra; } ll_hook_ms; }; BOOL todo; BOOL todo_value; BOOL broken; }; static const struct user_call empty_sequence[] = {{0}}; static struct user_call current_sequence[1024]; static LONG current_sequence_len; static const char *debugstr_wm( UINT msg ) { switch (msg) { case WM_CHAR: return "WM_CHAR"; case WM_KEYDOWN: return "WM_KEYDOWN"; case WM_KEYUP: return "WM_KEYUP"; case WM_SYSCHAR: return "WM_SYSCHAR"; case WM_SYSCOMMAND: return "WM_SYSCOMMAND"; case WM_SYSKEYDOWN: return "WM_SYSKEYDOWN"; case WM_SYSKEYUP: return "WM_SYSKEYUP"; case WM_MOUSEMOVE: return "WM_MOUSEMOVE"; case WM_LBUTTONDOWN: return "WM_LBUTTONDOWN"; case WM_LBUTTONUP: return "WM_LBUTTONUP"; case WM_LBUTTONDBLCLK: return "WM_LBUTTONDBLCLK"; case WM_NCHITTEST: return "WM_NCHITTEST"; } return wine_dbg_sprintf( "%#x", msg ); } static const char *debugstr_vk( UINT vkey ) { switch (vkey) { case VK_CONTROL: return "VK_CONTROL"; case VK_LCONTROL: return "VK_LCONTROL"; case VK_LMENU: return "VK_LMENU"; case VK_LSHIFT: return "VK_LSHIFT"; case VK_MENU: return "VK_MENU"; case VK_RCONTROL: return "VK_RCONTROL"; case VK_RMENU: return "VK_RMENU"; case VK_RSHIFT: return "VK_RSHIFT"; case VK_SHIFT: return "VK_SHIFT"; } if (vkey >= '0' && vkey <= '9') return wine_dbg_sprintf( "%c", vkey ); if (vkey >= 'A' && vkey <= 'Z') return wine_dbg_sprintf( "%c", vkey ); return wine_dbg_sprintf( "%#x", vkey ); } #define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b ) static int ok_call_( const char *file, int line, const struct user_call *expected, const struct user_call *received ) { int ret; if ((ret = expected->func - received->func)) goto done; switch (expected->func) { case MSG_TEST_WIN: if ((ret = expected->message.hwnd - received->message.hwnd)) goto done; if ((ret = expected->message.msg - received->message.msg)) goto done; if ((ret = (expected->message.wparam - received->message.wparam))) goto done; if ((ret = (expected->message.lparam - received->message.lparam))) goto done; break; case LL_HOOK_KEYBD: if ((ret = expected->ll_hook_kbd.msg - received->ll_hook_kbd.msg)) goto done; if ((ret = (expected->ll_hook_kbd.scan - received->ll_hook_kbd.scan))) goto done; if ((ret = expected->ll_hook_kbd.vkey - received->ll_hook_kbd.vkey)) goto done; if ((ret = (expected->ll_hook_kbd.flags - received->ll_hook_kbd.flags))) goto done; if ((ret = (expected->ll_hook_kbd.extra - received->ll_hook_kbd.extra))) goto done; break; case LL_HOOK_MOUSE: if ((ret = expected->ll_hook_ms.msg - received->ll_hook_ms.msg)) goto done; if ((ret = expected->ll_hook_ms.point.x - received->ll_hook_ms.point.x)) goto done; if ((ret = expected->ll_hook_ms.point.y - received->ll_hook_ms.point.y)) goto done; if ((ret = (expected->ll_hook_ms.data - received->ll_hook_ms.data))) goto done; if ((ret = (expected->ll_hook_ms.flags - received->ll_hook_ms.flags))) goto done; if (0 && (ret = expected->ll_hook_ms.time - received->ll_hook_ms.time)) goto done; if ((ret = (expected->ll_hook_ms.extra - received->ll_hook_ms.extra))) goto done; break; } done: if (ret && broken( expected->broken )) return ret; switch (received->func) { case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got MSG_TEST_WIN hwnd %p, msg %s, wparam %#Ix, lparam %#Ix\n", received->message.hwnd, debugstr_wm(received->message.msg), received->message.wparam, received->message.lparam ); return ret; case LL_HOOK_KEYBD: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got LL_HOOK_KEYBD msg %s scan %#x, vkey %s, flags %#x, extra %#Ix\n", debugstr_wm(received->ll_hook_kbd.msg), received->ll_hook_kbd.scan, debugstr_vk(received->ll_hook_kbd.vkey), received->ll_hook_kbd.flags, received->ll_hook_kbd.extra ); return ret; case LL_HOOK_MOUSE: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got LL_HOOK_MOUSE msg %s, point %s, data %#x, flags %#x, time %u, extra %#Ix\n", debugstr_wm(received->ll_hook_ms.msg), wine_dbgstr_point(&received->ll_hook_ms.point), received->ll_hook_ms.data, received->ll_hook_ms.flags, received->ll_hook_ms.time, received->ll_hook_ms.extra ); return ret; } switch (expected->func) { case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "MSG_TEST_WIN hwnd %p, %s, wparam %#Ix, lparam %#Ix\n", expected->message.hwnd, debugstr_wm(expected->message.msg), expected->message.wparam, expected->message.lparam ); break; case LL_HOOK_KEYBD: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "LL_HOOK_KBD msg %s scan %#x, vkey %s, flags %#x, extra %#Ix\n", debugstr_wm(expected->ll_hook_kbd.msg), expected->ll_hook_kbd.scan, debugstr_vk(expected->ll_hook_kbd.vkey), expected->ll_hook_kbd.flags, expected->ll_hook_kbd.extra ); break; case LL_HOOK_MOUSE: todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "LL_HOOK_MOUSE msg %s, point %s, data %#x, flags %#x, time %u, extra %#Ix\n", debugstr_wm(received->ll_hook_ms.msg), wine_dbgstr_point(&received->ll_hook_ms.point), received->ll_hook_ms.data, received->ll_hook_ms.flags, received->ll_hook_ms.time, received->ll_hook_ms.extra ); return ret; } return 0; } #define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a ) static void ok_seq_( const char *file, int line, const struct user_call *expected, const char *context ) { const struct user_call *received = current_sequence; UINT i = 0, ret; while (expected->func || received->func) { winetest_push_context( "%s %u%s%s", context, i++, !expected->func ? " (spurious)" : "", !received->func ? " (missing)" : "" ); ret = ok_call_( file, line, expected, received ); if (ret && expected->todo && expected->func && !strcmp( winetest_platform, "wine" )) expected++; else if (ret && broken(expected->broken)) expected++; else { if (expected->func) expected++; if (received->func) received++; } winetest_pop_context(); } memset( current_sequence, 0, sizeof(current_sequence) ); current_sequence_len = 0; } static BOOL append_message_hwnd; static BOOL (*p_accept_message)( UINT msg ); static void append_ll_hook_kbd( UINT msg, const KBDLLHOOKSTRUCT *info ) { struct user_call call = { .func = LL_HOOK_KEYBD, .ll_hook_kbd = { .msg = msg, .scan = info->scanCode, .vkey = info->vkCode, .flags = info->flags, .extra = info->dwExtraInfo } }; if (!p_accept_message || p_accept_message( msg )) { ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); current_sequence[index] = call; } } static void append_ll_hook_ms( UINT msg, const MSLLHOOKSTRUCT *info ) { struct user_call call = { .func = LL_HOOK_MOUSE, .ll_hook_ms = { .msg = msg, .point = info->pt, .data = info->mouseData, .flags = info->flags, .time = info->time, .extra = info->dwExtraInfo } }; if (!p_accept_message || p_accept_message( msg )) { ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); current_sequence[index] = call; } } static void append_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { if (!p_accept_message || p_accept_message( msg )) { struct user_call call = {.func = MSG_TEST_WIN, .message = {.hwnd = hwnd, .msg = msg, .wparam = wparam, .lparam = lparam}}; ULONG index = InterlockedIncrement( ¤t_sequence_len ) - 1; ok( index < ARRAY_SIZE(current_sequence), "got %lu calls\n", index ); if (!append_message_hwnd) call.message.hwnd = 0; current_sequence[index] = call; } } static LRESULT CALLBACK append_message_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { append_message( hwnd, msg, wparam, lparam ); return DefWindowProcW( hwnd, msg, wparam, lparam ); } /* globals */ #define DESKTOP_ALL_ACCESS 0x01ff static BOOL (WINAPI *pEnableMouseInPointer)( BOOL ); static BOOL (WINAPI *pIsMouseInPointerEnabled)(void); static BOOL (WINAPI *pGetCurrentInputMessageSource)( INPUT_MESSAGE_SOURCE *source ); static BOOL (WINAPI *pGetPointerType)(UINT32, POINTER_INPUT_TYPE*); static BOOL (WINAPI *pGetPointerInfo)(UINT32, POINTER_INFO*); static BOOL (WINAPI *pGetPointerInfoHistory)(UINT32, UINT32*, POINTER_INFO*); static BOOL (WINAPI *pGetPointerFrameInfo)(UINT32, UINT32*, POINTER_INFO*); static BOOL (WINAPI *pGetPointerFrameInfoHistory)(UINT32, UINT32*, UINT32*, POINTER_INFO*); static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD); static UINT (WINAPI *pGetRawInputDeviceList) (PRAWINPUTDEVICELIST, PUINT, UINT); static UINT (WINAPI *pGetRawInputDeviceInfoW) (HANDLE, UINT, void *, UINT *); static UINT (WINAPI *pGetRawInputDeviceInfoA) (HANDLE, UINT, void *, UINT *); static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); /**********************adapted from input.c **********************************/ static BOOL is_wow64; static void init_function_pointers(void) { HMODULE hdll = GetModuleHandleA("user32"); #define GET_PROC(func) \ if (!(p ## func = (void*)GetProcAddress(hdll, #func))) \ trace("GetProcAddress(%s) failed\n", #func) GET_PROC(EnableMouseInPointer); GET_PROC(IsMouseInPointerEnabled); GET_PROC(GetCurrentInputMessageSource); GET_PROC(GetMouseMovePointsEx); GET_PROC(GetPointerInfo); GET_PROC(GetPointerInfoHistory); GET_PROC(GetPointerFrameInfo); GET_PROC(GetPointerFrameInfoHistory); GET_PROC(GetPointerType); GET_PROC(GetRawInputDeviceList); GET_PROC(GetRawInputDeviceInfoW); GET_PROC(GetRawInputDeviceInfoA); hdll = GetModuleHandleA("kernel32"); GET_PROC(IsWow64Process); #undef GET_PROC if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; } #define run_in_process( a, b ) run_in_process_( __FILE__, __LINE__, a, b ) static void run_in_process_( const char *file, int line, char **argv, const char *args ) { STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; PROCESS_INFORMATION info = {0}; char cmdline[MAX_PATH * 2]; DWORD ret; sprintf( cmdline, "%s %s %s", argv[0], argv[1], args ); ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); if (!ret) return; wait_child_process( info.hProcess ); CloseHandle( info.hThread ); CloseHandle( info.hProcess ); } #define run_in_desktop( a, b, c ) run_in_desktop_( __FILE__, __LINE__, a, b, c ) static void run_in_desktop_( const char *file, int line, char **argv, const char *args, BOOL input ) { const char *desktop_name = "WineTest Desktop"; STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; PROCESS_INFORMATION info = {0}; HDESK old_desktop, desktop; char cmdline[MAX_PATH * 2]; DWORD ret; old_desktop = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS ); ok_(file, line)( !!old_desktop, "OpenInputDesktop failed, error %lu\n", GetLastError() ); desktop = CreateDesktopA( desktop_name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok_(file, line)( !!desktop, "CreateDesktopA failed, error %lu\n", GetLastError() ); if (input) { ret = SwitchDesktop( desktop ); ok_(file, line)( ret, "SwitchDesktop failed, error %lu\n", GetLastError() ); } startup.lpDesktop = (char *)desktop_name; sprintf( cmdline, "%s %s %s", argv[0], argv[1], args ); ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); if (!ret) return; wait_child_process( info.hProcess ); CloseHandle( info.hThread ); CloseHandle( info.hProcess ); if (input) { ret = SwitchDesktop( old_desktop ); ok_(file, line)( ret, "SwitchDesktop failed, error %lu\n", GetLastError() ); } ret = CloseDesktop( desktop ); ok_(file, line)( ret, "CloseDesktop failed, error %lu\n", GetLastError() ); ret = CloseDesktop( old_desktop ); ok_(file, line)( ret, "CloseDesktop failed, error %lu\n", GetLastError() ); } #define wait_messages( a, b ) msg_wait_for_events_( __FILE__, __LINE__, 0, NULL, a, b ) #define msg_wait_for_events( a, b, c ) msg_wait_for_events_( __FILE__, __LINE__, a, b, c, FALSE ) static DWORD msg_wait_for_events_( const char *file, int line, DWORD count, HANDLE *events, DWORD timeout, BOOL append_peeked ) { DWORD ret, end = GetTickCount() + min( timeout, 5000 ); MSG msg; while ((ret = MsgWaitForMultipleObjects( count, events, FALSE, min( timeout, 5000 ), QS_ALLINPUT )) <= count) { while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) { if (append_peeked) append_message( msg.hwnd, msg.message, msg.wParam, msg.lParam ); TranslateMessage( &msg ); DispatchMessageW( &msg ); } if (ret < count) return ret; if (timeout >= 5000) continue; if (end <= GetTickCount()) timeout = 0; else timeout = end - GetTickCount(); } if (timeout >= 5000) ok_(file, line)( 0, "MsgWaitForMultipleObjects returned %#lx\n", ret ); else ok_(file, line)( ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %#lx\n", ret ); return ret; } static inline BOOL is_keyboard_message( UINT message ) { return (message >= WM_KEYFIRST && message <= WM_KEYLAST); } static inline BOOL is_mouse_message( UINT message ) { return (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST); } /* try to make sure pending X events have been processed before continuing */ static void empty_message_queue(void) { MSG msg; int diff = 200; int min_timeout = 50; DWORD time = GetTickCount() + diff; while (diff > 0) { if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT) break; while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) { if (is_keyboard_message(msg.message) || is_mouse_message(msg.message)) ok(msg.time != 0, "message %#x has time set to 0\n", msg.message); TranslateMessage(&msg); DispatchMessageA(&msg); } diff = time - GetTickCount(); } } struct send_input_keyboard_test { WORD scan; WORD vkey; DWORD flags; struct user_call expect[8]; BYTE expect_state[256]; BOOL todo_state[256]; }; static LRESULT CALLBACK ll_hook_kbd_proc(int code, WPARAM wparam, LPARAM lparam) { KBDLLHOOKSTRUCT *hook_info = (KBDLLHOOKSTRUCT *)lparam; if (code == HC_ACTION) { append_ll_hook_kbd( wparam, hook_info ); if(0) /* For some reason not stable on Wine */ { if (wparam == WM_KEYDOWN || wparam == WM_SYSKEYDOWN) ok(!(GetAsyncKeyState(hook_info->vkCode) & 0x8000), "key %lx should be up\n", hook_info->vkCode); else if (wparam == WM_KEYUP || wparam == WM_SYSKEYUP) ok(GetAsyncKeyState(hook_info->vkCode) & 0x8000, "key %lx should be down\n", hook_info->vkCode); } if (winetest_debug > 1) trace("Hook: w=%Ix vk:%8lx sc:%8lx fl:%8lx %Ix\n", wparam, hook_info->vkCode, hook_info->scanCode, hook_info->flags, hook_info->dwExtraInfo); } return CallNextHookEx( 0, code, wparam, lparam ); } #define check_keyboard_state( a, b ) check_keyboard_state_( __LINE__, a, b ) static void check_keyboard_state_( int line, const BYTE expect_state[256], const BOOL todo_state[256] ) { BYTE state[256]; UINT i; ok_ret( 1, GetKeyboardState( state ) ); for (i = 0; i < ARRAY_SIZE(state); i++) { todo_wine_if( todo_state[i] ) ok_(__FILE__, line)( (expect_state[i] & 0x80) == (state[i] & 0x80), "got %s: %#x\n", debugstr_vk( i ), state[i] ); } } #define check_send_input_keyboard_test( a, b ) check_send_input_keyboard_test_( a, #a, b ) static void check_send_input_keyboard_test_( const struct send_input_keyboard_test *test, const char *context, BOOL peeked ) { static BYTE empty_state[256] = {0}; INPUT input = {.type = INPUT_KEYBOARD}; UINT i; winetest_push_context( "%s", context ); SetKeyboardState( empty_state ); for (i = 0; test->vkey || test->scan; i++, test++) { winetest_push_context( "%u", i ); input.ki.wScan = test->flags & (KEYEVENTF_SCANCODE | KEYEVENTF_UNICODE) ? test->scan : (i + 1); input.ki.dwFlags = test->flags; input.ki.wVk = test->vkey; ok_ret( 1, SendInput( 1, &input, sizeof(input) ) ); wait_messages( 5, peeked ); ok_seq( test->expect ); check_keyboard_state( test->expect_state, test->todo_state ); winetest_pop_context(); } SetKeyboardState( empty_state ); winetest_pop_context(); } static BOOL accept_keyboard_messages_syscommand( UINT msg ) { return is_keyboard_message( msg ) || msg == WM_SYSCOMMAND; } static BOOL keyboard_layout_has_altgr(void) { typedef struct { UINT64 pCharModifiers; UINT64 pVkToWcharTable; UINT64 pDeadKey; UINT64 pKeyNames; UINT64 pKeyNamesExt; UINT64 pKeyNamesDead; UINT64 pusVSCtoVK; BYTE bMaxVSCtoVK; UINT64 pVSCtoVK_E0; UINT64 pVSCtoVK_E1; DWORD fLocaleFlags; } KBDTABLES64; WCHAR layout_path[MAX_PATH] = {L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\"}, value[MAX_PATH]; KBDTABLES *(CDECL *pKbdLayerDescriptor)(void); DWORD value_size = sizeof(value), flags; KBDTABLES *tables; HMODULE module; ok_ret( 1, GetKeyboardLayoutNameW( layout_path + wcslen(layout_path) ) ); todo_wine ok_ret( 0, RegGetValueW( HKEY_LOCAL_MACHINE, layout_path, L"Layout File", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size) ); module = LoadLibraryW( value ); todo_wine ok_ne( NULL, module, HMODULE, "%p" ); pKbdLayerDescriptor = (void *)GetProcAddress( module, "KbdLayerDescriptor" ); todo_wine ok_ne( NULL, pKbdLayerDescriptor, void *, "%p" ); /* FIXME: Wine doesn't implement ALTGR behavior */ if (!pKbdLayerDescriptor) return FALSE; tables = pKbdLayerDescriptor(); ok_ne( NULL, tables, KBDTABLES *, "%p" ); flags = is_wow64 ? ((KBDTABLES64 *)tables)->fLocaleFlags : tables->fLocaleFlags; ok_ret( 1, FreeLibrary( module ) ); trace( "%s flags %#lx\n", debugstr_w(value), flags ); return !!(flags & KLLF_ALTGR); } static void get_test_scan( WORD vkey, WORD *scan, WCHAR *wch, WCHAR *wch_shift ) { HKL hkl = GetKeyboardLayout( 0 ); BYTE state[256] = {0}; *scan = MapVirtualKeyExW( vkey, MAPVK_VK_TO_VSC_EX, hkl ); ok_ne( 0, *scan, WORD, "%#x" ); ok_ret( 1, ToUnicodeEx( vkey, *scan, state, wch, 1, 0, hkl ) ); state[VK_SHIFT] = 0x80; ok_ret( 1, ToUnicodeEx( vkey, *scan, state, wch_shift, 1, 0, hkl ) ); /* zh_CN returns a different WM_(SYS)CHAR, possibly coming from IME */ if (HIWORD(hkl) == 0x0804) { *wch = 0x430; *wch_shift = 0x410; } } static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, WCHAR wch_shift, WCHAR wch_control ) { #define WIN_MSG(m, w, l, ...) {.func = MSG_TEST_WIN, .message = {.msg = m, .wparam = w, .lparam = l}, ## __VA_ARGS__} #define KBD_HOOK(m, s, v, f, ...) {.func = LL_HOOK_KEYBD, .ll_hook_kbd = {.msg = m, .scan = s, .vkey = v, .flags = f}, ## __VA_ARGS__} #define KEY_HOOK_(m, s, v, f, ...) KBD_HOOK( m, s, v, LLKHF_INJECTED | (m == WM_KEYUP || m == WM_SYSKEYUP ? LLKHF_UP : 0) | (f), ## __VA_ARGS__ ) #define KEY_HOOK(m, s, v, ...) KEY_HOOK_( m, s, v, 0, ## __VA_ARGS__ ) #define KEY_MSG_(m, s, v, f, ...) WIN_MSG( m, v, MAKELONG(1, (s) | (m == WM_KEYUP || m == WM_SYSKEYUP ? (KF_UP | KF_REPEAT) : 0) | (f)), ## __VA_ARGS__ ) #define KEY_MSG(m, s, v, ...) KEY_MSG_( m, s, v, 0, ## __VA_ARGS__ ) struct send_input_keyboard_test lmenu_vkey[] = { {.vkey = VK_LMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, { .vkey = vkey, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, /*[vkey] = 0x80*/}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 2, vkey, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 2, vkey, KF_ALTDOWN), WIN_MSG(WM_SYSCHAR, wch, MAKELONG(1, 2|KF_ALTDOWN)), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, wch), {0} } }, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYUP, 3, vkey, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 3, vkey, KF_ALTDOWN), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LMENU), KEY_MSG(WM_KEYUP, 4, VK_MENU), {0}}}, {0}, }; struct send_input_keyboard_test lmenu_vkey_peeked[] = { {.vkey = VK_LMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, { .vkey = vkey, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, /*[vkey] = 0x80*/}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 2, vkey, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 2, vkey, KF_ALTDOWN), WIN_MSG(WM_SYSCHAR, wch, MAKELONG(1, 2|KF_ALTDOWN)), {0} } }, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYUP, 3, vkey, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 3, vkey, KF_ALTDOWN), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LMENU), KEY_MSG(WM_KEYUP, 4, VK_MENU), {0}}}, {0}, }; struct send_input_keyboard_test lcontrol_vkey[] = { {.vkey = VK_LCONTROL, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 1, VK_CONTROL), {0}}}, {.vkey = vkey, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80, /*[vkey] = 0x80*/}, .expect = {KEY_HOOK(WM_KEYDOWN, 2, vkey), KEY_MSG(WM_KEYDOWN, 2, vkey), WIN_MSG(WM_CHAR, wch_control, MAKELONG(1, 2)), {0}}}, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYUP, 3, vkey), KEY_MSG(WM_KEYUP, 3, vkey), {0}}}, {.vkey = VK_LCONTROL, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LCONTROL), KEY_MSG(WM_KEYUP, 4, VK_CONTROL), {0}}}, {0}, }; struct send_input_keyboard_test lmenu_lcontrol_vkey[] = { {.vkey = VK_LMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, {.vkey = VK_LCONTROL, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 2, VK_LCONTROL), KEY_MSG_(WM_KEYDOWN, 2, VK_CONTROL, KF_ALTDOWN), {0}}}, {.vkey = vkey, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80, /*[vkey] = 0x80*/}, .expect = {KEY_HOOK(WM_KEYDOWN, 3, vkey), KEY_MSG_(WM_KEYDOWN, 3, vkey, KF_ALTDOWN), {0}}}, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYUP, 4, vkey), KEY_MSG_(WM_KEYUP, 4, vkey, KF_ALTDOWN), {0}}}, {.vkey = VK_LCONTROL, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYUP, 5, VK_LCONTROL, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 5, VK_CONTROL, KF_ALTDOWN), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 6, VK_LMENU), KEY_MSG(WM_KEYUP, 6, VK_MENU), {0}}}, {0}, }; struct send_input_keyboard_test shift_vkey[] = { {.vkey = VK_LSHIFT, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LSHIFT), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = vkey, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80, /*[vkey] = 0x80*/}, .expect = {KEY_HOOK(WM_KEYDOWN, 2, vkey), KEY_MSG(WM_KEYDOWN, 2, vkey), WIN_MSG(WM_CHAR, wch_shift, MAKELONG(1, 2)), {0}}}, {.vkey = vkey, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .expect = {KEY_HOOK(WM_KEYUP, 3, vkey), KEY_MSG(WM_KEYUP, 3, vkey), {0}}}, {.vkey = VK_LSHIFT, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LSHIFT), KEY_MSG(WM_KEYUP, 4, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test rshift[] = { {.vkey = VK_RSHIFT, .expect_state = {[VK_SHIFT] = 0x80, [VK_RSHIFT] = 0x80}, .todo_state = {[VK_RSHIFT] = TRUE, [VK_LSHIFT] = TRUE}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RSHIFT, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_RSHIFT, .flags = KEYEVENTF_KEYUP, .expect_state = {0}, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RSHIFT, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test lshift_ext[] = { {.vkey = VK_LSHIFT, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_SHIFT] = 0x80, [VK_RSHIFT] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_LSHIFT, LLKHF_EXTENDED), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_LSHIFT, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_LSHIFT, LLKHF_EXTENDED), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test rshift_ext[] = { {.vkey = VK_RSHIFT, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_SHIFT] = 0x80, [VK_RSHIFT] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RSHIFT, LLKHF_EXTENDED), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_RSHIFT, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect_state = {0}, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RSHIFT, LLKHF_EXTENDED), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test shift[] = { {.vkey = VK_SHIFT, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_SHIFT, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test shift_ext[] = { {.vkey = VK_SHIFT, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_SHIFT] = 0x80, [VK_RSHIFT] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_LSHIFT, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_SHIFT, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_LSHIFT, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_SHIFT), {0}}}, {0}, }; static const struct send_input_keyboard_test rcontrol[] = { {.vkey = VK_RCONTROL, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_RCONTROL), KEY_MSG(WM_KEYDOWN, 1, VK_CONTROL), {0}}}, {.vkey = VK_RCONTROL, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_RCONTROL), KEY_MSG(WM_KEYUP, 2, VK_CONTROL), {0}}}, {0}, }; static const struct send_input_keyboard_test lcontrol_ext[] = { {.vkey = VK_LCONTROL, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_LCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYDOWN, 1, VK_CONTROL, KF_EXTENDED), {0}}}, {.vkey = VK_LCONTROL, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_LCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYUP, 2, VK_CONTROL, KF_EXTENDED), {0}}}, {0}, }; static const struct send_input_keyboard_test rcontrol_ext[] = { {.vkey = VK_RCONTROL, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYDOWN, 1, VK_CONTROL, KF_EXTENDED), {0}}}, {.vkey = VK_RCONTROL, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RCONTROL, LLKHF_EXTENDED), KEY_MSG_(WM_KEYUP, 2, VK_CONTROL, KF_EXTENDED), {0}}}, {0}, }; static const struct send_input_keyboard_test control[] = { {.vkey = VK_CONTROL, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 1, VK_CONTROL), {0}}}, {.vkey = VK_CONTROL, .flags = KEYEVENTF_KEYUP, .expect_state = {0}, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LCONTROL, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 2, VK_CONTROL), {0}}}, {0}, }; static const struct send_input_keyboard_test control_ext[] = { {.vkey = VK_CONTROL, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 1, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_KEYDOWN, 1, VK_CONTROL, KF_EXTENDED), {0}}}, {.vkey = VK_CONTROL, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_KEYUP, 2, VK_CONTROL, KF_EXTENDED), {0}}}, {0}, }; static const struct send_input_keyboard_test rmenu[] = { {.vkey = VK_RMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, {.vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_RMENU, .todo_value = TRUE), KEY_MSG(WM_SYSKEYUP, 2, VK_MENU), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0), {0}}}, {0}, }; static const struct send_input_keyboard_test lmenu_ext[] = { {.vkey = VK_LMENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_LMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0), {0}}}, {0}, }; static const struct send_input_keyboard_test rmenu_ext[] = { {.vkey = VK_RMENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0), {0}}}, {0}, }; static const struct send_input_keyboard_test menu[] = { {.vkey = VK_MENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, {.vkey = VK_MENU, .flags = KEYEVENTF_KEYUP, .expect_state = {0}, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LMENU, .todo_value = TRUE), KEY_MSG(WM_SYSKEYUP, 2, VK_MENU), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0), {0}}}, {0}, }; static const struct send_input_keyboard_test menu_ext[] = { {.vkey = VK_MENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_MENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0), {0}}}, {0}, }; static const struct send_input_keyboard_test rmenu_peeked[] = { {.vkey = VK_RMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, {.vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_RMENU, .todo_value = TRUE), KEY_MSG(WM_SYSKEYUP, 2, VK_MENU), {0}}}, {0}, }; static const struct send_input_keyboard_test lmenu_ext_peeked[] = { {.vkey = VK_LMENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_LMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), {0}}}, {0}, }; static const struct send_input_keyboard_test rmenu_ext_peeked[] = { {.vkey = VK_RMENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), {0}}}, {0}, }; static const struct send_input_keyboard_test menu_peeked[] = { {.vkey = VK_MENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, {.vkey = VK_MENU, .flags = KEYEVENTF_KEYUP, .expect_state = {0}, .expect = {KEY_HOOK(WM_KEYUP, 2, VK_LMENU, .todo_value = TRUE), KEY_MSG(WM_SYSKEYUP, 2, VK_MENU), {0}}}, {0}, }; static const struct send_input_keyboard_test menu_ext_peeked[] = { {.vkey = VK_MENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0}}}, {.vkey = VK_MENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = {KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 2, VK_MENU, KF_EXTENDED), {0}}}, {0}, }; struct send_input_keyboard_test rmenu_altgr[] = { { .vkey = VK_RMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 0x21d, VK_LCONTROL, LLKHF_ALTDOWN), KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), KEY_MSG_(WM_KEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0} } }, { .vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP, .expect = { KEY_HOOK(WM_KEYUP, 0x21d, VK_LCONTROL), KEY_HOOK(WM_KEYUP, 2, VK_RMENU), KEY_MSG_(WM_SYSKEYUP, 0x1d, VK_CONTROL, KF_ALTDOWN), KEY_MSG(WM_KEYUP, 2, VK_MENU), {0} } }, {0}, }; struct send_input_keyboard_test rmenu_ext_altgr[] = { { .vkey = VK_RMENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 0x21d, VK_LCONTROL, LLKHF_ALTDOWN), KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), KEY_MSG_(WM_KEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0} } }, { .vkey = VK_RMENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = { KEY_HOOK(WM_KEYUP, 0x21d, VK_LCONTROL), KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 0x1d, VK_CONTROL, KF_ALTDOWN), KEY_MSG_(WM_KEYUP, 2, VK_MENU, KF_EXTENDED), {0} } }, {0}, }; struct send_input_keyboard_test menu_ext_altgr[] = { { .vkey = VK_MENU, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_MENU] = 0x80, [VK_RMENU] = 0x80, [VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 0x21d, VK_LCONTROL, LLKHF_ALTDOWN), KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_RMENU, LLKHF_ALTDOWN|LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), KEY_MSG_(WM_KEYDOWN, 1, VK_MENU, KF_ALTDOWN|KF_EXTENDED), {0} } }, { .vkey = VK_MENU, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect = { KEY_HOOK(WM_KEYUP, 0x21d, VK_LCONTROL), KEY_HOOK_(WM_KEYUP, 2, VK_RMENU, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 0x1d, VK_CONTROL, KF_ALTDOWN), KEY_MSG_(WM_KEYUP, 2, VK_MENU, KF_EXTENDED), {0} } }, {0}, }; static const struct send_input_keyboard_test lrshift_ext[] = { {.vkey = VK_LSHIFT, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 1, VK_LSHIFT), KEY_MSG(WM_KEYDOWN, 1, VK_SHIFT), {0}}}, {.vkey = VK_RSHIFT, .flags = KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80, [VK_RSHIFT] = 0x80}, .expect = {KEY_HOOK_(WM_KEYDOWN, 2, VK_RSHIFT, LLKHF_EXTENDED), KEY_MSG_(WM_KEYDOWN, 2, VK_SHIFT, KF_REPEAT, .todo_value = TRUE), {0}}}, {.vkey = VK_RSHIFT, .flags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .expect = {KEY_HOOK_(WM_KEYUP, 3, VK_RSHIFT, LLKHF_EXTENDED), {0, .todo = TRUE}, {0}}}, {.vkey = VK_LSHIFT, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LSHIFT), KEY_MSG(WM_KEYUP, 4, VK_SHIFT), {0}}}, {0}, }; struct send_input_keyboard_test rshift_scan[] = { {.scan = 0x36, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .todo_state = {[0] = TRUE, [VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE}, .expect = {KEY_HOOK(WM_KEYDOWN, 0x36, VK_RSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x36, VK_SHIFT, .todo_value = TRUE), {0}}}, {.scan = scan, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80, /*[vkey] = 0x80*/}, .todo_state = {[0] = TRUE, [VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE, /*[vkey] = TRUE*/}, .expect = {KEY_HOOK(WM_KEYDOWN, scan, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, scan, vkey, .todo_value = TRUE), WIN_MSG(WM_CHAR, wch_shift, MAKELONG(1, scan), .todo = TRUE), {0}}}, {.scan = scan, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_SHIFT] = 0x80, [VK_LSHIFT] = 0x80}, .todo_state = {[VK_SHIFT] = TRUE, [VK_LSHIFT] = TRUE}, .expect = {KEY_HOOK(WM_KEYUP, scan, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYUP, scan, vkey, .todo_value = TRUE), {0}}}, {.scan = 0x36, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 0x36, VK_RSHIFT, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x36, VK_SHIFT, .todo_value = TRUE), {0}}}, {0}, }; struct send_input_keyboard_test unicode[] = { {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_PACKET] = 0x80}, .expect = {KEY_HOOK(WM_KEYDOWN, 0x3c0, VK_PACKET), KEY_MSG(WM_KEYDOWN, 0, VK_PACKET, .todo_value = TRUE), WIN_MSG(WM_CHAR, 0x3c0, 1), {0}}}, {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 0x3c0, VK_PACKET, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0, VK_PACKET, .todo_value = TRUE), {0}}}, {0}, }; struct send_input_keyboard_test lmenu_unicode[] = { {.vkey = VK_LMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, { .scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_PACKET] = 0x80}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 0x3c0, VK_PACKET, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 0, VK_PACKET, KF_ALTDOWN, .todo_value = TRUE), WIN_MSG(WM_SYSCHAR, 0x3c0, MAKELONG(1, KF_ALTDOWN), .todo_value = TRUE), WIN_MSG(WM_SYSCOMMAND, SC_KEYMENU, 0x3c0, .todo = TRUE), {0}, }, }, {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYUP, 0x3c0, VK_PACKET, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 0, VK_PACKET, KF_ALTDOWN, .todo_value = TRUE), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LMENU), KEY_MSG(WM_KEYUP, 4, VK_MENU), {0}}}, {0}, }; struct send_input_keyboard_test lmenu_unicode_peeked[] = { {.vkey = VK_LMENU, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYDOWN, 1, VK_LMENU, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 1, VK_MENU, KF_ALTDOWN), {0}}}, { .scan = 0x3c0, .flags = KEYEVENTF_UNICODE, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80, [VK_PACKET] = 0x80}, .expect = { KEY_HOOK_(WM_SYSKEYDOWN, 0x3c0, VK_PACKET, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYDOWN, 0, VK_PACKET, KF_ALTDOWN, .todo_value = TRUE), WIN_MSG(WM_SYSCHAR, 0x3c0, MAKELONG(1, KF_ALTDOWN), .todo_value = TRUE), {0}, }, }, {.scan = 0x3c0, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, .expect_state = {[VK_MENU] = 0x80, [VK_LMENU] = 0x80}, .expect = {KEY_HOOK_(WM_SYSKEYUP, 0x3c0, VK_PACKET, LLKHF_ALTDOWN, .todo_value = TRUE), KEY_MSG_(WM_SYSKEYUP, 0, VK_PACKET, KF_ALTDOWN, .todo_value = TRUE), {0}}}, {.vkey = VK_LMENU, .flags = KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 4, VK_LMENU), KEY_MSG(WM_KEYUP, 4, VK_MENU), {0}}}, {0}, }; struct send_input_keyboard_test unicode_vkey[] = { {.scan = 0x3c0, .vkey = vkey, .flags = KEYEVENTF_UNICODE, .expect_state = {/*[vkey] = 0x80*/}, .expect = {KEY_HOOK(WM_KEYDOWN, 0xc0, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0xc0, vkey, .todo_value = TRUE), WIN_MSG(WM_CHAR, wch, MAKELONG(1, 0xc0), .todo_value = TRUE), {0}}}, {.scan = 0x3c0, .vkey = vkey, .flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, .expect = {KEY_HOOK(WM_KEYUP, 0xc0, vkey, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0xc0, vkey, .todo_value = TRUE), {0}}}, {0}, }; #undef WIN_MSG #undef KBD_HOOK #undef KEY_HOOK_ #undef KEY_HOOK #undef KEY_MSG_ #undef KEY_MSG BOOL altgr = keyboard_layout_has_altgr(), skip_altgr = FALSE; LONG_PTR old_proc; HHOOK hook; HWND hwnd; /* on 32-bit with ALTGR keyboard, the CONTROL key is sent to the hooks without the * LLKHF_INJECTED flag, skip the tests to keep it simple */ if (altgr && sizeof(void *) == 4 && !is_wow64) skip_altgr = TRUE; hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); hook = SetWindowsHookExW( WH_KEYBOARD_LL, ll_hook_kbd_proc, GetModuleHandleW( NULL ), 0 ); ok_ne( NULL, hook, HHOOK, "%p" ); p_accept_message = accept_keyboard_messages_syscommand; ok_seq( empty_sequence ); lmenu_vkey_peeked[1].expect_state[vkey] = 0x80; lmenu_vkey[1].expect_state[vkey] = 0x80; lcontrol_vkey[1].expect_state[vkey] = 0x80; lmenu_lcontrol_vkey[2].expect_state[vkey] = 0x80; shift_vkey[1].expect_state[vkey] = 0x80; rshift_scan[1].expect_state[vkey] = 0x80; rshift_scan[1].todo_state[vkey] = TRUE; unicode_vkey[0].expect_state[vkey] = 0x80; /* test peeked messages */ winetest_push_context( "peek" ); check_send_input_keyboard_test( lmenu_vkey_peeked, TRUE ); check_send_input_keyboard_test( lcontrol_vkey, TRUE ); check_send_input_keyboard_test( lmenu_lcontrol_vkey, TRUE ); check_send_input_keyboard_test( shift_vkey, TRUE ); check_send_input_keyboard_test( rshift, TRUE ); check_send_input_keyboard_test( lshift_ext, TRUE ); check_send_input_keyboard_test( rshift_ext, TRUE ); check_send_input_keyboard_test( shift, TRUE ); check_send_input_keyboard_test( shift_ext, TRUE ); check_send_input_keyboard_test( rcontrol, TRUE ); check_send_input_keyboard_test( lcontrol_ext, TRUE ); check_send_input_keyboard_test( rcontrol_ext, TRUE ); check_send_input_keyboard_test( control, TRUE ); check_send_input_keyboard_test( control_ext, TRUE ); if (skip_altgr) skip( "skipping rmenu_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( rmenu_altgr, TRUE ); else check_send_input_keyboard_test( rmenu_peeked, TRUE ); check_send_input_keyboard_test( lmenu_ext_peeked, TRUE ); if (skip_altgr) skip( "skipping rmenu_ext_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( rmenu_ext_altgr, TRUE ); else check_send_input_keyboard_test( rmenu_ext_peeked, TRUE ); check_send_input_keyboard_test( menu_peeked, TRUE ); if (skip_altgr) skip( "skipping menu_ext_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( menu_ext_altgr, TRUE ); else check_send_input_keyboard_test( menu_ext_peeked, TRUE ); check_send_input_keyboard_test( lrshift_ext, TRUE ); check_send_input_keyboard_test( rshift_scan, TRUE ); check_send_input_keyboard_test( unicode, TRUE ); check_send_input_keyboard_test( lmenu_unicode_peeked, TRUE ); check_send_input_keyboard_test( unicode_vkey, TRUE ); winetest_pop_context(); wait_messages( 100, FALSE ); ok_seq( empty_sequence ); /* test received messages */ old_proc = SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_proc, LONG_PTR, "%#Ix" ); winetest_push_context( "receive" ); check_send_input_keyboard_test( lmenu_vkey, FALSE ); check_send_input_keyboard_test( lcontrol_vkey, FALSE ); check_send_input_keyboard_test( lmenu_lcontrol_vkey, FALSE ); check_send_input_keyboard_test( shift_vkey, FALSE ); check_send_input_keyboard_test( rshift, FALSE ); check_send_input_keyboard_test( lshift_ext, FALSE ); check_send_input_keyboard_test( rshift_ext, FALSE ); check_send_input_keyboard_test( shift, FALSE ); check_send_input_keyboard_test( shift_ext, FALSE ); check_send_input_keyboard_test( rcontrol, FALSE ); check_send_input_keyboard_test( lcontrol_ext, FALSE ); check_send_input_keyboard_test( rcontrol_ext, FALSE ); check_send_input_keyboard_test( control, FALSE ); check_send_input_keyboard_test( control_ext, FALSE ); if (skip_altgr) skip( "skipping rmenu_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( rmenu_altgr, FALSE ); else check_send_input_keyboard_test( rmenu, FALSE ); check_send_input_keyboard_test( lmenu_ext, FALSE ); if (skip_altgr) skip( "skipping rmenu_ext_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( rmenu_ext_altgr, FALSE ); else check_send_input_keyboard_test( rmenu_ext, FALSE ); check_send_input_keyboard_test( menu, FALSE ); if (skip_altgr) skip( "skipping menu_ext_altgr test\n" ); else if (altgr) check_send_input_keyboard_test( menu_ext_altgr, FALSE ); else check_send_input_keyboard_test( menu_ext, FALSE ); check_send_input_keyboard_test( lrshift_ext, FALSE ); check_send_input_keyboard_test( rshift_scan, FALSE ); check_send_input_keyboard_test( unicode, FALSE ); check_send_input_keyboard_test( lmenu_unicode, FALSE ); check_send_input_keyboard_test( unicode_vkey, FALSE ); winetest_pop_context(); ok_ret( 1, DestroyWindow( hwnd ) ); ok_ret( 1, UnhookWindowsHookEx( hook ) ); wait_messages( 100, FALSE ); ok_seq( empty_sequence ); p_accept_message = NULL; } static void test_keynames(void) { int i, len; char buff[256]; for (i = 0; i < 512; i++) { strcpy(buff, "----"); len = GetKeyNameTextA(i << 16, buff, sizeof(buff)); ok(len || !buff[0], "%d: Buffer is not zeroed\n", i); } } static void test_GetMouseMovePointsEx( char **argv ) { #define BUFLIM 64 #define MYERROR 0xdeadbeef int i, count, retval; MOUSEMOVEPOINT in; MOUSEMOVEPOINT out[200]; POINT point; INPUT input; /* Get a valid content for the input struct */ if(!GetCursorPos(&point)) { win_skip("GetCursorPos() failed with error %lu\n", GetLastError()); return; } memset(&in, 0, sizeof(MOUSEMOVEPOINT)); in.x = point.x; in.y = point.y; /* test first parameter * everything different than sizeof(MOUSEMOVEPOINT) * is expected to fail with ERROR_INVALID_PARAMETER */ SetLastError(MYERROR); retval = pGetMouseMovePointsEx(0, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); if (retval == ERROR_INVALID_PARAMETER) { win_skip( "GetMouseMovePointsEx broken on WinME\n" ); return; } ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)+1, &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); /* test second and third parameter */ SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_NOACCESS || GetLastError() == MYERROR, "expected error ERROR_NOACCESS, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(ERROR_NOACCESS == GetLastError(), "expected error ERROR_NOACCESS, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(ERROR_NOACCESS == GetLastError(), "expected error ERROR_NOACCESS, got %lu\n", GetLastError()); SetLastError(MYERROR); count = 0; retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, count, GMMP_USE_DISPLAY_POINTS); if (retval == -1) ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %lu\n", GetLastError()); else ok(retval == count, "expected GetMouseMovePointsEx to succeed, got %d\n", retval); /* test fourth parameter * a value higher than 64 is expected to fail with ERROR_INVALID_PARAMETER */ SetLastError(MYERROR); count = -1; retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS); ok(retval == count, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); count = 0; retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS); if (retval == -1) ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %lu\n", GetLastError()); else ok(retval == count, "expected GetMouseMovePointsEx to succeed, got %d\n", retval); SetLastError(MYERROR); count = BUFLIM; retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, count, GMMP_USE_DISPLAY_POINTS); if (retval == -1) ok(GetLastError() == ERROR_POINT_NOT_FOUND, "unexpected error %lu\n", GetLastError()); else ok((0 <= retval) && (retval <= count), "expected GetMouseMovePointsEx to succeed, got %d\n", retval); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM+1, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); /* it was not possible to force an error with the fifth parameter on win2k */ /* test combinations of wrong parameters to see which error wins */ SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, NULL, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT)-1, &in, NULL, BUFLIM, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), NULL, out, BUFLIM+1, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(MYERROR); retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, NULL, BUFLIM+1, GMMP_USE_DISPLAY_POINTS); ok(retval == -1, "expected GetMouseMovePointsEx to fail, got %d\n", retval); ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); /* more than 64 to be sure we wrap around */ for (i = 0; i < 67; i++) { in.x = i; in.y = i*2; SetCursorPos( in.x, in.y ); } SetLastError( MYERROR ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( GetLastError() == MYERROR, "expected error to stay %x, got %lx\n", MYERROR, GetLastError() ); for (i = 0; i < retval; i++) { 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 ); in.x--; in.y -= 2; } in.x = 1500; in.y = 1500; SetLastError( MYERROR ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == -1, "expected to get -1 but got %d\n", retval ); ok( GetLastError() == ERROR_POINT_NOT_FOUND, "expected error to be set to %x, got %lx\n", ERROR_POINT_NOT_FOUND, GetLastError() ); /* make sure there's no deduplication */ in.x = 6; in.y = 6; SetCursorPos( in.x, in.y ); SetCursorPos( in.x, in.y ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); 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 ); 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 ); /* make sure 2 events are distinguishable by their timestamps */ in.x = 150; in.y = 75; SetCursorPos( 30, 30 ); SetCursorPos( in.x, in.y ); SetCursorPos( 150, 150 ); Sleep( 3 ); SetCursorPos( in.x, in.y ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); 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 ); 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 ); 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 ); in.time = out[2].time; retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 62, "expected to get 62 mouse move points but got %d\n", retval ); 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 ); 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 ); /* events created through other means should also be on the list with correct extra info */ mouse_event( MOUSEEVENTF_MOVE, -13, 17, 0, 0xcafecafe ); ok( GetCursorPos( &point ), "failed to get cursor position\n" ); ok( in.x != point.x && in.y != point.y, "cursor didn't change position after mouse_event()\n" ); in.time = 0; in.x = point.x; in.y = point.y; retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( out[0].dwExtraInfo == 0xcafecafe, "wrong extra info, got 0x%Ix expected 0xcafecafe\n", out[0].dwExtraInfo ); input.type = INPUT_MOUSE; memset( &input, 0, sizeof(input) ); input.mi.dwFlags = MOUSEEVENTF_MOVE; input.mi.dwExtraInfo = 0xdeadbeef; input.mi.dx = -17; input.mi.dy = 13; SendInput( 1, (INPUT *)&input, sizeof(INPUT) ); ok( GetCursorPos( &point ), "failed to get cursor position\n" ); ok( in.x != point.x && in.y != point.y, "cursor didn't change position after mouse_event()\n" ); in.time = 0; in.x = point.x; in.y = point.y; retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( out[0].dwExtraInfo == 0xdeadbeef, "wrong extra info, got 0x%Ix expected 0xdeadbeef\n", out[0].dwExtraInfo ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_HIGH_RESOLUTION_POINTS ); todo_wine ok( retval == 64, "expected to get 64 high resolution mouse move points but got %d\n", retval ); run_in_process( argv, "test_GetMouseMovePointsEx_process" ); #undef BUFLIM #undef MYERROR } static void test_GetMouseMovePointsEx_process(void) { int retval; MOUSEMOVEPOINT in; MOUSEMOVEPOINT out[64], out2[64]; POINT point; HDESK desk0, desk1; HWINSTA winstation0, winstation1; memset( out, 0, sizeof(out) ); memset( out2, 0, sizeof(out2) ); /* move point history is shared between desktops within the same windowstation */ ok( GetCursorPos( &point ), "failed to get cursor position\n" ); in.time = 0; in.x = point.x; in.y = point.y; retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); desk0 = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS ); ok( desk0 != NULL, "OpenInputDesktop has failed with %ld\n", GetLastError() ); desk1 = CreateDesktopA( "getmousemovepointsex_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( desk1 != NULL, "CreateDesktopA failed with %ld\n", GetLastError() ); ok( SetThreadDesktop( desk1 ), "SetThreadDesktop failed!\n" ); ok( SwitchDesktop( desk1 ), "SwitchDesktop failed\n" ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out2, ARRAY_SIZE(out2), GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( memcmp( out, out2, sizeof(out2) ) == 0, "expected to get exact same history on the new desktop\n" ); in.time = 0; in.x = 38; in.y = 27; SetCursorPos( in.x, in.y ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out2, ARRAY_SIZE(out2), GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( SetThreadDesktop( desk0 ), "SetThreadDesktop failed!\n" ); ok( SwitchDesktop( desk0 ), "SwitchDesktop failed\n" ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS ); ok( retval == 64, "expected to get 64 mouse move points but got %d\n", retval ); ok( memcmp( out, out2, sizeof( out2 ) ) == 0, "expected to get exact same history on the old desktop\n" ); CloseDesktop( desk1 ); CloseDesktop( desk0 ); /* non-default windowstations are non-interactive */ winstation0 = GetProcessWindowStation(); ok( winstation0 != NULL, "GetProcessWindowStation has failed with %ld\n", GetLastError() ); desk0 = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS ); ok( desk0 != NULL, "OpenInputDesktop has failed with %ld\n", GetLastError() ); winstation1 = CreateWindowStationA( "test_winstation", 0, WINSTA_ALL_ACCESS, NULL ); if (winstation1 == NULL && GetLastError() == ERROR_ACCESS_DENIED) { win_skip("not enough privileges for CreateWindowStation\n"); CloseDesktop( desk0 ); CloseWindowStation( winstation0 ); return; } ok( winstation1 != NULL, "CreateWindowStationA has failed with %ld\n", GetLastError() ); ok( SetProcessWindowStation( winstation1 ), "SetProcessWindowStation has failed\n" ); desk1 = CreateDesktopA( "getmousemovepointsex_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( desk1 != NULL, "CreateDesktopA failed with %ld\n", GetLastError() ); ok( SetThreadDesktop( desk1 ), "SetThreadDesktop failed!\n" ); SetLastError( 0xDEADBEEF ); retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, ARRAY_SIZE(out), GMMP_USE_DISPLAY_POINTS ); todo_wine ok( retval == -1, "expected to get -1 mouse move points but got %d\n", retval ); todo_wine ok( GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED got %ld\n", GetLastError() ); ok( SetProcessWindowStation( winstation0 ), "SetProcessWindowStation has failed\n" ); ok( SetThreadDesktop( desk0 ), "SetThreadDesktop failed!\n" ); CloseDesktop( desk1 ); CloseWindowStation( winstation1 ); CloseDesktop( desk0 ); CloseWindowStation( winstation0 ); } static void test_GetRawInputDeviceList(void) { RAWINPUTDEVICELIST devices[32]; UINT ret, oret, devcount, odevcount, i; DWORD err; BOOLEAN br; SetLastError(0xdeadbeef); ret = pGetRawInputDeviceList(NULL, NULL, 0); err = GetLastError(); ok(ret == -1, "expected -1, got %d\n", ret); ok(err == ERROR_INVALID_PARAMETER, "expected 87, got %ld\n", err); SetLastError(0xdeadbeef); ret = pGetRawInputDeviceList(NULL, NULL, sizeof(devices[0])); err = GetLastError(); ok(ret == -1, "expected -1, got %d\n", ret); ok(err == ERROR_NOACCESS, "expected 998, got %ld\n", err); devcount = 0; ret = pGetRawInputDeviceList(NULL, &devcount, sizeof(devices[0])); ok(ret == 0, "expected 0, got %d\n", ret); ok(devcount > 0, "expected non-zero\n"); SetLastError(0xdeadbeef); devcount = 0; ret = pGetRawInputDeviceList(devices, &devcount, sizeof(devices[0])); err = GetLastError(); ok(ret == -1, "expected -1, got %d\n", ret); ok(err == ERROR_INSUFFICIENT_BUFFER, "expected 122, got %ld\n", err); ok(devcount > 0, "expected non-zero\n"); /* devcount contains now the correct number of devices */ ret = pGetRawInputDeviceList(devices, &devcount, sizeof(devices[0])); ok(ret > 0, "expected non-zero\n"); if (devcount) { RID_DEVICE_INFO info; UINT size; SetLastError( 0xdeadbeef ); ret = pGetRawInputDeviceInfoW( UlongToHandle( 0xdeadbeef ), RIDI_DEVICEINFO, NULL, NULL ); ok( ret == ~0U, "GetRawInputDeviceInfoW returned %#x, expected ~0.\n", ret ); ok( GetLastError() == ERROR_NOACCESS, "GetRawInputDeviceInfoW last error %#lx, expected 0xdeadbeef.\n", GetLastError() ); SetLastError( 0xdeadbeef ); size = 0xdeadbeef; ret = pGetRawInputDeviceInfoW( UlongToHandle( 0xdeadbeef ), RIDI_DEVICEINFO, NULL, &size ); ok( ret == ~0U, "GetRawInputDeviceInfoW returned %#x, expected ~0.\n", ret ); ok( size == 0xdeadbeef, "GetRawInputDeviceInfoW returned size %#x, expected 0.\n", size ); ok( GetLastError() == ERROR_INVALID_HANDLE, "GetRawInputDeviceInfoW last error %#lx, expected 0xdeadbeef.\n", GetLastError() ); SetLastError( 0xdeadbeef ); size = 0xdeadbeef; ret = pGetRawInputDeviceInfoW( devices[0].hDevice, 0xdeadbeef, NULL, &size ); ok( ret == ~0U, "GetRawInputDeviceInfoW returned %#x, expected ~0.\n", ret ); ok( size == 0xdeadbeef, "GetRawInputDeviceInfoW returned size %#x, expected 0.\n", size ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputDeviceInfoW last error %#lx, expected 0xdeadbeef.\n", GetLastError() ); SetLastError( 0xdeadbeef ); ret = pGetRawInputDeviceInfoW( devices[0].hDevice, RIDI_DEVICEINFO, &info, NULL ); ok( ret == ~0U, "GetRawInputDeviceInfoW returned %#x, expected ~0.\n", ret ); ok( GetLastError() == ERROR_NOACCESS, "GetRawInputDeviceInfoW last error %#lx, expected 0xdeadbeef.\n", GetLastError() ); SetLastError( 0xdeadbeef ); size = 0; ret = pGetRawInputDeviceInfoW( devices[0].hDevice, RIDI_DEVICEINFO, &info, &size ); ok( ret == ~0U, "GetRawInputDeviceInfoW returned %#x, expected ~0.\n", ret ); ok( size == sizeof(info), "GetRawInputDeviceInfoW returned size %#x, expected %#Ix.\n", size, sizeof(info) ); ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputDeviceInfoW last error %#lx, expected 0xdeadbeef.\n", GetLastError() ); } for(i = 0; i < devcount; ++i) { WCHAR name[128]; char nameA[128]; UINT sz, len; RID_DEVICE_INFO info; HANDLE file; char *ppd; /* get required buffer size */ name[0] = '\0'; sz = 5; ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz); ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %ld\n", err); ok(sz > 5 && sz < ARRAY_SIZE(name), "Size should have been set and not too large (got: %u)\n", sz); /* buffer size for RIDI_DEVICENAME is in CHARs, not BYTEs */ ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz); ok(ret == sz, "GetRawInputDeviceInfo gave wrong return: %ld\n", err); len = lstrlenW(name); ok(len + 1 == ret, "GetRawInputDeviceInfo returned wrong length (name: %u, ret: %u)\n", len + 1, ret); /* test A variant with same size */ ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, nameA, &sz); ok(ret == sz, "GetRawInputDeviceInfoA gave wrong return: %ld\n", err); len = strlen(nameA); ok(len + 1 == ret, "GetRawInputDeviceInfoA returned wrong length (name: %u, ret: %u)\n", len + 1, ret); /* buffer size for RIDI_DEVICEINFO is in BYTEs */ memset(&info, 0, sizeof(info)); info.cbSize = sizeof(info); sz = sizeof(info) - 1; ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz); ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %ld\n", err); ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n"); ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz); ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %ld\n", err); ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n"); ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%lx\n", info.dwType); memset(&info, 0, sizeof(info)); info.cbSize = sizeof(info); ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz); ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %ld\n", err); ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n"); ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%lx\n", info.dwType); /* setupapi returns an NT device path, but CreateFile() < Vista can't * understand that; so use the \\?\ prefix instead */ name[1] = '\\'; file = CreateFileW(name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %lu\n", wine_dbgstr_w(name), GetLastError()); sz = 0; ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_PREPARSEDDATA, NULL, &sz); ok(ret == 0, "GetRawInputDeviceInfo gave wrong return: %u\n", ret); ok((info.dwType == RIM_TYPEHID && sz != 0) || (info.dwType != RIM_TYPEHID && sz == 0), "Got wrong PPD size for type 0x%lx: %u\n", info.dwType, sz); ppd = malloc(sz); ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_PREPARSEDDATA, ppd, &sz); ok(ret == sz, "GetRawInputDeviceInfo gave wrong return: %u, should be %u\n", ret, sz); if (file != INVALID_HANDLE_VALUE && ret == sz) { PHIDP_PREPARSED_DATA preparsed; if (info.dwType == RIM_TYPEHID) { br = HidD_GetPreparsedData(file, &preparsed); ok(br == TRUE, "HidD_GetPreparsedData failed\n"); if (br) ok(!memcmp(preparsed, ppd, sz), "Expected to get same preparsed data\n"); } else { /* succeeds on hardware, fails in some VMs */ br = HidD_GetPreparsedData(file, &preparsed); ok(br == TRUE || broken(br == FALSE), "HidD_GetPreparsedData failed\n"); } if (br) HidD_FreePreparsedData(preparsed); } free(ppd); CloseHandle(file); } /* check if variable changes from larger to smaller value */ devcount = odevcount = ARRAY_SIZE(devices); oret = ret = pGetRawInputDeviceList(devices, &odevcount, sizeof(devices[0])); ok(ret > 0, "expected non-zero\n"); ok(devcount == odevcount, "expected %d, got %d\n", devcount, odevcount); devcount = odevcount; odevcount = ARRAY_SIZE(devices); ret = pGetRawInputDeviceList(NULL, &odevcount, sizeof(devices[0])); ok(ret == 0, "expected 0, got %d\n", ret); ok(odevcount == oret, "expected %d, got %d\n", oret, odevcount); } static void test_GetRawInputData(void) { UINT size; UINT ret; /* Null raw input handle */ SetLastError(0xdeadbeef); ret = GetRawInputData(NULL, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); ok(ret == ~0U, "Expect ret %u, got %u\n", ~0U, ret); ok(GetLastError() == ERROR_INVALID_HANDLE, "GetRawInputData returned %08lx\n", GetLastError()); } static void test_RegisterRawInputDevices(void) { HWND hwnd; RAWINPUTDEVICE raw_devices[2]; UINT count, raw_devices_count; BOOL res; memset(raw_devices, 0, sizeof(raw_devices)); raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x05; raw_devices[1].usUsagePage = 0x01; raw_devices[1].usUsage = 0x04; hwnd = CreateWindowExA(WS_EX_TOPMOST, "static", "dinput", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL); ok(hwnd != NULL, "CreateWindowExA failed\n"); res = RegisterRawInputDevices(NULL, 0, 0); ok(res == FALSE, "RegisterRawInputDevices succeeded\n"); SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), 0); ok(res == FALSE, "RegisterRawInputDevices succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(res == TRUE, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); count = GetRegisteredRawInputDevices(NULL, NULL, 0); ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); SetLastError(0xdeadbeef); raw_devices_count = 0; count = GetRegisteredRawInputDevices(NULL, &raw_devices_count, 0); ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count); ok(raw_devices_count == 0, "Unexpected registered devices count: %u\n", raw_devices_count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); SetLastError(0xdeadbeef); raw_devices_count = 0; count = GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE)); ok(count == 0, "GetRegisteredRawInputDevices returned %u\n", count); ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count); ok(GetLastError() == 0xdeadbeef, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); SetLastError(0xdeadbeef); raw_devices_count = 0; count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); if (broken(count == 0) /* depends on windows versions */) win_skip("Ignoring GetRegisteredRawInputDevices success\n"); else { ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count); ok(raw_devices_count == 0, "Unexpected registered devices count: %u\n", raw_devices_count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); } SetLastError(0xdeadbeef); raw_devices_count = 1; count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); ok(count == ~0U, "GetRegisteredRawInputDevices returned %u\n", count); ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); SetLastError(0xdeadbeef); memset(raw_devices, 0, sizeof(raw_devices)); raw_devices_count = ARRAY_SIZE(raw_devices); count = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); ok(count == 2, "GetRegisteredRawInputDevices returned %u\n", count); ok(raw_devices_count == 2, "Unexpected registered devices count: %u\n", raw_devices_count); ok(GetLastError() == 0xdeadbeef, "GetRegisteredRawInputDevices unexpected error %08lx\n", GetLastError()); ok(raw_devices[0].usUsagePage == 0x01, "Unexpected usage page: %x\n", raw_devices[0].usUsagePage); ok(raw_devices[0].usUsage == 0x04, "Unexpected usage: %x\n", raw_devices[0].usUsage); ok(raw_devices[1].usUsagePage == 0x01, "Unexpected usage page: %x\n", raw_devices[1].usUsagePage); ok(raw_devices[1].usUsage == 0x05, "Unexpected usage: %x\n", raw_devices[1].usUsage); /* RIDEV_REMOVE requires hwndTarget == NULL */ raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = hwnd; raw_devices[1].dwFlags = RIDEV_REMOVE; raw_devices[1].hwndTarget = hwnd; SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(res == FALSE, "RegisterRawInputDevices succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08lx\n", GetLastError()); raw_devices[0].hwndTarget = 0; raw_devices[1].hwndTarget = 0; SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(res == TRUE, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); /* RIDEV_INPUTSINK requires hwndTarget != NULL */ raw_devices[0].dwFlags = RIDEV_INPUTSINK; raw_devices[0].hwndTarget = 0; raw_devices[1].dwFlags = RIDEV_INPUTSINK; raw_devices[1].hwndTarget = 0; SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(res == FALSE, "RegisterRawInputDevices failed\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08lx\n", GetLastError()); raw_devices[0].hwndTarget = hwnd; raw_devices[1].hwndTarget = hwnd; SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(res == TRUE, "RegisterRawInputDevices succeeded\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); DestroyWindow(hwnd); } static int rawinputbuffer_wndproc_count; typedef struct { DWORD dwType; DWORD dwSize; ULONG hDevice; ULONG wParam; } RAWINPUTHEADER32; #ifdef _WIN64 typedef RAWINPUTHEADER RAWINPUTHEADER64; typedef RAWINPUT RAWINPUT64; #else typedef struct { DWORD dwType; DWORD dwSize; ULONGLONG hDevice; ULONGLONG wParam; } RAWINPUTHEADER64; typedef struct { RAWINPUTHEADER64 header; union { RAWMOUSE mouse; RAWKEYBOARD keyboard; RAWHID hid; } data; } RAWINPUT64; #endif static int rawinput_buffer_mouse_x(void *buffer, size_t index) { if (is_wow64) return ((RAWINPUT64 *)buffer)[index].data.mouse.lLastX; return ((RAWINPUT *)buffer)[index].data.mouse.lLastX; } static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { UINT i, size, count, rawinput_size, iteration = rawinputbuffer_wndproc_count++; RAWINPUT ri; char buffer[16 * sizeof(RAWINPUT64)]; MSG message; if (is_wow64) rawinput_size = sizeof(RAWINPUT64); else rawinput_size = sizeof(RAWINPUT); if (msg == WM_INPUT) { SetLastError(0xdeadbeef); count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError()); size = sizeof(buffer); count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); ok(count == 0, "GetRawInputBuffer returned %u\n", count); ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size); size = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 3, "GetRawInputBuffer returned %u\n", count); ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size); for (i = 0; i < 3; ++i) { if (is_wow64) { const RAWINPUT64 *data = &((RAWINPUT64 *)buffer)[i]; ok(data->header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", data->header.dwType); ok(data->header.dwSize == sizeof(*data), "Unexpected rawinput size: %lu\n", data->header.dwSize); todo_wine_if (wparam) ok(data->header.wParam == wparam, "Unexpected wparam: %#I64x\n", data->header.wParam); } else { const RAWINPUT *data = &((RAWINPUT *)buffer)[i]; ok(data->header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", data->header.dwType); ok(data->header.dwSize == sizeof(*data), "Unexpected rawinput size: %lu\n", data->header.dwSize); todo_wine_if (wparam) ok(data->header.wParam == wparam, "Unexpected wparam: %#Ix\n", data->header.wParam); } } ok(rawinput_buffer_mouse_x(buffer, 0) == 2, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0)); ok(rawinput_buffer_mouse_x(buffer, 1) == 3, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 1)); ok(rawinput_buffer_mouse_x(buffer, 2) == 4, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 2)); /* the first event should be removed by the next GetRawInputBuffer call * and the others should do another round through the message loop but not more */ if (iteration == 0) { mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 6, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 2, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 3, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0); /* even though rawinput_size is the minimum required size, * it needs one more byte to return success */ size = rawinput_size + 1; memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 1, "GetRawInputBuffer returned %u\n", count); ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0)); /* peek the messages now, they should still arrive in the correct order */ while (PeekMessageA(&message, 0, WM_INPUT, WM_INPUT, PM_REMOVE)) DispatchMessageA(&message); } /* reading the message data now should fail on the second iteration, the data * from the first message has been overwritten. */ size = sizeof(ri); memset(&ri, 0, sizeof(ri)); SetLastError(0xdeadbeef); count = GetRawInputData((HRAWINPUT)lparam, RID_HEADER, &ri, &size, sizeof(RAWINPUTHEADER)); if (iteration == 1) { SetLastError(0xdeadbeef); count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, 0); ok(count == ~0u, "GetRawInputData returned %d\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER) + 1); ok(count == ~0u, "GetRawInputData returned %d\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); size = 0; count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputData succeeded\n"); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputData returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); size = sizeof(ri); count = GetRawInputData((HRAWINPUT)lparam, 0, &ri, &size, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputData succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); size = sizeof(ri); count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER)); ok(count == sizeof(ri), "GetRawInputData failed\n"); ok(ri.data.mouse.lLastX == 6, "Unexpected rawinput data: %ld\n", ri.data.mouse.lLastX); ok(GetLastError() == 0xdeadbeef, "GetRawInputData returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); size = sizeof(buffer); if (sizeof(void *) == 8) { count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER32)); ok(count == ~0u, "GetRawInputData returned %d\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); } else { count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER64)); if (is_wow64) { todo_wine ok(count == sizeof(ri), "GetRawInputData returned %d\n", count); ok(ri.data.mouse.lLastX == 6, "Unexpected rawinput data: %ld\n", ri.data.mouse.lLastX); todo_wine ok(GetLastError() == 0xdeadbeef, "GetRawInputData returned %08lx\n", GetLastError()); } else { ok(count == ~0u, "GetRawInputData returned %d\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); } } } else { ok(count == ~0U, "GetRawInputData succeeded\n"); ok(GetLastError() == ERROR_INVALID_HANDLE, "GetRawInputData returned %08lx\n", GetLastError()); } return 0; } return DefWindowProcA(hwnd, msg, wparam, lparam); } static void test_GetRawInputBuffer(void) { unsigned int size, count, rawinput_size, header_size, scan_code; RAWINPUTDEVICE raw_devices[1]; char buffer[16 * sizeof(RAWINPUT64)]; HWND hwnd; BOOL ret; POINT pt; #define HEADER_FIELD(field) (is_wow64 ? ((RAWINPUT64 *)buffer)->header.field : ((RAWINPUT *)buffer)->header.field) if (is_wow64) rawinput_size = sizeof(RAWINPUT64); else rawinput_size = sizeof(RAWINPUT); SetCursorPos(300, 300); GetCursorPos(&pt); ok(pt.x == 300 && pt.y == 300, "Unexpected cursor position pos %ldx%ld\n", pt.x, pt.y); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL); SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinputbuffer_wndproc); ok(hwnd != 0, "CreateWindow failed\n"); empty_message_queue(); raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x02; raw_devices[0].dwFlags = RIDEV_INPUTSINK; raw_devices[0].hwndTarget = hwnd; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); size = sizeof(buffer); count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); ok(count == 0U, "GetRawInputBuffer returned %u\n", count); ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size); size = 0; count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 0U, "GetRawInputBuffer returned %u\n", count); ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size); SetLastError(0xdeadbeef); size = sizeof(buffer); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, 0); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); size = sizeof(buffer); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 0U, "GetRawInputBuffer returned %u\n", count); ok(size == 0U, "GetRawInputBuffer returned unexpected size: %u\n", size); mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); SetLastError(0xdeadbeef); size = 0; count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputBuffer returned %08lx\n", GetLastError()); size = 0; count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); ok(count == 0, "GetRawInputBuffer returned %u\n", count); ok(size == rawinput_size, "GetRawInputBuffer returned unexpected size: %u\n", size); SetLastError(0xdeadbeef); size = sizeof(buffer); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, 0); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); SetLastError(0xdeadbeef); size = sizeof(buffer); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER) + 1); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); /* the function returns 64-bit RAWINPUT structures on WoW64, but still * forbids sizeof(RAWINPUTHEADER) from the wrong architecture */ SetLastError(0xdeadbeef); size = sizeof(buffer); header_size = (sizeof(void *) == 8 ? sizeof(RAWINPUTHEADER32) : sizeof(RAWINPUTHEADER64)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, header_size); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); size = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 1U, "GetRawInputBuffer returned %u\n", count); ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size); ok(HEADER_FIELD(dwType) == RIM_TYPEMOUSE, "Unexpected rawinput dwType: %ld\n", HEADER_FIELD(dwType)); ok(HEADER_FIELD(wParam) == 0 || HEADER_FIELD(wParam) == 1, "Expected wparam 0 or 1, got %Iu\n", (WPARAM)HEADER_FIELD(wParam)); ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0)); /* NOTE: calling with size == rawinput_size returns an error, */ /* BUT it fills the buffer nonetheless and empties the internal buffer (!!) */ mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); SetLastError(0xdeadbeef); size = rawinput_size; memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetRawInputBuffer returned %08lx\n", GetLastError()); ok(rawinput_buffer_mouse_x(buffer, 0) == 5, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0)); size = sizeof(buffer); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 0U, "GetRawInputBuffer returned %u\n", count); rawinputbuffer_wndproc_count = 0; mouse_event(MOUSEEVENTF_MOVE, 1, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 2, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 3, 0, 0, 0); mouse_event(MOUSEEVENTF_MOVE, 4, 0, 0, 0); empty_message_queue(); ok(rawinputbuffer_wndproc_count == 2, "Spurious WM_INPUT messages\n"); raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); /* some keyboard tests to better check fields under wow64 */ raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x06; raw_devices[0].dwFlags = RIDEV_INPUTSINK; raw_devices[0].hwndTarget = hwnd; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); keybd_event('X', 0x2d, 0, 0); keybd_event('X', 0x2d, KEYEVENTF_KEYUP, 0); size = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 2U, "GetRawInputBuffer returned %u\n", count); ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size); ok(HEADER_FIELD(dwType) == RIM_TYPEKEYBOARD, "Unexpected rawinput dwType: %ld\n", HEADER_FIELD(dwType)); ok(HEADER_FIELD(wParam) == 0 || HEADER_FIELD(wParam) == 1, "Expected wparam 0 or 1, got %Iu\n", (WPARAM)HEADER_FIELD(wParam)); scan_code = is_wow64 ? ((RAWINPUT64 *)buffer)->data.keyboard.MakeCode : ((RAWINPUT *)buffer)->data.keyboard.MakeCode; ok(scan_code == 0x2d, "Unexpected rawinput keyboard scan code: %x\n", scan_code); raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "RegisterRawInputDevices failed\n"); ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08lx\n", GetLastError()); DestroyWindow(hwnd); #undef HEADER_FIELD } static BOOL rawinput_test_received_legacy; static BOOL rawinput_test_received_raw; static BOOL rawinput_test_received_rawfg; static LRESULT CALLBACK rawinput_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { UINT ret, raw_size; RAWINPUT raw; if (msg == WM_INPUT) { todo_wine_if(rawinput_test_received_raw) ok(!rawinput_test_received_raw, "Unexpected spurious WM_INPUT message.\n"); ok(wparam == RIM_INPUT || wparam == RIM_INPUTSINK, "Unexpected wparam: %Iu\n", wparam); rawinput_test_received_raw = TRUE; if (wparam == RIM_INPUT) rawinput_test_received_rawfg = TRUE; ret = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &raw_size, sizeof(RAWINPUTHEADER)); ok(ret == 0, "GetRawInputData failed\n"); ok(raw_size <= sizeof(raw), "Unexpected rawinput data size: %u\n", raw_size); raw_size = sizeof(raw); ret = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &raw, &raw_size, sizeof(RAWINPUTHEADER)); ok(ret > 0 && ret != (UINT)-1, "GetRawInputData failed\n"); ok(raw.header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", raw.header.dwType); ok(raw.header.dwSize == raw_size, "Expected size %u, got %lu\n", raw_size, raw.header.dwSize); todo_wine_if (wparam) ok(raw.header.wParam == wparam, "Expected wparam %Iu, got %Iu\n", wparam, raw.header.wParam); ok(!(raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE), "Unexpected absolute rawinput motion\n"); ok(!(raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP), "Unexpected virtual desktop rawinput motion\n"); } if (msg == WM_MOUSEMOVE) rawinput_test_received_legacy = TRUE; return DefWindowProcA(hwnd, msg, wparam, lparam); } struct rawinput_test { BOOL register_device; BOOL register_window; DWORD register_flags; BOOL expect_legacy; BOOL expect_raw; BOOL expect_rawfg; BOOL todo_legacy; BOOL todo_raw; BOOL todo_rawfg; }; struct rawinput_test rawinput_tests[] = { { FALSE, FALSE, 0, TRUE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, FALSE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_NOLEGACY, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, /* same-process foreground tests */ { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, /* cross-process foreground tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, /* multi-process rawinput tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, /* cross-desktop foreground tests */ { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, }; static void rawinput_test_process(void) { RAWINPUTDEVICE raw_devices[1]; HANDLE ready, start, done; DWORD ret; POINT pt; HWND hwnd = NULL; MSG msg; int i; ready = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_ready"); ok(ready != 0, "OpenEventA failed, error: %lu\n", GetLastError()); start = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_start"); ok(start != 0, "OpenEventA failed, error: %lu\n", GetLastError()); done = OpenEventA(EVENT_ALL_ACCESS, FALSE, "rawinput_test_process_done"); ok(done != 0, "OpenEventA failed, error: %lu\n", GetLastError()); for (i = 0; i < ARRAY_SIZE(rawinput_tests); ++i) { WaitForSingleObject(ready, INFINITE); ResetEvent(ready); switch (i) { case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 16: GetCursorPos(&pt); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); ok(hwnd != 0, "CreateWindow failed\n"); empty_message_queue(); /* FIXME: Try to workaround X11/Win32 focus inconsistencies and * make the window visible and foreground as hard as possible. */ ShowWindow(hwnd, SW_SHOW); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); SetForegroundWindow(hwnd); UpdateWindow(hwnd); empty_message_queue(); if (i == 9 || i == 10 || i == 11 || i == 12) { raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x02; raw_devices[0].dwFlags = i == 11 ? RIDEV_INPUTSINK : 0; raw_devices[0].hwndTarget = i == 11 ? hwnd : 0; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "%d: RegisterRawInputDevices failed\n", i); ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08lx\n", i, GetLastError()); } rawinput_test_received_legacy = FALSE; rawinput_test_received_raw = FALSE; rawinput_test_received_rawfg = FALSE; /* fallthrough */ case 14: case 15: if (i != 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); empty_message_queue(); break; } SetEvent(start); while (MsgWaitForMultipleObjects(1, &done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ResetEvent(done); if (i == 9 || i == 10 || i == 11 || i == 12) { raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0; flaky_wine ok(rawinput_test_received_legacy, "%d: foreground process expected WM_MOUSEMOVE message\n", i); ok(rawinput_test_received_raw, "%d: foreground process expected WM_INPUT message\n", i); ok(rawinput_test_received_rawfg, "%d: foreground process expected RIM_INPUT message\n", i); SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "%d: RegisterRawInputDevices failed\n", i); ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08lx\n", i, GetLastError()); } if (hwnd) DestroyWindow(hwnd); } WaitForSingleObject(ready, INFINITE); CloseHandle(done); CloseHandle(start); CloseHandle(ready); } struct rawinput_test_thread_params { HDESK desk; HANDLE ready; HANDLE start; HANDLE done; }; static DWORD WINAPI rawinput_test_desk_thread(void *arg) { struct rawinput_test_thread_params *params = arg; RAWINPUTDEVICE raw_devices[1]; DWORD ret; POINT pt; HWND hwnd = NULL; MSG msg; int i; ok( SetThreadDesktop( params->desk ), "SetThreadDesktop failed\n" ); for (i = 14; i < ARRAY_SIZE(rawinput_tests); ++i) { WaitForSingleObject(params->ready, INFINITE); ResetEvent(params->ready); switch (i) { case 14: case 15: case 16: GetCursorPos(&pt); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); ok(hwnd != 0, "CreateWindow failed\n"); SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); empty_message_queue(); /* FIXME: Try to workaround X11/Win32 focus inconsistencies and * make the window visible and foreground as hard as possible. */ ShowWindow(hwnd, SW_SHOW); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); SetForegroundWindow(hwnd); UpdateWindow(hwnd); empty_message_queue(); raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x02; raw_devices[0].dwFlags = rawinput_tests[i].register_flags; raw_devices[0].hwndTarget = rawinput_tests[i].register_window ? hwnd : 0; rawinput_test_received_legacy = FALSE; rawinput_test_received_raw = FALSE; rawinput_test_received_rawfg = FALSE; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "%d: RegisterRawInputDevices failed\n", i); ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08lx\n", i, GetLastError()); break; } SetEvent(params->start); while (MsgWaitForMultipleObjects(1, ¶ms->done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ResetEvent(params->done); if (hwnd) DestroyWindow(hwnd); } return 0; } static DWORD WINAPI rawinput_test_thread(void *arg) { struct rawinput_test_thread_params *params = arg; HANDLE thread; POINT pt; HWND hwnd = NULL; int i; for (i = 0; i < 14; ++i) { WaitForSingleObject(params->ready, INFINITE); ResetEvent(params->ready); switch (i) { case 4: case 5: GetCursorPos(&pt); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); ok(hwnd != 0, "CreateWindow failed\n"); empty_message_queue(); /* FIXME: Try to workaround X11/Win32 focus inconsistencies and * make the window visible and foreground as hard as possible. */ ShowWindow(hwnd, SW_SHOW); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); SetForegroundWindow(hwnd); UpdateWindow(hwnd); empty_message_queue(); mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); empty_message_queue(); break; } SetEvent(params->start); WaitForSingleObject(params->done, INFINITE); ResetEvent(params->done); if (hwnd) DestroyWindow(hwnd); } thread = CreateThread(NULL, 0, rawinput_test_desk_thread, params, 0, NULL); ok(thread != NULL, "CreateThread failed\n"); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); return 0; } static void test_rawinput(const char* argv0) { struct rawinput_test_thread_params params; PROCESS_INFORMATION process_info; RAWINPUTDEVICE raw_devices[1]; STARTUPINFOA startup_info; HANDLE thread, process_ready, process_start, process_done; DWORD ret; POINT pt, newpt; HWND hwnd; BOOL skipped; char path[MAX_PATH]; int i; params.desk = CreateDesktopA( "rawinput_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( params.desk != NULL, "CreateDesktopA failed, last error: %lu\n", GetLastError() ); params.ready = CreateEventA(NULL, FALSE, FALSE, NULL); ok(params.ready != NULL, "CreateEvent failed\n"); params.start = CreateEventA(NULL, FALSE, FALSE, NULL); ok(params.start != NULL, "CreateEvent failed\n"); params.done = CreateEventA(NULL, FALSE, FALSE, NULL); ok(params.done != NULL, "CreateEvent failed\n"); thread = CreateThread(NULL, 0, rawinput_test_thread, ¶ms, 0, NULL); ok(thread != NULL, "CreateThread failed\n"); process_ready = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_ready"); ok(process_ready != NULL, "CreateEventA failed\n"); process_start = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_start"); ok(process_start != NULL, "CreateEventA failed\n"); process_done = CreateEventA(NULL, FALSE, FALSE, "rawinput_test_process_done"); ok(process_done != NULL, "CreateEventA failed\n"); memset(&startup_info, 0, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); startup_info.dwFlags = STARTF_USESHOWWINDOW; startup_info.wShowWindow = SW_SHOWNORMAL; sprintf(path, "%s input rawinput_test", argv0); ret = CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info ); ok(ret, "CreateProcess \"%s\" failed err %lu.\n", path, GetLastError()); SetCursorPos(100, 100); empty_message_queue(); for (i = 0; i < ARRAY_SIZE(rawinput_tests); ++i) { GetCursorPos(&pt); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); ok(hwnd != 0, "CreateWindow failed\n"); if (i != 14 && i != 15 && i != 16) SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); empty_message_queue(); /* FIXME: Try to workaround X11/Win32 focus inconsistencies and * make the window visible and foreground as hard as possible. */ ShowWindow(hwnd, SW_SHOW); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); SetForegroundWindow(hwnd); UpdateWindow(hwnd); empty_message_queue(); rawinput_test_received_legacy = FALSE; rawinput_test_received_raw = FALSE; rawinput_test_received_rawfg = FALSE; raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x02; raw_devices[0].dwFlags = rawinput_tests[i].register_flags; raw_devices[0].hwndTarget = rawinput_tests[i].register_window ? hwnd : 0; if (!rawinput_tests[i].register_device) skipped = FALSE; else { SetLastError(0xdeadbeef); skipped = !RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); if (rawinput_tests[i].register_flags == RIDEV_EXINPUTSINK && skipped) win_skip("RIDEV_EXINPUTSINK not supported\n"); else ok(!skipped, "%d: RegisterRawInputDevices failed: %lu\n", i, GetLastError()); } SetEvent(params.ready); WaitForSingleObject(params.start, INFINITE); ResetEvent(params.start); /* we need the main window to be over the other thread window, as although * it is in another desktop, it will receive the messages directly otherwise */ switch (i) { case 14: case 15: DestroyWindow(hwnd); hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); ok(hwnd != 0, "CreateWindow failed\n"); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); SetForegroundWindow(hwnd); empty_message_queue(); rawinput_test_received_legacy = FALSE; rawinput_test_received_raw = FALSE; rawinput_test_received_rawfg = FALSE; break; } SetEvent(process_ready); WaitForSingleObject(process_start, INFINITE); ResetEvent(process_start); if (i <= 3 || i == 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); empty_message_queue(); SetEvent(process_done); SetEvent(params.done); flaky_wine if (!skipped) { todo_wine_if(rawinput_tests[i].todo_legacy) ok(rawinput_test_received_legacy == rawinput_tests[i].expect_legacy, "%d: %sexpected WM_MOUSEMOVE message\n", i, rawinput_tests[i].expect_legacy ? "" : "un"); todo_wine_if(rawinput_tests[i].todo_raw) ok(rawinput_test_received_raw == rawinput_tests[i].expect_raw, "%d: %sexpected WM_INPUT message\n", i, rawinput_tests[i].expect_raw ? "" : "un"); todo_wine_if(rawinput_tests[i].todo_rawfg) ok(rawinput_test_received_rawfg == rawinput_tests[i].expect_rawfg, "%d: %sexpected RIM_INPUT message\n", i, rawinput_tests[i].expect_rawfg ? "" : "un"); } GetCursorPos(&newpt); ok((newpt.x - pt.x) == 5 || (newpt.x - pt.x) == 4, "%d: Unexpected cursor movement\n", i); if (rawinput_tests[i].register_device) { raw_devices[0].dwFlags = RIDEV_REMOVE; raw_devices[0].hwndTarget = 0; SetLastError(0xdeadbeef); ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); ok(ret, "%d: RegisterRawInputDevices failed: %lu\n", i, GetLastError()); } DestroyWindow(hwnd); } SetEvent(process_ready); winetest_wait_child_process(process_info.hProcess); CloseHandle(process_info.hProcess); CloseHandle(process_info.hThread); CloseHandle(process_done); CloseHandle(process_start); CloseHandle(process_ready); WaitForSingleObject(thread, INFINITE); CloseHandle(params.done); CloseHandle(params.start); CloseHandle(params.ready); CloseHandle(thread); CloseDesktop(params.desk); } static void test_DefRawInputProc(void) { LRESULT ret; SetLastError(0xdeadbeef); ret = DefRawInputProc(NULL, 0, sizeof(RAWINPUTHEADER)); ok(!ret, "got %Id\n", ret); ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError()); ret = DefRawInputProc(LongToPtr(0xcafe), 0xbeef, sizeof(RAWINPUTHEADER)); ok(!ret, "got %Id\n", ret); ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError()); ret = DefRawInputProc(NULL, 0, sizeof(RAWINPUTHEADER) - 1); ok(ret == -1, "got %Id\n", ret); ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError()); ret = DefRawInputProc(NULL, 0, sizeof(RAWINPUTHEADER) + 1); ok(ret == -1, "got %Id\n", ret); ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError()); } static void test_key_map(void) { HKL kl = GetKeyboardLayout(0); UINT kL, kR, s, sL; int i; static const UINT numpad_collisions[][2] = { { VK_NUMPAD0, VK_INSERT }, { VK_NUMPAD1, VK_END }, { VK_NUMPAD2, VK_DOWN }, { VK_NUMPAD3, VK_NEXT }, { VK_NUMPAD4, VK_LEFT }, { VK_NUMPAD6, VK_RIGHT }, { VK_NUMPAD7, VK_HOME }, { VK_NUMPAD8, VK_UP }, { VK_NUMPAD9, VK_PRIOR }, }; s = MapVirtualKeyExA(VK_SHIFT, MAPVK_VK_TO_VSC, kl); ok(s != 0, "MapVirtualKeyEx(VK_SHIFT) should return non-zero\n"); sL = MapVirtualKeyExA(VK_LSHIFT, MAPVK_VK_TO_VSC, kl); ok(s == sL || broken(sL == 0), /* win9x */ "%x != %x\n", s, sL); kL = MapVirtualKeyExA(0x2a, MAPVK_VSC_TO_VK, kl); ok(kL == VK_SHIFT, "Scan code -> vKey = %x (not VK_SHIFT)\n", kL); kR = MapVirtualKeyExA(0x36, MAPVK_VSC_TO_VK, kl); ok(kR == VK_SHIFT, "Scan code -> vKey = %x (not VK_SHIFT)\n", kR); kL = MapVirtualKeyExA(0x2a, MAPVK_VSC_TO_VK_EX, kl); ok(kL == VK_LSHIFT || broken(kL == 0), /* win9x */ "Scan code -> vKey = %x (not VK_LSHIFT)\n", kL); kR = MapVirtualKeyExA(0x36, MAPVK_VSC_TO_VK_EX, kl); ok(kR == VK_RSHIFT || broken(kR == 0), /* win9x */ "Scan code -> vKey = %x (not VK_RSHIFT)\n", kR); /* test that MAPVK_VSC_TO_VK prefers the non-numpad vkey if there's ambiguity */ for (i = 0; i < ARRAY_SIZE(numpad_collisions); i++) { UINT numpad_scan = MapVirtualKeyExA(numpad_collisions[i][0], MAPVK_VK_TO_VSC, kl); UINT other_scan = MapVirtualKeyExA(numpad_collisions[i][1], MAPVK_VK_TO_VSC, kl); /* do they really collide for this layout? */ if (numpad_scan && other_scan == numpad_scan) { UINT vkey = MapVirtualKeyExA(numpad_scan, MAPVK_VSC_TO_VK, kl); ok(vkey != numpad_collisions[i][0], "Got numpad vKey %x for scan code %x when there was another choice\n", vkey, numpad_scan); } } /* test the scan code prefixes of the right variant of a keys */ s = MapVirtualKeyExA(VK_RCONTROL, MAPVK_VK_TO_VSC, kl); ok(s >> 8 == 0x00, "Scan code prefixes should not be returned when not using MAPVK_VK_TO_VSC_EX %#1x\n", s >> 8); s = MapVirtualKeyExA(VK_RCONTROL, MAPVK_VK_TO_VSC_EX, kl); 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); s = MapVirtualKeyExA(VK_RMENU, MAPVK_VK_TO_VSC_EX, kl); 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); s = MapVirtualKeyExA(VK_RSHIFT, MAPVK_VK_TO_VSC_EX, kl); ok(s >> 8 == 0x00 || broken(s == 0), "The scan code shouldn't have a prefix, got %#1x\n", s >> 8); } #define shift 1 #define ctrl 2 #define menu 4 static const struct tounicode_tests { UINT vk; DWORD modifiers; WCHAR chr; /* if vk is 0, lookup vk using this char */ int expect_ret; WCHAR expect_buf[4]; } utests[] = { { 0, 0, 'a', 1, {'a',0}}, { 0, shift, 'a', 1, {'A',0}}, { 0, menu, 'a', 1, {'a',0}}, { 0, shift|menu, 'a', 1, {'A',0}}, { 0, shift|ctrl|menu, 'a', 0, {}}, { 0, ctrl, 'a', 1, {1, 0}}, { 0, shift|ctrl, 'a', 1, {1, 0}}, { VK_TAB, ctrl, 0, 0, {}}, { VK_TAB, shift|ctrl, 0, 0, {}}, { VK_RETURN, ctrl, 0, 1, {'\n', 0}}, { VK_RETURN, shift|ctrl, 0, 0, {}}, { 0, ctrl, '4', 0, {}}, { 0, shift|ctrl, '4', 0, {}}, { 0, ctrl, '!', 0, {}}, { 0, ctrl, '\"', 0, {}}, { 0, ctrl, '#', 0, {}}, { 0, ctrl, '$', 0, {}}, { 0, ctrl, '%', 0, {}}, { 0, ctrl, '\'', 0, {}}, { 0, ctrl, '(', 0, {}}, { 0, ctrl, ')', 0, {}}, { 0, ctrl, '*', 0, {}}, { 0, ctrl, '+', 0, {}}, { 0, ctrl, ',', 0, {}}, { 0, ctrl, '-', 0, {}}, { 0, ctrl, '.', 0, {}}, { 0, ctrl, '/', 0, {}}, { 0, ctrl, ':', 0, {}}, { 0, ctrl, ';', 0, {}}, { 0, ctrl, '<', 0, {}}, { 0, ctrl, '=', 0, {}}, { 0, ctrl, '>', 0, {}}, { 0, ctrl, '?', 0, {}}, { 0, ctrl, '@', 1, {0}}, { 0, ctrl, '[', 1, {0x1b}}, { 0, ctrl, '\\', 1, {0x1c}}, { 0, ctrl, ']', 1, {0x1d}}, { 0, ctrl, '^', 1, {0x1e}}, { 0, ctrl, '_', 1, {0x1f}}, { 0, ctrl, '`', 0, {}}, { VK_SPACE, 0, 0, 1, {' ',0}}, { VK_SPACE, shift, 0, 1, {' ',0}}, { VK_SPACE, ctrl, 0, 1, {' ',0}}, }; static void test_ToUnicode(void) { WCHAR wStr[4]; BYTE state[256]; const BYTE SC_RETURN = 0x1c, SC_TAB = 0x0f, SC_A = 0x1e; const BYTE HIGHEST_BIT = 0x80; int i, ret; BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409); for(i=0; i<256; i++) state[i]=0; wStr[1] = 0xAA; SetLastError(0xdeadbeef); ret = ToUnicode(VK_RETURN, SC_RETURN, state, wStr, 4, 0); if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { win_skip("ToUnicode is not implemented\n"); return; } ok(ret == 1, "ToUnicode for Return key didn't return 1 (was %i)\n", ret); if(ret == 1) { ok(wStr[0]=='\r', "ToUnicode for CTRL + Return was %i (expected 13)\n", wStr[0]); ok(wStr[1]==0 || broken(wStr[1]!=0) /* nt4 */, "ToUnicode didn't null-terminate the buffer when there was room.\n"); } for (i = 0; i < ARRAY_SIZE(utests); i++) { UINT vk = utests[i].vk, mod = utests[i].modifiers, scan; if(!vk) { short vk_ret; if (!us_kbd) continue; vk_ret = VkKeyScanW(utests[i].chr); if (vk_ret == -1) continue; vk = vk_ret & 0xff; if (vk_ret & 0x100) mod |= shift; if (vk_ret & 0x200) mod |= ctrl; } scan = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC); state[VK_SHIFT] = state[VK_LSHIFT] = (mod & shift) ? HIGHEST_BIT : 0; state[VK_CONTROL] = state[VK_LCONTROL] = (mod & ctrl) ? HIGHEST_BIT : 0; state[VK_MENU] = state[VK_LMENU] = (mod & menu) ? HIGHEST_BIT : 0; ret = ToUnicode(vk, scan, state, wStr, 4, 0); ok(ret == utests[i].expect_ret, "%d: got %d expected %d\n", i, ret, utests[i].expect_ret); if (ret) ok(!lstrcmpW(wStr, utests[i].expect_buf), "%d: got %s expected %s\n", i, wine_dbgstr_w(wStr), wine_dbgstr_w(utests[i].expect_buf)); } state[VK_SHIFT] = state[VK_LSHIFT] = 0; state[VK_CONTROL] = state[VK_LCONTROL] = 0; ret = ToUnicode(VK_TAB, SC_TAB, NULL, wStr, 4, 0); ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret); ret = ToUnicode(VK_RETURN, SC_RETURN, NULL, wStr, 4, 0); ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret); ret = ToUnicode('A', SC_A, NULL, wStr, 4, 0); ok(ret == 0, "ToUnicode with NULL keystate didn't return 0 (was %i)\n", ret); ret = ToUnicodeEx(VK_TAB, SC_TAB, NULL, wStr, 4, 0, GetKeyboardLayout(0)); ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret); ret = ToUnicodeEx(VK_RETURN, SC_RETURN, NULL, wStr, 4, 0, GetKeyboardLayout(0)); ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret); ret = ToUnicodeEx('A', SC_A, NULL, wStr, 4, 0, GetKeyboardLayout(0)); ok(ret == 0, "ToUnicodeEx with NULL keystate didn't return 0 (was %i)\n", ret); } static void test_ToAscii(void) { WCHAR wstr[16]; char str[16]; WORD character; BYTE state[256]; const BYTE SC_RETURN = 0x1c, SC_A = 0x1e; const BYTE HIGHEST_BIT = 0x80; int ret, len; DWORD gle; memset(state, 0, sizeof(state)); character = 0; ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0); ok(ret == 1, "ToAscii for Return key didn't return 1 (was %i)\n", ret); ok(character == '\r', "ToAscii for Return was %i (expected 13)\n", character); wstr[0] = 0; ret = ToUnicode('A', SC_A, state, wstr, ARRAY_SIZE(wstr), 0); ok(ret == 1, "ToUnicode(A) returned %i, expected 1\n", ret); str[0] = '\0'; len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, sizeof(str), NULL, NULL); gle = GetLastError(); ok(len > 0, "Could not convert %s (gle %lu)\n", wine_dbgstr_w(wstr), gle); character = 0; ret = ToAscii('A', SC_A, state, &character, 0); if (len == 1 || len == 2) ok(ret == 1, "ToAscii(A) returned %i, expected 1\n", ret); else /* ToAscii() can only return 2 chars => it fails if len > 2 */ ok(ret == 0, "ToAscii(A) returned %i, expected 0\n", ret); switch ((ULONG_PTR)GetKeyboardLayout(0)) { case 0x04090409: /* Qwerty */ case 0x04070407: /* Qwertz */ case 0x040c040c: /* Azerty */ ok(lstrcmpW(wstr, L"a") == 0, "ToUnicode(A) returned %s\n", wine_dbgstr_w(wstr)); ok(character == 'a', "ToAscii(A) returned char=%i, expected %i\n", character, 'a'); break; /* Other keyboard layouts may or may not return 'a' */ } state[VK_CONTROL] |= HIGHEST_BIT; state[VK_LCONTROL] |= HIGHEST_BIT; character = 0; ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0); ok(ret == 1, "ToAscii for CTRL + Return key didn't return 1 (was %i)\n", ret); ok(character == '\n', "ToAscii for CTRL + Return was %i (expected 10)\n", character); character = 0; ret = ToAscii('A', SC_A, state, &character, 0); ok(ret == 1, "ToAscii for CTRL + character 'A' didn't return 1 (was %i)\n", ret); ok(character == 1, "ToAscii for CTRL + character 'A' was %i (expected 1)\n", character); state[VK_SHIFT] |= HIGHEST_BIT; state[VK_LSHIFT] |= HIGHEST_BIT; ret = ToAscii(VK_RETURN, SC_RETURN, state, &character, 0); ok(ret == 0, "ToAscii for CTRL + Shift + Return key didn't return 0 (was %i)\n", ret); ret = ToAscii(VK_RETURN, SC_RETURN, NULL, &character, 0); ok(ret == 0, "ToAscii for NULL keystate didn't return 0 (was %i)\n", ret); ret = ToAscii('A', SC_A, NULL, &character, 0); ok(ret == 0, "ToAscii for NULL keystate didn't return 0 (was %i)\n", ret); ret = ToAsciiEx(VK_RETURN, SC_RETURN, NULL, &character, 0, GetKeyboardLayout(0)); ok(ret == 0, "ToAsciiEx for NULL keystate didn't return 0 (was %i)\n", ret); ret = ToAsciiEx('A', SC_A, NULL, &character, 0, GetKeyboardLayout(0)); ok(ret == 0, "ToAsciiEx for NULL keystate didn't return 0 (was %i)\n", ret); } static void test_get_async_key_state(void) { /* input value sanity checks */ ok(0 == GetAsyncKeyState(1000000), "GetAsyncKeyState did not return 0\n"); ok(0 == GetAsyncKeyState(-1000000), "GetAsyncKeyState did not return 0\n"); } static void test_keyboard_layout_name(void) { WCHAR klid[KL_NAMELENGTH], tmpklid[KL_NAMELENGTH], layout_path[MAX_PATH], value[5]; HKL layout, tmplayout, *layouts, *layouts_preload; DWORD status, value_size, klid_size, type, id; int i, j, len; HKEY hkey; BOOL ret; if (0) /* crashes on native system */ ret = GetKeyboardLayoutNameA(NULL); SetLastError(0xdeadbeef); ret = GetKeyboardLayoutNameW(NULL); ok(!ret, "got %d\n", ret); ok(GetLastError() == ERROR_NOACCESS, "got %ld\n", GetLastError()); layout = GetKeyboardLayout(0); if (broken( layout == (HKL)0x040a0c0a )) { /* The testbot w7u_es has a broken layout configuration, its active layout is 040a:0c0a, * with 0c0a its user locale and 040a its layout langid. Its layout preload list contains * a 00000c0a layout but the system layouts OTOH only contains the standard 0000040a layout. * Later, after activating 0409:0409 layout, GetKeyboardLayoutNameW returns 00000c0a. */ win_skip( "broken keyboard layout, skipping tests\n" ); return; } len = GetKeyboardLayoutList(0, NULL); ok(len > 0, "GetKeyboardLayoutList returned %d\n", len); layouts = malloc(len * sizeof(HKL)); ok(layouts != NULL, "Could not allocate memory\n"); len = GetKeyboardLayoutList(len, layouts); ok(len > 0, "GetKeyboardLayoutList returned %d\n", len); layouts_preload = calloc(1, sizeof(HKL)); ok(layouts_preload != NULL, "Could not allocate memory\n"); if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkey )) { i = 0; type = REG_SZ; klid_size = sizeof(klid); value_size = ARRAY_SIZE(value); while (!RegEnumValueW( hkey, i++, value, &value_size, NULL, &type, (void *)&klid, &klid_size )) { klid_size = sizeof(klid); value_size = ARRAY_SIZE(value); layouts_preload = realloc( layouts_preload, (i + 1) * sizeof(*layouts_preload) ); ok(layouts_preload != NULL, "Could not allocate memory\n"); layouts_preload[i - 1] = UlongToHandle( wcstoul( klid, NULL, 16 ) ); layouts_preload[i] = 0; id = (DWORD_PTR)layouts_preload[i - 1]; if (id & 0x80000000) todo_wine_if(HIWORD(id) == 0xe001) ok((id & 0xf0000000) == 0xd0000000, "Unexpected preloaded keyboard layout high bits %#lx\n", id); else ok(!(id & 0xf0000000), "Unexpected preloaded keyboard layout high bits %#lx\n", id); } RegCloseKey( hkey ); } if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes", &hkey )) { for (i = 0; layouts_preload[i]; ++i) { type = REG_SZ; klid_size = sizeof(klid); swprintf( tmpklid, KL_NAMELENGTH, L"%08x", HandleToUlong( layouts_preload[i] ) ); if (!RegQueryValueExW( hkey, tmpklid, NULL, &type, (void *)&klid, &klid_size )) { layouts_preload[i] = UlongToHandle( wcstoul( klid, NULL, 16 ) ); /* Substitute should contain the keyboard layout id, not the HKL high word */ id = (DWORD_PTR)layouts_preload[i]; ok(!(id & 0xf0000000), "Unexpected substitute keyboard layout high bits %#lx\n", id); } else { id = (DWORD_PTR)layouts_preload[i]; ok(!(id & 0xf0000000), "Unexpected preloaded keyboard layout high bits %#lx\n", id); } } RegCloseKey( hkey ); } for (i = len - 1; i >= 0; --i) { id = (DWORD_PTR)layouts[i]; winetest_push_context( "%08lx", id ); ActivateKeyboardLayout(layouts[i], 0); tmplayout = GetKeyboardLayout(0); todo_wine_if(tmplayout != layouts[i]) ok( tmplayout == layouts[i], "Failed to activate keyboard layout\n"); if (tmplayout != layouts[i]) { winetest_pop_context(); continue; } GetKeyboardLayoutNameW(klid); for (j = 0; layouts_preload[j]; ++j) { swprintf( tmpklid, KL_NAMELENGTH, L"%08X", layouts_preload[j] ); if (!wcscmp( tmpklid, klid )) break; } ok(j < len, "Could not find keyboard layout %s in preload list\n", wine_dbgstr_w(klid)); if (id & 0x80000000) { todo_wine ok((id >> 28) == 0xf, "hkl high bits %#lx, expected 0xf\n", id >> 28); value_size = sizeof(value); wcscpy(layout_path, L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\"); wcscat(layout_path, klid); status = RegGetValueW(HKEY_LOCAL_MACHINE, layout_path, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size); todo_wine ok(!status, "RegGetValueW returned %lx\n", status); ok(value_size == 5 * sizeof(WCHAR), "RegGetValueW returned size %ld\n", value_size); swprintf(tmpklid, KL_NAMELENGTH, L"%04X", (id >> 16) & 0x0fff); todo_wine ok(!wcsicmp(value, tmpklid), "RegGetValueW returned %s, expected %s\n", debugstr_w(value), debugstr_w(tmpklid)); } else { swprintf(tmpklid, KL_NAMELENGTH, L"%08X", id >> 16); ok(!wcsicmp(klid, tmpklid), "GetKeyboardLayoutNameW returned %s, expected %s\n", debugstr_w(klid), debugstr_w(tmpklid)); } ActivateKeyboardLayout(layout, 0); tmplayout = LoadKeyboardLayoutW(klid, KLF_ACTIVATE); /* The low word of HKL is the selected user lang and may be different as LoadKeyboardLayoutW also selects the default lang from the layout */ ok(((UINT_PTR)tmplayout & ~0xffff) == ((UINT_PTR)layouts[i] & ~0xffff), "LoadKeyboardLayoutW returned %p, expected %p\n", tmplayout, layouts[i]); /* The layout name only depends on the keyboard layout: the high word of HKL. */ GetKeyboardLayoutNameW(tmpklid); ok(!wcsicmp(klid, tmpklid), "GetKeyboardLayoutNameW returned %s, expected %s\n", debugstr_w(tmpklid), debugstr_w(klid)); winetest_pop_context(); } ActivateKeyboardLayout(layout, 0); free(layouts); free(layouts_preload); } static HKL expect_hkl; static HKL change_hkl; static int got_setfocus; static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { ok( msg != WM_INPUTLANGCHANGEREQUEST, "got WM_INPUTLANGCHANGEREQUEST\n" ); if (msg == WM_SETFOCUS) got_setfocus = 1; if (msg == WM_INPUTLANGCHANGE) { HKL layout = GetKeyboardLayout( 0 ); CHARSETINFO info; WCHAR klidW[64]; UINT codepage; LCID lcid; /* get keyboard layout lcid from its name, as the HKL might be aliased */ GetKeyboardLayoutNameW( klidW ); swscanf( klidW, L"%x", &lcid ); lcid = LOWORD(lcid); if (!(HIWORD(layout) & 0x8000)) ok( lcid == HIWORD(layout), "got lcid %#lx\n", lcid ); GetLocaleInfoA( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (char *)&codepage, sizeof(codepage) ); TranslateCharsetInfo( UlongToPtr( codepage ), &info, TCI_SRCCODEPAGE ); ok( !got_setfocus, "got WM_SETFOCUS before WM_INPUTLANGCHANGE\n" ); ok( layout == expect_hkl, "got layout %p\n", layout ); ok( wparam == info.ciCharset || broken(wparam == 0 && (HIWORD(layout) & 0x8000)), "got wparam %#Ix\n", wparam ); ok( lparam == (LPARAM)expect_hkl, "got lparam %#Ix\n", lparam ); change_hkl = (HKL)lparam; } return DefWindowProcW( hwnd, msg, wparam, lparam ); } static DWORD CALLBACK test_ActivateKeyboardLayout_thread_proc( void *arg ) { ActivateKeyboardLayout( arg, 0 ); return 0; } static void test_ActivateKeyboardLayout( char **argv ) { HKL layout, tmp_layout, *layouts; HWND hwnd1, hwnd2; HANDLE thread; UINT i, count; DWORD ret; layout = GetKeyboardLayout( 0 ); if (broken( layout == (HKL)0x040a0c0a )) { /* The testbot w7u_es has a broken layout configuration, see test_keyboard_layout_name above. */ win_skip( "broken keyboard layout, skipping tests\n" ); return; } count = GetKeyboardLayoutList( 0, NULL ); ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); layouts = malloc( count * sizeof(HKL) ); ok( layouts != NULL, "Could not allocate memory\n" ); count = GetKeyboardLayoutList( count, layouts ); ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); hwnd1 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL ); ok( !!hwnd1, "CreateWindow failed, error %lu\n", GetLastError() ); empty_message_queue(); SetWindowLongPtrA( hwnd1, GWLP_WNDPROC, (LONG_PTR)test_ActivateKeyboardLayout_window_proc ); for (i = 0; i < count; ++i) { BOOL broken_focus_activate = FALSE; HKL other_layout = layouts[i]; winetest_push_context( "%08x / %08x", (UINT)(UINT_PTR)layout, (UINT)(UINT_PTR)other_layout ); /* test WM_INPUTLANGCHANGE message */ change_hkl = 0; expect_hkl = other_layout; got_setfocus = 0; ActivateKeyboardLayout( other_layout, 0 ); if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); else todo_wine ok( change_hkl == other_layout, "got change_hkl %p\n", change_hkl ); change_hkl = expect_hkl = 0; tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); /* changing the layout from another thread doesn't send the message */ thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); ret = WaitForSingleObject( thread, 1000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( thread ); /* and has no immediate effect */ empty_message_queue(); tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); /* but the change only takes effect after focus changes */ hwnd2 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL ); ok( !!hwnd2, "CreateWindow failed, error %lu\n", GetLastError() ); tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == layout || broken(layout != other_layout && tmp_layout == other_layout) /* w7u */, "got tmp_layout %p\n", tmp_layout ); if (broken(layout != other_layout && tmp_layout == other_layout)) { win_skip( "Broken layout activation on focus change, skipping some tests\n" ); broken_focus_activate = TRUE; } empty_message_queue(); /* only the focused window receives the WM_INPUTLANGCHANGE message */ ActivateKeyboardLayout( other_layout, 0 ); ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); ret = WaitForSingleObject( thread, 1000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( thread ); tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); /* changing focus is enough for the layout change to take effect */ change_hkl = 0; expect_hkl = layout; got_setfocus = 0; SetFocus( hwnd1 ); if (broken_focus_activate) { ok( got_setfocus == 1, "got got_setfocus %d\n", got_setfocus ); ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); got_setfocus = 0; ActivateKeyboardLayout( layout, 0 ); } if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); else todo_wine ok( change_hkl == layout, "got change_hkl %p\n", change_hkl ); change_hkl = expect_hkl = 0; tmp_layout = GetKeyboardLayout( 0 ); todo_wine_if(layout != other_layout) ok( tmp_layout == layout, "got tmp_layout %p\n", tmp_layout ); DestroyWindow( hwnd2 ); empty_message_queue(); winetest_pop_context(); } DestroyWindow( hwnd1 ); free( layouts ); } static void test_key_names(void) { char buffer[40]; WCHAR bufferW[40]; int ret, prev; LONG lparam = 0x1d << 16; memset( buffer, 0xcc, sizeof(buffer) ); ret = GetKeyNameTextA( lparam, buffer, sizeof(buffer) ); ok( ret > 0, "wrong len %u for '%s'\n", ret, buffer ); ok( ret == strlen(buffer), "wrong len %u for '%s'\n", ret, buffer ); memset( buffer, 0xcc, sizeof(buffer) ); prev = ret; ret = GetKeyNameTextA( lparam, buffer, prev ); ok( ret == prev - 1, "wrong len %u for '%s'\n", ret, buffer ); ok( ret == strlen(buffer), "wrong len %u for '%s'\n", ret, buffer ); memset( buffer, 0xcc, sizeof(buffer) ); ret = GetKeyNameTextA( lparam, buffer, 0 ); ok( ret == 0, "wrong len %u for '%s'\n", ret, buffer ); ok( buffer[0] == 0, "wrong string '%s'\n", buffer ); memset( bufferW, 0xcc, sizeof(bufferW) ); ret = GetKeyNameTextW( lparam, bufferW, ARRAY_SIZE(bufferW)); ok( ret > 0, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) ); ok( ret == lstrlenW(bufferW), "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) ); memset( bufferW, 0xcc, sizeof(bufferW) ); prev = ret; ret = GetKeyNameTextW( lparam, bufferW, prev ); ok( ret == prev - 1, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) ); ok( ret == lstrlenW(bufferW), "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) ); memset( bufferW, 0xcc, sizeof(bufferW) ); ret = GetKeyNameTextW( lparam, bufferW, 0 ); ok( ret == 0, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) ); ok( bufferW[0] == 0xcccc, "wrong string %s\n", wine_dbgstr_w(bufferW) ); } static void simulate_click(BOOL left, int x, int y) { INPUT input[2]; UINT events_no; SetCursorPos(x, y); memset(input, 0, sizeof(input)); input[0].type = INPUT_MOUSE; input[0].mi.dx = x; input[0].mi.dy = y; input[0].mi.dwFlags = left ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN; input[1].type = INPUT_MOUSE; input[1].mi.dx = x; input[1].mi.dy = y; input[1].mi.dwFlags = left ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP; events_no = SendInput(2, input, sizeof(input[0])); ok(events_no == 2, "SendInput returned %d\n", events_no); } static BOOL wait_for_event(HANDLE event, int timeout) { DWORD end_time = GetTickCount() + timeout; MSG msg; do { if(MsgWaitForMultipleObjects(1, &event, FALSE, timeout, QS_ALLINPUT) == WAIT_OBJECT_0) return TRUE; while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); timeout = end_time - GetTickCount(); }while(timeout > 0); return FALSE; } static LRESULT CALLBACK httransparent_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { append_message( hwnd, msg, wparam, lparam ); if (msg == WM_NCHITTEST) return HTTRANSPARENT; return DefWindowProcW( hwnd, msg, wparam, lparam ); } struct create_transparent_window_params { HANDLE start_event; HANDLE end_event; HWND hwnd; }; static DWORD WINAPI create_transparent_window_thread(void *arg) { struct create_transparent_window_params *params = arg; ULONG_PTR old_proc; params->hwnd = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, params->hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); old_proc = SetWindowLongPtrW( params->hwnd, GWLP_WNDPROC, (LONG_PTR)httransparent_wndproc ); ok_ne( 0, old_proc, LONG_PTR, "%#Ix" ); ok_ret( 1, SetEvent( params->start_event ) ); wait_for_event( params->end_event, 5000 ); ok_ret( 1, DestroyWindow( params->hwnd ) ); wait_messages( 100, FALSE ); return 0; } static LRESULT CALLBACK mouse_move_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { static DWORD last_x = 50, expect_x = 60; if (msg == WM_MOUSEMOVE) { POINT pt = {LOWORD(lparam), HIWORD(lparam)}; MapWindowPoints(hwnd, NULL, &pt, 1); flaky if (pt.x != last_x) ok( pt.x == expect_x, "got unexpected WM_MOUSEMOVE x %ld, expected %ld\n", pt.x, expect_x ); expect_x = pt.x == 50 ? 60 : 50; last_x = pt.x; } return DefWindowProcW(hwnd, msg, wparam, lparam); } static LRESULT CALLBACK mouse_layered_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { append_message( hwnd, msg, wparam, lparam ); if (msg == WM_PAINT) { HBRUSH brush = CreateSolidBrush( RGB(50, 100, 255) ); PAINTSTRUCT paint; RECT client_rect; HDC hdc = BeginPaint( hwnd, &paint ); GetClientRect( hwnd, &client_rect ); FillRect( hdc, &client_rect, brush ); EndPaint( hwnd, &paint ); DeleteObject( brush ); return 0; } return DefWindowProcW( hwnd, msg, wparam, lparam ); } struct send_input_mouse_test { BOOL set_cursor_pos; POINT cursor_pos; UINT data; UINT flags; ULONG_PTR extra; struct user_call expect[8]; BYTE expect_state[256]; BOOL todo_state[256]; }; static LRESULT CALLBACK ll_hook_ms_proc( int code, WPARAM wparam, LPARAM lparam ) { MSLLHOOKSTRUCT *hook_info = (MSLLHOOKSTRUCT *)lparam; if (code == HC_ACTION) append_ll_hook_ms( wparam, hook_info ); return CallNextHookEx( 0, code, wparam, lparam ); } static LRESULT CALLBACK ll_hook_setpos_proc( int code, WPARAM wparam, LPARAM lparam ) { if (code == HC_ACTION) { MSLLHOOKSTRUCT *hook_info = (MSLLHOOKSTRUCT *)lparam; POINT pos, hook_pos = {40, 40}; ok( abs( hook_info->pt.x - 51 ) <= 1, "got x %ld\n", hook_info->pt.x ); ok( abs( hook_info->pt.y - 49 ) <= 1, "got y %ld\n", hook_info->pt.y ); /* spuriously moves by 0 or 1 pixels on Windows */ ok_ret( 1, GetCursorPos( &pos ) ); ok( abs( pos.x - 51 ) <= 1, "got x %ld\n", pos.x ); ok( abs( pos.y - 49 ) <= 1, "got y %ld\n", pos.y ); ok_ret( 1, SetCursorPos( 60, 60 ) ); hook_info->pt = hook_pos; } return CallNextHookEx( 0, code, wparam, lparam ); } static LRESULT CALLBACK ll_hook_getpos_proc( int code, WPARAM wparam, LPARAM lparam ) { if (code == HC_ACTION) { MSLLHOOKSTRUCT *hook_info = (MSLLHOOKSTRUCT *)lparam; POINT pos, expect_pos = {60, 60}, hook_pos = {40, 40}; ok_point( hook_pos, hook_info->pt ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); } return CallNextHookEx( 0, code, wparam, lparam ); } static BOOL accept_mouse_messages_nomove( UINT msg ) { return is_mouse_message( msg ) && msg != WM_MOUSEMOVE; } static void test_SendInput_mouse_messages(void) { #define WIN_MSG(m, h, w, l, ...) {.func = MSG_TEST_WIN, .message = {.msg = m, .hwnd = h, .wparam = w, .lparam = l}, ## __VA_ARGS__} #define MS_HOOK(m, x, y, ...) {.func = LL_HOOK_MOUSE, .ll_hook_ms = {.msg = m, .point = {x, y}, .flags = 1}, ## __VA_ARGS__} struct user_call mouse_move[] = { MS_HOOK(WM_MOUSEMOVE, 40, 40), {0}, }; struct user_call button_down_hwnd[] = { MS_HOOK(WM_LBUTTONDOWN, 50, 50), WIN_MSG(WM_LBUTTONDOWN, (HWND)-1/*hwnd*/, 0x1, MAKELONG(50, 50)), {0}, }; struct user_call button_down_hwnd_todo[] = { MS_HOOK(WM_LBUTTONDOWN, 50, 50), WIN_MSG(WM_LBUTTONDOWN, (HWND)-1/*hwnd*/, 0x1, MAKELONG(50, 50), .todo = TRUE), {0/* placeholder for Wine spurious messages */}, {0}, }; struct user_call button_up_hwnd[] = { MS_HOOK(WM_LBUTTONUP, 50, 50), WIN_MSG(WM_LBUTTONUP, (HWND)-1/*hwnd*/, 0, MAKELONG(50, 50)), {0}, }; struct user_call button_up_hwnd_todo[] = { MS_HOOK(WM_LBUTTONUP, 50, 50), WIN_MSG(WM_LBUTTONUP, (HWND)-1/*hwnd*/, 0, MAKELONG(50, 50), .todo = TRUE), {0/* placeholder for Wine spurious messages */}, {0}, }; struct user_call button_down_no_message[] = { MS_HOOK(WM_LBUTTONDOWN, 50, 50), {.todo = TRUE /* spurious message on Wine */}, {0}, }; struct user_call button_up_no_message[] = { MS_HOOK(WM_LBUTTONUP, 50, 50), {.todo = TRUE /* spurious message on Wine */}, {0}, }; #undef WIN_MSG #undef MS_HOOK static const POINT expect_60x50 = {60, 50}, expect_50x50 = {50, 50}; static const struct layered_test { UINT color; UINT alpha; UINT flags; BOOL expect_click; } layered_tests[] = { {.flags = LWA_ALPHA, .expect_click = FALSE}, {.alpha = 1, .flags = LWA_ALPHA, .expect_click = TRUE}, {.color = RGB(0, 255, 0), .flags = LWA_COLORKEY, .expect_click = TRUE}, {.color = RGB(50, 100, 255), .flags = LWA_COLORKEY, .expect_click = TRUE}, {.color = RGB(0, 255, 0), .flags = LWA_COLORKEY | LWA_ALPHA, .expect_click = FALSE}, {.color = RGB(0, 255, 0), .alpha = 1, .flags = LWA_COLORKEY | LWA_ALPHA, .expect_click = TRUE}, {.color = RGB(50, 100, 255), .alpha = 1, .flags = LWA_COLORKEY | LWA_ALPHA, .expect_click = TRUE}, }; struct create_transparent_window_params params = {0}; ULONG_PTR old_proc, old_other_proc; RECT clip_rect = {55, 55, 55, 55}; UINT dblclk_time, i; HWND hwnd, other; DWORD thread_id; HANDLE thread; HHOOK hook, hook_setpos, hook_getpos; HRGN hregion; RECT region; POINT pt; params.start_event = CreateEventA( NULL, FALSE, FALSE, NULL ); ok( !!params.start_event, "CreateEvent failed\n" ); params.end_event = CreateEventA( NULL, FALSE, FALSE, NULL ); ok( !!params.end_event, "CreateEvent failed\n" ); dblclk_time = GetDoubleClickTime(); ok_eq( 500, dblclk_time, UINT, "%u" ); ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, SetDoubleClickTime( 1 ) ); hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); trace( "hwnd %p\n", hwnd ); hook = SetWindowsHookExW( WH_MOUSE_LL, ll_hook_ms_proc, GetModuleHandleW( NULL ), 0 ); ok_ne( NULL, hook, HHOOK, "%p" ); /* test hooks only, WM_MOUSEMOVE messages are very brittle */ p_accept_message = is_mouse_message; ok_seq( empty_sequence ); /* SetCursorPos or ClipCursor don't call mouse ll hooks */ ok_ret( 1, SetCursorPos( 60, 60 ) ); wait_messages( 5, FALSE ); ok_ret( 1, ClipCursor( &clip_rect ) ); wait_messages( 5, FALSE ); ok_ret( 1, ClipCursor( NULL ) ); wait_messages( 5, FALSE ); ok_ret( 1, SetCursorPos( 50, 50 ) ); wait_messages( 100, FALSE ); todo_wine ok_seq( empty_sequence ); hook_getpos = SetWindowsHookExW( WH_MOUSE_LL, ll_hook_getpos_proc, GetModuleHandleW( NULL ), 0 ); ok_ne( NULL, hook_getpos, HHOOK, "%p" ); hook_setpos = SetWindowsHookExW( WH_MOUSE_LL, ll_hook_setpos_proc, GetModuleHandleW( NULL ), 0 ); ok_ne( NULL, hook_setpos, HHOOK, "%p" ); mouse_event( MOUSEEVENTF_MOVE, 0, 0, 0, 0 ); /* recent Windows versions don't call the hooks with no movement */ if (current_sequence_len) { ok_seq( mouse_move ); ok_ret( 1, SetCursorPos( 50, 50 ) ); } ok_seq( empty_sequence ); /* WH_MOUSE_LL hook is called even with 1 pixel moves */ mouse_event( MOUSEEVENTF_MOVE, +1, -1, 0, 0 ); ok_seq( mouse_move ); ok_ret( 1, UnhookWindowsHookEx( hook_getpos ) ); ok_ret( 1, UnhookWindowsHookEx( hook_setpos ) ); append_message_hwnd = TRUE; p_accept_message = accept_mouse_messages_nomove; ok_seq( empty_sequence ); /* basic button messages */ old_proc = SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_proc, LONG_PTR, "%#Ix" ); ok_ret( 1, SetCursorPos( 50, 50 ) ); wait_messages( 100, FALSE ); todo_wine ok_seq( empty_sequence ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); /* click through top-level window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = other; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = other; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); /* click through child window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_CHILD, 0, 0, 100, 100, hwnd, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = other; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = other; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); /* click through HTTRANSPARENT top-level window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)httransparent_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd_todo[1].message.hwnd = hwnd; ok_seq( button_down_hwnd_todo ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd_todo[1].message.hwnd = hwnd; ok_seq( button_up_hwnd_todo ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); /* click through HTTRANSPARENT child window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_CHILD, 0, 0, 100, 100, hwnd, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)httransparent_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); /* click on HTTRANSPARENT top-level window that belongs to other thread */ thread = CreateThread( NULL, 0, create_transparent_window_thread, ¶ms, 0, NULL ); ok_ne( NULL, thread, HANDLE, "%p" ); ok_ret( 0, WaitForSingleObject( params.start_event, 5000 ) ); ok_ret( 0, SendMessageW( params.hwnd, WM_USER, 0, 0 ) ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); ok_seq( button_down_no_message ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); ok_seq( button_up_no_message ); ok_ret( 1, SetEvent( params.end_event ) ); ok_ret( 0, WaitForSingleObject( thread, 5000 ) ); ok_ret( 1, CloseHandle( thread ) ); ResetEvent( params.start_event ); ResetEvent( params.end_event ); /* click on HTTRANSPARENT top-level window that belongs to other thread, * thread input queues are attached */ thread = CreateThread( NULL, 0, create_transparent_window_thread, ¶ms, 0, &thread_id ); ok_ne( NULL, thread, HANDLE, "%p" ); ok_ret( 0, WaitForSingleObject( params.start_event, 5000 ) ); todo_wine ok_ret( 1, AttachThreadInput( thread_id, GetCurrentThreadId(), TRUE ) ); ok_ret( 0, SendMessageW( params.hwnd, WM_USER, 0, 0 ) ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); todo_wine ok_ret( 1, AttachThreadInput( thread_id, GetCurrentThreadId(), FALSE ) ); ok_ret( 1, SetEvent( params.end_event ) ); ok_ret( 0, WaitForSingleObject( thread, 5000 ) ); ok_ret( 1, CloseHandle( thread ) ); ResetEvent( params.start_event ); ResetEvent( params.end_event ); /* click on top-level window with SetCapture called for the underlying window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); SetCapture( hwnd ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); SetCapture( 0 ); /* click through child window with SetCapture called for the underlying window */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_CHILD, 0, 0, 100, 100, hwnd, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); SetCapture( hwnd ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); SetCapture( 0 ); /* click through layered window with alpha channel / color key */ for (i = 0; i < ARRAY_SIZE(layered_tests); i++) { const struct layered_test *test = layered_tests + i; BOOL ret; winetest_push_context( "layered %u", i ); other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, other, HWND, "%p" ); old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)mouse_layered_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); wait_messages( 100, FALSE ); ok_ret( ERROR, GetWindowRgnBox( other, ®ion ) ); SetWindowLongW( other, GWL_EXSTYLE, GetWindowLongW( other, GWL_EXSTYLE ) | WS_EX_LAYERED ); ret = SetLayeredWindowAttributes( other, test->color, test->alpha, test->flags ); if (broken(!ret)) /* broken on Win7 probably because of the separate desktop */ { win_skip("Skipping broken SetLayeredWindowAttributes tests\n"); DestroyWindow( other ); break; } ok_ret( 1, RedrawWindow( other, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN ) ); wait_messages( 5, FALSE ); current_sequence_len = 0; mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd_todo[1].message.hwnd = test->expect_click ? other : hwnd; button_down_hwnd_todo[1].todo = !test->expect_click; button_down_hwnd_todo[2].todo = !test->alpha && (test->flags & LWA_ALPHA); ok_seq( button_down_hwnd_todo ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd_todo[1].message.hwnd = test->expect_click ? other : hwnd; button_up_hwnd_todo[1].todo = !test->expect_click; button_up_hwnd_todo[2].todo = !test->alpha && (test->flags & LWA_ALPHA); ok_seq( button_up_hwnd_todo ); /* removing the attribute isn't enough to get mouse input again? */ SetWindowLongW( other, GWL_EXSTYLE, GetWindowLongW( other, GWL_EXSTYLE ) & ~WS_EX_LAYERED ); ok_ret( 1, RedrawWindow( other, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN ) ); wait_messages( 5, FALSE ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd_todo[1].message.hwnd = test->expect_click ? other : hwnd; button_down_hwnd_todo[1].todo = !test->expect_click; button_down_hwnd_todo[2].todo = !test->alpha && (test->flags & LWA_ALPHA); ok_seq( button_down_hwnd_todo ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd_todo[1].message.hwnd = test->expect_click ? other : hwnd; button_up_hwnd_todo[1].todo = !test->expect_click; button_up_hwnd_todo[2].todo = !test->alpha && (test->flags & LWA_ALPHA); ok_seq( button_up_hwnd_todo ); ok_ret( 1, DestroyWindow( other ) ); winetest_pop_context(); } /* click on top-level window with SetWindowRgn called */ other = CreateWindowW( L"static", NULL, WS_VISIBLE | WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); current_sequence_len = 0; old_other_proc = SetWindowLongPtrW( other, GWLP_WNDPROC, (LONG_PTR)append_message_wndproc ); ok_ne( 0, old_other_proc, LONG_PTR, "%#Ix" ); hregion = CreateRectRgn( 0, 0, 10, 10 ); ok_ne( NULL, hregion, HRGN, "%p" ); ok_ret( 1, SetWindowRgn( other, hregion, TRUE ) ); DeleteObject( hregion ); ok_ret( SIMPLEREGION, GetWindowRgnBox( other, ®ion ) ); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_down_hwnd[1].message.hwnd = hwnd; ok_seq( button_down_hwnd ); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); wait_messages( 5, FALSE ); button_up_hwnd[1].message.hwnd = hwnd; ok_seq( button_up_hwnd ); ok_ret( 1, DestroyWindow( other ) ); wait_messages( 0, FALSE ); /* warm up test case by moving cursor and window a bit first */ ok_ret( 1, SetCursorPos( 60, 50 ) ); ok_ret( 1, SetWindowPos( hwnd, NULL, 10, 0, 0, 0, SWP_NOSIZE ) ); wait_messages( 5, FALSE ); ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, SetWindowPos( hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE ) ); wait_messages( 5, FALSE ); SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)mouse_move_wndproc ); ok_ret( 1, SetCursorPos( 60, 50 ) ); ok_ret( 1, SetWindowPos( hwnd, NULL, 10, 0, 0, 0, SWP_NOSIZE ) ); wait_messages( 5, FALSE ); ok_ret( 1, GetCursorPos( &pt ) ); ok_point( expect_60x50, pt ); ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, SetWindowPos( hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE ) ); wait_messages( 5, FALSE ); ok_ret( 1, GetCursorPos( &pt ) ); ok_point( expect_50x50, pt ); ok_ret( 1, UnhookWindowsHookEx( hook ) ); wait_messages( 100, FALSE ); todo_wine ok_seq( empty_sequence ); p_accept_message = NULL; append_message_hwnd = FALSE; ok_ret( 1, DestroyWindow( hwnd ) ); CloseHandle( params.start_event ); CloseHandle( params.end_event ); ok_ret( 1, SetDoubleClickTime( dblclk_time ) ); } static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_USER+1) { HWND hwnd = (HWND)lParam; ok(GetFocus() == hwnd, "thread expected focus %p, got %p\n", hwnd, GetFocus()); ok(GetActiveWindow() == hwnd, "thread expected active %p, got %p\n", hwnd, GetActiveWindow()); } return DefWindowProcA(hwnd, message, wParam, lParam); } struct wnd_event { HWND hwnd; HANDLE wait_event; HANDLE start_event; DWORD attach_from; DWORD attach_to; BOOL setWindows; }; static DWORD WINAPI thread_proc(void *param) { MSG msg; struct wnd_event *wnd_event = param; BOOL ret; if (wnd_event->wait_event) { ok(WaitForSingleObject(wnd_event->wait_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event->wait_event); } if (wnd_event->attach_from) { ret = AttachThreadInput(wnd_event->attach_from, GetCurrentThreadId(), TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); } if (wnd_event->attach_to) { ret = AttachThreadInput(GetCurrentThreadId(), wnd_event->attach_to, TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); } wnd_event->hwnd = CreateWindowExA(0, "TestWindowClass", "window caption text", WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, 0, 0, 0, NULL); ok(wnd_event->hwnd != 0, "Failed to create overlapped window\n"); if (wnd_event->setWindows) { SetFocus(wnd_event->hwnd); SetActiveWindow(wnd_event->hwnd); } SetEvent(wnd_event->start_event); while (GetMessageA(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessageA(&msg); } return 0; } static void test_attach_input(void) { HANDLE hThread; HWND ourWnd, Wnd2; DWORD ret, tid; struct wnd_event wnd_event; WNDCLASSA cls; cls.style = 0; cls.lpfnWndProc = MsgCheckProcA; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandleA(0); cls.hIcon = 0; cls.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW); cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); cls.lpszMenuName = NULL; cls.lpszClassName = "TestWindowClass"; if(!RegisterClassA(&cls)) return; wnd_event.wait_event = NULL; wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.attach_from = 0; wnd_event.attach_to = 0; wnd_event.setWindows = FALSE; if (!wnd_event.start_event) { win_skip("skipping interthread message test under win9x\n"); return; } hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event.start_event); ourWnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, 0, 0, 0, NULL); ok(ourWnd!= 0, "failed to create ourWnd window\n"); Wnd2 = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, 0, 0, 0, NULL); ok(Wnd2!= 0, "failed to create Wnd2 window\n"); SetFocus(ourWnd); SetActiveWindow(ourWnd); ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow()); ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)ourWnd); ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow()); ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0); ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow()); ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)ourWnd); SetActiveWindow(Wnd2); SetFocus(Wnd2); ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow()); ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)Wnd2); ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow()); ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0); ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); ok(ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(hThread); wnd_event.wait_event = NULL; wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.attach_from = 0; wnd_event.attach_to = 0; wnd_event.setWindows = TRUE; hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event.start_event); SetFocus(ourWnd); SetActiveWindow(ourWnd); ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == wnd_event.hwnd, "expected active %p, got %p\n", wnd_event.hwnd, GetActiveWindow()); ok(GetFocus() == wnd_event.hwnd, "expected focus %p, got %p\n", wnd_event.hwnd, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd); ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == 0, "expected active 0, got %p\n", GetActiveWindow()); ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd); ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == wnd_event.hwnd, "expected active %p, got %p\n", wnd_event.hwnd, GetActiveWindow()); ok(GetFocus() == wnd_event.hwnd, "expected focus %p, got %p\n", wnd_event.hwnd, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)wnd_event.hwnd); SetFocus(Wnd2); SetActiveWindow(Wnd2); ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow()); ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, (LPARAM)Wnd2); ret = AttachThreadInput(GetCurrentThreadId(), tid, FALSE); ok(ret, "AttachThreadInput error %ld\n", GetLastError()); ok(GetActiveWindow() == Wnd2, "expected active %p, got %p\n", Wnd2, GetActiveWindow()); ok(GetFocus() == Wnd2, "expected focus %p, got %p\n", Wnd2, GetFocus()); SendMessageA(wnd_event.hwnd, WM_USER+1, 0, 0); ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); ok(ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(hThread); wnd_event.wait_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.attach_from = 0; wnd_event.attach_to = 0; wnd_event.setWindows = TRUE; hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); SetLastError(0xdeadbeef); ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE); ok(!ret, "AttachThreadInput succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == 0xdeadbeef) /* <= Win XP */, "expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); SetLastError(0xdeadbeef); ret = AttachThreadInput(tid, GetCurrentThreadId(), TRUE); ok(!ret, "AttachThreadInput succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == 0xdeadbeef) /* <= Win XP */, "expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); SetEvent(wnd_event.wait_event); ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event.start_event); ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); ok(ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(hThread); wnd_event.wait_event = NULL; wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.attach_from = GetCurrentThreadId(); wnd_event.attach_to = 0; wnd_event.setWindows = FALSE; SetFocus(ourWnd); SetActiveWindow(ourWnd); hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event.start_event); ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow()); ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus()); ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); ok(ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(hThread); wnd_event.wait_event = NULL; wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); wnd_event.attach_from = 0; wnd_event.attach_to = GetCurrentThreadId(); wnd_event.setWindows = FALSE; SetFocus(ourWnd); SetActiveWindow(ourWnd); hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(wnd_event.start_event); ok(GetActiveWindow() == ourWnd, "expected active %p, got %p\n", ourWnd, GetActiveWindow()); ok(GetFocus() == ourWnd, "expected focus %p, got %p\n", ourWnd, GetFocus()); ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); ok(ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); CloseHandle(hThread); DestroyWindow(ourWnd); DestroyWindow(Wnd2); } struct get_key_state_test_desc { BOOL peek_message; BOOL peek_message_main; BOOL set_keyboard_state_main; BOOL set_keyboard_state; }; struct get_key_state_test_desc get_key_state_tests[] = { /* 0: not peeking in thread, no msg queue: GetKeyState / GetKeyboardState miss key press */ {FALSE, TRUE, FALSE, FALSE}, /* 1: peeking on thread init, not in main: GetKeyState / GetKeyboardState catch key press */ /* - GetKeyboardState misses key press if called before GetKeyState */ /* - GetKeyboardState catches key press, if called after GetKeyState */ { TRUE, FALSE, FALSE, FALSE}, /* 2: peeking on thread init, and in main: GetKeyState / GetKeyboardState catch key press */ { TRUE, TRUE, FALSE, FALSE}, /* same tests but with SetKeyboardState called in main thread */ /* 3: not peeking in thread, no msg queue: GetKeyState / GetKeyboardState miss key press */ {FALSE, TRUE, TRUE, FALSE}, /* 4: peeking on thread init, not in main: GetKeyState / GetKeyboardState catch key press */ /* - GetKeyboardState misses key press if called before GetKeyState */ /* - GetKeyboardState catches key press, if called after GetKeyState */ { TRUE, FALSE, TRUE, FALSE}, /* 5: peeking on thread init, and in main: GetKeyState / GetKeyboardState catch key press */ { TRUE, TRUE, TRUE, FALSE}, /* same tests but with SetKeyboardState called in other thread */ /* 6: not peeking in thread, no msg queue: GetKeyState / GetKeyboardState miss key press */ {FALSE, TRUE, TRUE, TRUE}, /* 7: peeking on thread init, not in main: GetKeyState / GetKeyboardState catch key press */ /* - GetKeyboardState misses key press if called before GetKeyState */ /* - GetKeyboardState catches key press, if called after GetKeyState */ { TRUE, FALSE, TRUE, TRUE}, /* 8: peeking on thread init, and in main: GetKeyState / GetKeyboardState catch key press */ { TRUE, TRUE, TRUE, TRUE}, }; struct get_key_state_thread_params { HANDLE semaphores[2]; int index; }; #define check_get_keyboard_state(i, j, c, x) check_get_keyboard_state_(i, j, c, x, __LINE__) static void check_get_keyboard_state_(int i, int j, int c, int x, int line) { unsigned char keystate[256]; BOOL ret; memset(keystate, 0, sizeof(keystate)); ret = GetKeyboardState(keystate); ok_(__FILE__, line)(ret, "GetKeyboardState failed, %lu\n", GetLastError()); ok_(__FILE__, line)(!(keystate['X'] & 0x80) == !x, "%d:%d: expected that X keystate is %s\n", i, j, x ? "set" : "unset"); ok_(__FILE__, line)(!(keystate['C'] & 0x80) == !c, "%d:%d: expected that C keystate is %s\n", i, j, c ? "set" : "unset"); /* calling it twice shouldn't change */ memset(keystate, 0, sizeof(keystate)); ret = GetKeyboardState(keystate); ok_(__FILE__, line)(ret, "GetKeyboardState failed, %lu\n", GetLastError()); ok_(__FILE__, line)(!(keystate['X'] & 0x80) == !x, "%d:%d: expected that X keystate is %s\n", i, j, x ? "set" : "unset"); ok_(__FILE__, line)(!(keystate['C'] & 0x80) == !c, "%d:%d: expected that C keystate is %s\n", i, j, c ? "set" : "unset"); } #define check_get_key_state(i, j, c, x) check_get_key_state_(i, j, c, x, __LINE__) static void check_get_key_state_(int i, int j, int c, int x, int line) { SHORT state; state = GetKeyState('X'); ok_(__FILE__, line)(!(state & 0x8000) == !x, "%d:%d: expected that X highest bit is %s, got %#x\n", i, j, x ? "set" : "unset", state); ok_(__FILE__, line)(!(state & 0x007e), "%d:%d: expected that X undefined bits are unset, got %#x\n", i, j, state); state = GetKeyState('C'); ok_(__FILE__, line)(!(state & 0x8000) == !c, "%d:%d: expected that C highest bit is %s, got %#x\n", i, j, c ? "set" : "unset", state); ok_(__FILE__, line)(!(state & 0x007e), "%d:%d: expected that C undefined bits are unset, got %#x\n", i, j, state); } static DWORD WINAPI get_key_state_thread(void *arg) { struct get_key_state_thread_params *params = arg; struct get_key_state_test_desc* test; HANDLE *semaphores = params->semaphores; DWORD result; BYTE keystate[256] = {0}; BOOL has_queue; BOOL expect_x, expect_c; MSG msg; int i = params->index, j; test = get_key_state_tests + i; has_queue = test->peek_message || test->set_keyboard_state; if (test->peek_message) { while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) ok(!is_keyboard_message(msg.message), "%d: PeekMessageA got keyboard message.\n", i); } for (j = 0; j < 4; ++j) { /* initialization */ ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "%d:%d: WaitForSingleObject returned %lu\n", i, j, result); if (test->set_keyboard_state) { keystate['C'] = 0xff; SetKeyboardState(keystate); } /* key pressed */ ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "%d:%d: WaitForSingleObject returned %lu\n", i, j, result); if (test->set_keyboard_state) expect_x = TRUE; else if (!has_queue && j == 0) expect_x = FALSE; else expect_x = TRUE; if (test->set_keyboard_state) expect_c = TRUE; else expect_c = FALSE; check_get_keyboard_state(i, j, expect_c, FALSE); check_get_key_state(i, j, expect_c, expect_x); check_get_keyboard_state(i, j, expect_c, expect_x); /* key released */ ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "%d: WaitForSingleObject returned %lu\n", i, result); check_get_keyboard_state(i, j, expect_c, expect_x); check_get_key_state(i, j, expect_c, FALSE); check_get_keyboard_state(i, j, expect_c, FALSE); } return 0; } static void test_GetKeyState(void) { struct get_key_state_thread_params params; HANDLE thread; DWORD result; BYTE keystate[256] = {0}; BOOL expect_x, expect_c; HWND hwnd; MSG msg; int i, j; BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409); if (!us_kbd) { skip("skipping test with inconsistent results on non-us keyboard\n"); return; } params.semaphores[0] = CreateSemaphoreA(NULL, 0, 1, NULL); ok(params.semaphores[0] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); params.semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL); ok(params.semaphores[1] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200, NULL, NULL, NULL, NULL); ok(hwnd != NULL, "CreateWindowA failed %lu\n", GetLastError()); empty_message_queue(); for (i = 0; i < ARRAY_SIZE(get_key_state_tests); ++i) { struct get_key_state_test_desc* test = get_key_state_tests + i; params.index = i; thread = CreateThread(NULL, 0, get_key_state_thread, ¶ms, 0, NULL); ok(thread != NULL, "CreateThread failed %lu\n", GetLastError()); for (j = 0; j < 4; ++j) { /* initialization */ result = WaitForSingleObject(params.semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "%d:%d: WaitForSingleObject returned %lu\n", i, j, result); SetForegroundWindow(hwnd); SetFocus(hwnd); empty_message_queue(); ReleaseSemaphore(params.semaphores[1], 1, NULL); /* key pressed */ result = WaitForSingleObject(params.semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "%d:%d: WaitForSingleObject returned %lu\n", i, j, result); keybd_event('X', 0, 0, 0); if (test->set_keyboard_state_main) { expect_c = TRUE; keystate['C'] = 0xff; SetKeyboardState(keystate); } else expect_c = FALSE; check_get_keyboard_state(i, j, expect_c, FALSE); check_get_key_state(i, j, expect_c, FALSE); check_get_keyboard_state(i, j, expect_c, FALSE); if (test->peek_message_main) while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); if (test->peek_message_main) expect_x = TRUE; else expect_x = FALSE; check_get_keyboard_state(i, j, expect_c, expect_x); check_get_key_state(i, j, expect_c, expect_x); check_get_keyboard_state(i, j, expect_c, expect_x); ReleaseSemaphore(params.semaphores[1], 1, NULL); /* key released */ result = WaitForSingleObject(params.semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "%d:%d: WaitForSingleObject returned %lu\n", i, j, result); keybd_event('X', 0, KEYEVENTF_KEYUP, 0); if (test->set_keyboard_state_main) { expect_x = FALSE; keystate['C'] = 0x00; SetKeyboardState(keystate); } check_get_keyboard_state(i, j, FALSE, expect_x); check_get_key_state(i, j, FALSE, expect_x); check_get_keyboard_state(i, j, FALSE, expect_x); if (test->peek_message_main) while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); check_get_keyboard_state(i, j, FALSE, FALSE); check_get_key_state(i, j, FALSE, FALSE); check_get_keyboard_state(i, j, FALSE, FALSE); ReleaseSemaphore(params.semaphores[1], 1, NULL); } result = WaitForSingleObject(thread, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); CloseHandle(thread); } DestroyWindow(hwnd); CloseHandle(params.semaphores[0]); CloseHandle(params.semaphores[1]); } static void test_OemKeyScan(void) { DWORD ret, expect, vkey, scan; WCHAR oem, wchr; char oem_char; BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409); if (!us_kbd) { skip("skipping test with inconsistent results on non-us keyboard\n"); return; } for (oem = 0; oem < 0x200; oem++) { ret = OemKeyScan( oem ); oem_char = LOBYTE( oem ); /* OemKeyScan returns -1 for any character that cannot be mapped, * whereas OemToCharBuff changes unmappable characters to question * marks. The ASCII characters 0-127, including the real question mark * character, are all mappable and are the same in all OEM codepages. */ if (!OemToCharBuffW( &oem_char, &wchr, 1 ) || (wchr == '?' && oem_char < 0)) expect = -1; else { vkey = VkKeyScanW( wchr ); scan = MapVirtualKeyW( LOBYTE( vkey ), MAPVK_VK_TO_VSC ); if (!scan) expect = -1; else { vkey &= 0xff00; vkey <<= 8; expect = vkey | scan; } } ok( ret == expect, "%04x: got %08lx expected %08lx\n", oem, ret, expect ); } } static INPUT_MESSAGE_SOURCE expect_src; static LRESULT WINAPI msg_source_proc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) { INPUT_MESSAGE_SOURCE source; MSG msg; ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" ); switch (message) { case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */ (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE), "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType ); ok( source.originId == expect_src.originId || (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM), "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId ); SendMessageA( hwnd, WM_USER, 0, 0 ); PostMessageA( hwnd, WM_USER, 0, 0 ); if (PeekMessageW( &msg, hwnd, WM_USER, WM_USER, PM_REMOVE )) DispatchMessageW( &msg ); ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */ (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE), "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType ); ok( source.originId == expect_src.originId || (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM), "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId ); break; default: ok( source.deviceType == IMDT_UNAVAILABLE, "%x: wrong deviceType %x\n", message, source.deviceType ); ok( source.originId == 0, "%x: wrong originId %x\n", message, source.originId ); break; } return DefWindowProcA( hwnd, message, wp, lp ); } static void test_input_message_source(void) { WNDCLASSA cls; INPUT inputs[2]; HWND hwnd; RECT rc; MSG msg; cls.style = 0; cls.lpfnWndProc = msg_source_proc; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandleA(0); cls.hIcon = 0; cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); cls.hbrBackground = 0; cls.lpszMenuName = NULL; cls.lpszClassName = "message source class"; RegisterClassA(&cls); hwnd = CreateWindowA( cls.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, 0, 0 ); ShowWindow( hwnd, SW_SHOWNORMAL ); UpdateWindow( hwnd ); SetForegroundWindow( hwnd ); SetFocus( hwnd ); inputs[0].type = INPUT_KEYBOARD; inputs[0].ki.dwExtraInfo = 0; inputs[0].ki.time = 0; inputs[0].ki.wVk = 0; inputs[0].ki.wScan = 0x3c0; inputs[0].ki.dwFlags = KEYEVENTF_UNICODE; inputs[1] = inputs[0]; inputs[1].ki.dwFlags |= KEYEVENTF_KEYUP; expect_src.deviceType = IMDT_UNAVAILABLE; expect_src.originId = IMO_UNAVAILABLE; SendMessageA( hwnd, WM_KEYDOWN, 0, 0 ); SendMessageA( hwnd, WM_MOUSEMOVE, 0, 0 ); SendInput( 2, inputs, sizeof(INPUT) ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) { expect_src.deviceType = IMDT_KEYBOARD; expect_src.originId = IMO_INJECTED; TranslateMessage( &msg ); DispatchMessageW( &msg ); } GetWindowRect( hwnd, &rc ); simulate_click( TRUE, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 ); simulate_click( FALSE, (rc.left + rc.right) / 2 + 1, (rc.top + rc.bottom) / 2 + 1 ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) { expect_src.deviceType = IMDT_MOUSE; expect_src.originId = IMO_INJECTED; TranslateMessage( &msg ); DispatchMessageW( &msg ); } expect_src.deviceType = IMDT_UNAVAILABLE; expect_src.originId = IMO_UNAVAILABLE; SendMessageA( hwnd, WM_KEYDOWN, 0, 0 ); SendMessageA( hwnd, WM_LBUTTONDOWN, 0, 0 ); PostMessageA( hwnd, WM_KEYUP, 0, 0 ); PostMessageA( hwnd, WM_LBUTTONUP, 0, 0 ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) { TranslateMessage( &msg ); DispatchMessageW( &msg ); } expect_src.deviceType = IMDT_UNAVAILABLE; expect_src.originId = IMO_SYSTEM; SetCursorPos( (rc.left + rc.right) / 2 - 1, (rc.top + rc.bottom) / 2 - 1 ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) { TranslateMessage( &msg ); DispatchMessageW( &msg ); } DestroyWindow( hwnd ); UnregisterClassA( cls.lpszClassName, GetModuleHandleA(0) ); } static void test_UnregisterDeviceNotification(void) { BOOL ret = UnregisterDeviceNotification(NULL); ok(ret == FALSE, "Unregistering NULL Device Notification returned: %d\n", ret); } static void test_SendInput( WORD vkey, WCHAR wch ) { const struct user_call broken_sequence[] = { {.func = MSG_TEST_WIN, .message = {.msg = WM_KEYDOWN, .wparam = vkey, .lparam = MAKELONG(1, 0)}}, {.func = MSG_TEST_WIN, .message = {.msg = WM_CHAR, .wparam = wch, .lparam = MAKELONG(1, 0)}}, {.func = MSG_TEST_WIN, .message = {.msg = WM_KEYUP, .wparam = vkey, .lparam = MAKELONG(1, KF_UP | KF_REPEAT)}}, {0} }; INPUT input[16]; UINT res, i; HWND hwnd; hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 0, NULL, 0 ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 1, NULL, 0 ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 1, NULL, sizeof(*input) ) ); ok( GetLastError() == ERROR_NOACCESS || GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError returned %#lx\n", GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 0, input, sizeof(*input) ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 0, NULL, sizeof(*input) ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); memset( input, 0, sizeof(input) ); SetLastError( 0xdeadbeef ); ok_ret( 1, SendInput( 1, input, sizeof(*input) ) ); ok_ret( 0xdeadbeef, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 16, SendInput( 16, input, sizeof(*input) ) ); ok_ret( 0xdeadbeef, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 1, input, 0 ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 1, input, sizeof(*input) + 1 ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 1, input, sizeof(*input) - 1 ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_KEYBOARD; SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 16, input, offsetof( INPUT, ki ) + sizeof(KEYBDINPUT) ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); SetLastError( 0xdeadbeef ); ok_ret( 16, SendInput( 16, input, sizeof(*input) ) ); ok_ret( 0xdeadbeef, GetLastError() ); for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_HARDWARE; SetLastError( 0xdeadbeef ); ok_ret( 0, SendInput( 16, input, offsetof( INPUT, hi ) + sizeof(HARDWAREINPUT) ) ); ok_ret( ERROR_INVALID_PARAMETER, GetLastError() ); wait_messages( 100, FALSE ); ok_seq( empty_sequence ); p_accept_message = is_keyboard_message; input[0].hi.uMsg = WM_KEYDOWN; input[0].hi.wParamL = 0; input[0].hi.wParamH = 'A'; input[1].hi.uMsg = WM_KEYUP; input[1].hi.wParamL = 0; input[1].hi.wParamH = 'A' | 0xc000; SetLastError( 0xdeadbeef ); res = SendInput( 16, input, sizeof(*input) ); ok( (res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) || broken(res == 16 && GetLastError() == 0xdeadbeef) /* 32bit */, "SendInput returned %u, error %#lx\n", res, GetLastError() ); wait_messages( 100, TRUE ); ok_seq( empty_sequence ); memset( input, 0, sizeof(input) ); input[0].type = INPUT_HARDWARE; input[1].type = INPUT_KEYBOARD; input[1].ki.wVk = vkey; input[1].ki.dwFlags = 0; input[2].type = INPUT_KEYBOARD; input[2].ki.wVk = vkey; input[2].ki.dwFlags = KEYEVENTF_KEYUP; SetLastError( 0xdeadbeef ); res = SendInput( 16, input, sizeof(*input) ); ok( (res == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) || broken(res == 16 && GetLastError() == 0xdeadbeef), "SendInput returned %u, error %#lx\n", res, GetLastError() ); wait_messages( 100, TRUE ); if (broken(res == 16)) ok_seq( broken_sequence ); else ok_seq( empty_sequence ); for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type = INPUT_HARDWARE + 1; SetLastError( 0xdeadbeef ); ok_ret( 16, SendInput( 16, input, sizeof(*input) ) ); ok_ret( 0xdeadbeef, GetLastError() ); wait_messages( 100, TRUE ); ok_seq( empty_sequence ); ok_ret( 1, DestroyWindow( hwnd ) ); wait_messages( 100, FALSE ); ok_seq( empty_sequence ); p_accept_message = NULL; } #define check_pointer_info( a, b ) check_pointer_info_( __LINE__, a, b ) static void check_pointer_info_( int line, const POINTER_INFO *actual, const POINTER_INFO *expected ) { check_member( *actual, *expected, "%#lx", pointerType ); check_member( *actual, *expected, "%#x", pointerId ); check_member( *actual, *expected, "%#x", frameId ); check_member( *actual, *expected, "%#x", pointerFlags ); check_member( *actual, *expected, "%p", sourceDevice ); check_member( *actual, *expected, "%p", hwndTarget ); check_member( *actual, *expected, "%+ld", ptPixelLocation.x ); check_member( *actual, *expected, "%+ld", ptPixelLocation.y ); check_member( *actual, *expected, "%+ld", ptHimetricLocation.x ); check_member( *actual, *expected, "%+ld", ptHimetricLocation.y ); check_member( *actual, *expected, "%+ld", ptPixelLocationRaw.x ); check_member( *actual, *expected, "%+ld", ptPixelLocationRaw.y ); check_member( *actual, *expected, "%+ld", ptHimetricLocationRaw.x ); check_member( *actual, *expected, "%+ld", ptHimetricLocationRaw.y ); check_member( *actual, *expected, "%lu", dwTime ); check_member( *actual, *expected, "%u", historyCount ); check_member( *actual, *expected, "%#x", InputData ); check_member( *actual, *expected, "%#lx", dwKeyStates ); check_member( *actual, *expected, "%I64u", PerformanceCount ); check_member( *actual, *expected, "%#x", ButtonChangeType ); } static DWORD CALLBACK test_GetPointerInfo_thread( void *arg ) { POINTER_INFO pointer_info; HWND hwnd; BOOL ret; hwnd = CreateWindowW( L"test", L"test name", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, NULL, 0 ); memset( &pointer_info, 0xcd, sizeof(pointer_info) ); ret = pGetPointerInfo( 1, &pointer_info ); ok( !ret, "GetPointerInfo succeeded\n" ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); DestroyWindow( hwnd ); return 0; } static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled ) { POINTER_INFO pointer_info[4], expect_pointer; void *invalid_ptr = (void *)0xdeadbeef; UINT32 entry_count, pointer_count; POINTER_INPUT_TYPE type; WNDCLASSW cls = { .lpfnWndProc = DefWindowProcW, .hInstance = GetModuleHandleW( NULL ), .hbrBackground = GetStockObject( WHITE_BRUSH ), .lpszClassName = L"test", }; HANDLE thread; ATOM class; DWORD res; HWND hwnd; BOOL ret; if (!pGetPointerType) { todo_wine win_skip( "GetPointerType not found, skipping tests\n" ); return; } SetLastError( 0xdeadbeef ); ret = pGetPointerType( 1, NULL ); ok( !ret, "GetPointerType succeeded\n" ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); SetLastError( 0xdeadbeef ); ret = pGetPointerType( 0xdead, &type ); todo_wine ok( !ret, "GetPointerType succeeded\n" ); todo_wine ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); ret = pGetPointerType( 1, &type ); ok( ret, "GetPointerType failed, error %lu\n", GetLastError() ); ok( type == PT_MOUSE, " type %ld\n", type ); if (!pGetPointerInfo) { todo_wine win_skip( "GetPointerInfo not found, skipping tests\n" ); return; } class = RegisterClassW( &cls ); ok( class, "RegisterClassW failed: %lu\n", GetLastError() ); ret = pGetPointerInfo( 1, invalid_ptr ); ok( !ret, "GetPointerInfo succeeded\n" ); todo_wine ok( GetLastError() == ERROR_NOACCESS || broken(GetLastError() == ERROR_INVALID_PARAMETER) /* w10 32bit */, "got error %lu\n", GetLastError() ); memset( pointer_info, 0xcd, sizeof(pointer_info) ); ret = pGetPointerInfo( 1, pointer_info ); ok( !ret, "GetPointerInfo succeeded\n" ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); SetCursorPos( 500, 500 ); /* avoid generating mouse message on window creation */ hwnd = CreateWindowW( L"test", L"test name", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, NULL, 0 ); empty_message_queue(); memset( pointer_info, 0xcd, sizeof(pointer_info) ); ret = pGetPointerInfo( 1, pointer_info ); ok( !ret, "GetPointerInfo succeeded\n" ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); SetCursorPos( 200, 200 ); empty_message_queue(); mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 ); empty_message_queue(); mouse_event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 ); empty_message_queue(); mouse_event( MOUSEEVENTF_MOVE, 10, 10, 0, 0 ); empty_message_queue(); memset( pointer_info, 0xcd, sizeof(pointer_info) ); ret = pGetPointerInfo( 0xdead, pointer_info ); ok( !ret, "GetPointerInfo succeeded\n" ); ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); memset( pointer_info, 0xcd, sizeof(pointer_info) ); ret = pGetPointerInfo( 1, pointer_info ); todo_wine_if(mouse_in_pointer_enabled) ok( ret == mouse_in_pointer_enabled, "GetPointerInfo failed, error %lu\n", GetLastError() ); if (!mouse_in_pointer_enabled) { ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); return; } todo_wine ok( pointer_info[0].pointerType == PT_MOUSE, "got pointerType %lu\n", pointer_info[0].pointerType ); todo_wine ok( pointer_info[0].pointerId == 1, "got pointerId %u\n", pointer_info[0].pointerId ); ok( !!pointer_info[0].frameId, "got frameId %u\n", pointer_info[0].frameId ); todo_wine ok( pointer_info[0].pointerFlags == (0x20000 | POINTER_MESSAGE_FLAG_INRANGE | POINTER_MESSAGE_FLAG_PRIMARY), "got pointerFlags %#x\n", pointer_info[0].pointerFlags ); todo_wine ok( pointer_info[0].sourceDevice == INVALID_HANDLE_VALUE || broken(!!pointer_info[0].sourceDevice) /* < w10 & 32bit */, "got sourceDevice %p\n", pointer_info[0].sourceDevice ); todo_wine ok( pointer_info[0].hwndTarget == hwnd, "got hwndTarget %p\n", pointer_info[0].hwndTarget ); ok( !!pointer_info[0].ptPixelLocation.x, "got ptPixelLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocation ) ); ok( !!pointer_info[0].ptPixelLocation.y, "got ptPixelLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocation ) ); ok( !!pointer_info[0].ptHimetricLocation.x, "got ptHimetricLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocation ) ); ok( !!pointer_info[0].ptHimetricLocation.y, "got ptHimetricLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocation ) ); ok( !!pointer_info[0].ptPixelLocationRaw.x, "got ptPixelLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocationRaw ) ); ok( !!pointer_info[0].ptPixelLocationRaw.y, "got ptPixelLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocationRaw ) ); ok( !!pointer_info[0].ptHimetricLocationRaw.x, "got ptHimetricLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocationRaw ) ); ok( !!pointer_info[0].ptHimetricLocationRaw.y, "got ptHimetricLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocationRaw ) ); ok( !!pointer_info[0].dwTime, "got dwTime %lu\n", pointer_info[0].dwTime ); todo_wine ok( pointer_info[0].historyCount == 1, "got historyCount %u\n", pointer_info[0].historyCount ); todo_wine ok( pointer_info[0].InputData == 0, "got InputData %u\n", pointer_info[0].InputData ); todo_wine ok( pointer_info[0].dwKeyStates == 0, "got dwKeyStates %lu\n", pointer_info[0].dwKeyStates ); ok( !!pointer_info[0].PerformanceCount, "got PerformanceCount %I64u\n", pointer_info[0].PerformanceCount ); todo_wine ok( pointer_info[0].ButtonChangeType == 0, "got ButtonChangeType %u\n", pointer_info[0].ButtonChangeType ); thread = CreateThread( NULL, 0, test_GetPointerInfo_thread, NULL, 0, NULL ); res = WaitForSingleObject( thread, 5000 ); ok( !res, "WaitForSingleObject returned %#lx, error %lu\n", res, GetLastError() ); expect_pointer = pointer_info[0]; memset( pointer_info, 0xa5, sizeof(pointer_info) ); entry_count = pointer_count = 2; if (!pGetPointerFrameInfo) ret = FALSE; else ret = pGetPointerFrameInfo( 1, &pointer_count, pointer_info ); todo_wine_if(!pGetPointerFrameInfo) ok( ret, "GetPointerFrameInfo failed, error %lu\n", GetLastError() ); todo_wine_if(!pGetPointerFrameInfo) ok( pointer_count == 1, "got pointer_count %u\n", pointer_count ); todo_wine_if(!pGetPointerFrameInfo) check_pointer_info( &pointer_info[0], &expect_pointer ); memset( pointer_info, 0xa5, sizeof(pointer_info) ); entry_count = pointer_count = 2; if (!pGetPointerInfoHistory) ret = FALSE; else ret = pGetPointerInfoHistory( 1, &entry_count, pointer_info ); todo_wine_if(!pGetPointerInfoHistory) ok( ret, "GetPointerInfoHistory failed, error %lu\n", GetLastError() ); todo_wine_if(!pGetPointerInfoHistory) ok( entry_count == 1, "got entry_count %u\n", entry_count ); todo_wine_if(!pGetPointerInfoHistory) check_pointer_info( &pointer_info[0], &expect_pointer ); memset( pointer_info, 0xa5, sizeof(pointer_info) ); entry_count = pointer_count = 2; if (!pGetPointerFrameInfoHistory) ret = FALSE; else ret = pGetPointerFrameInfoHistory( 1, &entry_count, &pointer_count, pointer_info ); todo_wine_if(!pGetPointerFrameInfoHistory) ok( ret, "GetPointerFrameInfoHistory failed, error %lu\n", GetLastError() ); todo_wine_if(!pGetPointerFrameInfoHistory) ok( entry_count == 1, "got pointer_count %u\n", pointer_count ); todo_wine_if(!pGetPointerFrameInfoHistory) ok( pointer_count == 1, "got pointer_count %u\n", pointer_count ); todo_wine_if(!pGetPointerFrameInfoHistory) check_pointer_info( &pointer_info[0], &expect_pointer ); DestroyWindow( hwnd ); ret = UnregisterClassW( L"test", GetModuleHandleW( NULL ) ); ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() ); } static void test_EnableMouseInPointer( const char *arg ) { DWORD enable = strtoul( arg, 0, 10 ); BOOL ret; winetest_push_context( "enable %lu", enable ); ret = pEnableMouseInPointer( enable ); todo_wine ok( ret, "EnableMouseInPointer failed, error %lu\n", GetLastError() ); SetLastError( 0xdeadbeef ); ret = pEnableMouseInPointer( !enable ); ok( !ret, "EnableMouseInPointer succeeded\n" ); todo_wine ok( GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError() ); ret = pIsMouseInPointerEnabled(); todo_wine_if(enable) ok( ret == enable, "IsMouseInPointerEnabled returned %u, error %lu\n", ret, GetLastError() ); ret = pEnableMouseInPointer( enable ); todo_wine ok( ret, "EnableMouseInPointer failed, error %lu\n", GetLastError() ); ret = pIsMouseInPointerEnabled(); todo_wine_if(enable) ok( ret == enable, "IsMouseInPointerEnabled returned %u, error %lu\n", ret, GetLastError() ); test_GetPointerInfo( enable ); winetest_pop_context(); } static BOOL CALLBACK get_virtual_screen_proc( HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM lp ) { RECT *virtual_rect = (RECT *)lp; UnionRect( virtual_rect, virtual_rect, rect ); return TRUE; } RECT get_virtual_screen_rect(void) { RECT rect = {0}; EnumDisplayMonitors( 0, NULL, get_virtual_screen_proc, (LPARAM)&rect ); return rect; } static void test_ClipCursor_dirty( const char *arg ) { RECT rect, expect_rect = {1, 2, 3, 4}; /* check leaked clip rect from another desktop or process */ ok_ret( 1, GetClipCursor( &rect ) ); todo_wine_if( !strcmp( arg, "desktop" ) ) ok_rect( expect_rect, rect ); /* intentionally leaking clipping rect */ } static DWORD CALLBACK test_ClipCursor_thread( void *arg ) { RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); HWND hwnd; clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; /* creating a window doesn't reset clipping rect */ hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* setting a window foreground does, even from the same process */ ok_ret( 1, SetForegroundWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( virtual_rect, rect ); /* destroying the window doesn't reset the clipping rect */ InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, DestroyWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* intentionally leaking clipping rect */ return 0; } static void test_ClipCursor_process(void) { RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); HWND hwnd, tmp_hwnd; HANDLE thread; clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* creating an invisible window doesn't reset clip cursor */ hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); ok_ret( 1, DestroyWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* setting a window foreground, even invisible, resets it */ hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); ok_ret( 1, SetForegroundWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( virtual_rect, rect ); ok_ret( 1, ClipCursor( &clip_rect ) ); /* creating and setting another window foreground doesn't reset it */ tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL ); ok( !!tmp_hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); ok_ret( 1, SetForegroundWindow( tmp_hwnd ) ); ok_ret( 1, DestroyWindow( tmp_hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* but changing foreground to another thread in the same process reset it */ thread = CreateThread( NULL, 0, test_ClipCursor_thread, NULL, 0, NULL ); ok( !!thread, "CreateThread failed, error %lu\n", GetLastError() ); msg_wait_for_events( 1, &thread, 5000 ); /* thread exit and foreground window destruction doesn't reset the clipping rect */ InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, DestroyWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* intentionally leaking clipping rect */ } static void test_ClipCursor_desktop( char **argv ) { RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( virtual_rect, rect ); /* ClipCursor clips rectangle to the virtual screen rect */ clip_rect = virtual_rect; InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( virtual_rect, rect ); clip_rect = virtual_rect; InflateRect( &clip_rect, -1, -1 ); ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* ClipCursor(NULL) resets to the virtual screen rect */ ok_ret( 1, ClipCursor( NULL ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( virtual_rect, rect ); clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* ClipCursor rejects invalid rectangles */ clip_rect.right -= 1; clip_rect.bottom -= 1; SetLastError( 0xdeadbeef ); ok_ret( 0, ClipCursor( &clip_rect ) ); todo_wine ok_ret( ERROR_ACCESS_DENIED, GetLastError() ); /* which doesn't reset the previous clip rect */ clip_rect.right += 1; clip_rect.bottom += 1; ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* running a process causes it to leak until foreground actually changes */ run_in_process( argv, "test_ClipCursor_process" ); /* as foreground window is now transient, cursor clipping isn't reset */ InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* intentionally leaking clipping rect */ } static void test_ClipCursor( char **argv ) { RECT rect, clip_rect = {1, 2, 3, 4}, virtual_rect = get_virtual_screen_rect(); ok_ret( 1, ClipCursor( &clip_rect ) ); /* running a new process doesn't reset clipping rectangle */ run_in_process( argv, "test_ClipCursor_dirty process" ); /* running in a separate desktop, without switching desktop as well */ run_in_desktop( argv, "test_ClipCursor_dirty desktop", 0 ); ok_ret( 1, GetClipCursor( &rect ) ); ok_rect( clip_rect, rect ); /* running in a desktop and switching input resets the clipping rect */ run_in_desktop( argv, "test_ClipCursor_desktop", 1 ); ok_ret( 1, GetClipCursor( &rect ) ); todo_wine ok_rect( virtual_rect, rect ); if (!EqualRect( &rect, &virtual_rect )) ok_ret( 1, ClipCursor( NULL ) ); } static void test_SetCursorPos(void) { RECT clip_rect = {50, 50, 51, 51}; POINT pos, expect_pos = {50, 50}; ok_ret( 0, GetCursorPos( NULL ) ); todo_wine ok_ret( ERROR_NOACCESS, GetLastError() ); /* immediate cursor position updates */ ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); /* without MOUSEEVENTF_MOVE cursor doesn't move */ mouse_event( MOUSEEVENTF_LEFTUP, 123, 456, 0, 0 ); mouse_event( MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE, 123, 456, 0, 0 ); mouse_event( MOUSEEVENTF_RIGHTUP, 456, 123, 0, 0 ); mouse_event( MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE, 456, 123, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); /* need to move by at least 3 pixels to update, but not consistent */ mouse_event( MOUSEEVENTF_MOVE, -1, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); mouse_event( MOUSEEVENTF_MOVE, +1, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); /* spuriously moves by 1 or 2 pixels on Windows */ expect_pos.x -= 2; mouse_event( MOUSEEVENTF_MOVE, -4, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok( abs( expect_pos.x - pos.x ) <= 1, "got pos %ld\n", pos.x ); expect_pos.x += 2; mouse_event( MOUSEEVENTF_MOVE, +4, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok( abs( expect_pos.x - pos.x ) <= 1, "got pos %ld\n", pos.x ); /* test ClipCursor holding the cursor in place */ expect_pos.x = expect_pos.y = 50; ok_ret( 1, SetCursorPos( 49, 51 ) ); ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 49, 49 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 48, 48 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 51, 51 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 52, 52 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 49, 51 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 51, 49 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); mouse_event( MOUSEEVENTF_MOVE, -1, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); mouse_event( MOUSEEVENTF_MOVE, 0, +1, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); mouse_event( MOUSEEVENTF_MOVE, +1, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); mouse_event( MOUSEEVENTF_MOVE, 0, -1, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); /* weird behavior when ClipCursor rect is empty */ clip_rect.right = clip_rect.bottom = 50; ok_ret( 1, SetCursorPos( 50, 50 ) ); expect_pos.x = expect_pos.y = 49; ok_ret( 1, ClipCursor( &clip_rect ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = expect_pos.y = 50; ok_ret( 1, SetCursorPos( 49, 49 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); expect_pos.x = expect_pos.y = 49; ok_ret( 1, SetCursorPos( 50, 50 ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = expect_pos.y = 50; ok_ret( 1, SetCursorPos( 48, 48 ) ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); expect_pos.x = expect_pos.y = 49; ok_ret( 1, SetCursorPos( 51, 51 ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); ok_ret( 1, SetCursorPos( 52, 52 ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 50; expect_pos.y = 49; ok_ret( 1, SetCursorPos( 49, 51 ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 49; expect_pos.y = 50; ok_ret( 1, SetCursorPos( 51, 49 ) ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 50; expect_pos.y = 49; mouse_event( MOUSEEVENTF_MOVE, -10, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 49; expect_pos.y = 49; mouse_event( MOUSEEVENTF_MOVE, 0, +10, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 49; expect_pos.y = 50; mouse_event( MOUSEEVENTF_MOVE, +10, 0, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); todo_wine ok_point( expect_pos, pos ); expect_pos.x = 50; expect_pos.y = 50; mouse_event( MOUSEEVENTF_MOVE, 0, -10, 0, 0 ); ok_ret( 1, GetCursorPos( &pos ) ); ok_point( expect_pos, pos ); ok_ret( 1, ClipCursor( NULL ) ); } static HANDLE ll_keyboard_event; static LRESULT CALLBACK ll_keyboard_event_wait(int code, WPARAM wparam, LPARAM lparam) { if (code == HC_ACTION) { ok_ret( WAIT_TIMEOUT, WaitForSingleObject( ll_keyboard_event, 100 ) ); return -123; } return CallNextHookEx( 0, code, wparam, lparam ); } static void test_keyboard_ll_hook_blocking(void) { INPUT input = {.type = INPUT_KEYBOARD, .ki = {.wVk = VK_RETURN}}; HHOOK hook; HWND hwnd; hwnd = CreateWindowW( L"static", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); wait_messages( 100, FALSE ); hook = SetWindowsHookExW( WH_KEYBOARD_LL, ll_keyboard_event_wait, GetModuleHandleW( NULL ), 0 ); ok_ne( NULL, hook, HHOOK, "%p" ); ll_keyboard_event = CreateEventW( NULL, FALSE, FALSE, NULL ); ok_ne( NULL, ll_keyboard_event, HANDLE, "%p" ); ok_ret( 1, SendInput( 1, &input, sizeof(input) ) ); input.ki.dwFlags = KEYEVENTF_KEYUP; ok_ret( 1, SendInput( 1, &input, sizeof(input) ) ); ok_ret( 1, SetEvent( ll_keyboard_event ) ); ok_ret( 1, CloseHandle( ll_keyboard_event ) ); ok_ret( 1, UnhookWindowsHookEx( hook ) ); ok_ret( 1, DestroyWindow( hwnd ) ); } /* run the tests in a separate desktop to avoid interaction with other * tests, current desktop state, or user actions. */ static void test_input_desktop( char **argv ) { HKL hkl = GetKeyboardLayout( 0 ); WCHAR wch, wch_shift; POINT pos; WORD scan; trace( "hkl %p\n", hkl ); ok_ret( 1, GetCursorPos( &pos ) ); test_SetCursorPos(); get_test_scan( 'F', &scan, &wch, &wch_shift ); test_SendInput( 'F', wch ); test_SendInput_keyboard_messages( 'F', scan, wch, wch_shift, '\x06' ); test_SendInput_mouse_messages(); test_keyboard_ll_hook_blocking(); ok_ret( 1, SetCursorPos( pos.x, pos.y ) ); } static void test_keyboard_layout(void) { const CHAR *layout_name; LANGID lang_id; HKL hkl; /* Test that the high word of the keyboard layout in CJK locale is the same as the low word, * even when IME is on */ lang_id = PRIMARYLANGID(GetUserDefaultLCID()); if (lang_id == LANG_CHINESE || lang_id == LANG_JAPANESE || lang_id == LANG_KOREAN) { hkl = GetKeyboardLayout(0); ok(HIWORD(hkl) == LOWORD(hkl), "Got unexpected hkl %p.\n", hkl); if (lang_id == LANG_CHINESE) layout_name = "00000804"; else if (lang_id == LANG_JAPANESE) layout_name = "00000411"; else if (lang_id == LANG_KOREAN) layout_name = "00000412"; hkl = LoadKeyboardLayoutA(layout_name, 0); ok(HIWORD(hkl) == LOWORD(hkl), "Got unexpected hkl %p.\n", hkl); } } START_TEST(input) { char **argv; int argc; POINT pos; init_function_pointers(); GetCursorPos( &pos ); argc = winetest_get_mainargs(&argv); if (argc >= 3 && !strcmp( argv[2], "rawinput_test" )) return rawinput_test_process(); if (argc >= 3 && !strcmp( argv[2], "test_GetMouseMovePointsEx_process" )) return test_GetMouseMovePointsEx_process(); if (argc >= 4 && !strcmp( argv[2], "test_EnableMouseInPointer" )) return test_EnableMouseInPointer( argv[3] ); if (argc >= 4 && !strcmp( argv[2], "test_ClipCursor_dirty" )) return test_ClipCursor_dirty( argv[3] ); if (argc >= 3 && !strcmp( argv[2], "test_ClipCursor_process" )) return test_ClipCursor_process(); if (argc >= 3 && !strcmp( argv[2], "test_ClipCursor_desktop" )) return test_ClipCursor_desktop( argv ); if (argc >= 3 && !strcmp( argv[2], "test_input_desktop" )) return test_input_desktop( argv ); run_in_desktop( argv, "test_input_desktop", 1 ); test_keynames(); test_key_map(); test_ToUnicode(); test_ToAscii(); test_get_async_key_state(); test_keyboard_layout(); test_keyboard_layout_name(); test_ActivateKeyboardLayout( argv ); test_key_names(); test_attach_input(); test_GetKeyState(); test_OemKeyScan(); test_GetRawInputData(); test_GetRawInputBuffer(); test_RegisterRawInputDevices(); test_rawinput(argv[0]); test_DefRawInputProc(); if(pGetMouseMovePointsEx) test_GetMouseMovePointsEx( argv ); else win_skip("GetMouseMovePointsEx is not available\n"); if(pGetRawInputDeviceList) test_GetRawInputDeviceList(); else win_skip("GetRawInputDeviceList is not available\n"); if (pGetCurrentInputMessageSource) test_input_message_source(); else win_skip("GetCurrentInputMessageSource is not available\n"); SetCursorPos( pos.x, pos.y ); if (pGetPointerType) test_GetPointerInfo( FALSE ); else win_skip( "GetPointerType is not available\n" ); test_UnregisterDeviceNotification(); if (!pEnableMouseInPointer) win_skip( "EnableMouseInPointer not found, skipping tests\n" ); else { run_in_process( argv, "test_EnableMouseInPointer 0" ); run_in_process( argv, "test_EnableMouseInPointer 1" ); } test_ClipCursor( argv ); }