server: Invoke low-level hardware hooks directly from the server side.
diff --git a/server/queue.c b/server/queue.c
index 13e3f47..d1aa715 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -58,6 +58,8 @@
     int                    replied;       /* has it been replied to? */
     unsigned int           error;         /* error code to pass back to sender */
     lparam_t               result;        /* reply result */
+    struct message        *hardware_msg;  /* hardware message if low-level hook result */
+    struct desktop        *desktop;       /* desktop for hardware message */
     struct message        *callback_msg;  /* message to queue for callback */
     void                  *data;          /* message reply data */
     unsigned int           data_size;     /* size of message reply data */
@@ -201,6 +203,7 @@
 /* pointer to input structure of foreground thread */
 static unsigned int last_input_time;
 
+static void queue_hardware_message( struct desktop *desktop, struct message *msg );
 static void free_message( struct message *msg );
 
 /* set the caret window in a given thread input */
@@ -436,6 +439,8 @@
     if (result->timeout) remove_timeout_user( result->timeout );
     free( result->data );
     if (result->callback_msg) free_message( result->callback_msg );
+    if (result->hardware_msg) free_message( result->hardware_msg );
+    if (result->desktop) release_object( result->desktop );
     free( result );
 }
 
@@ -460,6 +465,17 @@
         remove_timeout_user( res->timeout );
         res->timeout = NULL;
     }
+
+    if (res->hardware_msg)
+    {
+        if (!error && result)  /* rejected by the hook */
+            free_message( res->hardware_msg );
+        else
+            queue_hardware_message( res->desktop, res->hardware_msg );
+
+        res->hardware_msg = NULL;
+    }
+
     if (res->sender)
     {
         if (res->callback_msg)
@@ -479,7 +495,7 @@
                 set_queue_bits( res->sender, QS_SMRESULT );
         }
     }
-
+    else if (!res->receiver) free_result( res );
 }
 
 /* free a message when deleting a queue or window */
@@ -489,12 +505,8 @@
     if (result)
     {
         result->msg = NULL;
-        if (result->sender)
-        {
-            result->receiver = NULL;
-            store_message_result( result, 0, STATUS_ACCESS_DENIED /*FIXME*/ );
-        }
-        else free_result( result );
+        result->receiver = NULL;
+        store_message_result( result, 0, STATUS_ACCESS_DENIED /*FIXME*/ );
     }
     free( msg->data );
     free( msg );
@@ -535,13 +547,7 @@
         msg->result = NULL;
         remove_queue_message( result->receiver, msg, SEND_MESSAGE );
         result->receiver = NULL;
-        if (!result->sender)
-        {
-            free_result( result );
-            return;
-        }
     }
-
     store_message_result( result, 0, STATUS_TIMEOUT );
 }
 
