Another thread is created to execute the tests, for SetThreadDesktop to succeed consistently. It seems to fail spuriously if it is called from a thread that already created some windows before.
This shows that rawinput messages may be dispatched across desktops, but only if the subscribing process has a window in the input desktop, and it is the foreground process (even if the target window may be in another desktop).
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v3: Add a cross-desktop test and rewrite queue_rawinput_message to be less suspicious, hopefully. Only enumerate processes when actually needed, ie: when implementing RIDEV_INPUTSINK flag support.
dlls/user32/tests/input.c | 119 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 9ec829860af..01733317b2b 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -50,6 +50,7 @@
#include "windef.h" #include "winbase.h" +#include "wingdi.h" #include "winuser.h" #include "winnls.h" #include "ddk/hidsdi.h" @@ -60,6 +61,8 @@ static HWND hWndTest; static LONG timetag = 0x10000000;
+#define DESKTOP_ALL_ACCESS 0x01ff + static struct { LONG last_key_down; LONG last_key_up; @@ -1891,6 +1894,11 @@ struct rawinput_test rawinput_tests[] =
{ TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, + + /* cross-desktop foreground tests */ + { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, + { TRUE, TRUE, 0, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, + { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, };
static void rawinput_test_process(void) @@ -1927,6 +1935,7 @@ static void rawinput_test_process(void) case 11: case 12: case 13: + case 16: GetCursorPos(&pt);
hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, @@ -1960,6 +1969,9 @@ static void rawinput_test_process(void) rawinput_test_received_raw = FALSE; rawinput_test_received_rawfg = FALSE;
+ /* fallthrough */ + case 14: + case 15: if (i != 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); empty_message_queue(); break; @@ -1998,19 +2010,87 @@ static void rawinput_test_process(void)
struct rawinput_test_thread_params { + HDESK desk; HANDLE ready; HANDLE start; HANDLE done; };
+static DWORD WINAPI rawinput_test_desk_thread(void *arg) +{ + struct rawinput_test_thread_params *params = arg; + RAWINPUTDEVICE raw_devices[1]; + DWORD ret; + POINT pt; + HWND hwnd = NULL; + MSG msg; + int i; + + ok( SetThreadDesktop( params->desk ), "SetThreadDesktop failed\n" ); + + for (i = 14; i < ARRAY_SIZE(rawinput_tests); ++i) + { + WaitForSingleObject(params->ready, INFINITE); + ResetEvent(params->ready); + + switch (i) + { + case 14: + case 15: + case 16: + GetCursorPos(&pt); + + hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, + pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); + ok(hwnd != 0, "CreateWindow failed\n"); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); + empty_message_queue(); + + /* FIXME: Try to workaround X11/Win32 focus inconsistencies and + * make the window visible and foreground as hard as possible. */ + ShowWindow(hwnd, SW_SHOW); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(hwnd); + UpdateWindow(hwnd); + empty_message_queue(); + + raw_devices[0].usUsagePage = 0x01; + raw_devices[0].usUsage = 0x02; + raw_devices[0].dwFlags = rawinput_tests[i].register_flags; + raw_devices[0].hwndTarget = rawinput_tests[i].register_window ? hwnd : 0; + + rawinput_test_received_legacy = FALSE; + rawinput_test_received_raw = FALSE; + rawinput_test_received_rawfg = FALSE; + + SetLastError(0xdeadbeef); + ret = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); + ok(ret, "%d: RegisterRawInputDevices failed\n", i); + ok(GetLastError() == 0xdeadbeef, "%d: RegisterRawInputDevices returned %08x\n", i, GetLastError()); + break; + } + + SetEvent(params->start); + + while (MsgWaitForMultipleObjects(1, ¶ms->done, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + ResetEvent(params->done); + if (hwnd) DestroyWindow(hwnd); + } + + return 0; +} + static DWORD WINAPI rawinput_test_thread(void *arg) { struct rawinput_test_thread_params *params = arg; + HANDLE thread; POINT pt; HWND hwnd = NULL; int i;
- for (i = 0; i < ARRAY_SIZE(rawinput_tests); ++i) + for (i = 0; i < 14; ++i) { WaitForSingleObject(params->ready, INFINITE); ResetEvent(params->ready); @@ -2046,6 +2126,11 @@ static DWORD WINAPI rawinput_test_thread(void *arg) if (hwnd) DestroyWindow(hwnd); }
+ thread = CreateThread(NULL, 0, rawinput_test_desk_thread, params, 0, NULL); + ok(thread != NULL, "CreateThread failed\n"); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + return 0; }
@@ -2063,6 +2148,9 @@ static void test_rawinput(const char* argv0) char path[MAX_PATH]; int i;
+ params.desk = CreateDesktopA( "rawinput_test_desktop", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + ok( params.desk != NULL, "CreateDesktopA failed, last error: %u\n", GetLastError() ); + params.ready = CreateEventA(NULL, FALSE, FALSE, NULL); ok(params.ready != NULL, "CreateEvent failed\n");
@@ -2103,7 +2191,8 @@ static void test_rawinput(const char* argv0) hwnd = CreateWindowA("static", "static", WS_VISIBLE | WS_POPUP, pt.x - 50, pt.y - 50, 100, 100, 0, NULL, NULL, NULL); ok(hwnd != 0, "CreateWindow failed\n"); - SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); + if (i != 14 && i != 15 && i != 16) + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)rawinput_wndproc); empty_message_queue();
/* FIXME: Try to workaround X11/Win32 focus inconsistencies and @@ -2135,14 +2224,30 @@ static void test_rawinput(const char* argv0) ok(!skipped, "%d: RegisterRawInputDevices failed: %u\n", i, GetLastError()); }
- SetEvent(process_ready); - WaitForSingleObject(process_start, INFINITE); - ResetEvent(process_start); - SetEvent(params.ready); WaitForSingleObject(params.start, INFINITE); ResetEvent(params.start);
+ /* we need the main window to be over the other thread window, as although + * it is in another desktop, it will receive the messages directly otherwise */ + switch (i) + { + case 14: + case 15: + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + SetForegroundWindow(hwnd); + empty_message_queue(); + + rawinput_test_received_legacy = FALSE; + rawinput_test_received_raw = FALSE; + rawinput_test_received_rawfg = FALSE; + break; + } + + SetEvent(process_ready); + WaitForSingleObject(process_start, INFINITE); + ResetEvent(process_start); + if (i <= 3 || i == 8) mouse_event(MOUSEEVENTF_MOVE, 5, 0, 0, 0); empty_message_queue();
@@ -2192,6 +2297,8 @@ static void test_rawinput(const char* argv0) CloseHandle(params.start); CloseHandle(params.ready); CloseHandle(thread); + + CloseDesktop(params.desk); }
static void test_key_map(void)
This delivers the rawinput messages to the correct process, regardless of where the input was received.
As for now RIDEV_INPUTSINK is still not implemented, this only fixes the case where input is injected in a background process and where it should not receive rawinput -as in the test- or when cursor moves over a background window and the foreground process should have received rawinput messages.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 2 +- server/queue.c | 93 +++++++++++++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 01733317b2b..6f1fa9b3e9c 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1885,7 +1885,7 @@ struct rawinput_test rawinput_tests[] = /* cross-process foreground tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, - { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, TRUE, TRUE }, + { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
/* multi-process rawinput tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, diff --git a/server/queue.c b/server/queue.c index 84ee0f9a4ea..4a14b1bf554 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1596,13 +1596,68 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa return 1; }
+/* get the foreground thread for a desktop and a window receiving input */ +static struct thread *get_foreground_thread( struct desktop *desktop, user_handle_t window ) +{ + /* if desktop has no foreground process, assume the receiving window is */ + if (desktop->foreground_input) return get_window_thread( desktop->foreground_input->active ); + if (window) return get_window_thread( get_user_full_handle( window ) ); + return NULL; +} + +struct rawinput_message +{ + struct thread *foreground; + struct hw_msg_source source; + unsigned int time; + struct hardware_msg_data data; +}; + +static int queue_rawinput_message( struct process* process, void *arg ) +{ + const struct rawinput_message* raw_msg = arg; + const struct rawinput_device *device = NULL; + struct desktop *target_desktop = NULL; + struct thread *target_thread = 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 (device->target) target_thread = get_window_thread( device->target ); + else target_thread = (struct thread *)grab_object( raw_msg->foreground ); + + if (!target_thread || target_thread->process != process) goto done; + if (!(target_desktop = get_thread_desktop( target_thread, 0 ))) 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( target_desktop, msg, 0 ); + +done: + if (target_thread) release_object( target_thread ); + if (target_desktop) release_object( target_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 ) { - const struct rawinput_device *device; struct hardware_msg_data *msg_data; + struct rawinput_message raw_msg; struct message *msg; + struct thread *foreground; unsigned int i, time, flags; struct hw_msg_source source = { IMDT_MOUSE, origin }; int wait = 0, x, y; @@ -1651,23 +1706,22 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons y = desktop->cursor.y; }
- if ((device = current->process->rawinput_mouse)) + if ((foreground = get_foreground_thread( desktop, win ))) { - 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.foreground = foreground; + 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 ); + queue_rawinput_message( foreground->process, &raw_msg ); + release_object( foreground ); }
for (i = 0; i < ARRAY_SIZE( messages ); i++) @@ -1704,9 +1758,10 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int origin, struct msg_queue *sender ) { 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; + struct thread *foreground; unsigned char vkey = input->kbd.vkey; unsigned int message_code, time; int wait; @@ -1777,22 +1832,22 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
- if ((device = current->process->rawinput_kbd)) + if ((foreground = get_foreground_thread( desktop, win ))) { - 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.foreground = foreground; + 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 ); + queue_rawinput_message( foreground->process, &raw_msg ); + release_object( foreground ); }
if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0;
On 2020-06-24 13:23, Rémi Bernon wrote:
This delivers the rawinput messages to the correct process, regardless of where the input was received.
As for now RIDEV_INPUTSINK is still not implemented, this only fixes the case where input is injected in a background process and where it should not receive rawinput -as in the test- or when cursor moves over a background window and the foreground process should have received rawinput messages.
And now that I'm not head down into trying to tweak the patches anymore, and that I start looking around, I realize it's probably still quite suspicious... sorry about the noise.
+/* get the foreground thread for a desktop and a window receiving input */ +static struct thread *get_foreground_thread( struct desktop *desktop, user_handle_t window ) +{
- /* if desktop has no foreground process, assume the receiving window is */
- if (desktop->foreground_input) return get_window_thread( desktop->foreground_input->active );
- if (window) return get_window_thread( get_user_full_handle( window ) );
- return NULL;
+}
This should for instance be ->focus and not ->active.
I also now see that the target window dispatch is already pretty much in queue_hardware_message and find_hardware_message_window. So queue_rawinput_message could be simplified again.
I don't see anywhere a cross-desktop check like the one that seems to be required for rawinput (where if the target window is on another desktop, messages should not be sent unless the process has input focus).
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- server/queue.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/server/queue.c b/server/queue.c index 4a14b1bf554..8c3d3e6009b 100644 --- a/server/queue.c +++ b/server/queue.c @@ -366,6 +366,20 @@ static struct message *alloc_hardware_message( lparam_t info, struct hw_msg_sour return msg; }
+static int update_desktop_cursor_pos( struct desktop *desktop, int x, int y ) +{ + int updated; + + x = max( min( x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); + y = max( min( y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); + updated = (desktop->cursor.x != x || desktop->cursor.y != y); + desktop->cursor.x = x; + desktop->cursor.y = y; + desktop->cursor.last_change = get_tick_count(); + + return updated; +} + /* set the cursor position and queue the corresponding mouse message */ static void set_cursor_pos( struct desktop *desktop, int x, int y ) { @@ -1500,15 +1514,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg } else if (msg->msg != WM_INPUT) { - if (msg->msg == WM_MOUSEMOVE) - { - int x = max( min( msg->x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); - int y = max( min( msg->y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); - if (desktop->cursor.x != x || desktop->cursor.y != y) always_queue = 1; - desktop->cursor.x = x; - desktop->cursor.y = y; - desktop->cursor.last_change = get_tick_count(); - } + if (msg->msg == WM_MOUSEMOVE && update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; if (desktop->keystate[VK_LBUTTON] & 0x80) msg->wparam |= MK_LBUTTON; if (desktop->keystate[VK_MBUTTON] & 0x80) msg->wparam |= MK_MBUTTON; if (desktop->keystate[VK_RBUTTON] & 0x80) msg->wparam |= MK_RBUTTON;
So that we can update individual states in next patch.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- server/queue.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/server/queue.c b/server/queue.c index 8c3d3e6009b..9b5089bdc57 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1290,12 +1290,12 @@ static void set_input_key_state( unsigned char *keystate, unsigned char key, int
/* update the input key state for a keyboard message */ static void update_input_key_state( struct desktop *desktop, unsigned char *keystate, - const struct message *msg ) + unsigned int msg, lparam_t wparam ) { unsigned char key; int down = 0;
- switch (msg->msg) + switch (msg) { case WM_LBUTTONDOWN: down = (keystate == desktop->keystate) ? 0xc0 : 0x80; @@ -1319,8 +1319,8 @@ static void update_input_key_state( struct desktop *desktop, unsigned char *keys down = (keystate == desktop->keystate) ? 0xc0 : 0x80; /* fall through */ case WM_XBUTTONUP: - if (msg->wparam >> 16 == XBUTTON1) set_input_key_state( keystate, VK_XBUTTON1, down ); - else if (msg->wparam >> 16 == XBUTTON2) set_input_key_state( keystate, VK_XBUTTON2, down ); + if (wparam >> 16 == XBUTTON1) set_input_key_state( keystate, VK_XBUTTON1, down ); + else if (wparam >> 16 == XBUTTON2) set_input_key_state( keystate, VK_XBUTTON2, down ); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: @@ -1328,7 +1328,7 @@ static void update_input_key_state( struct desktop *desktop, unsigned char *keys /* fall through */ case WM_KEYUP: case WM_SYSKEYUP: - key = (unsigned char)msg->wparam; + key = (unsigned char)wparam; set_input_key_state( keystate, key, down ); switch(key) { @@ -1382,7 +1382,7 @@ static void release_hardware_message( struct msg_queue *queue, unsigned int hw_i } if (clr_bit) clear_queue_bits( queue, clr_bit );
- update_input_key_state( input->desktop, input->keystate, msg ); + update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); } @@ -1501,7 +1501,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg struct thread_input *input; unsigned int msg_code;
- update_input_key_state( desktop, desktop->keystate, msg ); + update_input_key_state( desktop, desktop->keystate, msg->msg, msg->wparam ); last_input_time = get_tick_count(); if (msg->msg != WM_MOUSEMOVE) always_queue = 1;
@@ -1536,7 +1536,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg win = find_hardware_message_window( desktop, input, msg, &msg_code, &thread ); if (!win || !thread) { - if (input) update_input_key_state( input->desktop, input->keystate, msg ); + if (input) update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); free_message( msg ); return; } @@ -1981,7 +1981,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user if (!win || !win_thread) { /* no window at all, remove it */ - update_input_key_state( input->desktop, input->keystate, msg ); + update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); continue; @@ -1997,7 +1997,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user else { /* for another thread input, drop it */ - update_input_key_state( input->desktop, input->keystate, msg ); + update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); }
This makes legacy mouse window messages such as WM_MOUSEMOVE and others, to stop being sent, including to low-level hooks. The desktop mouse state should still be udpated.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/rawinput.c | 2 +- dlls/user32/tests/input.c | 2 +- server/queue.c | 45 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index dd2ac2e208b..7a9eb6ced17 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -306,7 +306,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U TRACE("device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, devices[i].usUsagePage, devices[i].usUsage, devices[i].dwFlags, devices[i].hwndTarget); - if (devices[i].dwFlags & ~RIDEV_REMOVE) + if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY)) FIXME("Unhandled flags %#x for device %u.\n", devices[i].dwFlags, i);
d[i].usage_page = devices[i].usUsagePage; diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 6f1fa9b3e9c..8ae44e4ab92 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1876,7 +1876,7 @@ struct rawinput_test rawinput_tests[] = { FALSE, FALSE, 0, TRUE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, FALSE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, TRUE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE }, - { TRUE, TRUE, RIDEV_NOLEGACY, FALSE, TRUE, TRUE, /* todos: */ TRUE, FALSE, FALSE }, + { TRUE, TRUE, RIDEV_NOLEGACY, FALSE, TRUE, TRUE, /* todos: */ FALSE, FALSE, FALSE },
/* same-process foreground tests */ { TRUE, FALSE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, diff --git a/server/queue.c b/server/queue.c index 9b5089bdc57..8d8eea21120 100644 --- a/server/queue.c +++ b/server/queue.c @@ -384,8 +384,15 @@ static int update_desktop_cursor_pos( struct desktop *desktop, int x, int y ) static void set_cursor_pos( struct desktop *desktop, int x, int y ) { static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; + const struct rawinput_device *device; struct message *msg;
+ if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) + { + update_desktop_cursor_pos( desktop, x, y ); + return; + } + if (!(msg = alloc_hardware_message( 0, source, get_tick_count() ))) return;
msg->msg = WM_MOUSEMOVE; @@ -1352,6 +1359,30 @@ static void update_input_key_state( struct desktop *desktop, unsigned char *keys } }
+/* update the desktop key state according to a mouse message flags */ +static void update_desktop_mouse_state( struct desktop *desktop, unsigned int flags, + int x, int y, lparam_t wparam ) +{ + if (flags & MOUSEEVENTF_MOVE) + update_desktop_cursor_pos( desktop, x, y ); + if (flags & MOUSEEVENTF_LEFTDOWN) + update_input_key_state( desktop, desktop->keystate, WM_LBUTTONDOWN, wparam ); + if (flags & MOUSEEVENTF_LEFTUP) + update_input_key_state( desktop, desktop->keystate, WM_LBUTTONUP, wparam ); + if (flags & MOUSEEVENTF_RIGHTDOWN) + update_input_key_state( desktop, desktop->keystate, WM_RBUTTONDOWN, wparam ); + if (flags & MOUSEEVENTF_RIGHTUP) + update_input_key_state( desktop, desktop->keystate, WM_RBUTTONUP, wparam ); + if (flags & MOUSEEVENTF_MIDDLEDOWN) + update_input_key_state( desktop, desktop->keystate, WM_MBUTTONDOWN, wparam ); + if (flags & MOUSEEVENTF_MIDDLEUP) + update_input_key_state( desktop, desktop->keystate, WM_MBUTTONUP, wparam ); + if (flags & MOUSEEVENTF_XDOWN) + update_input_key_state( desktop, desktop->keystate, WM_XBUTTONDOWN, wparam ); + if (flags & MOUSEEVENTF_XUP) + update_input_key_state( desktop, desktop->keystate, WM_XBUTTONUP, wparam ); +} + /* release the hardware message currently being processed by the given thread */ static void release_hardware_message( struct msg_queue *queue, unsigned int hw_id, int remove ) @@ -1660,6 +1691,7 @@ done: static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, unsigned int origin, struct msg_queue *sender ) { + const struct rawinput_device *device; struct hardware_msg_data *msg_data; struct rawinput_message raw_msg; struct message *msg; @@ -1730,6 +1762,12 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons release_object( foreground ); }
+ if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) + { + update_desktop_mouse_state( desktop, flags, x, y, input->mouse.data << 16 ); + return 0; + } + for (i = 0; i < ARRAY_SIZE( messages ); i++) { if (!messages[i]) continue; @@ -1764,6 +1802,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int origin, struct msg_queue *sender ) { 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; @@ -1856,6 +1895,12 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c release_object( foreground ); }
+ if ((device = current->process->rawinput_kbd) && (device->flags & RIDEV_NOLEGACY)) + { + update_input_key_state( desktop, desktop->keystate, message_code, vkey ); + return 0; + } + if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->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=74103
Your paranoid android.
=== debiant (64 bit WoW report) ===
user32: win.c:10143: Test failed: Expected foreground window 0, got 00E10102 win.c:10149: Test failed: Expected foreground window 000D013E, got 00E10102
This flag allows applications to receive rawinput messages while in background. They have to specify a target hwnd, which will receive them, and the messages will carry a RIM_INPUTSINK wparam if the process wasn't foreground.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/rawinput.c | 9 ++++++++- dlls/user32/tests/input.c | 8 +++----- server/queue.c | 18 ++++++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index 7a9eb6ced17..103835e0e33 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -291,6 +291,13 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U
for (i = 0; i < device_count; ++i) { + if ((devices[i].dwFlags & RIDEV_INPUTSINK) && + (devices[i].hwndTarget == NULL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if ((devices[i].dwFlags & RIDEV_REMOVE) && (devices[i].hwndTarget != NULL)) { @@ -306,7 +313,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U TRACE("device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, devices[i].usUsagePage, devices[i].usUsage, devices[i].dwFlags, devices[i].hwndTarget); - if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY)) + if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY|RIDEV_INPUTSINK)) FIXME("Unhandled flags %#x for device %u.\n", devices[i].dwFlags, i);
d[i].usage_page = devices[i].usUsagePage; diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 8ae44e4ab92..51b18242444 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1807,9 +1807,7 @@ static void test_RegisterRawInputDevices(void)
SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); - todo_wine ok(res == FALSE, "RegisterRawInputDevices failed\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
raw_devices[0].hwndTarget = hwnd; @@ -1884,13 +1882,13 @@ struct rawinput_test rawinput_tests[] =
/* cross-process foreground tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, - { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, + { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
/* multi-process rawinput tests */ { TRUE, TRUE, 0, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, - { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, - { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, + { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, + { TRUE, TRUE, RIDEV_INPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, FALSE, FALSE },
{ TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, FALSE, FALSE, /* todos: */ FALSE, FALSE, FALSE }, { TRUE, TRUE, RIDEV_EXINPUTSINK, FALSE, TRUE, FALSE, /* todos: */ FALSE, TRUE, FALSE }, diff --git a/server/queue.c b/server/queue.c index 8d8eea21120..5114b9cd242 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1654,9 +1654,10 @@ static int queue_rawinput_message( struct process* process, void *arg ) { const struct rawinput_message* raw_msg = arg; const struct rawinput_device *device = NULL; - struct desktop *target_desktop = NULL; + struct desktop *target_desktop = NULL, *input_desktop = NULL; struct thread *target_thread = NULL; struct message *msg; + int wparam = RIM_INPUT;
if (raw_msg->data.rawinput.type == RIM_TYPEMOUSE) device = process->rawinput_mouse; @@ -1670,12 +1671,20 @@ static int queue_rawinput_message( struct process* process, void *arg ) if (!target_thread || target_thread->process != process) goto done; if (!(target_desktop = get_thread_desktop( target_thread, 0 ))) goto done;
+ if (process != raw_msg->foreground->process) + { + if (!(device->flags & RIDEV_INPUTSINK)) goto done; + if (!(input_desktop = get_thread_desktop( raw_msg->foreground, 0 ))) goto done; + if (target_desktop != input_desktop) goto done; + wparam = RIM_INPUTSINK; + } + 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->wparam = wparam; msg->lparam = 0; memcpy( msg->data, &raw_msg->data, sizeof(raw_msg->data) );
@@ -1684,6 +1693,7 @@ static int queue_rawinput_message( struct process* process, void *arg ) done: if (target_thread) release_object( target_thread ); if (target_desktop) release_object( target_desktop ); + if (input_desktop) release_object( input_desktop ); return 0; }
@@ -1758,7 +1768,7 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons msg_data->rawinput.mouse.y = y - desktop->cursor.y; msg_data->rawinput.mouse.data = input->mouse.data;
- queue_rawinput_message( foreground->process, &raw_msg ); + enum_processes( queue_rawinput_message, &raw_msg ); release_object( foreground ); }
@@ -1891,7 +1901,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c msg_data->rawinput.kbd.vkey = vkey; msg_data->rawinput.kbd.scan = input->kbd.scan;
- queue_rawinput_message( foreground->process, &raw_msg ); + enum_processes( queue_rawinput_message, &raw_msg ); release_object( foreground ); }
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=74104
Your paranoid android.
=== debiant (32 bit report) ===
user32: win.c:10185: Test failed: Expected foreground window 000D013E, got 00000000 win.c:10200: Test failed: GetActiveWindow() = 000D013E win.c:10200: Test failed: GetFocus() = 000D013E win.c:10212: Test succeeded inside todo block: Expected WM_ACTIVATEAPP(0), did not receive it.
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=74099
Your paranoid android.
=== w1064v1809_ja (32 bit report) ===
user32: input.c:1424: Test failed: Wrong new pos: (150,150)