From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/sysparams.c | 1 + dlls/win32u/win32u_private.h | 36 +++++++++++++++++++ dlls/win32u/winstation.c | 70 ++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 3b6cab5bdc9..25448f62f7e 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 shared_desktop *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 673082056b1..06042816e2e 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6204,6 +6204,7 @@ static void thread_detach(void) destroy_thread_windows(); cleanup_imm_thread(); NtClose( thread_info->server_queue ); + if (thread_info->shared_desktop) shared_desktop_release( thread_info->shared_desktop );
exiting_thread_id = 0; } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index d5f010a8249..990176d389c 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -194,6 +194,19 @@ extern void user_unlock(void); extern void user_check_not_lock(void);
/* winstation.c */ + +struct shared_session; + +struct shared_desktop +{ + LONG ref; + const desktop_shm_t *shared; + struct shared_session *session; +}; + +extern void shared_desktop_release( struct shared_desktop *desktop ); +extern struct shared_desktop *get_shared_desktop( BOOL force ); + extern BOOL is_virtual_desktop(void);
/* window.c */ @@ -362,4 +375,27 @@ static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2 return !IsRectEmpty( dst ); }
+#if defined(__i386__) || defined(__x86_64__) +/* this prevents compilers from incorrectly reordering non-volatile reads (e.g., memcpy) from shared memory */ +#define __SHARED_READ_FENCE do { __asm__ __volatile__( "" ::: "memory" ); } while (0) +#else +#define __SHARED_READ_FENCE __atomic_thread_fence( __ATOMIC_ACQUIRE ) +#endif + +#define SHARED_READ_BEGIN( ptr, type ) \ + do { \ + const type *shared = (ptr); \ + LONG64 __seq; \ + do { \ + while ((__seq = ReadNoFence64( &shared->obj.seq )) & 1) \ + YieldProcessor(); \ + __SHARED_READ_FENCE; \ + do + +#define SHARED_READ_END \ + while (0); \ + __SHARED_READ_FENCE; \ + } while (ReadNoFence64( &shared->obj.seq ) != __seq); \ + } while(0) + #endif /* __WINE_WIN32U_PRIVATE */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 7537ce944d5..9cc35e994b5 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -122,6 +122,73 @@ static struct shared_session *get_shared_session( BOOL force ) return session; }
+static struct shared_desktop *shared_desktop_acquire( struct shared_desktop *desktop ) +{ + InterlockedIncrement( &desktop->ref ); + return desktop; +} + +void shared_desktop_release( struct shared_desktop *desktop ) +{ + if (!InterlockedDecrement( &desktop->ref )) + { + shared_session_release( desktop->session ); + free( desktop ); + } +} + +struct shared_desktop *get_shared_desktop( BOOL force ) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + struct shared_desktop *desktop; + + if (!thread_info->shared_desktop) force = TRUE; + if (force && (desktop = calloc( 1, sizeof(*desktop) ))) + { + const desktop_shm_t *ptr = NULL; + struct shared_session *session; + BOOL invalid = FALSE; + unsigned int idx; + + while ((session = get_shared_session( invalid ))) + { + SERVER_START_REQ( get_thread_desktop ) + { + req->tid = GetCurrentThreadId(); + if (wine_server_call_err( req )) idx = -1; + else idx = reply->session_index; + } + SERVER_END_REQ; + if (idx == -1) break; + + SHARED_READ_BEGIN( session->shared, session_shm_t ) + { + if ((invalid = shared->obj.invalid)) ptr = NULL; + else ptr = &shared->objects[idx].desktop; + } + SHARED_READ_END; + + if (!invalid) break; + shared_session_release( session ); + } + + if (idx == -1) + { + free( desktop ); + return NULL; + } + + desktop->ref = 1; + if (!(desktop->shared = ptr)) ERR( "Failed to map desktop shared memory\n" ); + desktop->session = session; + + if (thread_info->shared_desktop) shared_desktop_release( thread_info->shared_desktop ); + thread_info->shared_desktop = desktop; + } + + return shared_desktop_acquire( thread_info->shared_desktop ); +} + BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); @@ -346,6 +413,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) shared_desktop_release( 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;