@@ -553,13 +559,16 @@
     struct message_result *result = mem_alloc( sizeof(*result) );
     if (result)
     {
-        result->msg       = msg;
-        result->sender    = send_queue;
-        result->receiver  = recv_queue;
-        result->replied   = 0;
-        result->data      = NULL;
-        result->data_size = 0;
-        result->timeout   = NULL;
+        result->msg          = msg;
+        result->sender       = send_queue;
+        result->receiver     = recv_queue;
+        result->replied      = 0;
+        result->data         = NULL;
+        result->data_size    = 0;
+        result->timeout      = NULL;
+        result->hardware_msg = NULL;
+        result->desktop      = NULL;
+        result->callback_msg = NULL;
 
         if (msg->type == MSG_CALLBACK)
         {
@@ -586,11 +595,7 @@
             result->callback_msg = callback_msg;
             list_add_head( &send_queue->callback_result, &result->sender_entry );
         }
-        else
-        {
-            result->callback_msg = NULL;
-            list_add_head( &send_queue->send_result, &result->sender_entry );
-        }
+        else if (send_queue) list_add_head( &send_queue->send_result, &result->sender_entry );
 
         if (timeout != TIMEOUT_INFINITE)
             result->timeout = add_timeout_user( timeout, result_timeout, result );
@@ -641,7 +646,7 @@
     {
         queue->recv_result = res->recv_next;
         res->receiver = NULL;
-        if (!res->sender)  /* no one waiting for it */
+        if (!res->sender && !res->hardware_msg)  /* no one waiting for it */
         {
             free_result( res );
             return;
@@ -1328,13 +1333,58 @@
     release_object( thread );
 }
 
+/* send the low-level hook message for a given hardware message */
+static int send_hook_ll_message( struct desktop *desktop, struct message *hardware_msg,
+                                 const hw_input_t *input, struct msg_queue *sender )
+{
+    struct thread *hook_thread;
+    struct msg_queue *queue;
+    struct message *msg;
+    timeout_t timeout = 2000 * -10000;  /* FIXME: load from registry */
+    int id = (input->type == INPUT_MOUSE) ? WH_MOUSE_LL : WH_KEYBOARD_LL;
+
+    if (!(hook_thread = get_first_global_hook( id ))) return 0;
+    if (!(queue = hook_thread->queue)) return 0;
+
+    if (!(msg = mem_alloc( sizeof(*msg) ))) return 0;
+
+    msg->type      = MSG_HOOK_LL;
+    msg->win       = 0;
+    msg->msg       = id;
+    msg->wparam    = hardware_msg->msg;
+    msg->time      = hardware_msg->time;
+    msg->data_size = hardware_msg->data_size;
+    msg->result    = NULL;
+
+    if (input->type == INPUT_KEYBOARD)
+    {
+        unsigned short vkey = input->kbd.vkey;
+        if (input->kbd.flags & KEYEVENTF_UNICODE) vkey = VK_PACKET;
+        msg->lparam = (input->kbd.scan << 16) | vkey;
+    }
+    else msg->lparam = input->mouse.data;
+
+    if (!(msg->data = memdup( hardware_msg->data, hardware_msg->data_size )) ||
+        !(msg->result = alloc_message_result( sender, queue, msg, timeout )))
+    {
+        free_message( msg );
+        return 0;
+    }
+    msg->result->hardware_msg = hardware_msg;
+    msg->result->desktop = (struct desktop *)grab_object( desktop );
+    list_add_tail( &queue->msg_list[SEND_MESSAGE], &msg->entry );
+    set_queue_bits( queue, QS_SENDMESSAGE );
+    return 1;
+}
+
 /* queue a hardware message for a mouse event */
-static void queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input )
+static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input,
+                                unsigned int hook_flags, struct msg_queue *sender )
 {
     struct hardware_msg_data *msg_data;
     struct message *msg;
     unsigned int i, time, flags;
-    int x, y;
+    int wait = 0, x, y;
 
     static const unsigned int messages[] =
     {
@@ -1383,12 +1433,13 @@
     {
         if (!messages[i]) continue;
         if (!(flags & (1 << i))) continue;
+        flags &= ~(1 << i);
 
-        if (!(msg = mem_alloc( sizeof(*msg) ))) return;
+        if (!(msg = mem_alloc( sizeof(*msg) ))) return 0;
         if (!(msg_data = mem_alloc( sizeof(*msg_data) )))
         {
             free( msg );
-            return;
+            return 0;
         }
         memset( msg_data, 0, sizeof(*msg_data) );
 
@@ -1404,23 +1455,34 @@
         msg_data->x    = x;
         msg_data->y    = y;
         msg_data->info = input->mouse.info;
+        if (hook_flags & SEND_HWMSG_INJECTED) msg_data->flags = LLMHF_INJECTED;
 
-        queue_hardware_message( desktop, msg );
+        /* specify a sender only when sending the last message */
+        if (!(flags & ((1 << sizeof(messages)/sizeof(messages[0])) - 1)))
+        {
+            if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
+                queue_hardware_message( desktop, msg );
+        }
+        else if (!send_hook_ll_message( desktop, msg, input, NULL ))
+            queue_hardware_message( desktop, msg );
     }
+    return wait;
 }
 
 /* queue a hardware message for a keyboard event */
-static void queue_keyboard_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input )
+static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input,
+                                   unsigned int hook_flags, struct msg_queue *sender )
 {
     struct hardware_msg_data *msg_data;
     struct message *msg;
     unsigned char vkey = input->kbd.vkey;
+    int wait;
 
-    if (!(msg = mem_alloc( sizeof(*msg) ))) return;
+    if (!(msg = mem_alloc( sizeof(*msg) ))) return 0;
     if (!(msg_data = mem_alloc( sizeof(*msg_data) )))
     {
         free( msg );
-        return;
+        return 0;
     }
     memset( msg_data, 0, sizeof(*msg_data) );
 
@@ -1433,6 +1495,7 @@
     msg->data_size = sizeof(*msg_data);
     msg_data->info = input->kbd.info;
     if (!msg->time) msg->time = get_tick_count();
+    if (hook_flags & SEND_HWMSG_INJECTED) msg_data->flags = LLKHF_INJECTED;
 
     if (input->kbd.flags & KEYEVENTF_UNICODE)
     {
@@ -1440,6 +1503,7 @@
     }
     else
     {
+        unsigned int flags = 0;
         switch (vkey)
         {
         case VK_MENU:
@@ -1458,12 +1522,14 @@
             vkey = (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT;
             break;
         }
-        if (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) msg->lparam |= KF_EXTENDED << 16;
+        if (input->kbd.flags & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED;
         /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
-        if (input->kbd.flags & KEYEVENTF_KEYUP) msg->lparam |= (KF_REPEAT | KF_UP) << 16;
-        else if (desktop->keystate[vkey] & 0x80) msg->lparam |= KF_REPEAT << 16;
+        if (input->kbd.flags & KEYEVENTF_KEYUP) flags |= KF_REPEAT | KF_UP;
+        else if (desktop->keystate[vkey] & 0x80) flags |= KF_REPEAT;
 
         msg->wparam = vkey;
+        msg->lparam |= flags << 16;
+        msg_data->flags |= (flags & (KF_EXTENDED | KF_ALTDOWN | KF_UP)) >> 8;
     }
 
     msg->msg = (input->kbd.flags & KEYEVENTF_KEYUP) ? WM_KEYUP : WM_KEYDOWN;
@@ -1508,8 +1574,10 @@
         desktop->keystate[VK_MENU] &= ~0x02;
         break;
     }
+    if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
+        queue_hardware_message( desktop, msg );
 
-    queue_hardware_message( desktop, msg );
+    return wait;
 }
 
 /* queue a hardware message for a custom type of event */
@@ -1941,6 +2009,7 @@
             break;
         case MSG_HARDWARE:  /* should use send_hardware_message instead */
         case MSG_CALLBACK_RESULT:  /* cannot send this one */
+        case MSG_HOOK_LL:  /* generated internally */
         default:
             set_error( STATUS_INVALID_PARAMETER );
             free( msg );
@@ -1955,6 +2024,7 @@
 {
     struct thread *thread = NULL;
     struct desktop *desktop;
+    struct msg_queue *sender = get_current_queue();
 
     if (req->win)
     {
@@ -1966,10 +2036,10 @@
     switch (req->input.type)
     {
     case INPUT_MOUSE:
-        queue_mouse_message( desktop, req->win, &req->input );
+        reply->wait = queue_mouse_message( desktop, req->win, &req->input, req->flags, sender );
         break;
     case INPUT_KEYBOARD:
-        queue_keyboard_message( desktop, req->win, &req->input );
+        reply->wait = queue_keyboard_message( desktop, req->win, &req->input, req->flags, sender );
         break;
     case INPUT_HARDWARE:
         queue_custom_hardware_message( desktop, req->win, &req->input );