From a30297532d391a0fb9688e4db5abe8ba2e4fb7ab Mon Sep 17 00:00:00 2001 From: Vincent Povirk Date: Thu, 26 May 2011 15:52:17 -0500 Subject: [PATCH 1/8] server: Implement RegisterHotKey/UnregisterHotKey. --- dlls/user32/input.c | 40 ++++++++++-- dlls/user32/tests/msg.c | 14 ++-- server/protocol.def | 23 +++++++ server/queue.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++ server/user.h | 2 + server/window.c | 1 + server/winstation.c | 2 + 7 files changed, 229 insertions(+), 13 deletions(-) diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 28377e7..335be1f 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -983,9 +983,25 @@ UINT WINAPI GetKeyboardLayoutList(INT nBuff, HKL *layouts) */ BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk) { - static int once; - if (!once++) FIXME_(keyboard)("(%p,%d,0x%08x,%X): stub\n",hwnd,id,modifiers,vk); - return TRUE; + BOOL ret; + + TRACE_(keyboard)("(%p,%d,0x%08x,%X)\n",hwnd,id,modifiers,vk); + + /* FIXME: Register hotkey with user driver. */ + + SERVER_START_REQ( register_hotkey ) + { + req->window = wine_server_user_handle( hwnd ); + req->id = id; + req->flags = modifiers; + req->vkey = vk; + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; + + /* FIXME: Unregister new or replaced hotkey with user driver if necessary. */ + + return ret; } /*********************************************************************** @@ -993,9 +1009,21 @@ BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk) */ BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id) { - static int once; - if (!once++) FIXME_(keyboard)("(%p,%d): stub\n",hwnd,id); - return TRUE; + BOOL ret; + + TRACE_(keyboard)("(%p,%d)\n",hwnd,id); + + SERVER_START_REQ( unregister_hotkey ) + { + req->window = wine_server_user_handle( hwnd ); + req->id = id; + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; + + /* FIXME: Unregister hotkey with user driver if necessary. */ + + return ret; } /*********************************************************************** diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 3d9452d..0f1f40d 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -13132,8 +13132,8 @@ static void test_hotkey(void) SetLastError(0xdeadbeef); ret = UnregisterHotKey(NULL, 0); - todo_wine ok(ret == FALSE, "expected FALSE, got %i\n", ret); - todo_wine ok(GetLastError() == ERROR_HOTKEY_NOT_REGISTERED, "unexpected error %d\n", GetLastError()); + ok(ret == FALSE, "expected FALSE, got %i\n", ret); + ok(GetLastError() == ERROR_HOTKEY_NOT_REGISTERED, "unexpected error %d\n", GetLastError()); if (ret == TRUE) { @@ -13224,7 +13224,7 @@ static void test_hotkey(void) } DispatchMessage(&msg); } - ok_sequence(WmHotkeyPress, "window hotkey press", FALSE); + ok_sequence(WmHotkeyPress, "window hotkey press", TRUE); key_state = GetAsyncKeyState(hotkey_letter); ok((key_state & 0x8000) == 0x8000, "unexpected key state %x\n", key_state); @@ -13232,7 +13232,7 @@ static void test_hotkey(void) keybd_event(hotkey_letter, 0, KEYEVENTF_KEYUP, 0); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); - ok_sequence(WmHotkeyRelease, "window hotkey release", FALSE); + ok_sequence(WmHotkeyRelease, "window hotkey release", TRUE); keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) @@ -13255,7 +13255,7 @@ static void test_hotkey(void) } DispatchMessage(&msg); } - ok_sequence(WmHotkeyCombined, "window hotkey combined", FALSE); + ok_sequence(WmHotkeyCombined, "window hotkey combined", TRUE); /* Register same hwnd/id with different key combination */ ret = RegisterHotKey(test_window, 5, 0, hotkey_letter); @@ -13303,7 +13303,7 @@ static void test_hotkey(void) ok(msg.hwnd != NULL, "unexpected thread message %x\n", msg.message); DispatchMessage(&msg); } - ok_sequence(WmHotkeyPress, "thread hotkey press", FALSE); + ok_sequence(WmHotkeyPress, "thread hotkey press", TRUE); keybd_event(hotkey_letter, 0, KEYEVENTF_KEYUP, 0); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) @@ -13311,7 +13311,7 @@ static void test_hotkey(void) ok(msg.hwnd != NULL, "unexpected thread message %x\n", msg.message); DispatchMessage(&msg); } - ok_sequence(WmHotkeyRelease, "thread hotkey release", FALSE); + ok_sequence(WmHotkeyRelease, "thread hotkey release", TRUE); keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) diff --git a/server/protocol.def b/server/protocol.def index f5ebbe7..e9c996b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2632,6 +2632,29 @@ enum coords_relative #define SET_USER_OBJECT_FLAGS 1 +/* Register a hotkey */ +@REQ(register_hotkey) + user_handle_t window; /* handle to the window */ + int id; /* hotkey identifier */ + int flags; /* modifier keys */ + int vkey; /* virtual key code */ +@REPLY + int replaced; /* did we replace an existing hotkey? */ + int flags; /* flags of replaced hotkey */ + int vkey; /* virtual key code of replaced hotkey */ +@END + + +/* Unregister a hotkey */ +@REQ(unregister_hotkey) + user_handle_t window; /* handle to the window */ + int id; /* hotkey identifier */ +@REPLY + int flags; /* flags of removed hotkey */ + int vkey; /* virtual key code of removed hotkey */ +@END + + /* Attach (or detach) thread inputs */ @REQ(attach_thread_input) thread_id_t tid_from; /* thread to be attached */ diff --git a/server/queue.c b/server/queue.c index 8bf2336..cffdb32 100644 --- a/server/queue.c +++ b/server/queue.c @@ -136,6 +136,16 @@ struct msg_queue timeout_t last_get_msg; /* time of last get message call */ }; +struct hotkey +{ + struct list entry; /* entry in desktop hotkey list */ + struct msg_queue *queue; /* queue owning this hotkey */ + user_handle_t win; /* window handle */ + unsigned int id; /* hotkey id */ + unsigned int vkey; /* virtual key code */ + unsigned int flags; /* key modifiers */ +}; + static void msg_queue_dump( struct object *obj, int verbose ); static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry ); static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); @@ -905,11 +915,21 @@ static void msg_queue_destroy( struct object *obj ) { struct msg_queue *queue = (struct msg_queue *)obj; struct list *ptr; + struct hotkey *hotkey, *hotkey2; int i; cleanup_results( queue ); for (i = 0; i < NB_MSG_KINDS; i++) empty_msg_list( &queue->msg_list[i] ); + LIST_FOR_EACH_ENTRY_SAFE( hotkey, hotkey2, &queue->input->desktop->hotkeys, struct hotkey, entry ) + { + if (hotkey->queue == queue) + { + list_remove( &hotkey->entry ); + free( hotkey ); + } + } + while ((ptr = list_head( &queue->pending_timers ))) { struct timer *timer = LIST_ENTRY( ptr, struct timer, entry ); @@ -1920,6 +1940,21 @@ void post_win_event( struct thread *thread, unsigned int event, } } +/* free all hotkeys on a desktop, optionally filtering by window */ +void free_hotkeys( struct desktop *desktop, user_handle_t window ) +{ + struct hotkey *hotkey, *hotkey2; + + LIST_FOR_EACH_ENTRY_SAFE( hotkey, hotkey2, &desktop->hotkeys, struct hotkey, entry ) + { + if (!window || hotkey->win == window) + { + list_remove( &hotkey->entry ); + free( hotkey ); + } + } +} + /* check if the thread owning the window is hung */ DECL_HANDLER(is_window_hung) @@ -2363,6 +2398,131 @@ DECL_HANDLER(kill_win_timer) release_object( thread ); } +DECL_HANDLER(register_hotkey) +{ + struct desktop *desktop; + user_handle_t win_handle = 0; + struct hotkey *hotkey; + struct hotkey *new_hotkey = NULL; + struct thread *thread; + const int modifier_flags = MOD_ALT|MOD_CONTROL|MOD_SHIFT|MOD_WIN; + + reply->replaced = 0; + + if (!(desktop = get_thread_desktop( current, 0 ))) return; + + if (req->window) + { + win_handle = get_user_full_handle( req->window ); + + if (!get_user_object( win_handle, USER_WINDOW )) + { + release_object( desktop ); + set_win32_error( ERROR_INVALID_WINDOW_HANDLE ); + return; + } + + thread = get_window_thread( win_handle ); + if (thread) + release_object( thread ); + + if (thread != current) + { + release_object( desktop ); + set_win32_error( ERROR_WINDOW_OF_OTHER_THREAD ); + return; + } + } + + LIST_FOR_EACH_ENTRY( hotkey, &desktop->hotkeys, struct hotkey, entry ) + { + if (req->vkey == hotkey->vkey && + (req->flags & modifier_flags) == (hotkey->flags & modifier_flags)) + { + release_object( desktop ); + set_win32_error( ERROR_HOTKEY_ALREADY_REGISTERED ); + return; + } + if (current->queue == hotkey->queue && win_handle == hotkey->win && req->id == hotkey->id) + new_hotkey = hotkey; + } + + if (new_hotkey) + { + reply->replaced = 1; + reply->flags = new_hotkey->flags; + reply->vkey = new_hotkey->vkey; + } + else + { + new_hotkey = mem_alloc( sizeof(*new_hotkey) ); + if (new_hotkey) + { + list_add_tail( &desktop->hotkeys, &new_hotkey->entry ); + new_hotkey->queue = current->queue; + new_hotkey->win = win_handle; + new_hotkey->id = req->id; + } + } + + if (new_hotkey) + { + new_hotkey->flags = req->flags; + new_hotkey->vkey = req->vkey; + } + + release_object( desktop ); +} + +DECL_HANDLER(unregister_hotkey) +{ + struct desktop *desktop; + user_handle_t win_handle = 0; + struct hotkey *hotkey; + struct thread *thread; + + if (!(desktop = get_thread_desktop( current, 0 ))) return; + + if (req->window) + { + win_handle = get_user_full_handle( req->window ); + + if (!get_user_object( win_handle, USER_WINDOW )) + { + release_object( desktop ); + set_win32_error( ERROR_INVALID_WINDOW_HANDLE ); + return; + } + + thread = get_window_thread( win_handle ); + if (thread) + release_object( thread ); + + if (thread != current) + { + release_object( desktop ); + set_win32_error( ERROR_WINDOW_OF_OTHER_THREAD ); + return; + } + } + + LIST_FOR_EACH_ENTRY( hotkey, &desktop->hotkeys, struct hotkey, entry ) + { + if (current->queue == hotkey->queue && win_handle == hotkey->win && req->id == hotkey->id) + goto found; + } + + release_object( desktop ); + set_win32_error( ERROR_HOTKEY_NOT_REGISTERED ); + return; + +found: + reply->flags = hotkey->flags; + reply->vkey = hotkey->vkey; + list_remove( &hotkey->entry ); + free( hotkey ); + release_object( desktop ); +} /* attach (or detach) thread inputs */ DECL_HANDLER(attach_thread_input) diff --git a/server/user.h b/server/user.h index 9df2453..2947de7 100644 --- a/server/user.h +++ b/server/user.h @@ -70,6 +70,7 @@ struct desktop struct window *top_window; /* desktop window for this desktop */ struct window *msg_window; /* HWND_MESSAGE top window */ struct hook_table *global_hooks; /* table of global hooks on this desktop */ + struct list hotkeys; /* list of registered hotkeys */ struct timeout_user *close_timeout; /* timeout before closing the desktop */ struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ @@ -114,6 +115,7 @@ extern void post_win_event( struct thread *thread, unsigned int event, unsigned int child_id, client_ptr_t proc, const WCHAR *module, data_size_t module_size, user_handle_t handle ); +extern void free_hotkeys( struct desktop *desktop, user_handle_t window ); /* region functions */ diff --git a/server/window.c b/server/window.c index 3465eba..329fdb6 100644 --- a/server/window.c +++ b/server/window.c @@ -1727,6 +1727,7 @@ void destroy_window( struct window *win ) if (win == shell_listview) shell_listview = NULL; if (win == progman_window) progman_window = NULL; if (win == taskman_window) taskman_window = NULL; + free_hotkeys( win->desktop, win->handle ); free_user_handle( win->handle ); destroy_properties( win ); list_remove( &win->entry ); diff --git a/server/winstation.c b/server/winstation.c index 20656aa..e0b7613 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -235,6 +235,7 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned memset( &desktop->cursor, 0, sizeof(desktop->cursor) ); memset( desktop->keystate, 0, sizeof(desktop->keystate) ); list_add_tail( &winstation->desktops, &desktop->entry ); + list_init( &desktop->hotkeys ); } } free( full_name ); @@ -273,6 +274,7 @@ static void desktop_destroy( struct object *obj ) { struct desktop *desktop = (struct desktop *)obj; + free_hotkeys( desktop, 0 ); if (desktop->top_window) destroy_window( desktop->top_window ); if (desktop->msg_window) destroy_window( desktop->msg_window ); if (desktop->global_hooks) release_object( desktop->global_hooks ); -- 1.7.1