From: Rémi Bernon rbernon@codeweavers.com
When hwnd is specified, it is because it received a direct host input, so switch the input desktop to match the one that is receiving it.
We don't validate that the sending thread uses the same desktop as the target window: it may not even be the case for drivers with a separate thread that listens on input events. --- dlls/user32/tests/winstation.c | 6 ++---- server/queue.c | 39 +++++++++++++++++++++++----------- server/user.h | 2 ++ server/winstation.c | 10 ++++++++- 4 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/dlls/user32/tests/winstation.c b/dlls/user32/tests/winstation.c index 11ad7685e97..bcbb55ee918 100644 --- a/dlls/user32/tests/winstation.c +++ b/dlls/user32/tests/winstation.c @@ -652,9 +652,8 @@ static void test_inputdesktop(void) win_skip("Skip tests on NT4\n"); return; } - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08lx\n", GetLastError()); - ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %ld\n", ret); + ok(ret == 0 || broken(ret == 1) /* Win32 */, "unexpected return count %ld\n", ret);
/* Set thread desktop back to the old thread desktop, SendInput should success. */ ret = SetThreadDesktop(old_thread_desk); @@ -699,9 +698,8 @@ static void test_inputdesktop(void)
SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08lx\n", GetLastError()); - ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %ld\n", ret); + ok(ret == 0 || broken(ret == 1) /* Win32 */, "unexpected return count %ld\n", ret);
/* Set thread desktop to the new desktop, SendInput should success. */ ret = SetThreadDesktop(new_desk); diff --git a/server/queue.c b/server/queue.c index 6f38227aa84..6fa68f9c6b2 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2764,25 +2764,41 @@ DECL_HANDLER(send_message) release_object( thread ); }
+static struct desktop *get_hardware_input_desktop( user_handle_t win ) +{ + struct winstation *winstation; + struct desktop *desktop; + struct thread *thread; + + if (!win || !(thread = get_window_thread( win ))) + { + if (!(winstation = get_visible_winstation())) return NULL; + return get_input_desktop( winstation ); + } + else + { + /* if window is specified, use its desktop to make it the input desktop */ + desktop = (struct desktop *)grab_object( thread->queue->input->desktop ); + release_object( thread ); + } + + return desktop; +} + /* send a hardware message to a thread queue */ DECL_HANDLER(send_hardware_message) { - struct thread *thread = NULL; struct desktop *desktop; unsigned int origin = (req->flags & SEND_HWMSG_INJECTED ? IMO_INJECTED : IMO_HARDWARE); struct msg_queue *sender = get_current_queue();
- if (!(desktop = get_thread_desktop( current, 0 ))) return; - - if (req->win) + if (!(desktop = get_hardware_input_desktop( req->win ))) return; + if ((origin == IMO_INJECTED && desktop != current->queue->input->desktop) || + !set_input_desktop( desktop->winstation, desktop )) { - if (!(thread = get_window_thread( req->win ))) return; - if (desktop != thread->queue->input->desktop) - { - /* don't allow queuing events to a different desktop */ - release_object( desktop ); - return; - } + release_object( desktop ); + set_error( STATUS_ACCESS_DENIED ); + return; }
reply->prev_x = desktop->cursor.x; @@ -2802,7 +2818,6 @@ DECL_HANDLER(send_hardware_message) default: set_error( STATUS_INVALID_PARAMETER ); } - if (thread) release_object( thread );
reply->new_x = desktop->cursor.x; reply->new_y = desktop->cursor.y; diff --git a/server/user.h b/server/user.h index 15bf08e1e4f..c3020af5586 100644 --- a/server/user.h +++ b/server/user.h @@ -185,6 +185,8 @@ extern client_ptr_t get_class_client_ptr( struct window_class *class );
/* windows station functions */
+extern struct winstation *get_visible_winstation(void); +extern struct desktop *get_input_desktop( struct winstation *winsta ); extern int set_input_desktop( struct winstation *winstation, struct desktop *desktop ); extern struct desktop *get_desktop_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern struct winstation *get_process_winstation( struct process *process, unsigned int access ); diff --git a/server/winstation.c b/server/winstation.c index b1b445aeefa..e0b912dbed6 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -468,7 +468,15 @@ void release_thread_desktop( struct thread *thread, int close ) } }
-static struct desktop *get_input_desktop( struct winstation *winstation ) +struct winstation *get_visible_winstation(void) +{ + struct winstation *winsta; + LIST_FOR_EACH_ENTRY( winsta, &winstation_list, struct winstation, entry ) + if (winsta->flags & WSF_VISIBLE) return winsta; + return NULL; +} + +struct desktop *get_input_desktop( struct winstation *winstation ) { struct desktop *desktop; if (!(desktop = winstation->input_desktop)) return NULL;