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

diff --git a/server/protocol.def b/server/protocol.def
index 1aeb6c4..43f4fa0 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1908,3 +1908,26 @@
     int            total;         /* total number of properties */
     VARARG(props,properties);     /* list of properties */
 @END
+
+
+/* Attach (or detach) thread inputs */
+@REQ(attach_thread_input)
+    thread_id_t    tid_from;       /* thread to be attached */
+    thread_id_t    tid_to;         /* thread to which tid_from should be attached */
+    int            attach;         /* is it an attach? */
+@END
+
+
+/* Get input data for a given thread */
+@REQ(get_thread_input)
+    thread_id_t    tid;           /* id of thread */
+@REPLY
+    user_handle_t  focus;         /* handle to the focus window */
+    user_handle_t  capture;       /* handle to the capture window */
+    user_handle_t  active;        /* handle to the active window */
+    user_handle_t  foreground;    /* handle to the global foreground window */
+    user_handle_t  menu_owner;    /* handle to the menu owner */
+    user_handle_t  move_size;     /* handle to the moving/resizing window */
+    user_handle_t  caret;         /* handle to the caret window */
+    rectangle_t    rect;          /* caret rectangle */
+@END
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 );
+}
diff --git a/server/request.h b/server/request.h
index 0c744b4..3f1a036 100644
--- a/server/request.h
+++ b/server/request.h
@@ -259,6 +259,8 @@
 DECL_HANDLER(remove_window_property);
 DECL_HANDLER(get_window_property);
 DECL_HANDLER(get_window_properties);
+DECL_HANDLER(attach_thread_input);
+DECL_HANDLER(get_thread_input);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -421,6 +423,8 @@
     (req_handler)req_remove_window_property,
     (req_handler)req_get_window_property,
     (req_handler)req_get_window_properties,
+    (req_handler)req_attach_thread_input,
+    (req_handler)req_get_thread_input,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 18cda73..776370f 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2175,6 +2175,31 @@
     dump_varargs_properties( cur_size );
 }
 
+static void dump_attach_thread_input_request( const struct attach_thread_input_request *req )
+{
+    fprintf( stderr, " tid_from=%08x,", req->tid_from );
+    fprintf( stderr, " tid_to=%08x,", req->tid_to );
+    fprintf( stderr, " attach=%d", req->attach );
+}
+
+static void dump_get_thread_input_request( const struct get_thread_input_request *req )
+{
+    fprintf( stderr, " tid=%08x", req->tid );
+}
+
+static void dump_get_thread_input_reply( const struct get_thread_input_reply *req )
+{
+    fprintf( stderr, " focus=%08x,", req->focus );
+    fprintf( stderr, " capture=%08x,", req->capture );
+    fprintf( stderr, " active=%08x,", req->active );
+    fprintf( stderr, " foreground=%08x,", req->foreground );
+    fprintf( stderr, " menu_owner=%08x,", req->menu_owner );
+    fprintf( stderr, " move_size=%08x,", req->move_size );
+    fprintf( stderr, " caret=%08x,", req->caret );
+    fprintf( stderr, " rect=" );
+    dump_rectangle( &req->rect );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -2332,6 +2357,8 @@
     (dump_func)dump_remove_window_property_request,
     (dump_func)dump_get_window_property_request,
     (dump_func)dump_get_window_properties_request,
+    (dump_func)dump_attach_thread_input_request,
+    (dump_func)dump_get_thread_input_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -2491,6 +2518,8 @@
     (dump_func)dump_remove_window_property_reply,
     (dump_func)dump_get_window_property_reply,
     (dump_func)dump_get_window_properties_reply,
+    (dump_func)0,
+    (dump_func)dump_get_thread_input_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -2650,6 +2679,8 @@
     "remove_window_property",
     "get_window_property",
     "get_window_properties",
+    "attach_thread_input",
+    "get_thread_input",
 };
 
 /* ### make_requests end ### */
diff --git a/server/user.h b/server/user.h
index 6d5766e..b1cc439 100644
--- a/server/user.h
+++ b/server/user.h
@@ -46,6 +46,7 @@
 extern void free_msg_queue( struct thread *thread );
 extern void inc_queue_paint_count( struct thread *thread, int incr );
 extern void queue_cleanup_window( struct thread *thread, user_handle_t win );
+extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to );
 extern void post_message( user_handle_t win, unsigned int message,
                           unsigned int wparam, unsigned int lparam );
 
diff --git a/server/window.c b/server/window.c
index e513b87..ad51288 100644
--- a/server/window.c
+++ b/server/window.c
@@ -287,6 +287,9 @@
     }
     else win->next = win->prev = NULL;
 
+    /* if parent belongs to a different thread, attach the two threads */
+    if (parent && parent->thread && parent->thread != current)
+        attach_thread_input( current, parent->thread );
     return win;
 }