From: "Vincas Miliƫnas" Subject: [PATCH 01/10] user32/tests: Added client-side raw input function tests (try 12) Message-Id: <4E11CE41.9010401@gmail.com> Date: Mon, 04 Jul 2011 17:29:21 +0300 Changelog: * Added implementation and tests for RIDEV_NOLEGACY raw input device flag. It's contained as a separate part, because it's invasive to existing WINE's functionality. * Added tests for other device flag precondition assertions and fixed detected issues. * Compiling with clang reported usage of an obsolete method of initializing a structure field (e.g. {mouse: {...). Fixed it. * Prioritized direct device registrations over whole page registrations, when searching for one. * In this release I decided to finally revert the usage of a circular buffer data structure for input queue in favor of a list. I was good suggestion and the only way to see it working was for me to implement it. The main reason why a circular buffer is not fit for the task is because it creates vacancies inside the queue, whereas a list does not have this problem. This leads to a non-desired behavior, where the middle of the buffer is filled with vacant entries and once a maximum length is reached, the oldest valid entries (although already processed) are forcibly dropped. An illustration of the queue (of max length = 8) (each snapshot is taken after adding a new entry), when input from a keyboard is followed by input from a mouse: Ka Kb Ma Mb Ka Kb V Mb Mc Ka Kb V V Mc Md Ka Kb V V V Md Me Ka Kb V V V V Me Mf Kb V V V V V Mf Mg Mg Mh The queue cannot be compacted, because the entries are referenced by ids that are sent to the application. A non-list data structure performing correctly would be using 2 arrays to construct mapping from event id to event data pointer. * Each simulation test now takes dedicated 0.5s to process messages instead of the old way of quitting after a WM_INPUT or after a 2s timeout. * Some other minor refactorings. Previous changelog - http://source.winehq.org/patches/data/75950 Also a good idea is to share the app I'm using to log raw input events - http://dl.dropbox.com/u/6901628/raw-logger.c A comment about going beyond mouse/keyboard for raw input: Other devices then mouse&keyboard produce undocumented blobs of bytes; to interpret them, functions from the hid.dll are used. Currently they are unimplemented stubs in WINE as well as not the problem I am solving. There is a recent article about this subject - Using the Raw Input API to Process Joystick Input - http://www.codeproject.com/KB/game/RawInputJoystick.aspx --- dlls/user32/tests/input.c | 994 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 994 insertions(+), 0 deletions(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index d45edf1..07afb5c 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -77,6 +77,18 @@ static struct { static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t); static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD); +static LRESULT (WINAPI *pDefRawInputProc) (PRAWINPUT *, INT, UINT); +static UINT (WINAPI *pGetRawInputBuffer) (PRAWINPUT, PUINT, UINT); +static INT (WINAPI *pGetRawInputData) (HRAWINPUT, UINT, LPVOID, PUINT, UINT); +static UINT (WINAPI *pGetRawInputDeviceList) (PRAWINPUTDEVICELIST, PUINT, UINT); +static UINT (WINAPI *pGetRawInputDeviceInfoA) (HANDLE, UINT, LPVOID, PUINT); +static UINT (WINAPI *pGetRawInputDeviceInfoW) (HANDLE, UINT, LPVOID, PUINT); +static UINT (WINAPI *pGetRegisteredRawInputDevices) (PRAWINPUTDEVICE, PUINT, UINT); +static BOOL (WINAPI *pRegisterRawInputDevices) (PRAWINPUTDEVICE, UINT, UINT); + +#define HID_USAGE_PAGE_GENERIC ((unsigned short)0x01) +#define HID_USAGE_GENERIC_MOUSE ((unsigned short)0x02) +#define HID_USAGE_GENERIC_KEYBOARD ((unsigned short)0x06) #define MAXKEYEVENTS 12 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one @@ -160,6 +172,14 @@ static void init_function_pointers(void) GET_PROC(SendInput) GET_PROC(GetMouseMovePointsEx) + GET_PROC(DefRawInputProc) + GET_PROC(GetRawInputBuffer) + GET_PROC(GetRawInputData) + GET_PROC(GetRawInputDeviceList) + GET_PROC(GetRawInputDeviceInfoA) + GET_PROC(GetRawInputDeviceInfoW) + GET_PROC(GetRegisteredRawInputDevices) + GET_PROC(RegisterRawInputDevices) #undef GET_PROC } @@ -1598,6 +1618,969 @@ static void test_keyboard_layout_name(void) ok(!strcmp(klid, "00000409"), "expected 00000409, got %s\n", klid); } +static void test_get_raw_input_device_list(void) +{ + RAWINPUTDEVICELIST *device_list; + UINT ret, count, count2; + DWORD error; + + if (!pGetRawInputDeviceList) + { + win_skip("GetRawInputDeviceList is not available\n"); + return; + } + + count = 0; + ret = pGetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)); + todo_wine ok(ret == 0 && count >= 1, "Given (NULL, &count, sizeof), " + "GetRawInputDeviceList should return the number of raw input devices\n"); + + device_list = HeapAlloc(GetProcessHeap(), 0, count * sizeof(RAWINPUTDEVICELIST)); + memset(device_list, 0xFF, count * sizeof(RAWINPUTDEVICELIST)); + + SetLastError(0xdeadbeef); + count2 = 0; + ret = pGetRawInputDeviceList(device_list, &count2, sizeof(RAWINPUTDEVICELIST)); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INSUFFICIENT_BUFFER && count2 == count, + "Given (device_list, &count2 = 0, sizeof), GetRawInputDeviceList should return an error, " + "but got a wrong one: %u\n", error); + + count2 = count; + ret = pGetRawInputDeviceList(device_list, &count2, sizeof(RAWINPUTDEVICELIST)); + ok(ret == count && count == count2, "Given (device_list, &count2 = count, sizeof), " + "GetRawInputDeviceList should return the list of raw input devices\n"); + + todo_wine ok(device_list[0].hDevice != NULL, + "First device should have a non-null handle\n"); + ok(device_list[0].dwType == RIM_TYPEMOUSE || device_list[0].dwType == RIM_TYPEKEYBOARD, + "First device should be a mouse or a keyboard"); + + HeapFree(GetProcessHeap(), 0, device_list); +} + +static void test_get_raw_input_data(void) +{ + UINT ret, count; + DWORD error; + + if (!pGetRawInputData) + { + win_skip("GetRawInputData is not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pGetRawInputData(NULL, 0, NULL, NULL, 0); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, 0, NULL, NULL, 0), " + "GetRawInputData should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRawInputData(NULL, 0, NULL, NULL, sizeof(RAWINPUTHEADER)); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_HANDLE, + "Given (NULL, 0, NULL, NULL, sizeof), " + "GetRawInputData should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRawInputData(NULL, 0, NULL, &count, 1); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, 0, NULL, &count, 1), " + "GetRawInputData should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRawInputData(NULL, 0, NULL, &count, sizeof(RAWINPUTHEADER)); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_HANDLE, + "Given (NULL, 0, NULL, &count, sizeof), " + "GetRawInputData should return an error, but got a wrong one: %u\n", error); +} + +static void test_get_raw_input_buffer(void) +{ + UINT ret, size; + DWORD error; + + if (!pGetRawInputBuffer) + { + win_skip("GetRawInputBuffer is not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pGetRawInputBuffer(NULL, NULL, 0); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, NULL, 0), " + "GetRawInputBuffer should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER)); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, NULL, sizeof), " + "GetRawInputBuffer should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRawInputBuffer(NULL, &size, 1); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, &size, 1), " + "GetRawInputBuffer should return an error, but got a wrong one: %u\n", error); + + size = (UINT)-1; + ret = pGetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); + todo_wine ok(ret == 0 && size == 0, "Given (NULL, &size, sizeof), " + "GetRawInputBuffer should that no data is buffered\n"); +} + +static void test_get_raw_input_device_info_a(void) +{ + UINT ret, size = 0, count; + RAWINPUTDEVICELIST *devices; + char buffer[1024]; + RID_DEVICE_INFO info; + DWORD error; + + if (!pGetRawInputDeviceInfoA || !pGetRawInputDeviceList) + { + win_skip("GetRawInputDeviceInfoA and pGetRawInputDeviceList are not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pGetRawInputDeviceInfoA(NULL, 0, NULL, NULL); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_NOACCESS, + "Given (NULL, 0, NULL, NULL), " + "GetRawInputDeviceInfoA should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoA(NULL, 0, NULL, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_HANDLE, + "Given (NULL, 0, NULL, &size), " + "GetRawInputDeviceInfoA should return an error, but got a wrong one: %u\n", error); + + ok(pGetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) == 0, + "Failed to get raw input device count\n"); + todo_wine ok(count > 0, "Should have at least one raw input device available\n"); + ok((devices = HeapAlloc(GetProcessHeap(), 0, count * sizeof(RAWINPUTDEVICELIST))) != NULL, + "Failed to allocate memory for devices\n"); + ok(pGetRawInputDeviceList(devices, &count, sizeof(RAWINPUTDEVICELIST)) == count, + "Failed to retrieve raw input device list"); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoA(devices[0].hDevice, 0, NULL, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (hDevice, 0, NULL, &size), " + "GetRawInputDeviceInfoA should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoA(devices[0].hDevice, RIDI_DEVICENAME, buffer, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INSUFFICIENT_BUFFER, + "Given (hDevice, RIDI_DEVICENAME, buffer, &size = 0), " + "GetRawInputDeviceInfoA should return an error, but got a wrong one: %u\n", error); + + size = 0; + ret = pGetRawInputDeviceInfoA(devices[0].hDevice, RIDI_DEVICENAME, NULL, &size); + todo_wine ok(ret == 0 && size > 0, "Given (hDevice, RIDI_DEVICENAME, NULL, &size), " + "GetRawInputDeviceInfoA should return the required size to retrieve " + "the device info\n"); + + buffer[0] = 0; + ret = pGetRawInputDeviceInfoA(devices[0].hDevice, RIDI_DEVICENAME, buffer, &size); + todo_wine ok(ret != (UINT)-1 && size > 0 && strlen(buffer) > 5, + "Given (hDevice, RIDI_DEVICENAME, buffer, &size), " + "GetRawInputDeviceInfoA should retrieve the device name\n"); + + size = sizeof(RID_DEVICE_INFO); + info.cbSize = sizeof(RID_DEVICE_INFO); + ret = pGetRawInputDeviceInfoA(devices[0].hDevice, RIDI_DEVICEINFO, &info, &size); + todo_wine ok(ret != (UINT)-1 && ret > 0 && size > 0, + "Given (hDevice, RIDI_DEVICEINFO, &info, &size), " + "GetRawInputDeviceInfoA should retrieve the device info\n"); + + HeapFree(GetProcessHeap(), 0, devices); +} + +static void test_get_raw_input_device_info_w(void) +{ + UINT ret, size = 0, count; + RAWINPUTDEVICELIST *devices; + WCHAR buffer[1024]; + RID_DEVICE_INFO info; + DWORD error; + + if (!pGetRawInputDeviceInfoW || !pGetRawInputDeviceList) + { + win_skip("GetRawInputDeviceInfoW and pGetRawInputDeviceList are not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pGetRawInputDeviceInfoW(NULL, 0, NULL, NULL); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_NOACCESS, "Given (NULL, 0, NULL, NULL), " + "GetRawInputDeviceInfoW should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoW(NULL, 0, NULL, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_HANDLE, + "Given (NULL, 0, NULL, &size), " + "GetRawInputDeviceInfoW should return an error, but got a wrong one: %u\n", error); + + ok(pGetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) == 0, + "Failed to get raw input device count\n"); + todo_wine ok(count > 0, "Should have at least one raw input device available\n"); + ok((devices = HeapAlloc(GetProcessHeap(), 0, count * sizeof(RAWINPUTDEVICELIST))) != NULL, + "Failed to allocate memory for devices\n"); + ok(pGetRawInputDeviceList(devices, &count, sizeof(RAWINPUTDEVICELIST)) == count, + "Failed to retrieve raw input device list"); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoW(devices[0].hDevice, 0, NULL, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (hDevice, 0, NULL, &size), " + "GetRawInputDeviceInfoW should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + size = 0; + ret = pGetRawInputDeviceInfoW(devices[0].hDevice, RIDI_DEVICENAME, buffer, &size); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INSUFFICIENT_BUFFER, + "Given (hDevice, RIDI_DEVICENAME, buffer, &size = 0), " + "GetRawInputDeviceInfoW should return an error, but got a wrong one: %u\n", error); + + size = 0; + ret = pGetRawInputDeviceInfoW(devices[0].hDevice, RIDI_DEVICENAME, NULL, &size); + todo_wine ok(ret == 0 && size > 0, "Given (hDevice, RIDI_DEVICENAME, NULL, &size), " + "GetRawInputDeviceInfoW should return the required size to retrieve the device info\n"); + + buffer[0] = 0; + ret = pGetRawInputDeviceInfoW(devices[0].hDevice, RIDI_DEVICENAME, buffer, &size); + todo_wine ok(ret != (UINT)-1 && size > 0 && lstrlenW(buffer) > 5, + "Given (hDevice, RIDI_DEVICENAME, buffer, &size), " + "GetRawInputDeviceInfoW should retrieve the device name\n"); + + size = sizeof(RID_DEVICE_INFO); + info.cbSize = sizeof(RID_DEVICE_INFO); + ret = pGetRawInputDeviceInfoW(devices[0].hDevice, RIDI_DEVICEINFO, &info, &size); + todo_wine ok(ret != (UINT)-1 && ret > 0 && size > 0, + "Given (hDevice, RIDI_DEVICEINFO, &info, &size), " + "GetRawInputDeviceInfoW should retrieve the device info\n"); + + HeapFree(GetProcessHeap(), 0, devices); +} + +static void test_register_raw_input_devices(void) +{ + RAWINPUTDEVICE device; + BOOL ret; + DWORD error; + + if (!pRegisterRawInputDevices) + { + win_skip("RegisterRawInputDevices is not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(NULL, 0, 0); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, "Given (NULL, 0, 0), " + "RegisterRawInputDevices should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(NULL, 0, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(ret == FALSE && error == ERROR_INVALID_PARAMETER, + "Given (NULL, 0, sizeof), " + "RegisterRawInputDevices should return an error, but got a wrong one: %u\n", error); + + device.usUsagePage = 0; + device.usUsage = 0; + device.dwFlags = 0; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 0, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{0,0,0,0}, 0, sizeof), " + "RegisterRawInputDevices should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(ret == FALSE && error == ERROR_INVALID_PARAMETER, + "Given (&device{0,0,0,0}, 1, sizeof), " + "RegisterRawInputDevices should return an error, but got a wrong one: %u\n", error); + + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = HID_USAGE_GENERIC_MOUSE; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,0,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully subscribe to " + "a mouse raw input device\n"); + + device.dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from " + "a mouse raw input device\n"); + + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should return success even if already unsubscribed\n"); + + device.usUsagePage = 0xFF; + device.usUsage = 0xFF; + device.dwFlags = 0; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{0xFF,0xFF,0,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully subscribe to a non-existing device\n"); + + device.dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{0xFF,0xFF,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from " + "a non-existing device\n"); + + /* Test precondition assertions of various flags */ + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_REMOVE; + device.hwndTarget = (HWND)-1; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,2,RIDEV_REMOVE,-1}, 1, sizeof), " + "RegisterRawInputDevices should fail to unsubscribe from a device, " + "when hwndTarget is non-null, but got a wrong error: %u\n", error); + + device.usUsage = 0xFF; + device.dwFlags = RIDEV_PAGEONLY; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,0,RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_PAGEONLY " + "requires usUsage to be zero, but got a wrong error: %u\n", error); + + device.usUsage = 0; + device.dwFlags = RIDEV_EXCLUDE; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,0,RIDEV_EXCLUDE,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_EXCLUDE " + "requires usUsage to be non-zero, but got a wrong error: %u\n", error); + + device.usUsage = 0xFF; + device.dwFlags = RIDEV_NOLEGACY; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,0xFF,RIDEV_NOLEGACY,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_NOLEGACY is only available for " + "mouse or keyboard devices, but got a wrong error: %u\n", error); + + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_INPUTSINK; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,2,RIDEV_INPUTSINK,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_INPUTSINK " + "requires hwndTarget to be non-null, but got a wrong error: %u\n", error); + + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_CAPTUREMOUSE; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_FLAGS, + "Given (&device{1,2,RIDEV_CAPTUREMOUSE,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_CAPTUREMOUSE " + "must be combined with RIDEV_NOLEGACY, but got a wrong error: %u\n", error); + + device.usUsage = HID_USAGE_GENERIC_KEYBOARD; + device.dwFlags = RIDEV_APPKEYS; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_FLAGS, + "Given (&device{1,6,RIDEV_APPKEYS,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_APPKEYS " + "must be combined with RIDEV_NOLEGACY, but got a wrong error: %u\n", error); + + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_NOLEGACY | RIDEV_APPKEYS; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_FLAGS, + "Given (&device{1,2,RIDEV_NOLEGACY|RIDEV_APPKEYS,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_APPKEYS " + "can be used only on a keyboard device, but got a wrong error: %u\n", error); + + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_NOHOTKEYS; + device.hwndTarget = 0; + SetLastError(0xdeadbeef); + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_FLAGS, + "Given (&device{1,2,RIDEV_NOHOTKEYS,0}, 1, sizeof), " + "RegisterRawInputDevices should fail, because RIDEV_NOHOTKEYS " + "can be used only on a keyboard device, but got a wrong error: %u\n", error); +} + +static void test_get_registered_raw_input_devices(void) +{ + RAWINPUTDEVICE device, device2; + UINT ret, count; + BOOL ret2; + DWORD error; + + if (!pGetRegisteredRawInputDevices || !pRegisterRawInputDevices) + { + win_skip("GetRegisteredRawInputDevices and pRegisterRawInputDevices are not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pGetRegisteredRawInputDevices(NULL, NULL, 0); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, NULL, 0), " + "GetRegisteredRawInputDevices should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRegisteredRawInputDevices(NULL, NULL, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_NOACCESS, "Given (NULL, NULL, sizeof), " + "GetRegisteredRawInputDevices should return an error, but got a wrong one: %u\n", error); + + SetLastError(0xdeadbeef); + ret = pGetRegisteredRawInputDevices(NULL, &count, 1); + error = GetLastError(); + todo_wine ok(ret == (UINT)-1 && error == ERROR_INVALID_PARAMETER, + "Given (NULL, &count, 1), " + "GetRegisteredRawInputDevices should return an error, but got a wrong one: %u\n", error); + + count = 0xdeadbeef; + ret = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = 0; + device.hwndTarget = 0; + ret2 = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret2, "Given (&device{1,2,0,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully subscribe to a mouse raw input device\n"); + + ret = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret == 0 && count == 1, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that one device is registered\n"); + + memset(&device2, 0xFF, sizeof(RAWINPUTDEVICE)); + ret = pGetRegisteredRawInputDevices(&device2, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret == 1 && memcmp(&device, &device2, sizeof(RAWINPUTDEVICE)) == 0, + "Given (&device2, &count, sizeof), GetRegisteredRawInputDevices " + "should return an identical device to the one registered\n"); + + ret2 = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret2, "Given (&device{1,2,0,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully reregister a device\n"); + + memset(&device2, 0xFF, sizeof(RAWINPUTDEVICE)); + ret = pGetRegisteredRawInputDevices(&device2, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret == 1 && memcmp(&device, &device2, sizeof(RAWINPUTDEVICE)) == 0, + "Given (&device2, &count, sizeof), " + "GetRegisteredRawInputDevices should still return one device registered\n"); + + device.dwFlags = RIDEV_REMOVE; + ret2 = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret2, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from a mouse raw input device\n"); + + count = 0xdeadbeef; + ret = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); +} + +static void test_register_raw_input_devices_with_flags(void) +{ + RAWINPUTDEVICE device, devices[2]; + BOOL ret; + UINT ret2, count; + DWORD error; + + if (!pGetRegisteredRawInputDevices || !pRegisterRawInputDevices) + { + win_skip("GetRegisteredRawInputDevices and RegisterRawInputDevices are not available\n"); + return; + } + + /* Assert that there are not devices registered */ + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Subscribe and unsubscribe from all devices in usage page */ + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = 0; + device.dwFlags = RIDEV_PAGEONLY; + device.hwndTarget = 0; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,0,RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully subscribe to " + "devices from that usage page\n"); + + device.dwFlags = RIDEV_REMOVE | RIDEV_PAGEONLY; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,0,RIDEV_REMOVE|RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from " + "devices from that usage page\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Should not allow to include and exclude devices from a usage page */ + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = 0; + device.dwFlags = RIDEV_PAGEONLY; + device.hwndTarget = 0; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,0,RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully subscribe to " + "devices from that usage page\n"); + + SetLastError(0xdeadbeef); + device.dwFlags = RIDEV_PAGEONLY | RIDEV_EXCLUDE; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + error = GetLastError(); + todo_wine ok(!ret && error == ERROR_INVALID_PARAMETER, + "Given (&device{1,0,RIDEV_PAGEONLY|RIDEV_EXCLUDE,0}, 1, sizeof), " + "RegisterRawInputDevices should not allow to exclude previously included devices, " + "but got a wrong error: %u\n", error); + + device.dwFlags = RIDEV_REMOVE | RIDEV_PAGEONLY; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,0,RIDEV_REMOVE|RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from " + "devices from that usage page\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Should override a device exclusion with an inclusion */ + devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[0].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[0].dwFlags = RIDEV_EXCLUDE; + devices[0].hwndTarget = 0; + devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[1].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[1].dwFlags = 0; + devices[1].hwndTarget = 0; + ret = pRegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&devices{{1,2,RIDEV_EXCLUDE,0},{1,2,0,0}}, 2, sizeof), " + "RegisterRawInputDevices should successfully subscribe to a device\n"); + + devices[1].dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(&devices[1], 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from a device\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Should register a usage page and a device from that usage page, + unsubscribing from them separately */ + devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[0].usUsage = 0; + devices[0].dwFlags = RIDEV_PAGEONLY; + devices[0].hwndTarget = 0; + devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[1].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[1].dwFlags = 0; + devices[1].hwndTarget = 0; + ret = pRegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&devices{{1,0,RIDEV_PAGEONLY,0},{1,2,0,0}}, 2, sizeof), " + "RegisterRawInputDevices should successfully subscribe to " + "usage page devices and a device from that usage page\n"); + + devices[0].dwFlags = RIDEV_REMOVE | RIDEV_PAGEONLY; + ret = pRegisterRawInputDevices(&devices[0], 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE|RIDEV_PAGEONLY,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from a mouse raw input device\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 1, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that one device is registered\n"); + + devices[1].dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(&devices[1], 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully unsubscribe from a mouse raw input device\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Should allow to exclude a device */ + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_EXCLUDE; + device.hwndTarget = 0; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_EXCLUDE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully exclude a device from input feed\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 1, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that one device is registered\n"); + + device.dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&device{1,2,RIDEV_REMOVE,0}, 1, sizeof), " + "RegisterRawInputDevices should successfully remove the subscribtion\n"); + + /* Should include a usage page and exclude a device */ + devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[0].usUsage = 0; + devices[0].dwFlags = RIDEV_PAGEONLY; + devices[0].hwndTarget = 0; + devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[1].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[1].dwFlags = RIDEV_EXCLUDE; + devices[1].hwndTarget = 0; + ret = pRegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&devices{{1,2,RIDEV_EXCLUDE,0},{1,2,0,0}}, 2, sizeof), " + "RegisterRawInputDevices should successfully subscribe to a device\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 2, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return two entries\n"); + + devices[0].dwFlags = RIDEV_REMOVE | RIDEV_PAGEONLY; + devices[1].dwFlags = RIDEV_REMOVE; + ret = pRegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&devices{{1,2,RIDEV_REMOVE|RIDEV_PAGEONLY,0},{1,2,RIDEV_REMOVE,0}}, 1, sizeof)," + "RegisterRawInputDevices should successfully remove the subscribtions\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + /* Should allow to subscribe and unsubscribe in the same call */ + devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[0].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[0].dwFlags = 0; + devices[0].hwndTarget = 0; + devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + devices[1].usUsage = HID_USAGE_GENERIC_MOUSE; + devices[1].dwFlags = RIDEV_REMOVE; + devices[1].hwndTarget = 0; + ret = pRegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)); + ok(ret, "Given (&devices{{1,2,0,0},{1,2,RIDEV_REMOVE,0}}, 2, sizeof), " + "RegisterRawInputDevices should successfully subscribe and unsubscribe\n"); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); +} + +static void test_def_raw_input_proc(void) +{ + RAWINPUT *input = NULL; + LRESULT ret; + + if (!pDefRawInputProc) + { + win_skip("DefRawInputProc is not available\n"); + return; + } + + ret = pDefRawInputProc(&input, 0, sizeof(RAWINPUTHEADER)); + ok(ret == S_OK, "Given (&input, 0, sizeof), " + "DefRawInputProc should acknowledge that no data was provided\n"); +} + +static BOOL wm_input_recieved; +static BOOL legacy_mouse_message_recieved; +static BOOL legacy_keyboard_message_recieved; + +static LRESULT CALLBACK get_raw_input_data_wnd_proc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + UINT dwSize, ret2; + RAWINPUT *raw; + BOOL ret; + LRESULT ret3; + DWORD error; + + legacy_mouse_message_recieved |= msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST; + legacy_keyboard_message_recieved |= msg >= WM_KEYFIRST && msg <= WM_KEYLAST; + + switch (msg) + { + case WM_INPUT: + /* Now that we have a valid HRAWINPUT handle, + let's test the case, when &dwSize is NULL */ + SetLastError(0xdeadbeef); + ret = pGetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, NULL, + sizeof(RAWINPUTHEADER)); + error = GetLastError(); + ok(ret == (UINT)-1 && error == ERROR_NOACCESS, + "Given (lParam, RID_INPUT, NULL, NULL, sizeof), " + "GetRawInputData should return an error, but got a wrong one: %u\n", error); + + /* Test retrieving of RAWINPUT data */ + ret2 = pGetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, + sizeof(RAWINPUTHEADER)); + ok(ret2 == 0, "Failed to retrieve raw input data size\n"); + if (!(raw = HeapAlloc(GetProcessHeap(), 0, dwSize))) + break; + ret2 = pGetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw, &dwSize, + sizeof(RAWINPUTHEADER)); + ok(ret2 == dwSize, "Failed to retrieve raw input data\n"); + ok(raw->header.dwType == RIM_TYPEMOUSE || raw->header.dwType == RIM_TYPEKEYBOARD, + "Raw input data entry must be from mouse or keyboard\n"); + ret3 = pDefRawInputProc(&raw, 1, sizeof(RAWINPUTHEADER)); + HeapFree(GetProcessHeap(), 0, raw); + wm_input_recieved = TRUE; + return ret3; + } + return DefWindowProcA(hWnd, msg, wParam, lParam); +} + +#define ID_TIMER 1 + +static void timer_proc(HWND hParent, UINT uMsg, UINT uEventID, DWORD dwTimer) +{ + PostQuitMessage(0); +} + +static HWND test_get_raw_input_data_simulation_setup(HANDLE hInstance, const char *class_name, + RAWINPUTDEVICE *device, INPUT *input, UINT input_count) +{ + MSG msg; + BOOL ret; + + HWND hWnd = CreateWindowA(class_name, "GetRawInputDataTest", + WS_OVERLAPPEDWINDOW, 10, 10, 200, 200, + NULL, NULL, hInstance, NULL); + assert(hWnd); + + ShowWindow(hWnd, SW_SHOW); + SetWindowPos(hWnd, HWND_TOPMOST, 10, 10, 200, 200, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(hWnd); + UpdateWindow(hWnd); + SetFocus(hWnd); + + wm_input_recieved = FALSE; + legacy_mouse_message_recieved = FALSE; + legacy_keyboard_message_recieved = FALSE; + + device->hwndTarget = hWnd; + ret = pRegisterRawInputDevices(device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Failed to register raw input devices: %u\n", GetLastError()); + + pSendInput(input_count, input, sizeof(INPUT)); + + /* Give a second of running time for each test to be sure all messages are processed */ + SetTimer(hWnd, ID_TIMER, 500, (TIMERPROC)timer_proc); + + while (1 == 1) + { + ret = GetMessageA(&msg, hWnd, 0, 0); + if (!ret || ret == (BOOL)-1) + break; + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + ok(ret != -1, "Error getting window message: %u\n", GetLastError()); + todo_wine ok(wm_input_recieved, "WM_INPUT was not received\n"); + + return hWnd; +} + +static void test_get_raw_input_data_simulation_teardown(HWND hWnd, RAWINPUTDEVICE *device) +{ + BOOL ret; + UINT size, ret2; + DWORD count; + RAWINPUT *raw; + + KillTimer(hWnd, ID_TIMER); + + device->dwFlags = RIDEV_REMOVE | (device->usUsage == 0 ? RIDEV_PAGEONLY : 0); + device->hwndTarget = 0; + ret = pRegisterRawInputDevices(device, 1, sizeof(RAWINPUTDEVICE)); + ok(ret, "Failed to unregister raw input device: %u\n", GetLastError()); + + count = 0xdeadbeef; + ret2 = pGetRegisteredRawInputDevices(NULL, &count, sizeof(RAWINPUTDEVICE)); + todo_wine ok(ret2 == 0 && count == 0, "Given (NULL, &count, sizeof), " + "GetRegisteredRawInputDevices should return that no devices are registered\n"); + + DestroyWindow(hWnd); + + /* Clear raw input buffer */ + ret2 = pGetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); + if (ret2 == 0 && size > 0 && + (raw = HeapAlloc(GetProcessHeap(), 0, size * sizeof(RAWINPUT)))) + { + pGetRawInputBuffer(raw, &size, sizeof(RAWINPUTHEADER)); + HeapFree(GetProcessHeap(), 0, raw); + } +} + +static void test_get_raw_input_data_simulation(void) +{ + HWND hWnd; + WNDCLASSA wclass; + HANDLE hInstance = GetModuleHandleA( NULL ); + INPUT mouse_input[5], keyboard_input; + ATOM registration; + RAWINPUTDEVICE device; + unsigned int i; + + if (!pRegisterRawInputDevices || !pGetRawInputData || !pDefRawInputProc || + !pGetRawInputBuffer || !pSendInput) + { + win_skip("Functions required to perform raw input simulation are not available\n"); + return; + } + + wclass.lpszClassName = "GetRawInputDataTestClass"; + wclass.style = CS_HREDRAW | CS_VREDRAW; + wclass.lpfnWndProc = get_raw_input_data_wnd_proc; + wclass.hInstance = hInstance; + wclass.hIcon = LoadIconA(0, IDI_APPLICATION); + wclass.hCursor = LoadCursorA(NULL, IDC_ARROW); + wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wclass.lpszMenuName = 0; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + registration = RegisterClassA(&wclass); + assert(registration); + + /* Setup fixtures */ + device.usUsagePage = HID_USAGE_PAGE_GENERIC; + + memset(&keyboard_input, 0, sizeof(keyboard_input)); + keyboard_input.type = INPUT_KEYBOARD; + keyboard_input.ki.wVk = VK_SPACE; + + /* Be sure to move over the window, because the absolute window position on the screen + depends on how desktop widgets are placed */ + memset(&mouse_input, 0, sizeof(mouse_input)); + mouse_input[0].type = INPUT_MOUSE; + mouse_input[0].mi.dx = mouse_input[0].mi.dy = 15; + mouse_input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; + mouse_input[1].type = INPUT_MOUSE; + mouse_input[1].mi.mouseData = 0x0078; + mouse_input[1].mi.dwFlags = MOUSEEVENTF_WHEEL; + for (i = 2; i < sizeof(mouse_input) / sizeof(mouse_input[0]); i++) + { + mouse_input[i].type = INPUT_MOUSE; + mouse_input[i].mi.dx = mouse_input[i].mi.dy = 30; + mouse_input[i].mi.dwFlags = MOUSEEVENTF_MOVE; + } + + /* Test WM_INPUT for mouse */ + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = 0; + + hWnd = test_get_raw_input_data_simulation_setup(hInstance, wclass.lpszClassName, &device, + mouse_input, 5); + ok(legacy_mouse_message_recieved || legacy_keyboard_message_recieved, + "Should have received legacy messages\n"); + test_get_raw_input_data_simulation_teardown(hWnd, &device); + + /* Test WM_INPUT for keyboard */ + device.usUsage = HID_USAGE_GENERIC_KEYBOARD; + device.dwFlags = 0; + + hWnd = test_get_raw_input_data_simulation_setup(hInstance, wclass.lpszClassName, &device, + &keyboard_input, 1); + ok(legacy_mouse_message_recieved || legacy_keyboard_message_recieved, + "Should have received legacy messages\n"); + test_get_raw_input_data_simulation_teardown(hWnd, &device); + + /* Test RIDEV_PAEGONLY */ + device.usUsage = 0; + device.dwFlags = RIDEV_PAGEONLY; + + hWnd = test_get_raw_input_data_simulation_setup(hInstance, wclass.lpszClassName, &device, + &keyboard_input, 1); + ok(legacy_mouse_message_recieved || legacy_keyboard_message_recieved, + "Should have received legacy messages\n"); + test_get_raw_input_data_simulation_teardown(hWnd, &device); + + /* Test RIDEV_NOLEGACY for mouse */ + device.usUsage = HID_USAGE_GENERIC_MOUSE; + device.dwFlags = RIDEV_NOLEGACY; + + hWnd = test_get_raw_input_data_simulation_setup(hInstance, wclass.lpszClassName, &device, + mouse_input, 5); + todo_wine ok(!legacy_mouse_message_recieved, "Should have not received legacy mouse messages\n"); + test_get_raw_input_data_simulation_teardown(hWnd, &device); + + /* Test RIDEV_NOLEGACY for keyboard */ + device.usUsage = HID_USAGE_GENERIC_KEYBOARD; + device.dwFlags = RIDEV_NOLEGACY; + + hWnd = test_get_raw_input_data_simulation_setup(hInstance, wclass.lpszClassName, &device, + &keyboard_input, 1); + todo_wine ok(!legacy_keyboard_message_recieved, "Should have not received legacy keyboard messages\n"); + test_get_raw_input_data_simulation_teardown(hWnd, &device); +} + START_TEST(input) { init_function_pointers(); @@ -1617,6 +2600,17 @@ START_TEST(input) test_get_async_key_state(); test_keyboard_layout_name(); + test_get_raw_input_device_list(); + test_get_raw_input_data(); + test_get_raw_input_buffer(); + test_get_raw_input_device_info_a(); + test_get_raw_input_device_info_w(); + test_register_raw_input_devices(); + test_get_registered_raw_input_devices(); + test_register_raw_input_devices_with_flags(); + test_def_raw_input_proc(); + test_get_raw_input_data_simulation(); + if(pGetMouseMovePointsEx) test_GetMouseMovePointsEx(); else