Run WH_KEYBOARD_LL and WH_MOUSE_LL hooks in the context of the thread
that set the hook.

diff --git a/server/hook.c b/server/hook.c
index 65bd3ba..fa42d8d 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -28,6 +28,7 @@
 #include "winuser.h"
 
 #include "object.h"
+#include "process.h"
 #include "request.h"
 #include "user.h"
 
@@ -91,16 +92,16 @@
 }
 
 /* create a new hook and add it to the specified table */
-static struct hook *add_hook( struct thread *thread, int index )
+static struct hook *add_hook( struct thread *thread, int index, int global )
 {
     struct hook *hook;
-    struct hook_table *table = thread ? get_queue_hooks(thread) : global_hooks;
+    struct hook_table *table = global ? global_hooks : get_queue_hooks(thread);
 
     if (!table)
     {
         if (!(table = alloc_hook_table())) return NULL;
-        if (thread) set_queue_hooks( thread, table );
-        else global_hooks = table;
+        if (global) global_hooks = table;
+        else set_queue_hooks( thread, table );
     }
     if (!(hook = mem_alloc( sizeof(*hook) ))) return NULL;
 
@@ -145,7 +146,10 @@
 /* get the hook table that a given hook belongs to */
 inline static struct hook_table *get_table( struct hook *hook )
 {
-    return hook->thread ? get_queue_hooks(hook->thread) : global_hooks;
+    if (!hook->thread) return global_hooks;
+    if (hook->index + WH_MINHOOK == WH_KEYBOARD_LL) return global_hooks;
+    if (hook->index + WH_MINHOOK == WH_MOUSE_LL) return global_hooks;
+    return get_queue_hooks(hook->thread);
 }
 
 /* get the first hook in the chain */
@@ -236,6 +240,25 @@
     }
 }
 
+/* remove all global hooks owned by a given thread */
+void remove_thread_hooks( struct thread *thread )
+{
+    int index;
+
+    if (!global_hooks) return;
+
+    /* only low-level keyboard/mouse global hooks can be owned by a thread */
+    for (index = WH_KEYBOARD_LL - WH_MINHOOK; index <= WH_MOUSE_LL - WH_MINHOOK; index++)
+    {
+        struct hook *hook = get_first_hook( global_hooks, index );
+        while (hook)
+        {
+            struct hook *next = HOOK_ENTRY( list_next( &global_hooks->hooks[index], &hook->chain ) );
+            if (hook->thread == thread) remove_hook( hook );
+            hook = next;
+        }
+    }
+}
 
 /* set a window hook */
 DECL_HANDLER(set_hook)
@@ -243,6 +266,7 @@
     struct thread *thread;
     struct hook *hook;
     WCHAR *module;
+    int global;
     size_t module_size = get_req_data_size();
 
     if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
@@ -250,7 +274,14 @@
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
-    if (!req->tid)
+    if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL)
+    {
+        /* low-level hardware hooks are special: always global, but without a module */
+        thread = (struct thread *)grab_object( current );
+        module = NULL;
+        global = 1;
+    }
+    else if (!req->tid)
     {
         if (!module_size)
         {
@@ -259,14 +290,16 @@
         }
         if (!(module = memdup( get_req_data(), module_size ))) return;
         thread = NULL;
+        global = 1;
     }
     else
     {
         module = NULL;
+        global = 0;
         if (!(thread = get_thread_from_id( req->tid ))) return;
     }
 
-    if ((hook = add_hook( thread, req->id - WH_MINHOOK )))
+    if ((hook = add_hook( thread, req->id - WH_MINHOOK, global )))
     {
         hook->proc        = req->proc;
         hook->unicode     = req->unicode;
@@ -319,8 +352,20 @@
             !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK )))
             return;  /* no hook set */
     }
+
+    if (hook->thread && hook->thread != current)  /* must run in other thread */
+    {
+        reply->pid  = get_process_id( hook->thread->process );
+        reply->tid  = get_thread_id( hook->thread );
+        reply->proc = 0;
+    }
+    else
+    {
+        reply->pid  = 0;
+        reply->tid  = 0;
+        reply->proc = hook->proc;
+    }
     reply->handle  = hook->handle;
-    reply->proc    = hook->proc;
     reply->unicode = hook->unicode;
     table->counts[hook->index]++;
     if (hook->module) set_reply_data( hook->module, hook->module_size );
@@ -358,9 +403,20 @@
     {
         reply->next = next->handle;
         reply->id   = next->index + WH_MINHOOK;
-        reply->proc = next->proc;
         reply->prev_unicode = hook->unicode;
         reply->next_unicode = next->unicode;
         if (next->module) set_reply_data( next->module, next->module_size );
+        if (next->thread && next->thread != current)
+        {
+            reply->pid  = get_process_id( next->thread->process );
+            reply->tid  = get_thread_id( next->thread );
+            reply->proc = 0;
+        }
+        else
+        {
+            reply->pid  = 0;
+            reply->tid  = 0;
+            reply->proc = next->proc;
+        }
     }
 }
diff --git a/server/protocol.def b/server/protocol.def
index 6a8c460..7020c9c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2108,6 +2108,8 @@
     int            id;             /* id of the hook */
 @REPLY
     user_handle_t  handle;         /* handle to the next hook */
+    process_id_t   pid;            /* process id for low-level keyboard/mouse hooks */
+    thread_id_t    tid;            /* thread id for low-level keyboard/mouse hooks */
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
     VARARG(module,unicode_str);    /* module name */
@@ -2126,6 +2128,8 @@
 @REPLY
     user_handle_t  next;           /* handle to the next hook */
     int            id;             /* id of the next hook */
+    process_id_t   pid;            /* process id for low-level keyboard/mouse hooks */
+    thread_id_t    tid;            /* thread id for low-level keyboard/mouse hooks */
     void*          proc;           /* next hook procedure */
     int            prev_unicode;   /* was the previous a unicode hook? */
     int            next_unicode;   /* is the next a unicode hook? */
diff --git a/server/queue.c b/server/queue.c
index ce72462..eb2c11c 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -239,6 +239,7 @@
 {
     struct process *process = thread->process;
 
+    remove_thread_hooks( thread );
     if (!thread->queue) return;
     if (process->queue == thread->queue)  /* is it the process main queue? */
     {
diff --git a/server/trace.c b/server/trace.c
index 5c7970c..bfab082 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2437,6 +2437,8 @@
 static void dump_start_hook_chain_reply( const struct start_hook_chain_reply *req )
 {
     fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " pid=%04x,", req->pid );
+    fprintf( stderr, " tid=%04x,", req->tid );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " unicode=%d,", req->unicode );
     fprintf( stderr, " module=" );
@@ -2457,6 +2459,8 @@
 {
     fprintf( stderr, " next=%p,", req->next );
     fprintf( stderr, " id=%d,", req->id );
+    fprintf( stderr, " pid=%04x,", req->pid );
+    fprintf( stderr, " tid=%04x,", req->tid );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " prev_unicode=%d,", req->prev_unicode );
     fprintf( stderr, " next_unicode=%d,", req->next_unicode );
diff --git a/server/user.h b/server/user.h
index e4229a0..a075dd7 100644
--- a/server/user.h
+++ b/server/user.h
@@ -50,6 +50,7 @@
 /* hook functions */
 
 extern void close_global_hooks(void);
+extern void remove_thread_hooks( struct thread *thread );
 
 /* queue functions */