v2: * Add some tests to exhibit broken behavior, then fix it. * Add some tests for foreground/background behavior, and fixed the assumptions made about it. * Removed the dinput8 patches for now, they can be added later. * Renamed the SEND_HWMSG_* flags.
Rémi Bernon (8): user32/tests: Add rawinput test for ClipCursor interactions. user32/tests: Add rawinput test for cross-thread interactions. user32/tests: Add rawinput test for cross-process interactions. server: Add send_hardware_message flags for rawinput translation. server: Broadcast rawinput message if request flag is SEND_HWMSG_RAWINPUT. user32: Add __wine_send_input flags to hint raw input translation. winex11.drv: Advertise XInput2 version 2.1 support. winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread.
dlls/user32/input.c | 8 +- dlls/user32/tests/input.c | 335 ++++++++++++++++++++++++++++++++ dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 +- dlls/winemac.drv/ime.c | 4 +- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/event.c | 10 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 128 +++++++----- dlls/winex11.drv/x11drv.h | 6 +- dlls/winex11.drv/x11drv_main.c | 4 + include/winuser.h | 2 +- server/protocol.def | 2 + server/queue.c | 110 ++++++++--- 16 files changed, 528 insertions(+), 95 deletions(-)
This shows unexpected rawinput messages triggered from cursor clipping.
The sleeps are required to let native cursor clipping time to activate. We also repeat the clipping multiple times to be increase the chances that the counts will be wrong as it doesn't always trigger.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 226 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index d0e76038115..8b26f8ed4be 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1823,6 +1823,231 @@ static void test_RegisterRawInputDevices(void) DestroyWindow(hwnd); }
+static int rawinput_received; +static int rawinput_received_foreground; +static int rawinput_motion_x; +static int rawinput_motion_y; +static HANDLE rawinput_wndproc_done; + +static LRESULT CALLBACK rawinput_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + UINT ret, raw_size; + RAWINPUT raw; + + if (msg == WM_INPUT) + { + rawinput_received++; + + ok(wparam == RIM_INPUT || wparam == RIM_INPUTSINK, "Unexpected wparam: %lu\n", wparam); + if (wparam == RIM_INPUT) + rawinput_received_foreground++; + + 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", raw_size); + + if (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: %u\n", raw.header.dwType); + + if (raw.header.dwType == RIM_TYPEMOUSE) + { + 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"); + rawinput_motion_x += raw.data.mouse.lLastX; + rawinput_motion_y += raw.data.mouse.lLastY; + } + } + } + + if (msg == WM_USER) + SetEvent(rawinput_wndproc_done); + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +struct rawinput_mouse_thread_params +{ + int step; + HWND window; + HANDLE ready; + HANDLE start; +}; + +static DWORD WINAPI rawinput_mouse_thread(void *arg) +{ + struct rawinput_mouse_thread_params *params = arg; + RECT rect_105 = { 105, 105, 105, 105 }; + RECT rect_110 = { 110, 110, 110, 110 }; + int i; + + while (WaitForSingleObject(params->ready, INFINITE) == 0) + { + ResetEvent(params->ready); + SetEvent(params->start); + + switch (params->step) + { + case 0: + case 1: + case 2: + mouse_event(MOUSEEVENTF_MOVE, -1, 0, 0, 0); + mouse_event(MOUSEEVENTF_MOVE, 0, -1, 0, 0); + break; + case 3: + for (i = 0; i < 10; ++i) + { + ClipCursor(&rect_105); + Sleep(5); + ClipCursor(&rect_110); + Sleep(5); + ClipCursor(NULL); + Sleep(5); + } + break; + case 4: + for (i = 0; i < 10; ++i) + { + ClipCursor(&rect_110); + Sleep(5); + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + ClipCursor(NULL); + Sleep(5); + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + ClipCursor(&rect_110); + ClipCursor(NULL); + Sleep(5); + } + break; + default: + return 0; + } + + PostMessageA(params->window, WM_USER, 0, 0); + } + + return 0; +} + +struct rawinput_mouse_test +{ + BOOL register_device; + BOOL register_window; + DWORD register_flags; + int expect_received; + int expect_received_foreground; + int expect_motion_x; + int expect_motion_y; + BOOL todo; +}; + +static void test_rawinput_mouse(void) +{ + struct rawinput_mouse_thread_params params; + RAWINPUTDEVICE raw_devices[1]; + HANDLE thread; + DWORD ret; + int i; + + struct rawinput_mouse_test tests[] = + { + { FALSE, FALSE, 0, 0, 0, 0, 0, FALSE }, + { TRUE, FALSE, 0, 2, 2, -1, -1, FALSE }, + { TRUE, TRUE, 0, 2, 2, -1, -1, FALSE }, + { TRUE, TRUE, 0, 0, 0, 0, 0, TRUE }, + { TRUE, TRUE, 0, 20, 20, 20, 20, TRUE }, + }; + + mouse_event(MOUSEEVENTF_ABSOLUTE, 100, 100, 0, 0); + SetCursorPos(100, 100); + + rawinput_wndproc_done = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(rawinput_wndproc_done != NULL, "CreateEvent failed\n"); + + params.window = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL); + ok(params.window != 0, "CreateWindow failed\n"); + + ShowWindow(params.window, SW_SHOW); + SetWindowPos(params.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(params.window); + UpdateWindow(params.window); + empty_message_queue(); + + SetWindowLongPtrA(params.window, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); + + params.step = 0; + 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"); + + thread = CreateThread(NULL, 0, rawinput_mouse_thread, ¶ms, 0, NULL); + ok(thread != NULL, "CreateThread failed\n"); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + rawinput_received = 0; + rawinput_received_foreground = 0; + rawinput_motion_x = 0; + rawinput_motion_y = 0; + + raw_devices[0].usUsagePage = 0x01; + raw_devices[0].usUsage = 0x02; + raw_devices[0].dwFlags = tests[i].register_flags; + raw_devices[0].hwndTarget = tests[i].register_window ? params.window : 0; + + if (tests[i].register_device) + { + 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 %08x\n", i, GetLastError()); + } + + params.step = i; + SetEvent(params.ready); + + WaitForSingleObject(params.start, INFINITE); + ResetEvent(params.start); + + while (MsgWaitForMultipleObjects(1, &rawinput_wndproc_done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + empty_message_queue(); + ResetEvent(rawinput_wndproc_done); + + /* Wine is sometimes passing some of the conditions, but not always, let's test + * all at once in the todo block, there should be at least one that fails. */ + todo_wine_if(tests[i].todo) + ok(rawinput_received == tests[i].expect_received && + rawinput_received_foreground == tests[i].expect_received_foreground && + rawinput_motion_x == tests[i].expect_motion_x && + rawinput_motion_y == tests[i].expect_motion_y, + "%d: Unexpected rawinput results: received %d, %d in foreground, motion is %dx%d\n", + i, rawinput_received, rawinput_received_foreground, rawinput_motion_x, rawinput_motion_y); + + if (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\n", i); + ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08x\n", i, GetLastError()); + } + } + + params.step = -1; + SetEvent(params.ready); + WaitForSingleObject(thread, INFINITE); + + CloseHandle(params.start); + CloseHandle(params.ready); + CloseHandle(thread); +} + static void test_key_map(void) { HKL kl = GetKeyboardLayout(0); @@ -3016,6 +3241,7 @@ START_TEST(input) test_OemKeyScan(); test_GetRawInputData(); test_RegisterRawInputDevices(); + test_rawinput_mouse();
if(pGetMouseMovePointsEx) test_GetMouseMovePointsEx();
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60212
Your paranoid android.
=== w1064v1809_2scr (32 bit report) ===
user32: input.c:1424: Test failed: Wrong new pos: (150,150)
The rawinput messages are received on the target window if is from the same process as the foreground window, it doesn't need to be the foreground window itself.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 8b26f8ed4be..ca66ecb51c9 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1881,6 +1881,7 @@ static DWORD WINAPI rawinput_mouse_thread(void *arg) struct rawinput_mouse_thread_params *params = arg; RECT rect_105 = { 105, 105, 105, 105 }; RECT rect_110 = { 110, 110, 110, 110 }; + HWND window; int i;
while (WaitForSingleObject(params->ready, INFINITE) == 0) @@ -1921,6 +1922,26 @@ static DWORD WINAPI rawinput_mouse_thread(void *arg) Sleep(5); } break; + case 5: + case 6: + window = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL); + ok(window != 0, "%d: CreateWindow failed\n", params->step); + + ShowWindow(window, SW_SHOW); + SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(window); + UpdateWindow(window); + empty_message_queue(); + + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + SendMessageA(GetForegroundWindow(), WM_USER, 0, 0); + mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0); + SendMessageA(GetForegroundWindow(), WM_USER, 0, 0); + + empty_message_queue(); + + DestroyWindow(window); + break; default: return 0; } @@ -1956,8 +1977,14 @@ static void test_rawinput_mouse(void) { FALSE, FALSE, 0, 0, 0, 0, 0, FALSE }, { TRUE, FALSE, 0, 2, 2, -1, -1, FALSE }, { TRUE, TRUE, 0, 2, 2, -1, -1, FALSE }, + + /* clip cursor tests */ { TRUE, TRUE, 0, 0, 0, 0, 0, TRUE }, { TRUE, TRUE, 0, 20, 20, 20, 20, TRUE }, + + /* same-process foreground tests */ + { TRUE, TRUE, 0, 2, 2, 0, 0, TRUE }, + { TRUE, TRUE, RIDEV_INPUTSINK, 2, 2, 0, 0, TRUE }, };
mouse_event(MOUSEEVENTF_ABSOLUTE, 100, 100, 0, 0);
Validating the rest of the assumption, rawinput messages are not received anymore if the foreground window is from another process.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index ca66ecb51c9..f107cc5e862 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -49,6 +49,7 @@ #define _WIN32_IE 0x0500
#include <stdarg.h> +#include <stdio.h> #include <assert.h>
#include "windef.h" @@ -1874,8 +1875,36 @@ struct rawinput_mouse_thread_params HWND window; HANDLE ready; HANDLE start; + const char *argv0; };
+static void rawinput_mouse_process(void) +{ + HWND window; + HANDLE start_event, stop_event; + + start_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_rawinput_mouse_start"); + ok(start_event != 0, "OpenEventA failed, error: %u\n", GetLastError()); + + stop_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_rawinput_mouse_stop"); + ok(stop_event != 0, "OpenEventA failed, error: %u\n", GetLastError()); + + window = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, 200, 100, 100, 100, 0, NULL, NULL, NULL); + ok(window != 0, "CreateWindow failed\n"); + + ShowWindow(window, SW_SHOW); + SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(window); + UpdateWindow(window); + empty_message_queue(); + + SetEvent(start_event); + while (MsgWaitForMultipleObjects(1, &stop_event, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + empty_message_queue(); + + DestroyWindow(window); +} + static DWORD WINAPI rawinput_mouse_thread(void *arg) { struct rawinput_mouse_thread_params *params = arg; @@ -1883,6 +1912,11 @@ static DWORD WINAPI rawinput_mouse_thread(void *arg) RECT rect_110 = { 110, 110, 110, 110 }; HWND window; int i; + char path[MAX_PATH]; + PROCESS_INFORMATION process_info; + STARTUPINFOA startup_info; + HANDLE start_event, stop_event; + BOOL ret;
while (WaitForSingleObject(params->ready, INFINITE) == 0) { @@ -1942,6 +1976,39 @@ static DWORD WINAPI rawinput_mouse_thread(void *arg)
DestroyWindow(window); break; + case 7: + case 8: + start_event = CreateEventA(NULL, 0, 0, "test_rawinput_mouse_start"); + ok(start_event != 0, "%d: CreateEventA failed, error %u\n", params->step, GetLastError()); + + stop_event = CreateEventA(NULL, 0, 0, "test_rawinput_mouse_stop"); + ok(stop_event != 0, "%d: CreateEventA failed, error %u\n", params->step, GetLastError()); + + 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 test_rawinput_mouse", params->argv0); + ret = CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info ); + ok(ret, "%d: CreateProcess '%s' failed err %u.\n", params->step, path, GetLastError()); + + ret = WaitForSingleObject(start_event, 5000); + ok(ret == WAIT_OBJECT_0, "%d: WaitForSingleObject failed\n", params->step); + + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + SendMessageA(GetForegroundWindow(), WM_USER, 0, 0); + mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0); + SendMessageA(GetForegroundWindow(), WM_USER, 0, 0); + + SetEvent(stop_event); + + winetest_wait_child_process(process_info.hProcess); + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + CloseHandle(start_event); + CloseHandle(stop_event); + break; default: return 0; } @@ -1964,7 +2031,7 @@ struct rawinput_mouse_test BOOL todo; };
-static void test_rawinput_mouse(void) +static void test_rawinput_mouse(const char *argv0) { struct rawinput_mouse_thread_params params; RAWINPUTDEVICE raw_devices[1]; @@ -1985,11 +2052,17 @@ static void test_rawinput_mouse(void) /* same-process foreground tests */ { TRUE, TRUE, 0, 2, 2, 0, 0, TRUE }, { TRUE, TRUE, RIDEV_INPUTSINK, 2, 2, 0, 0, TRUE }, + + /* cross-process foreground tests */ + { TRUE, TRUE, 0, 0, 0, 0, 0, TRUE }, + { TRUE, TRUE, RIDEV_INPUTSINK, 2, 0, 0, 0, TRUE }, };
mouse_event(MOUSEEVENTF_ABSOLUTE, 100, 100, 0, 0); SetCursorPos(100, 100);
+ params.argv0 = argv0; + rawinput_wndproc_done = CreateEventA(NULL, FALSE, FALSE, NULL); ok(rawinput_wndproc_done != NULL, "CreateEvent failed\n");
@@ -3246,11 +3319,20 @@ static void test_GetPointerType(void)
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], "test_rawinput_mouse") == 0) + { + rawinput_mouse_process(); + return; + } + test_Input_blackbox(); test_Input_whitebox(); test_Input_unicode(); @@ -3268,7 +3350,7 @@ START_TEST(input) test_OemKeyScan(); test_GetRawInputData(); test_RegisterRawInputDevices(); - test_rawinput_mouse(); + test_rawinput_mouse(argv[0]);
if(pGetMouseMovePointsEx) test_GetMouseMovePointsEx();
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 6 +++--- server/protocol.def | 2 ++ server/queue.c | 20 ++++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 0325e2ce3df..fe7fc111aef 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -124,7 +124,7 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) */ BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ) { - NTSTATUS status = send_hardware_message( hwnd, input, 0 ); + NTSTATUS status = send_hardware_message( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; } @@ -192,9 +192,9 @@ UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size ) /* we need to update the coordinates to what the server expects */ INPUT input = inputs[i]; update_mouse_coords( &input ); - status = send_hardware_message( 0, &input, SEND_HWMSG_INJECTED ); + status = send_hardware_message( 0, &input, SEND_HWMSG_INJECTED|SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); } - else status = send_hardware_message( 0, &inputs[i], SEND_HWMSG_INJECTED ); + else status = send_hardware_message( 0, &inputs[i], SEND_HWMSG_INJECTED|SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW );
if (status) { diff --git a/server/protocol.def b/server/protocol.def index 3a0df20bdba..e2afa476770 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2314,6 +2314,8 @@ enum message_type VARARG(keystate,bytes); /* global state array for all the keys */ @END #define SEND_HWMSG_INJECTED 0x01 +#define SEND_HWMSG_RAWINPUT 0x02 +#define SEND_HWMSG_WINDOW 0x04
/* Get a message from the current queue */ diff --git a/server/queue.c b/server/queue.c index b5e17be18fb..fb5452b1f17 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1598,7 +1598,7 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa
/* queue a hardware message for a mouse event */ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, - unsigned int origin, struct msg_queue *sender ) + unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { const struct rawinput_device *device; struct hardware_msg_data *msg_data; @@ -1651,7 +1651,8 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons y = desktop->cursor.y; }
- if ((device = current->process->rawinput_mouse)) + if ((device = current->process->rawinput_mouse) && + (req_flags & SEND_HWMSG_RAWINPUT)) { if (!(msg = alloc_hardware_message( input->mouse.info, source, time ))) return 0; msg_data = msg->data; @@ -1670,6 +1671,9 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons queue_hardware_message( desktop, msg, 0 ); }
+ if (!(req_flags & SEND_HWMSG_WINDOW)) + return 0; + for (i = 0; i < ARRAY_SIZE( messages ); i++) { if (!messages[i]) continue; @@ -1701,7 +1705,7 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
/* queue a hardware message for a keyboard event */ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, - unsigned int origin, struct msg_queue *sender ) + unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { struct hw_msg_source source = { IMDT_KEYBOARD, origin }; const struct rawinput_device *device; @@ -1777,7 +1781,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
- if ((device = current->process->rawinput_kbd)) + if ((device = current->process->rawinput_kbd) && + (req_flags & SEND_HWMSG_RAWINPUT)) { if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->data; @@ -1795,6 +1800,9 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c queue_hardware_message( desktop, msg, 0 ); }
+ if (!(req_flags & SEND_HWMSG_WINDOW)) + return 0; + if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->data;
@@ -2351,10 +2359,10 @@ DECL_HANDLER(send_hardware_message) switch (req->input.type) { case INPUT_MOUSE: - reply->wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender ); + reply->wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender, req->flags ); break; case INPUT_KEYBOARD: - reply->wait = queue_keyboard_message( desktop, req->win, &req->input, origin, sender ); + reply->wait = queue_keyboard_message( desktop, req->win, &req->input, origin, sender, req->flags ); break; case INPUT_HARDWARE: queue_custom_hardware_message( desktop, req->win, origin, &req->input );
If the request flag is equal to SEND_HWMSG_RAWINPUT, we broadcast the message to all listening processes -or at least to the foreground process until RIDEV_INPUTSINK is supported.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 2 +- server/queue.c | 98 ++++++++++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 22 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f107cc5e862..8114bab228e 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2054,7 +2054,7 @@ static void test_rawinput_mouse(const char *argv0) { TRUE, TRUE, RIDEV_INPUTSINK, 2, 2, 0, 0, TRUE },
/* cross-process foreground tests */ - { TRUE, TRUE, 0, 0, 0, 0, 0, TRUE }, + { TRUE, TRUE, 0, 0, 0, 0, 0, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, 2, 0, 0, 0, TRUE }, };
diff --git a/server/queue.c b/server/queue.c index fb5452b1f17..05c9481f8a9 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1596,12 +1596,67 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa return 1; }
+struct rawinput_message +{ + struct desktop *desktop; + struct hw_msg_source source; + unsigned int time; + struct hardware_msg_data data; +}; + +static int queue_rawinput_message( struct process* process, void* user ) +{ + const struct rawinput_message* raw_msg = user; + const struct rawinput_device *device = NULL; + struct desktop *desktop = NULL; + struct thread *thread = NULL, *foreground = NULL; + struct message *msg; + + if (raw_msg->data.rawinput.type == RIM_TYPEMOUSE) + device = process->rawinput_mouse; + else if (raw_msg->data.rawinput.type == RIM_TYPEKEYBOARD) + device = process->rawinput_kbd; + + if (!device) + goto done; + + if (!(desktop = get_desktop_obj( process, process->desktop, 0 )) || + (raw_msg->desktop && desktop != raw_msg->desktop)) + goto done; + + if (!(thread = get_window_thread( device->target ? device->target : desktop->foreground_input->active )) || + process != thread->process) + goto done; + + /* FIXME: Implement RIDEV_INPUTSINK */ + if (!(foreground = get_window_thread( desktop->foreground_input->active )) || + thread->process != foreground->process) + goto done; + + if (!(msg = alloc_hardware_message( raw_msg->data.info, raw_msg->source, raw_msg->time ))) + goto done; + + msg->win = device->target; + msg->msg = WM_INPUT; + msg->wparam = RIM_INPUT; + msg->lparam = 0; + memcpy( msg->data, &raw_msg->data, sizeof(raw_msg->data) ); + + queue_hardware_message( desktop, msg, 0 ); + +done: + if (foreground) release_object( foreground ); + if (thread) release_object( thread ); + if (desktop) release_object( desktop ); + return 0; +} + /* queue a hardware message for a mouse event */ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { - const struct rawinput_device *device; struct hardware_msg_data *msg_data; + struct rawinput_message raw_msg; struct message *msg; unsigned int i, time, flags; struct hw_msg_source source = { IMDT_MOUSE, origin }; @@ -1651,24 +1706,24 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons y = desktop->cursor.y; }
- if ((device = current->process->rawinput_mouse) && - (req_flags & SEND_HWMSG_RAWINPUT)) + if (req_flags & SEND_HWMSG_RAWINPUT) { - if (!(msg = alloc_hardware_message( input->mouse.info, source, time ))) return 0; - msg_data = msg->data; - - msg->win = device->target; - msg->msg = WM_INPUT; - msg->wparam = RIM_INPUT; - msg->lparam = 0; + raw_msg.desktop = desktop; + raw_msg.source = source; + raw_msg.time = time;
+ msg_data = &raw_msg.data; + msg_data->info = input->mouse.info; msg_data->flags = flags; msg_data->rawinput.type = RIM_TYPEMOUSE; msg_data->rawinput.mouse.x = x - desktop->cursor.x; msg_data->rawinput.mouse.y = y - desktop->cursor.y; msg_data->rawinput.mouse.data = input->mouse.data;
- queue_hardware_message( desktop, msg, 0 ); + if (req_flags == SEND_HWMSG_RAWINPUT) + enum_processes( queue_rawinput_message, &raw_msg ); + else + queue_rawinput_message( current->process, &raw_msg ); }
if (!(req_flags & SEND_HWMSG_WINDOW)) @@ -1708,8 +1763,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { struct hw_msg_source source = { IMDT_KEYBOARD, origin }; - const struct rawinput_device *device; struct hardware_msg_data *msg_data; + struct rawinput_message raw_msg; struct message *msg; unsigned char vkey = input->kbd.vkey; unsigned int message_code, time; @@ -1781,23 +1836,24 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
- if ((device = current->process->rawinput_kbd) && - (req_flags & SEND_HWMSG_RAWINPUT)) + if (req_flags & SEND_HWMSG_RAWINPUT) { - if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; - msg_data = msg->data; - - msg->win = device->target; - msg->msg = WM_INPUT; - msg->wparam = RIM_INPUT; + raw_msg.desktop = desktop; + raw_msg.source = source; + raw_msg.time = time;
+ msg_data = &raw_msg.data; + msg_data->info = input->kbd.info; msg_data->flags = input->kbd.flags; msg_data->rawinput.type = RIM_TYPEKEYBOARD; msg_data->rawinput.kbd.message = message_code; msg_data->rawinput.kbd.vkey = vkey; msg_data->rawinput.kbd.scan = input->kbd.scan;
- queue_hardware_message( desktop, msg, 0 ); + if (req_flags == SEND_HWMSG_RAWINPUT) + enum_processes( queue_rawinput_message, &raw_msg ); + else + queue_rawinput_message( current->process, &raw_msg ); }
if (!(req_flags & SEND_HWMSG_WINDOW))
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60216
Your paranoid android.
=== debian10 (32 bit report) ===
user32: msg.c:8782: Test failed: WaitForSingleObject failed 102 msg.c:8788: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8788: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8788: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
=== debian10 (32 bit Chinese:China report) ===
user32: clipboard.c:760: Test failed: 7: gle 5 clipboard.c:765: Test failed: 7.0: got 0000 instead of 0011 clipboard.c:805: Test failed: 7: gle 1418 clipboard.c:815: Test failed: 7: count 3 clipboard.c:818: Test failed: 7: gle 1418 clipboard.c:852: Test failed: 7: format 0011 got data 0012E600 clipboard.c:853: Test failed: 7.0: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.0: formats 00000000 have been rendered clipboard.c:852: Test failed: 7: format 0002 got data 0009003F clipboard.c:853: Test failed: 7.1: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.1: formats 00000000 have been rendered clipboard.c:852: Test failed: 7: format 0008 got data 0012F290 clipboard.c:853: Test failed: 7.2: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.2: formats 00000000 have been rendered
=== debian10 (32 bit WoW report) ===
user32: msg.c:17555: Test succeeded inside todo block: SendMessage from other thread 1: marked "todo_wine" but succeeds msg.c:17559: Test succeeded inside todo block: WaitForSingleObject failed, ret:0 msg.c:17575: Test succeeded inside todo block: SendMessage from other thread 3: marked "todo_wine" but succeeds msg.c:17583: Test succeeded inside todo block: wrong status 00000000 msg.c:17587: Test succeeded inside todo block: SendMessage from other thread 5: marked "todo_wine" but succeeds
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 4 ++-- dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 ++-- dlls/winemac.drv/ime.c | 4 ++-- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 8 ++++---- include/winuser.h | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index fe7fc111aef..46f78cbce89 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -122,9 +122,9 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) * * Internal SendInput function to allow the graphics driver to inject real events. */ -BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ) +BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, UINT flags ) { - NTSTATUS status = send_hardware_message( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); + NTSTATUS status = send_hardware_message( hwnd, input, flags ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; } diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index f9a4ae26df4..3b934dc4057 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -832,5 +832,5 @@ # All functions must be prefixed with '__wine_' (for internal functions) # or 'wine_' (for user-visible functions) to avoid namespace conflicts. # -@ cdecl __wine_send_input(long ptr) +@ cdecl __wine_send_input(long ptr long) @ cdecl __wine_set_pixel_format(long long) diff --git a/dlls/wineandroid.drv/keyboard.c b/dlls/wineandroid.drv/keyboard.c index 2c37c42e0d4..7fdf3a4ffb3 100644 --- a/dlls/wineandroid.drv/keyboard.c +++ b/dlls/wineandroid.drv/keyboard.c @@ -680,7 +680,7 @@ static void send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD flags ) input.u.ki.time = 0; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); }
/*********************************************************************** diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index eb05aaf2832..c1e7b000a8f 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -521,7 +521,7 @@ static int process_events( DWORD mask ) } SERVER_END_REQ; } - __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input ); + __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); } break;
@@ -535,7 +535,7 @@ static int process_events( DWORD mask ) event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wScan ); update_keyboard_lock_state( event->data.kbd.input.u.ki.wVk, event->data.kbd.lock_state ); - __wine_send_input( 0, &event->data.kbd.input ); + __wine_send_input( 0, &event->data.kbd.input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); break;
default: diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index dabe6654f98..2ed6e6f66a3 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -1427,10 +1427,10 @@ void macdrv_im_set_text(const macdrv_event *event) { input.ki.wScan = chars[i]; input.ki.dwFlags = KEYEVENTF_UNICODE; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW);
input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW); } }
diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index aed4ff0d6e4..1061deff259 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -929,7 +929,7 @@ static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD fl input.ki.time = time; input.ki.dwExtraInfo = 0;
- __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW); }
diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c index dd6443fe1ba..91cafdf1362 100644 --- a/dlls/winemac.drv/mouse.c +++ b/dlls/winemac.drv/mouse.c @@ -165,7 +165,7 @@ static void send_mouse_input(HWND hwnd, macdrv_window cocoa_window, UINT flags, input.mi.time = time; input.mi.dwExtraInfo = 0;
- __wine_send_input(top_level_hwnd, &input); + __wine_send_input(top_level_hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW); }
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 131c5f5442f..73670adbe32 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1148,7 +1148,7 @@ static void X11DRV_send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD f input.u.ki.time = time; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 290732fa936..e95f39e86f9 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -620,7 +620,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU } input->u.mi.dx += clip_rect.left; input->u.mi.dy += clip_rect.top; - __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); return; }
@@ -681,7 +681,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU
input->u.mi.dx = pt.x; input->u.mi.dy = pt.y; - __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); }
#ifdef SONAME_LIBXCURSOR @@ -1627,7 +1627,7 @@ void move_resize_window( HWND hwnd, int dir ) input.u.mi.dwFlags = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = GetTickCount(); input.u.mi.dwExtraInfo = 0; - __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); }
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) @@ -1854,7 +1854,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy );
input.type = INPUT_MOUSE; - __wine_send_input( 0, &input ); + __wine_send_input( 0, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); return TRUE; }
diff --git a/include/winuser.h b/include/winuser.h index 51c73d25c2f..10cebfa97d0 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -4389,7 +4389,7 @@ static inline BOOL WINAPI SetRectEmpty(LPRECT rect) WORD WINAPI SYSTEM_KillSystemTimer( WORD );
#ifdef __WINESRC__ -WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ); +WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, UINT flags ); #endif
#ifdef __cplusplus
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60217
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
user32: clipboard.c:760: Test failed: 7: gle 5 clipboard.c:765: Test failed: 7.0: got 0000 instead of 0011 clipboard.c:805: Test failed: 7: gle 1418 clipboard.c:815: Test failed: 7: count 3 clipboard.c:818: Test failed: 7: gle 1418 clipboard.c:852: Test failed: 7: format 0011 got data 0012E600 clipboard.c:853: Test failed: 7.0: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.0: formats 00000000 have been rendered clipboard.c:852: Test failed: 7: format 0002 got data 0009003F clipboard.c:853: Test failed: 7.1: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.1: formats 00000000 have been rendered clipboard.c:852: Test failed: 7: format 0008 got data 0012F290 clipboard.c:853: Test failed: 7.2: formats 00000000 have been rendered clipboard.c:858: Test failed: 7.2: formats 00000000 have been rendered menu.c:2354: Test failed: test 27
=== debian10 (64 bit WoW report) ===
user32: menu.c:2354: Test failed: test 27
Under XInput2 protocol version < 2.1, RawEvents are not supposed to be sent if a pointer grab is active. However slave device events are still received regardless of this specification and Wine implemented a workaround to receive RawEvents during pointer grabs by listening to these slave device events. Then, as soon as a mouse button is pressed only the grabbing client will receive the raw motion events.
By advertising the support of XInput2 version >= 2.1, where RawEvents are sent even during pointer grabs, we ensure to receive the RawMotion events from the desktop window thread, even if a mouse grab is active.
It is now also possible to simplify the code by listening to master device events only and get rid of slave device id tracking.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 49 ++++++++------------------------------- dlls/winex11.drv/x11drv.h | 3 --- 2 files changed, 10 insertions(+), 42 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index e95f39e86f9..5ae6dd2b9b1 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -304,12 +304,16 @@ static void enable_xinput2(void)
if (data->xi2_state == xi_unknown) { - int major = 2, minor = 0; - if (!pXIQueryVersion( data->display, &major, &minor )) data->xi2_state = xi_disabled; + int major = 2, minor = 1; + if (!pXIQueryVersion( data->display, &major, &minor ) && major == 2 && minor > 0) + { + TRACE( "XInput2 v%d.%d available\n", major, minor ); + data->xi2_state = xi_disabled; + } else { data->xi2_state = xi_unavailable; - WARN( "X Input 2 not available\n" ); + WARN( "XInput v2.1 not available\n" ); } } if (data->xi2_state == xi_unavailable) return; @@ -317,7 +321,7 @@ static void enable_xinput2(void)
mask.mask = mask_bits; mask.mask_len = sizeof(mask_bits); - mask.deviceid = XIAllDevices; + mask.deviceid = XIAllMasterDevices; memset( mask_bits, 0, sizeof(mask_bits) ); XISetMask( mask_bits, XI_DeviceChanged ); XISetMask( mask_bits, XI_RawMotion ); @@ -329,16 +333,6 @@ static void enable_xinput2(void) update_relative_valuators( pointer_info->classes, pointer_info->num_classes ); pXIFreeDeviceInfo( pointer_info );
- /* This device info list is only used to find the initial current slave if - * no XI_DeviceChanged events happened. If any hierarchy change occurred that - * might be relevant here (eg. user switching mice after (un)plugging), a - * XI_DeviceChanged event will point us to the right slave. So this list is - * safe to be obtained statically at enable_xinput2() time. - */ - if (data->xi2_devices) pXIFreeDeviceInfo( data->xi2_devices ); - data->xi2_devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count ); - data->xi2_current_slave = 0; - data->xi2_state = xi_enabled; #endif } @@ -359,15 +353,12 @@ static void disable_xinput2(void)
mask.mask = NULL; mask.mask_len = 0; - mask.deviceid = XIAllDevices; + mask.deviceid = XIAllMasterDevices;
pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 ); - pXIFreeDeviceInfo( data->xi2_devices ); data->x_rel_valuator.number = -1; data->y_rel_valuator.number = -1; - data->xi2_devices = NULL; data->xi2_core_pointer = 0; - data->xi2_current_slave = 0; #endif }
@@ -1771,7 +1762,6 @@ static BOOL X11DRV_DeviceChanged( XGenericEventCookie *xev ) if (event->reason != XISlaveSwitch) return FALSE;
update_relative_valuators( event->classes, event->num_classes ); - data->xi2_current_slave = event->sourceid; return TRUE; }
@@ -1792,26 +1782,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) if (thread_data->x_rel_valuator.number < 0 || thread_data->y_rel_valuator.number < 0) return FALSE; if (!event->valuators.mask_len) return FALSE; if (thread_data->xi2_state != xi_enabled) return FALSE; - - /* If there is no slave currently detected, no previous motion nor device - * change events were received. Look it up now on the device list in this - * case. - */ - if (!thread_data->xi2_current_slave) - { - XIDeviceInfo *devices = thread_data->xi2_devices; - - for (i = 0; i < thread_data->xi2_device_count; i++) - { - if (devices[i].use != XISlavePointer) continue; - if (devices[i].deviceid != event->deviceid) continue; - if (devices[i].attachment != thread_data->xi2_core_pointer) continue; - thread_data->xi2_current_slave = event->deviceid; - break; - } - } - - if (event->deviceid != thread_data->xi2_current_slave) return FALSE; + if (event->deviceid != thread_data->xi2_core_pointer) return FALSE;
x_rel = &thread_data->x_rel_valuator; y_rel = &thread_data->y_rel_valuator; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index ef8a42d609d..814231802b5 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -338,12 +338,9 @@ struct x11drv_thread_data DWORD clip_reset; /* time when clipping was last reset */ HKL kbd_layout; /* active keyboard layout */ enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } xi2_state; /* XInput2 state */ - void *xi2_devices; /* list of XInput2 devices (valid when state is enabled) */ - int xi2_device_count; struct x11drv_valuator_data x_rel_valuator; struct x11drv_valuator_data y_rel_valuator; int xi2_core_pointer; /* XInput2 core pointer id */ - int xi2_current_slave; /* Current slave driving the Core pointer */ };
extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60218
Your paranoid android.
=== debian10 (32 bit report) ===
user32: menu: Timeout monitor: Timeout msg: Timeout resource: Timeout scroll: Timeout static: Timeout sysparams: Timeout text: Timeout uitools: Timeout win: Timeout winstation: Timeout
=== debian10 (build log) ===
0009:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0009:err:winediag:nodrv_CreateWindow The explorer process failed to start. Task: WineTest did not produce the win32_zh_CN report
We still need to send "normal" input from the clipping window thread to trigger low-level hooks callbacks when clipping cursor. This is for instance used in our dinput implementation.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 8 ++-- dlls/winex11.drv/event.c | 10 ++++- dlls/winex11.drv/mouse.c | 79 ++++++++++++++++++++++++++++------ dlls/winex11.drv/x11drv.h | 3 ++ dlls/winex11.drv/x11drv_main.c | 4 ++ 5 files changed, 84 insertions(+), 20 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 8114bab228e..cb5efb4e33f 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2046,12 +2046,12 @@ static void test_rawinput_mouse(const char *argv0) { TRUE, TRUE, 0, 2, 2, -1, -1, FALSE },
/* clip cursor tests */ - { TRUE, TRUE, 0, 0, 0, 0, 0, TRUE }, - { TRUE, TRUE, 0, 20, 20, 20, 20, TRUE }, + { TRUE, TRUE, 0, 0, 0, 0, 0, FALSE }, + { TRUE, TRUE, 0, 20, 20, 20, 20, FALSE },
/* same-process foreground tests */ - { TRUE, TRUE, 0, 2, 2, 0, 0, TRUE }, - { TRUE, TRUE, RIDEV_INPUTSINK, 2, 2, 0, 0, TRUE }, + { TRUE, TRUE, 0, 2, 2, 0, 0, FALSE }, + { TRUE, TRUE, RIDEV_INPUTSINK, 2, 2, 0, 0, FALSE },
/* cross-process foreground tests */ { TRUE, TRUE, 0, 0, 0, 0, 0, FALSE }, diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index dd8837c11da..3e4519db66c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -321,6 +321,10 @@ static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawE */ static enum event_merge_action merge_events( XEvent *prev, XEvent *next ) { +#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H + struct x11drv_thread_data *thread_data = x11drv_thread_data(); +#endif + switch (prev->type) { case ConfigureNotify: @@ -352,19 +356,21 @@ static enum event_merge_action merge_events( XEvent *prev, XEvent *next ) case GenericEvent: if (next->xcookie.extension != xinput2_opcode) break; if (next->xcookie.evtype != XI_RawMotion) break; - if (x11drv_thread_data()->warp_serial) break; + if (thread_data->xi2_rawinput_only) break; + if (thread_data->warp_serial) break; return MERGE_KEEP; } break; case GenericEvent: if (prev->xcookie.extension != xinput2_opcode) break; if (prev->xcookie.evtype != XI_RawMotion) break; + if (thread_data->xi2_rawinput_only) break; switch (next->type) { case GenericEvent: if (next->xcookie.extension != xinput2_opcode) break; if (next->xcookie.evtype != XI_RawMotion) break; - if (x11drv_thread_data()->warp_serial) break; + if (thread_data->warp_serial) break; return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data ); #endif } diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 5ae6dd2b9b1..573d049ffc9 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -289,9 +289,9 @@ static void update_relative_valuators(XIAnyClassInfo **valuators, int n_valuator
/*********************************************************************** - * enable_xinput2 + * X11DRV_XInput2_Enable */ -static void enable_xinput2(void) +void X11DRV_XInput2_Enable(void) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H struct x11drv_thread_data *data = x11drv_thread_data(); @@ -323,9 +323,21 @@ static void enable_xinput2(void) mask.mask_len = sizeof(mask_bits); mask.deviceid = XIAllMasterDevices; memset( mask_bits, 0, sizeof(mask_bits) ); + XISetMask( mask_bits, XI_DeviceChanged ); XISetMask( mask_bits, XI_RawMotion ); - XISetMask( mask_bits, XI_ButtonPress ); + + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + { + XISetMask( mask_bits, XI_RawButtonPress ); + XISetMask( mask_bits, XI_RawButtonRelease ); + data->xi2_rawinput_only = TRUE; + } + else + { + XISetMask( mask_bits, XI_ButtonPress ); + data->xi2_rawinput_only = FALSE; + }
pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
@@ -338,9 +350,9 @@ static void enable_xinput2(void) }
/*********************************************************************** - * disable_xinput2 + * X11DRV_XInput2_Disable */ -static void disable_xinput2(void) +void X11DRV_XInput2_Disable(void) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H struct x11drv_thread_data *data = x11drv_thread_data(); @@ -400,7 +412,7 @@ static BOOL grab_clipping_window( const RECT *clip ) }
/* enable XInput2 unless we are already clipping */ - if (!data->clip_hwnd) enable_xinput2(); + if (!data->clip_hwnd) X11DRV_XInput2_Enable();
if (data->xi2_state != xi_enabled) { @@ -430,7 +442,7 @@ static BOOL grab_clipping_window( const RECT *clip )
if (!clipping_cursor) { - disable_xinput2(); + X11DRV_XInput2_Disable(); DestroyWindow( msg_hwnd ); return FALSE; } @@ -511,7 +523,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) TRACE( "clip hwnd reset from %p\n", hwnd ); data->clip_hwnd = 0; data->clip_reset = GetTickCount(); - disable_xinput2(); + X11DRV_XInput2_Disable(); DestroyWindow( hwnd ); } else if (hwnd == GetForegroundWindow()) /* request to clip */ @@ -611,7 +623,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU } input->u.mi.dx += clip_rect.left; input->u.mi.dy += clip_rect.top; - __wine_send_input( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); + __wine_send_input( hwnd, input, SEND_HWMSG_WINDOW ); return; }
@@ -672,7 +684,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU
input->u.mi.dx = pt.x; input->u.mi.dy = pt.y; - __wine_send_input( hwnd, input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); + __wine_send_input( hwnd, input, SEND_HWMSG_WINDOW ); }
#ifdef SONAME_LIBXCURSOR @@ -1618,7 +1630,7 @@ void move_resize_window( HWND hwnd, int dir ) input.u.mi.dwFlags = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = GetTickCount(); input.u.mi.dwExtraInfo = 0; - __wine_send_input( hwnd, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); + __wine_send_input( hwnd, &input, SEND_HWMSG_WINDOW ); }
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) @@ -1787,6 +1799,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) x_rel = &thread_data->x_rel_valuator; y_rel = &thread_data->y_rel_valuator;
+ input.type = INPUT_MOUSE; input.u.mi.mouseData = 0; input.u.mi.dwFlags = MOUSEEVENTF_MOVE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); @@ -1822,10 +1835,44 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) return FALSE; }
- TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + if (!thread_data->xi2_rawinput_only) + { + TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + __wine_send_input( 0, &input, SEND_HWMSG_WINDOW ); + } + else + { + TRACE( "raw pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + __wine_send_input( 0, &input, SEND_HWMSG_RAWINPUT ); + } + return TRUE; +} + +/*********************************************************************** + * X11DRV_RawButtonEvent + */ +static BOOL X11DRV_RawButtonEvent( XGenericEventCookie *cookie ) +{ + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XIRawEvent *event = cookie->data; + int button = event->detail - 1; + INPUT input;
- input.type = INPUT_MOUSE; - __wine_send_input( 0, &input, SEND_HWMSG_RAWINPUT|SEND_HWMSG_WINDOW ); + if (button >= NB_BUTTONS) return FALSE; + if (thread_data->xi2_state != xi_enabled) return FALSE; + if (event->deviceid != thread_data->xi2_core_pointer) return FALSE; + + TRACE( "raw button %u %s\n", button, event->evtype == XI_RawButtonRelease ? "up" : "down" ); + + input.type = INPUT_MOUSE; + input.u.mi.dx = 0; + input.u.mi.dy = 0; + input.u.mi.mouseData = event->evtype == XI_RawButtonRelease ? button_up_data[button] : button_down_data[button]; + input.u.mi.dwFlags = event->evtype == XI_RawButtonRelease ? button_up_flags[button] : button_down_flags[button]; + input.u.mi.time = EVENT_x11_time_to_win32_time(event->time); + input.u.mi.dwExtraInfo = 0; + + __wine_send_input( 0, &input, SEND_HWMSG_RAWINPUT ); return TRUE; }
@@ -1893,6 +1940,10 @@ BOOL X11DRV_GenericEvent( HWND hwnd, XEvent *xev ) case XI_RawMotion: ret = X11DRV_RawMotion( event ); break; + case XI_RawButtonPress: + case XI_RawButtonRelease: + ret = X11DRV_RawButtonEvent( event ); + break;
default: TRACE( "Unhandled event %#x\n", event->evtype ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 814231802b5..1188c10286b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -196,6 +196,8 @@ extern BOOL CDECL X11DRV_UnrealizePalette( HPALETTE hpal ) DECLSPEC_HIDDEN;
extern void X11DRV_Xcursor_Init(void) DECLSPEC_HIDDEN; extern void X11DRV_XInput2_Init(void) DECLSPEC_HIDDEN; +extern void X11DRV_XInput2_Enable(void) DECLSPEC_HIDDEN; +extern void X11DRV_XInput2_Disable(void) DECLSPEC_HIDDEN;
extern DWORD copy_image_bits( BITMAPINFO *info, BOOL is_r8g8b8, XImage *image, const struct gdi_image_bits *src_bits, struct gdi_image_bits *dst_bits, @@ -341,6 +343,7 @@ struct x11drv_thread_data struct x11drv_valuator_data x_rel_valuator; struct x11drv_valuator_data y_rel_valuator; int xi2_core_pointer; /* XInput2 core pointer id */ + int xi2_rawinput_only; };
extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 4f611f5faa6..aedc0faad53 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -634,6 +634,8 @@ void CDECL X11DRV_ThreadDetach(void)
if (data) { + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + X11DRV_XInput2_Disable(); if (data->xim) XCloseIM( data->xim ); if (data->font_set) XFreeFontSet( data->display, data->font_set ); XCloseDisplay( data->display ); @@ -703,6 +705,8 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) TlsSetValue( thread_data_tls_index, data );
if (use_xim) X11DRV_SetupXIM(); + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + X11DRV_XInput2_Enable();
return data; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60219
Your paranoid android.
=== w1064v1809 (64 bit report) ===
user32: input.c:2590: Test failed: expected WM_LBUTTONDOWN message input.c:2591: Test failed: expected WM_LBUTTONUP message
=== debian10 (32 bit Chinese:China report) ===
user32: msg.c:8782: Test failed: WaitForSingleObject failed 102 msg.c:8788: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8788: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8788: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
=== debian10 (32 bit WoW report) ===
user32: msg.c:8782: Test failed: WaitForSingleObject failed 102 msg.c:8788: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8788: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8788: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000