From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/sysparams.c | 1 + dlls/win32u/win32u_private.h | 14 ++++ dlls/win32u/winstation.c | 129 +++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 3b6cab5bdc9..524195941f3 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_object *shared_desktop; /* thread desktop shared session object */ };
C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index a31d586a5b6..7ccdd544633 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6262,6 +6262,7 @@ static void thread_detach(void) destroy_thread_windows(); cleanup_imm_thread(); NtClose( thread_info->server_queue ); + if (thread_info->shared_desktop) session_object_release_weak( thread_info->shared_desktop );
exiting_thread_id = 0; } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index f7d25ffc6f3..1d3626be3f5 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -194,6 +194,20 @@ extern void user_unlock(void); extern void user_check_not_lock(void);
/* winstation.c */ + +struct shared_session; +struct session_object +{ + LONG ref; + UINT64 id; + const session_obj_t *shared; + struct shared_session *session; +}; + +extern void session_object_release( struct session_object *desktop ); +extern void session_object_release_weak( struct session_object *desktop ); +extern struct session_object *get_shared_desktop( BOOL force ); + extern BOOL is_virtual_desktop(void);
/* window.c */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index deb4cd0bf02..647204e396a 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -56,6 +56,13 @@ struct shared_session const session_shm_t *shared; };
+/* acquire a weak reference on a session */ +static void shared_session_acquire_weak( struct shared_session *session ) +{ + int ref = InterlockedIncrement( &session->weak_ref ); + TRACE( "session %p incrementing weak_ref to %d\n", session, ref ); +} + /* try acquiring a strong reference from a possibly weak referenced session */ static struct shared_session *shared_session_acquire( struct shared_session *session ) { @@ -170,6 +177,125 @@ static struct shared_session *get_shared_session( BOOL force ) return session; }
+/* acquire an object with a weak reference on its session */ +static struct session_object *session_object_acquire_weak( struct session_object *object ) +{ + int ref = InterlockedIncrement( &object->ref ); + TRACE( "object %p incrementing ref to %d\n", object, ref ); + return object; +} + +/* try acquiring an object with a strong reference on its session */ +static struct session_object *session_object_acquire( struct session_object *object ) +{ + /* try upgrading the session reference, which fails if the session has been invalidated */ + if (object && shared_session_acquire( object->session )) + return session_object_acquire_weak( object ); + + TRACE( "object %p failed to upgrade from weak ref\n", object ); + return NULL; +} + +/* release an object with a weak session reference */ +void session_object_release_weak( struct session_object *object ) +{ + int ref = InterlockedDecrement( &object->ref ); + TRACE( "object %p decrementing ref to %d\n", object, ref ); + if (!ref) + { + shared_session_release_weak( object->session ); + free( object ); + } +} + +/* release an object with a strong session reference */ +void session_object_release( struct session_object *object ) +{ + shared_session_release( object->session ); + session_object_release_weak( object ); +} + +enum object_type +{ + OBJECT_TYPE_DESKTOP = 1, +}; + +static int 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 strong reference on the last known session object for a thread id and type */ +static struct session_object *get_thread_session_object( UINT tid, enum object_type type ) +{ + struct shared_session *session; + struct session_object *object; + BOOL valid = TRUE; + int index; + + TRACE( "tid %04x, type %u\n", tid, type ); + + if (!(object = calloc( 1, sizeof(*object) ))) return NULL; + object->ref = 1; + + while ((session = get_shared_session( !valid ))) + { + if ((index = get_thread_session_object_index( tid, type, &object->id )) < 0) break; + if ((valid = index < session->object_capacity)) + { + object->shared = &session->shared->objects[index]; + break; + } + shared_session_release( session ); + } + + if (!object->shared) + { + WARN( "Failed to find object type %u for thread %04x\n", type, tid ); + if (session) shared_session_release( session ); + free( object ); + return NULL; + } + + /* every object holds an additional weak ref on their session */ + shared_session_acquire_weak( session ); + object->session = session; + + TRACE( "returning session %p, object %p\n", session, object ); + return object; +} + +struct session_object *get_shared_desktop( BOOL force ) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + struct session_object *desktop; + + TRACE( "force %u\n", force ); + + if (force || !(desktop = session_object_acquire( thread_info->shared_desktop ))) + { + if (!(desktop = get_thread_session_object( GetCurrentThreadId(), OBJECT_TYPE_DESKTOP ))) return NULL; + if (thread_info->shared_desktop) session_object_release_weak( thread_info->shared_desktop ); + thread_info->shared_desktop = session_object_acquire_weak( desktop ); + } + + return desktop; +} + BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); @@ -394,6 +520,9 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ) thread_info->client_info.top_window = 0; thread_info->client_info.msg_window = 0; if (key_state_info) key_state_info->time = 0; + if (thread_info->shared_desktop) session_object_release_weak( thread_info->shared_desktop ); + thread_info->shared_desktop = NULL; + get_shared_desktop( FALSE ); if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( TRUE ); } return ret;