From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/sysparams.c | 1 + dlls/win32u/win32u_private.h | 13 ++++ dlls/win32u/winstation.c | 123 +++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 3b6cab5bdc9..27430c3478b 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -127,6 +127,7 @@ struct user_thread_info UINT spy_indent; /* Current spy indent */ BOOL clipping_cursor; /* thread is currently clipping */ DWORD clipping_reset; /* time when clipping was last reset */ + struct session_thread_data *session_data; /* shared session thread data */ };
C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 9457c9010f0..f856c9fe05e 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6039,6 +6039,7 @@ static void thread_detach(void) destroy_thread_windows(); cleanup_imm_thread(); NtClose( thread_info->server_queue ); + free( thread_info->session_data );
exiting_thread_id = 0; } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 582204d9f04..f5088829fb8 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -193,6 +193,19 @@ extern void user_unlock(void); extern void user_check_not_lock(void);
/* winstation.c */ + +struct shared_session; + +struct object_lock +{ + UINT64 id; + const session_obj_t *shared; /* only valid when locked, read inside SHARED_READ_BEGIN */ + struct shared_session *session; /* only valid when locked */ +}; + +extern void object_lock_release( struct object_lock *lock ); +extern BOOL get_shared_desktop( struct object_lock *lock ); + extern BOOL is_virtual_desktop(void);
/* window.c */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 9066a7279b7..bf985036fb1 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -43,6 +43,18 @@ WINE_DECLARE_DEBUG_CHANNEL(win);
#define DESKTOP_ALL_ACCESS 0x01ff
+struct object_info +{ + UINT64 session_id; + UINT64 id; + UINT index; +}; + +struct session_thread_data +{ + struct object_info shared_desktop; /* thread desktop shared session object info */ +}; + struct shared_session { LONG ref; @@ -54,6 +66,13 @@ struct shared_session static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER; static struct shared_session *shared_session;
+static struct session_thread_data *get_session_thread_data(void) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + if (!thread_info->session_data) thread_info->session_data = calloc(1, sizeof(struct session_thread_data)); + return thread_info->session_data; +} + static struct shared_session *shared_session_acquire( struct shared_session *session ) { int ref = InterlockedIncrement( &session->ref ); @@ -149,6 +168,107 @@ static struct shared_session *get_shared_session( BOOL force ) return session; }
+static struct shared_session *shared_session_try_acquire( UINT64 session_id ) +{ + struct shared_session *session; + + pthread_mutex_lock( &session_lock ); + + if (shared_session && shared_session->id == session_id) + session = shared_session_acquire( shared_session ); + else + { + TRACE( "session %s has been discarded\n", wine_dbgstr_longlong(session_id) ); + session = NULL; + } + + pthread_mutex_unlock( &session_lock ); + + return session; +} + +static BOOL try_lock_session_object( struct object_info *info, struct object_lock *lock ) +{ + if (!(lock->id = info->id) || !(lock->session = shared_session_try_acquire( info->session_id ))) return FALSE; + lock->shared = &lock->session->shared->objects[info->index]; + return TRUE; +} + +void object_lock_release( struct object_lock *lock ) +{ + shared_session_release( lock->session ); + memset( lock, 0, sizeof(*lock) ); + lock->id = -1; /* force object to be refreshed when retrying lock */ +} + +enum object_type +{ + OBJECT_TYPE_DESKTOP = 1, +}; + +static UINT get_thread_session_object_index( UINT tid, enum object_type type, UINT64 *id ) +{ + switch (type) + { + case OBJECT_TYPE_DESKTOP: + SERVER_START_REQ( get_thread_desktop ) + { + req->tid = tid; + if (wine_server_call_err( req )) return -1; + *id = reply->object_id; + return reply->index; + } + SERVER_END_REQ; + break; + } + + return -1; +} + +/* return a locked session object for a thread id and type */ +static BOOL get_thread_session_object( UINT tid, enum object_type type, struct object_info *info, + struct object_lock *lock ) +{ + struct shared_session *session; + BOOL valid = TRUE; + + TRACE( "tid %04x, type %u\n", tid, type ); + + memset( info, 0, sizeof(*info) ); + info->index = -1; + + while ((session = get_shared_session( !valid ))) + { + info->session_id = session->id; + if ((info->index = get_thread_session_object_index( tid, type, &info->id )) == -1) break; + if ((valid = info->index < session->object_capacity)) break; + shared_session_release( session ); + } + + if (valid && info->index == -1) + { + WARN( "Failed to find object type %u for thread %04x\n", type, tid ); + if (session) shared_session_release( session ); + return FALSE; + } + + lock->id = info->id; + lock->session = session; + lock->shared = &session->shared->objects[info->index]; + return TRUE; +} + +BOOL get_shared_desktop( struct object_lock *lock ) +{ + struct session_thread_data *data = get_session_thread_data(); + struct object_info *info = &data->shared_desktop; + + TRACE( "lock %p\n", lock ); + + if (lock->id != -1 && try_lock_session_object( info, lock )) return TRUE; + return get_thread_session_object( GetCurrentThreadId(), OBJECT_TYPE_DESKTOP, info, lock ); +} + BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); @@ -370,9 +490,12 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ) { struct user_thread_info *thread_info = get_user_thread_info(); struct user_key_state_info *key_state_info = thread_info->key_state; + struct object_lock lock = {0}; thread_info->client_info.top_window = 0; thread_info->client_info.msg_window = 0; if (key_state_info) key_state_info->time = 0; + memset( &get_session_thread_data()->shared_desktop, 0, sizeof(struct object_info) ); + if (get_shared_desktop( &lock )) object_lock_release( &lock ); if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( TRUE ); } return ret;