Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server-side window hooks support |
| 3 | * |
| 4 | * Copyright (C) 2002 Alexandre Julliard |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 5 | * Copyright (C) 2005 Dmitry Timoshkov |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 6 | * |
| 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This library is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with this library; if not, write to the Free Software |
Jonathan Ernst | 360a3f9 | 2006-05-18 14:49:52 +0200 | [diff] [blame] | 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 20 | */ |
| 21 | |
| 22 | #include "config.h" |
| 23 | #include "wine/port.h" |
| 24 | |
| 25 | #include <assert.h> |
Alexandre Julliard | e37c6e1 | 2003-09-05 23:08:26 +0000 | [diff] [blame] | 26 | #include <stdarg.h> |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 27 | #include <stdio.h> |
| 28 | |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 29 | #include "ntstatus.h" |
| 30 | #define WIN32_NO_STATUS |
Alexandre Julliard | e37c6e1 | 2003-09-05 23:08:26 +0000 | [diff] [blame] | 31 | #include "windef.h" |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 32 | #include "winbase.h" |
| 33 | #include "winuser.h" |
Ge van Geldorp | 1a1583a | 2005-11-28 17:32:54 +0100 | [diff] [blame] | 34 | #include "winternl.h" |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 35 | |
| 36 | #include "object.h" |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 37 | #include "process.h" |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 38 | #include "request.h" |
| 39 | #include "user.h" |
| 40 | |
| 41 | struct hook_table; |
| 42 | |
| 43 | struct hook |
| 44 | { |
| 45 | struct list chain; /* hook chain entry */ |
| 46 | user_handle_t handle; /* user handle for this hook */ |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 47 | struct process *process; /* process the hook is set to */ |
| 48 | struct thread *thread; /* thread the hook is set to */ |
| 49 | struct thread *owner; /* owner of the out of context hook */ |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 50 | struct hook_table *table; /* hook table that contains this hook */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 51 | int index; /* hook table index */ |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 52 | int event_min; |
| 53 | int event_max; |
| 54 | int flags; |
Alexandre Julliard | cc55fd3 | 2008-12-29 17:35:35 +0100 | [diff] [blame] | 55 | client_ptr_t proc; /* hook function */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 56 | int unicode; /* is it a unicode hook? */ |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 57 | WCHAR *module; /* module name for global hooks */ |
Alexandre Julliard | 0f273c1 | 2006-07-26 10:43:25 +0200 | [diff] [blame] | 58 | data_size_t module_size; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 59 | }; |
| 60 | |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 61 | #define WH_WINEVENT (WH_MAXHOOK+1) |
| 62 | |
| 63 | #define NB_HOOKS (WH_WINEVENT-WH_MINHOOK+1) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 64 | #define HOOK_ENTRY(p) LIST_ENTRY( (p), struct hook, chain ) |
| 65 | |
| 66 | struct hook_table |
| 67 | { |
| 68 | struct object obj; /* object header */ |
| 69 | struct list hooks[NB_HOOKS]; /* array of hook chains */ |
| 70 | int counts[NB_HOOKS]; /* use counts for each hook chain */ |
| 71 | }; |
| 72 | |
| 73 | static void hook_table_dump( struct object *obj, int verbose ); |
| 74 | static void hook_table_destroy( struct object *obj ); |
| 75 | |
| 76 | static const struct object_ops hook_table_ops = |
| 77 | { |
| 78 | sizeof(struct hook_table), /* size */ |
| 79 | hook_table_dump, /* dump */ |
Alexandre Julliard | 8382eb0 | 2007-12-05 18:16:42 +0100 | [diff] [blame] | 80 | no_get_type, /* get_type */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 81 | no_add_queue, /* add_queue */ |
| 82 | NULL, /* remove_queue */ |
| 83 | NULL, /* signaled */ |
| 84 | NULL, /* satisfied */ |
Mike McCormack | f92fff6 | 2005-04-24 17:35:52 +0000 | [diff] [blame] | 85 | no_signal, /* signal */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 86 | no_get_fd, /* get_fd */ |
Alexandre Julliard | 28beba3 | 2005-12-12 14:57:40 +0100 | [diff] [blame] | 87 | no_map_access, /* map_access */ |
Rob Shearman | c1707d8 | 2007-10-03 13:10:37 +0100 | [diff] [blame] | 88 | default_get_sd, /* get_sd */ |
| 89 | default_set_sd, /* set_sd */ |
Vitaliy Margolen | baffcb9 | 2005-11-22 14:55:42 +0000 | [diff] [blame] | 90 | no_lookup_name, /* lookup_name */ |
Alexandre Julliard | 7e71c1d | 2007-03-22 11:44:29 +0100 | [diff] [blame] | 91 | no_open_file, /* open_file */ |
Alexandre Julliard | b9b1ea9 | 2005-06-09 15:39:52 +0000 | [diff] [blame] | 92 | no_close_handle, /* close_handle */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 93 | hook_table_destroy /* destroy */ |
| 94 | }; |
| 95 | |
| 96 | |
| 97 | /* create a new hook table */ |
| 98 | static struct hook_table *alloc_hook_table(void) |
| 99 | { |
| 100 | struct hook_table *table; |
| 101 | int i; |
| 102 | |
Alexandre Julliard | e66207e | 2003-02-19 00:33:32 +0000 | [diff] [blame] | 103 | if ((table = alloc_object( &hook_table_ops ))) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 104 | { |
| 105 | for (i = 0; i < NB_HOOKS; i++) |
| 106 | { |
| 107 | list_init( &table->hooks[i] ); |
| 108 | table->counts[i] = 0; |
| 109 | } |
| 110 | } |
| 111 | return table; |
| 112 | } |
| 113 | |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 114 | static struct hook_table *get_global_hooks( struct thread *thread ) |
| 115 | { |
| 116 | struct hook_table *table; |
Alexandre Julliard | e4faa12 | 2009-12-01 15:10:23 +0100 | [diff] [blame] | 117 | struct desktop *desktop; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 118 | |
Alexandre Julliard | e4faa12 | 2009-12-01 15:10:23 +0100 | [diff] [blame] | 119 | if (!thread->desktop) return NULL; |
| 120 | if (!(desktop = get_thread_desktop( thread, 0 ))) return NULL; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 121 | table = desktop->global_hooks; |
| 122 | release_object( desktop ); |
| 123 | return table; |
| 124 | } |
| 125 | |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 126 | /* create a new hook and add it to the specified table */ |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 127 | static struct hook *add_hook( struct desktop *desktop, struct thread *thread, int index, int global ) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 128 | { |
| 129 | struct hook *hook; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 130 | struct hook_table *table = global ? desktop->global_hooks : get_queue_hooks(thread); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 131 | |
| 132 | if (!table) |
| 133 | { |
| 134 | if (!(table = alloc_hook_table())) return NULL; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 135 | if (global) desktop->global_hooks = table; |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 136 | else set_queue_hooks( thread, table ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 137 | } |
| 138 | if (!(hook = mem_alloc( sizeof(*hook) ))) return NULL; |
| 139 | |
| 140 | if (!(hook->handle = alloc_user_handle( hook, USER_HOOK ))) |
| 141 | { |
| 142 | free( hook ); |
| 143 | return NULL; |
| 144 | } |
| 145 | hook->thread = thread ? (struct thread *)grab_object( thread ) : NULL; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 146 | hook->table = table; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 147 | hook->index = index; |
| 148 | list_add_head( &table->hooks[index], &hook->chain ); |
Alexandre Julliard | 92fec7b | 2005-06-28 19:37:52 +0000 | [diff] [blame] | 149 | if (thread) thread->desktop_users++; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 150 | return hook; |
| 151 | } |
| 152 | |
| 153 | /* free a hook, removing it from its chain */ |
| 154 | static void free_hook( struct hook *hook ) |
| 155 | { |
| 156 | free_user_handle( hook->handle ); |
Michael Stefaniuc | 5ceccec | 2006-10-09 23:34:36 +0200 | [diff] [blame] | 157 | free( hook->module ); |
Alexandre Julliard | 92fec7b | 2005-06-28 19:37:52 +0000 | [diff] [blame] | 158 | if (hook->thread) |
| 159 | { |
| 160 | assert( hook->thread->desktop_users > 0 ); |
| 161 | hook->thread->desktop_users--; |
| 162 | release_object( hook->thread ); |
| 163 | } |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 164 | if (hook->process) release_object( hook->process ); |
| 165 | release_object( hook->owner ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 166 | list_remove( &hook->chain ); |
| 167 | free( hook ); |
| 168 | } |
| 169 | |
| 170 | /* find a hook from its index and proc */ |
Alexandre Julliard | cc55fd3 | 2008-12-29 17:35:35 +0100 | [diff] [blame] | 171 | static struct hook *find_hook( struct thread *thread, int index, client_ptr_t proc ) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 172 | { |
| 173 | struct list *p; |
Alexandre Julliard | d55e7f1 | 2003-07-03 18:16:48 +0000 | [diff] [blame] | 174 | struct hook_table *table = get_queue_hooks( thread ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 175 | |
| 176 | if (table) |
| 177 | { |
| 178 | LIST_FOR_EACH( p, &table->hooks[index] ) |
| 179 | { |
| 180 | struct hook *hook = HOOK_ENTRY( p ); |
| 181 | if (hook->proc == proc) return hook; |
| 182 | } |
| 183 | } |
| 184 | return NULL; |
| 185 | } |
| 186 | |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 187 | /* get the first hook in the chain */ |
Andrew Talbot | b1788c8 | 2007-03-17 10:52:14 +0000 | [diff] [blame] | 188 | static inline struct hook *get_first_hook( struct hook_table *table, int index ) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 189 | { |
| 190 | struct list *elem = list_head( &table->hooks[index] ); |
| 191 | return elem ? HOOK_ENTRY( elem ) : NULL; |
| 192 | } |
| 193 | |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 194 | /* check if a given hook should run in the current thread */ |
Andrew Talbot | b1788c8 | 2007-03-17 10:52:14 +0000 | [diff] [blame] | 195 | static inline int run_hook_in_current_thread( struct hook *hook ) |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 196 | { |
| 197 | if ((!hook->process || hook->process == current->process) && |
| 198 | (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process)) |
| 199 | { |
| 200 | if ((!hook->thread || hook->thread == current) && |
| 201 | (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current)) |
| 202 | return 1; |
| 203 | } |
| 204 | return 0; |
| 205 | } |
| 206 | |
Alexandre Julliard | 6d85f3b | 2006-01-27 12:12:15 +0100 | [diff] [blame] | 207 | /* check if a given hook should run in the owner thread instead of the current thread */ |
Andrew Talbot | b1788c8 | 2007-03-17 10:52:14 +0000 | [diff] [blame] | 208 | static inline int run_hook_in_owner_thread( struct hook *hook ) |
Alexandre Julliard | 6d85f3b | 2006-01-27 12:12:15 +0100 | [diff] [blame] | 209 | { |
| 210 | if ((hook->index == WH_MOUSE_LL - WH_MINHOOK || |
| 211 | hook->index == WH_KEYBOARD_LL - WH_MINHOOK)) |
| 212 | return hook->owner != current; |
| 213 | return 0; |
| 214 | } |
| 215 | |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 216 | /* find the first non-deleted hook in the chain */ |
Andrew Talbot | b1788c8 | 2007-03-17 10:52:14 +0000 | [diff] [blame] | 217 | static inline struct hook *get_first_valid_hook( struct hook_table *table, int index, |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 218 | int event, user_handle_t win, |
| 219 | int object_id, int child_id ) |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 220 | { |
| 221 | struct hook *hook = get_first_hook( table, index ); |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 222 | |
| 223 | while (hook) |
| 224 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 225 | if (hook->proc && run_hook_in_current_thread( hook )) |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 226 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 227 | if (event >= hook->event_min && event <= hook->event_max) |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 228 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 229 | if (hook->flags & WINEVENT_INCONTEXT) return hook; |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 230 | |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 231 | /* only winevent hooks may be out of context */ |
| 232 | assert(hook->index + WH_MINHOOK == WH_WINEVENT); |
| 233 | post_win_event( hook->owner, event, win, object_id, child_id, |
| 234 | hook->proc, hook->module, hook->module_size, |
| 235 | hook->handle ); |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 236 | } |
| 237 | } |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 238 | hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) ); |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 239 | } |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 240 | return hook; |
| 241 | } |
| 242 | |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 243 | /* find the next hook in the chain, skipping the deleted ones */ |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 244 | static struct hook *get_next_hook( struct thread *thread, struct hook *hook, int event, |
| 245 | user_handle_t win, int object_id, int child_id ) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 246 | { |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 247 | struct hook_table *global_hooks, *table = hook->table; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 248 | int index = hook->index; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 249 | |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 250 | while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) ))) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 251 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 252 | if (hook->proc && run_hook_in_current_thread( hook )) |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 253 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 254 | if (event >= hook->event_min && event <= hook->event_max) |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 255 | { |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 256 | if (hook->flags & WINEVENT_INCONTEXT) return hook; |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 257 | |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 258 | /* only winevent hooks may be out of context */ |
| 259 | assert(hook->index + WH_MINHOOK == WH_WINEVENT); |
| 260 | post_win_event( hook->owner, event, win, object_id, child_id, |
| 261 | hook->proc, hook->module, hook->module_size, |
| 262 | hook->handle ); |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 263 | } |
| 264 | } |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 265 | } |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 266 | global_hooks = get_global_hooks( thread ); |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 267 | if (global_hooks && table != global_hooks) /* now search through the global table */ |
| 268 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 269 | hook = get_first_valid_hook( global_hooks, index, event, win, object_id, child_id ); |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 270 | } |
| 271 | return hook; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 272 | } |
| 273 | |
| 274 | static void hook_table_dump( struct object *obj, int verbose ) |
| 275 | { |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 276 | /* struct hook_table *table = (struct hook_table *)obj; */ |
| 277 | fprintf( stderr, "Hook table\n" ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 278 | } |
| 279 | |
| 280 | static void hook_table_destroy( struct object *obj ) |
| 281 | { |
| 282 | int i; |
| 283 | struct hook *hook; |
| 284 | struct hook_table *table = (struct hook_table *)obj; |
| 285 | |
| 286 | for (i = 0; i < NB_HOOKS; i++) |
| 287 | { |
| 288 | while ((hook = get_first_hook( table, i )) != NULL) free_hook( hook ); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | /* remove a hook, freeing it if the chain is not in use */ |
| 293 | static void remove_hook( struct hook *hook ) |
| 294 | { |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 295 | if (hook->table->counts[hook->index]) |
Alexandre Julliard | cc55fd3 | 2008-12-29 17:35:35 +0100 | [diff] [blame] | 296 | hook->proc = 0; /* chain is in use, just mark it and return */ |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 297 | else |
| 298 | free_hook( hook ); |
| 299 | } |
| 300 | |
| 301 | /* release a hook chain, removing deleted hooks if the use count drops to 0 */ |
| 302 | static void release_hook_chain( struct hook_table *table, int index ) |
| 303 | { |
| 304 | if (!table->counts[index]) /* use count shouldn't already be 0 */ |
| 305 | { |
| 306 | set_error( STATUS_INVALID_PARAMETER ); |
| 307 | return; |
| 308 | } |
| 309 | if (!--table->counts[index]) |
| 310 | { |
| 311 | struct hook *hook = get_first_hook( table, index ); |
| 312 | while (hook) |
| 313 | { |
| 314 | struct hook *next = HOOK_ENTRY( list_next( &table->hooks[hook->index], &hook->chain ) ); |
| 315 | if (!hook->proc) free_hook( hook ); |
| 316 | hook = next; |
| 317 | } |
| 318 | } |
| 319 | } |
| 320 | |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 321 | /* remove all global hooks owned by a given thread */ |
| 322 | void remove_thread_hooks( struct thread *thread ) |
| 323 | { |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 324 | struct hook_table *global_hooks = get_global_hooks( thread ); |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 325 | int index; |
| 326 | |
| 327 | if (!global_hooks) return; |
| 328 | |
| 329 | /* only low-level keyboard/mouse global hooks can be owned by a thread */ |
| 330 | for (index = WH_KEYBOARD_LL - WH_MINHOOK; index <= WH_MOUSE_LL - WH_MINHOOK; index++) |
| 331 | { |
| 332 | struct hook *hook = get_first_hook( global_hooks, index ); |
| 333 | while (hook) |
| 334 | { |
| 335 | struct hook *next = HOOK_ENTRY( list_next( &global_hooks->hooks[index], &hook->chain ) ); |
| 336 | if (hook->thread == thread) remove_hook( hook ); |
| 337 | hook = next; |
| 338 | } |
| 339 | } |
| 340 | } |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 341 | |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 342 | /* get a bitmap of active hooks in a hook table */ |
| 343 | static int is_hook_active( struct hook_table *table, int index ) |
| 344 | { |
| 345 | struct hook *hook = get_first_hook( table, index ); |
| 346 | |
| 347 | while (hook) |
| 348 | { |
| 349 | if (hook->proc && run_hook_in_current_thread( hook )) return 1; |
| 350 | hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) ); |
| 351 | } |
| 352 | return 0; |
| 353 | } |
| 354 | |
| 355 | /* get a bitmap of all active hooks for the current thread */ |
| 356 | unsigned int get_active_hooks(void) |
| 357 | { |
| 358 | struct hook_table *table = get_queue_hooks( current ); |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 359 | struct hook_table *global_hooks = get_global_hooks( current ); |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 360 | unsigned int ret = 1 << 31; /* set high bit to indicate that the bitmap is valid */ |
| 361 | int id; |
| 362 | |
| 363 | for (id = WH_MINHOOK; id <= WH_WINEVENT; id++) |
| 364 | { |
| 365 | if ((table && is_hook_active( table, id - WH_MINHOOK )) || |
| 366 | (global_hooks && is_hook_active( global_hooks, id - WH_MINHOOK ))) |
| 367 | ret |= 1 << (id - WH_MINHOOK); |
| 368 | } |
| 369 | return ret; |
| 370 | } |
| 371 | |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 372 | /* set a window hook */ |
| 373 | DECL_HANDLER(set_hook) |
| 374 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 375 | struct process *process = NULL; |
| 376 | struct thread *thread = NULL; |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 377 | struct desktop *desktop; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 378 | struct hook *hook; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 379 | WCHAR *module; |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 380 | int global; |
Alexandre Julliard | 0f273c1 | 2006-07-26 10:43:25 +0200 | [diff] [blame] | 381 | data_size_t module_size = get_req_data_size(); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 382 | |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 383 | if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 384 | { |
| 385 | set_error( STATUS_INVALID_PARAMETER ); |
| 386 | return; |
| 387 | } |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 388 | |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 389 | if (!(desktop = get_thread_desktop( current, DESKTOP_HOOKCONTROL ))) return; |
| 390 | |
| 391 | if (req->pid && !(process = get_process_from_id( req->pid ))) goto done; |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 392 | |
| 393 | if (req->tid) |
| 394 | { |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 395 | if (!(thread = get_thread_from_id( req->tid ))) goto done; |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 396 | if (process && process != thread->process) |
| 397 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 398 | set_error( STATUS_INVALID_PARAMETER ); |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 399 | goto done; |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 400 | } |
| 401 | } |
| 402 | |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 403 | if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL) |
| 404 | { |
| 405 | /* low-level hardware hooks are special: always global, but without a module */ |
Alexandre Julliard | 6d85f3b | 2006-01-27 12:12:15 +0100 | [diff] [blame] | 406 | if (thread) |
| 407 | { |
| 408 | set_error( STATUS_INVALID_PARAMETER ); |
| 409 | goto done; |
| 410 | } |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 411 | module = NULL; |
| 412 | global = 1; |
| 413 | } |
| 414 | else if (!req->tid) |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 415 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 416 | /* out of context hooks do not need a module handle */ |
| 417 | if (!module_size && (req->flags & WINEVENT_INCONTEXT)) |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 418 | { |
| 419 | set_error( STATUS_INVALID_PARAMETER ); |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 420 | goto done; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 421 | } |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 422 | if (!(module = memdup( get_req_data(), module_size ))) goto done; |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 423 | global = 1; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 424 | } |
| 425 | else |
| 426 | { |
Alexandre Julliard | 04a9f93 | 2007-08-22 11:59:21 +0200 | [diff] [blame] | 427 | /* module is optional only if hook is in current process */ |
| 428 | if (!module_size) |
| 429 | { |
| 430 | module = NULL; |
| 431 | if (thread->process != current->process) |
| 432 | { |
| 433 | set_error( STATUS_INVALID_PARAMETER ); |
| 434 | goto done; |
| 435 | } |
| 436 | } |
| 437 | else if (!(module = memdup( get_req_data(), module_size ))) goto done; |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 438 | global = 0; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 439 | } |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 440 | |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 441 | if ((hook = add_hook( desktop, thread, req->id - WH_MINHOOK, global ))) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 442 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 443 | hook->owner = (struct thread *)grab_object( current ); |
| 444 | hook->process = process ? (struct process *)grab_object( process ) : NULL; |
| 445 | hook->event_min = req->event_min; |
| 446 | hook->event_max = req->event_max; |
| 447 | hook->flags = req->flags; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 448 | hook->proc = req->proc; |
| 449 | hook->unicode = req->unicode; |
| 450 | hook->module = module; |
| 451 | hook->module_size = module_size; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 452 | reply->handle = hook->handle; |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 453 | reply->active_hooks = get_active_hooks(); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 454 | } |
Michael Stefaniuc | 5ceccec | 2006-10-09 23:34:36 +0200 | [diff] [blame] | 455 | else free( module ); |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 456 | |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 457 | done: |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 458 | if (process) release_object( process ); |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 459 | if (thread) release_object( thread ); |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 460 | release_object( desktop ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 461 | } |
| 462 | |
| 463 | |
| 464 | /* remove a window hook */ |
| 465 | DECL_HANDLER(remove_hook) |
| 466 | { |
| 467 | struct hook *hook; |
| 468 | |
Dmitry Timoshkov | ed04d36 | 2005-01-27 11:14:19 +0000 | [diff] [blame] | 469 | if (req->handle) |
| 470 | { |
| 471 | if (!(hook = get_user_object( req->handle, USER_HOOK ))) |
| 472 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 473 | set_error( STATUS_INVALID_HANDLE ); |
Dmitry Timoshkov | ed04d36 | 2005-01-27 11:14:19 +0000 | [diff] [blame] | 474 | return; |
| 475 | } |
| 476 | } |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 477 | else |
| 478 | { |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 479 | if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 480 | { |
| 481 | set_error( STATUS_INVALID_PARAMETER ); |
| 482 | return; |
| 483 | } |
| 484 | if (!(hook = find_hook( current, req->id - WH_MINHOOK, req->proc ))) |
Dmitry Timoshkov | ed04d36 | 2005-01-27 11:14:19 +0000 | [diff] [blame] | 485 | { |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 486 | set_error( STATUS_INVALID_PARAMETER ); |
Dmitry Timoshkov | ed04d36 | 2005-01-27 11:14:19 +0000 | [diff] [blame] | 487 | return; |
| 488 | } |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 489 | } |
Dmitry Timoshkov | ed04d36 | 2005-01-27 11:14:19 +0000 | [diff] [blame] | 490 | remove_hook( hook ); |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 491 | reply->active_hooks = get_active_hooks(); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 492 | } |
| 493 | |
| 494 | |
| 495 | /* start calling a hook chain */ |
| 496 | DECL_HANDLER(start_hook_chain) |
| 497 | { |
| 498 | struct hook *hook; |
Alexandre Julliard | d55e7f1 | 2003-07-03 18:16:48 +0000 | [diff] [blame] | 499 | struct hook_table *table = get_queue_hooks( current ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 500 | |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 501 | if (req->id < WH_MINHOOK || req->id > WH_WINEVENT) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 502 | { |
| 503 | set_error( STATUS_INVALID_PARAMETER ); |
| 504 | return; |
| 505 | } |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 506 | |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 507 | reply->active_hooks = get_active_hooks(); |
| 508 | |
| 509 | if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event, |
| 510 | req->window, req->object_id, req->child_id ))) |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 511 | { |
| 512 | /* try global table */ |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 513 | if (!(table = get_global_hooks( current )) || |
| 514 | !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event, |
Alexandre Julliard | 6334235 | 2005-05-11 13:03:15 +0000 | [diff] [blame] | 515 | req->window, req->object_id, req->child_id ))) |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 516 | return; /* no hook set */ |
| 517 | } |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 518 | |
Alexandre Julliard | 6d85f3b | 2006-01-27 12:12:15 +0100 | [diff] [blame] | 519 | if (run_hook_in_owner_thread( hook )) |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 520 | { |
Alexandre Julliard | 6d85f3b | 2006-01-27 12:12:15 +0100 | [diff] [blame] | 521 | reply->pid = get_process_id( hook->owner->process ); |
| 522 | reply->tid = get_thread_id( hook->owner ); |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 523 | } |
| 524 | else |
| 525 | { |
| 526 | reply->pid = 0; |
| 527 | reply->tid = 0; |
Alexandre Julliard | ca3ac8f | 2003-07-11 21:55:58 +0000 | [diff] [blame] | 528 | } |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 529 | reply->proc = hook->proc; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 530 | reply->handle = hook->handle; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 531 | reply->unicode = hook->unicode; |
| 532 | table->counts[hook->index]++; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 533 | if (hook->module) set_reply_data( hook->module, hook->module_size ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 534 | } |
| 535 | |
| 536 | |
| 537 | /* finished calling a hook chain */ |
| 538 | DECL_HANDLER(finish_hook_chain) |
| 539 | { |
Alexandre Julliard | d55e7f1 | 2003-07-03 18:16:48 +0000 | [diff] [blame] | 540 | struct hook_table *table = get_queue_hooks( current ); |
Alexandre Julliard | 4a40b2e | 2005-07-11 18:05:50 +0000 | [diff] [blame] | 541 | struct hook_table *global_hooks = get_global_hooks( current ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 542 | int index = req->id - WH_MINHOOK; |
| 543 | |
Dmitry Timoshkov | 6dba0a7 | 2005-02-03 16:40:20 +0000 | [diff] [blame] | 544 | if (req->id < WH_MINHOOK || req->id > WH_WINEVENT) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 545 | { |
| 546 | set_error( STATUS_INVALID_PARAMETER ); |
| 547 | return; |
| 548 | } |
| 549 | if (table) release_hook_chain( table, index ); |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 550 | if (global_hooks) release_hook_chain( global_hooks, index ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 551 | } |
| 552 | |
| 553 | |
Alexandre Julliard | 2f80fcd | 2006-10-05 14:05:48 +0200 | [diff] [blame] | 554 | /* get the hook information */ |
| 555 | DECL_HANDLER(get_hook_info) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 556 | { |
Alexandre Julliard | 2f80fcd | 2006-10-05 14:05:48 +0200 | [diff] [blame] | 557 | struct hook *hook; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 558 | |
| 559 | if (!(hook = get_user_object( req->handle, USER_HOOK ))) return; |
Alexandre Julliard | 14e68ba | 2002-11-20 19:54:32 +0000 | [diff] [blame] | 560 | if (hook->thread && (hook->thread != current)) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 561 | { |
| 562 | set_error( STATUS_INVALID_HANDLE ); |
| 563 | return; |
| 564 | } |
Alexandre Julliard | 2f80fcd | 2006-10-05 14:05:48 +0200 | [diff] [blame] | 565 | if (req->get_next && !(hook = get_next_hook( current, hook, req->event, req->window, |
| 566 | req->object_id, req->child_id ))) |
| 567 | return; |
| 568 | |
| 569 | reply->handle = hook->handle; |
| 570 | reply->id = hook->index + WH_MINHOOK; |
| 571 | reply->unicode = hook->unicode; |
| 572 | if (hook->module) set_reply_data( hook->module, min(hook->module_size,get_reply_max_size()) ); |
| 573 | if (run_hook_in_owner_thread( hook )) |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 574 | { |
Alexandre Julliard | 2f80fcd | 2006-10-05 14:05:48 +0200 | [diff] [blame] | 575 | reply->pid = get_process_id( hook->owner->process ); |
| 576 | reply->tid = get_thread_id( hook->owner ); |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 577 | } |
Alexandre Julliard | 2f80fcd | 2006-10-05 14:05:48 +0200 | [diff] [blame] | 578 | else |
| 579 | { |
| 580 | reply->pid = 0; |
| 581 | reply->tid = 0; |
| 582 | } |
| 583 | reply->proc = hook->proc; |
Alexandre Julliard | 0286135 | 2002-10-29 00:41:42 +0000 | [diff] [blame] | 584 | } |