Added server-side infrastructure for the thread input structure.
Reimplemented AttachThreadInput() and added GetGUIThreadInfo().
diff --git a/dlls/user/message.c b/dlls/user/message.c
index 55270a5..8621241 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -2231,3 +2231,58 @@
/* now obsolete the message queue will be expanded dynamically as necessary */
return TRUE;
}
+
+
+/**********************************************************************
+ * AttachThreadInput (USER32.@)
+ *
+ * Attaches the input processing mechanism of one thread to that of
+ * another thread.
+ */
+BOOL WINAPI AttachThreadInput( DWORD from, DWORD to, BOOL attach )
+{
+ BOOL ret;
+
+ SERVER_START_REQ( attach_thread_input )
+ {
+ req->tid_from = from;
+ req->tid_to = to;
+ req->attach = attach;
+ ret = !wine_server_call_err( req );
+ }
+ SERVER_END_REQ;
+ return ret;
+}
+
+
+/**********************************************************************
+ * GetGUIThreadInfo (USER32.@)
+ */
+BOOL WINAPI GetGUIThreadInfo( DWORD id, GUITHREADINFO *info )
+{
+ BOOL ret;
+
+ SERVER_START_REQ( get_thread_input )
+ {
+ req->tid = id;
+ if ((ret = !wine_server_call_err( req )))
+ {
+ info->flags = 0;
+ info->hwndActive = reply->active;
+ info->hwndFocus = reply->focus;
+ info->hwndCapture = reply->capture;
+ info->hwndMenuOwner = reply->menu_owner;
+ info->hwndMoveSize = reply->move_size;
+ info->hwndCaret = reply->caret;
+ info->rcCaret.left = reply->rect.left;
+ info->rcCaret.top = reply->rect.top;
+ info->rcCaret.right = reply->rect.right;
+ info->rcCaret.bottom = reply->rect.bottom;
+ if (reply->menu_owner) info->flags |= GUI_INMENUMODE;
+ if (reply->move_size) info->flags |= GUI_INMOVESIZE;
+ if (reply->caret) info->flags |= GUI_CARETBLINKING;
+ }
+ }
+ SERVER_END_REQ;
+ return ret;
+}
diff --git a/dlls/user/user32.spec b/dlls/user/user32.spec
index 19da4e6..e7cfc38 100644
--- a/dlls/user/user32.spec
+++ b/dlls/user/user32.spec
@@ -244,6 +244,7 @@
@ stdcall GetDoubleClickTime() GetDoubleClickTime
@ stdcall GetFocus() GetFocus
@ stdcall GetForegroundWindow() GetForegroundWindow
+@ stdcall GetGUIThreadInfo(long ptr) GetGUIThreadInfo
@ stdcall GetIconInfo(long ptr) GetIconInfo
@ stub GetInputDesktop
@ stdcall GetInputState() GetInputState
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 8b2a63f..01b6e19 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2729,6 +2729,40 @@
};
+
+struct attach_thread_input_request
+{
+ struct request_header __header;
+ thread_id_t tid_from;
+ thread_id_t tid_to;
+ int attach;
+};
+struct attach_thread_input_reply
+{
+ struct reply_header __header;
+};
+
+
+
+struct get_thread_input_request
+{
+ struct request_header __header;
+ thread_id_t tid;
+};
+struct get_thread_input_reply
+{
+ struct reply_header __header;
+ user_handle_t focus;
+ user_handle_t capture;
+ user_handle_t active;
+ user_handle_t foreground;
+ user_handle_t menu_owner;
+ user_handle_t move_size;
+ user_handle_t caret;
+ rectangle_t rect;
+};
+
+
enum request
{
REQ_new_process,
@@ -2887,6 +2921,8 @@
REQ_remove_window_property,
REQ_get_window_property,
REQ_get_window_properties,
+ REQ_attach_thread_input,
+ REQ_get_thread_input,
REQ_NB_REQUESTS
};
@@ -3050,6 +3086,8 @@
struct remove_window_property_request remove_window_property_request;
struct get_window_property_request get_window_property_request;
struct get_window_properties_request get_window_properties_request;
+ struct attach_thread_input_request attach_thread_input_request;
+ struct get_thread_input_request get_thread_input_request;
};
union generic_reply
{
@@ -3211,8 +3249,10 @@
struct remove_window_property_reply remove_window_property_reply;
struct get_window_property_reply get_window_property_reply;
struct get_window_properties_reply get_window_properties_reply;
+ struct attach_thread_input_reply attach_thread_input_reply;
+ struct get_thread_input_reply get_thread_input_reply;
};
-#define SERVER_PROTOCOL_VERSION 84
+#define SERVER_PROTOCOL_VERSION 85
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winuser.h b/include/winuser.h
index cce01e4..f7c29ff 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -397,6 +397,27 @@
} INPUT, *PINPUT, *LPINPUT;
+typedef struct tagGUITHREADINFO
+{
+ DWORD cbSize;
+ DWORD flags;
+ HWND hwndActive;
+ HWND hwndFocus;
+ HWND hwndCapture;
+ HWND hwndMenuOwner;
+ HWND hwndMoveSize;
+ HWND hwndCaret;
+ RECT rcCaret;
+} GUITHREADINFO, *PGUITHREADINFO, *LPGUITHREADINFO;
+
+#define GUI_CARETBLINKING 0x00000001
+#define GUI_INMOVESIZE 0x00000002
+#define GUI_INMENUMODE 0x00000004
+#define GUI_SYSTEMMENUMODE 0x00000008
+#define GUI_POPUPMENUMODE 0x00000010
+#define GUI_16BITTASK 0x00000020
+
+
/***** Dialogs *****/
/* Gcc on Solaris has a version of this that we don't care about */
@@ -4079,15 +4100,16 @@
INT WINAPI GetDlgItemTextA(HWND,INT,LPSTR,UINT);
INT WINAPI GetDlgItemTextW(HWND,INT,LPWSTR,UINT);
#define GetDlgItemText WINELIB_NAME_AW(GetDlgItemText)
-UINT WINAPI GetDoubleClickTime(void);
-HWND WINAPI GetFocus(void);
-HWND WINAPI GetForegroundWindow(void);
-BOOL WINAPI GetInputState(void);
-UINT WINAPI GetInternalWindowPos(HWND,LPRECT,LPPOINT);
-UINT WINAPI GetKBCodePage(void);
-INT WINAPI GetKeyboardType(INT);
-INT WINAPI GetKeyNameTextA(LONG,LPSTR,INT);
-INT WINAPI GetKeyNameTextW(LONG,LPWSTR,INT);
+UINT WINAPI GetDoubleClickTime(void);
+HWND WINAPI GetFocus(void);
+HWND WINAPI GetForegroundWindow(void);
+BOOL WINAPI GetGUIThreadInfo(DWORD,GUITHREADINFO*);
+BOOL WINAPI GetInputState(void);
+UINT WINAPI GetInternalWindowPos(HWND,LPRECT,LPPOINT);
+UINT WINAPI GetKBCodePage(void);
+INT WINAPI GetKeyboardType(INT);
+INT WINAPI GetKeyNameTextA(LONG,LPSTR,INT);
+INT WINAPI GetKeyNameTextW(LONG,LPWSTR,INT);
#define GetKeyNameText WINELIB_NAME_AW(GetKeyNameText)
INT WINAPI GetKeyboardLayoutNameA(LPSTR);
INT WINAPI GetKeyboardLayoutNameW(LPWSTR);
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;
}
diff --git a/windows/queue.c b/windows/queue.c
index 83c2d3e..291c2cc 100644
--- a/windows/queue.c
+++ b/windows/queue.c
@@ -593,88 +593,3 @@
if (!(queue = QUEUE_Current())) return 0;
return queue->GetMessageExtraInfoVal;
}
-
-
-/**********************************************************************
- * AttachThreadInput (USER32.@) Attaches input of 1 thread to other
- *
- * Attaches the input processing mechanism of one thread to that of
- * another thread.
- *
- * RETURNS
- * Success: TRUE
- * Failure: FALSE
- *
- * TODO:
- * 1. Reset the Key State (currenly per thread key state is not maintained)
- */
-BOOL WINAPI AttachThreadInput(
- DWORD idAttach, /* [in] Thread to attach */
- DWORD idAttachTo, /* [in] Thread to attach to */
- BOOL fAttach) /* [in] Attach or detach */
-{
- MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
- BOOL16 bRet = 0;
-
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
- /* A thread cannot attach to itself */
- if ( idAttach == idAttachTo )
- goto CLEANUP;
-
- /* According to the docs this method should fail if a
- * "Journal record" hook is installed. (attaches all input queues together)
- */
- if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
- goto CLEANUP;
-
- /* Retrieve message queues corresponding to the thread id's */
- pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
- pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
-
- /* Ensure we have message queues and that Src and Tgt threads
- * are not system threads.
- */
- if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
- goto CLEANUP;
-
- if (fAttach) /* Attach threads */
- {
- /* Only attach if currently detached */
- if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
- {
- /* First release the target threads perQData */
- PERQDATA_Release( pTgtMsgQ->pQData );
-
- /* Share a reference to the source threads perQDATA */
- PERQDATA_Addref( pSrcMsgQ->pQData );
- pTgtMsgQ->pQData = pSrcMsgQ->pQData;
- }
- }
- else /* Detach threads */
- {
- /* Only detach if currently attached */
- if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
- {
- /* First release the target threads perQData */
- PERQDATA_Release( pTgtMsgQ->pQData );
-
- /* Give the target thread its own private perQDATA once more */
- pTgtMsgQ->pQData = PERQDATA_CreateInstance();
- }
- }
-
- /* TODO: Reset the Key State */
-
- bRet = 1; /* Success */
-
-CLEANUP:
-
- /* Unlock the queues before returning */
- if ( pSrcMsgQ )
- QUEUE_Unlock( pSrcMsgQ );
- if ( pTgtMsgQ )
- QUEUE_Unlock( pTgtMsgQ );
-
- return bRet;
-}