Added server-side infrastructure for the thread input structure.
Reimplemented AttachThreadInput() and added GetGUIThreadInfo().

diff --git a/server/queue.c b/server/queue.c
index e250fae..5a08668 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -89,6 +89,19 @@
     unsigned int    lparam;    /* lparam for message */
 };
 
+struct thread_input
+{
+    struct object          obj;           /* object header */
+    user_handle_t          focus;         /* focus window */
+    user_handle_t          capture;       /* capture window */
+    user_handle_t          active;        /* active window */
+    user_handle_t          menu_owner;    /* current menu owner window */
+    user_handle_t          move_size;     /* current moving/resizing window */
+    user_handle_t          caret;         /* caret window */
+    rectangle_t            rect;          /* caret rectangle */
+    unsigned char          keystate[256]; /* state of each key */
+};
+
 struct msg_queue
 {
     struct object          obj;           /* object header */
@@ -106,6 +119,7 @@
     struct timer          *last_timer;    /* tail of timer list */
     struct timer          *next_timer;    /* next timer to expire */
     struct timeout_user   *timeout;       /* timeout for next timer to expire */
+    struct thread_input   *input;         /* thread input descriptor */
 };
 
 static void msg_queue_dump( struct object *obj, int verbose );
@@ -114,6 +128,8 @@
 static int msg_queue_signaled( struct object *obj, struct thread *thread );
 static int msg_queue_satisfied( struct object *obj, struct thread *thread );
 static void msg_queue_destroy( struct object *obj );
+static void thread_input_dump( struct object *obj, int verbose );
+static void thread_input_destroy( struct object *obj );
 static void timer_callback( void *private );
 
 static const struct object_ops msg_queue_ops =
@@ -134,11 +150,55 @@
 };
 
 
-static struct msg_queue *create_msg_queue( struct thread *thread )
+static const struct object_ops thread_input_ops =
+{
+    sizeof(struct thread_input),  /* size */
+    thread_input_dump,            /* dump */
+    no_add_queue,                 /* add_queue */
+    NULL,                         /* remove_queue */
+    NULL,                         /* signaled */
+    NULL,                         /* satisfied */
+    NULL,                         /* get_poll_events */
+    NULL,                         /* poll_event */
+    no_get_fd,                    /* get_fd */
+    no_flush,                     /* flush */
+    no_get_file_info,             /* get_file_info */
+    NULL,                         /* queue_async */
+    thread_input_destroy          /* destroy */
+};
+
+/* create a thread input object */
+static struct thread_input *create_thread_input(void)
+{
+    struct thread_input *input;
+
+    if ((input = alloc_object( &thread_input_ops, -1 )))
+    {
+        input->focus       = 0;
+        input->capture     = 0;
+        input->active      = 0;
+        input->menu_owner  = 0;
+        input->move_size   = 0;
+        input->caret       = 0;
+        input->rect.left   = 0;
+        input->rect.top    = 0;
+        input->rect.right  = 0;
+        input->rect.bottom = 0;
+        memset( input->keystate, 0, sizeof(input->keystate) );
+    }
+    return input;
+}
+
+/* pointer to input structure of foreground thread */
+static struct thread_input *foreground_input;
+
+/* create a message queue object */
+static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_input *input )
 {
     struct msg_queue *queue;
     int i;
 
+    if (!input && !(input = create_thread_input())) return NULL;
     if ((queue = alloc_object( &msg_queue_ops, -1 )))
     {
         queue->wake_bits       = 0;
@@ -153,6 +213,7 @@
         queue->last_timer      = NULL;
         queue->next_timer      = NULL;
         queue->timeout         = NULL;
+        queue->input           = (struct thread_input *)grab_object( input );
         for (i = 0; i < NB_MSG_KINDS; i++)
             queue->msg_list[i].first = queue->msg_list[i].last = NULL;
 
@@ -160,6 +221,7 @@
         if (!thread->process->queue)
             thread->process->queue = (struct msg_queue *)grab_object( queue );
     }
+    release_object( input );
     return queue;
 }
 
@@ -217,7 +279,7 @@
 inline static struct msg_queue *get_current_queue(void)
 {
     struct msg_queue *queue = current->queue;
-    if (!queue) queue = create_msg_queue( current );
+    if (!queue) queue = create_msg_queue( current, NULL );
     return queue;
 }
 
@@ -529,8 +591,76 @@
         timer = next;
     }
     if (queue->timeout) remove_timeout_user( queue->timeout );
+    if (queue->input) release_object( queue->input );
 }
 
+static void thread_input_dump( struct object *obj, int verbose )
+{
+    struct thread_input *input = (struct thread_input *)obj;
+    fprintf( stderr, "Thread input focus=%x capture=%x active=%x\n",
+             input->focus, input->capture, input->active );
+}
+
+static void thread_input_destroy( struct object *obj )
+{
+    struct thread_input *input = (struct thread_input *)obj;
+
+    if (foreground_input == input) foreground_input = NULL;
+}
+
+/* fix the thread input data when a window is destroyed */
+inline static void thread_input_cleanup_window( struct msg_queue *queue, user_handle_t window )
+{
+    struct thread_input *input = queue->input;
+
+    if (window == input->focus) input->focus = 0;
+    if (window == input->capture) input->capture = 0;
+    if (window == input->active) input->active = 0;
+    if (window == input->menu_owner) input->menu_owner = 0;
+    if (window == input->move_size) input->move_size = 0;
+    if (window == input->caret) input->caret = 0;
+}
+
+/* attach two thread input data structures */
+int attach_thread_input( struct thread *thread_from, struct thread *thread_to )
+{
+    struct thread_input *input;
+
+    if (!thread_to->queue && !(thread_to->queue = create_msg_queue( thread_to, NULL ))) return 0;
+    input = (struct thread_input *)grab_object( thread_to->queue->input );
+
+    if (thread_from->queue)
+    {
+        release_object( thread_from->queue->input );
+        thread_from->queue->input = input;
+    }
+    else
+    {
+        if (!(thread_from->queue = create_msg_queue( thread_from, input ))) return 0;
+    }
+    memset( input->keystate, 0, sizeof(input->keystate) );
+    return 1;
+}
+
+/* detach two thread input data structures */
+static void detach_thread_input( struct thread *thread_from, struct thread *thread_to )
+{
+    struct thread_input *input;
+
+    if (!thread_from->queue || !thread_to->queue ||
+        thread_from->queue->input != thread_to->queue->input)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return;
+    }
+    if ((input = create_thread_input()))
+    {
+        release_object( thread_from->queue->input );
+        thread_from->queue->input = input;
+    }
+}
+
+
 /* set the next timer to expire */
 static void set_next_timer( struct msg_queue *queue, struct timer *timer )
 {
@@ -706,6 +836,8 @@
             msg = next;
         }
     }
+
+    thread_input_cleanup_window( queue, win );
 }
 
 /* post a message to a window; used by socket handling */
@@ -1098,3 +1230,65 @@
     if (!queue || !kill_timer( queue, get_user_full_handle(req->win), req->msg, req->id ))
         set_error( STATUS_INVALID_PARAMETER );
 }
+
+
+/* attach (or detach) thread inputs */
+DECL_HANDLER(attach_thread_input)
+{
+    struct thread *thread_from = get_thread_from_id( req->tid_from );
+    struct thread *thread_to = get_thread_from_id( req->tid_to );
+
+    if (!thread_from || !thread_to)
+    {
+        if (thread_from) release_object( thread_from );
+        if (thread_to) release_object( thread_to );
+        return;
+    }
+    if (thread_from != thread_to)
+    {
+        if (req->attach) attach_thread_input( thread_from, thread_to );
+        else detach_thread_input( thread_from, thread_to );
+    }
+    else set_error( STATUS_ACCESS_DENIED );
+    release_object( thread_from );
+    release_object( thread_to );
+}
+
+
+/* get thread input data */
+DECL_HANDLER(get_thread_input)
+{
+    struct thread *thread = NULL;
+    struct thread_input *input;
+
+    if (req->tid)
+    {
+        if (!(thread = get_thread_from_id( req->tid ))) return;
+        input = thread->queue ? thread->queue->input : NULL;
+    }
+    else input = foreground_input;  /* get the foreground thread info */
+
+    if (input)
+    {
+        reply->focus      = input->focus;
+        reply->capture    = input->capture;
+        reply->active     = input->active;
+        reply->menu_owner = input->menu_owner;
+        reply->move_size  = input->move_size;
+        reply->caret      = input->caret;
+        reply->rect       = input->rect;
+    }
+    else
+    {
+        reply->focus      = 0;
+        reply->capture    = 0;
+        reply->active     = 0;
+        reply->menu_owner = 0;
+        reply->move_size  = 0;
+        reply->caret      = 0;
+        reply->rect.left = reply->rect.top = reply->rect.right = reply->rect.bottom = 0;
+    }
+    /* foreground window is active window of foreground thread */
+    reply->foreground = foreground_input ? foreground_input->active : 0;
+    if (thread) release_object( thread );
+}