First part of Proton shared memory series. The full branch can be seen at https://gitlab.winehq.org/rbernon/wine/-/commits/mr/shared-memories.
-- v9: user32: Use the desktop shared data for GetCursorPos(). server: Add a sequence number to the shared data. server: Move the cursor position and last change time to the shared data. server: Use the helper to update the cursor last change time. server: Create a desktop shared mapping.
From: Rémi Bernon rbernon@codeweavers.com
Based on a patch by Huw Davies huw@codeweavers.com. --- server/directory.c | 1 + server/file.h | 2 ++ server/handle.c | 5 ++++- server/mapping.c | 36 +++++++++++++++++++++++++++++++++++- server/protocol.def | 6 ++++++ server/user.h | 2 ++ server/winstation.c | 38 +++++++++++++++++++++++++++++++++++++- 7 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/server/directory.c b/server/directory.c index 23d7eb0a2b7..ffb9b762584 100644 --- a/server/directory.c +++ b/server/directory.c @@ -37,6 +37,7 @@ #include "process.h" #include "file.h" #include "unicode.h" +#include "user.h"
#define HASH_SIZE 7 /* default hash size */
diff --git a/server/file.h b/server/file.h index 39a833cd105..5452ff2eccd 100644 --- a/server/file.h +++ b/server/file.h @@ -186,6 +186,8 @@ extern void free_mapped_views( struct process *process ); extern int get_page_size(void); extern struct mapping *create_fd_mapping( struct object *root, const struct unicode_str *name, struct fd *fd, unsigned int attr, const struct security_descriptor *sd ); +extern struct object *create_object_mapping( struct object *object, mem_size_t size, unsigned int attr, + const struct security_descriptor *sd, void **ptr ); extern struct object *create_user_data_mapping( struct object *root, const struct unicode_str *name, unsigned int attr, const struct security_descriptor *sd );
diff --git a/server/handle.c b/server/handle.c index 0595fdb403b..10803c0e0ea 100644 --- a/server/handle.c +++ b/server/handle.c @@ -620,6 +620,7 @@ obj_handle_t open_object( struct process *process, obj_handle_t parent, unsigned const struct object_ops *ops, const struct unicode_str *name, unsigned int attributes ) { + static const WCHAR object_mappingW[] = {'_','_','w','i','n','e','_','m','a','p','p','i','n','g'}; obj_handle_t handle = 0; struct object *obj, *root = NULL;
@@ -631,7 +632,9 @@ obj_handle_t open_object( struct process *process, obj_handle_t parent, unsigned
if (parent) { - if (name->len) + if (name->len == sizeof(object_mappingW) && !memcmp( name->str, object_mappingW, name->len )) + root = get_handle_obj( process, parent, 0, NULL ); + else if (name->len) root = get_directory_obj( process, parent ); else /* opening the object itself can work for non-directories too */ root = get_handle_obj( process, parent, 0, NULL ); diff --git a/server/mapping.c b/server/mapping.c index f754078acf7..d3c738b81e7 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -29,6 +29,7 @@ #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> +#include <errno.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -139,6 +140,7 @@ struct memory_view
static const WCHAR mapping_name[] = {'S','e','c','t','i','o','n'}; +static const WCHAR object_mappingW[] = {'_','_','w','i','n','e','_','m','a','p','p','i','n','g'};
struct type_descr mapping_type = { @@ -165,6 +167,7 @@ struct mapping
static void mapping_dump( struct object *obj, int verbose ); static struct fd *mapping_get_fd( struct object *obj ); +static int mapping_link_name( struct object *obj, struct object_name *name, struct object *parent ); static void mapping_destroy( struct object *obj ); static enum server_fd_type mapping_get_fd_type( struct fd *fd );
@@ -184,7 +187,7 @@ static const struct object_ops mapping_ops = default_set_sd, /* set_sd */ default_get_full_name, /* get_full_name */ no_lookup_name, /* lookup_name */ - directory_link_name, /* link_name */ + mapping_link_name, /* link_name */ default_unlink_name, /* unlink_name */ no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ @@ -1139,6 +1142,17 @@ static struct fd *mapping_get_fd( struct object *obj ) return (struct fd *)grab_object( mapping->fd ); }
+static int mapping_link_name( struct object *obj, struct object_name *name, struct object *parent ) +{ + if (name->len == sizeof(object_mappingW) && !memcmp( name->name, object_mappingW, name->len )) + { + list_init( &name->entry ); + return 1; + } + + return directory_link_name( obj, name, parent ); +} + static void mapping_destroy( struct object *obj ) { struct mapping *mapping = (struct mapping *)obj; @@ -1222,6 +1236,26 @@ int get_page_size(void) return page_mask + 1; }
+struct object *create_object_mapping( struct object *object, mem_size_t size, unsigned int attr, + const struct security_descriptor *sd, void **ptr ) +{ + static const struct unicode_str name_str = {object_mappingW, sizeof(object_mappingW)}; + static unsigned int access = FILE_READ_DATA | FILE_WRITE_DATA; + struct mapping *mapping; + void *tmp; + + if (!(mapping = create_mapping( object, &name_str, attr, size, SEC_COMMIT, 0, access, sd ))) return NULL; + if ((tmp = mmap( NULL, mapping->size, PROT_WRITE, MAP_SHARED, get_unix_fd( mapping->fd ), 0 )) == MAP_FAILED) + { + fprintf( stderr, "wine: Failed to map shared memory: %u %m\n", errno ); + release_object( &mapping->obj ); + return NULL; + } + + *ptr = tmp; + return &mapping->obj; +} + struct object *create_user_data_mapping( struct object *root, const struct unicode_str *name, unsigned int attr, const struct security_descriptor *sd ) { diff --git a/server/protocol.def b/server/protocol.def index 5d60e7fcda3..db039f45255 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -893,6 +893,12 @@ typedef struct lparam_t info; } cursor_pos_t;
+struct desktop_shared_memory +{ + int placeholder; +}; +typedef volatile struct desktop_shared_memory desktop_shm_t; + /****************************************************************/ /* Request declarations */
diff --git a/server/user.h b/server/user.h index 8fa55e09b0f..446e06ebb0c 100644 --- a/server/user.h +++ b/server/user.h @@ -76,6 +76,8 @@ struct desktop unsigned int users; /* processes and threads using this desktop */ struct global_cursor cursor; /* global cursor information */ unsigned char keystate[256]; /* asynchronous key state */ + struct object *shared_mapping; /* desktop shared memory mapping */ + const desktop_shm_t *shared; /* desktop shared memory */ };
/* user handles functions */ diff --git a/server/winstation.c b/server/winstation.c index 5903497d61e..b4c16314b32 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdarg.h> #include <sys/types.h> +#include <sys/mman.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -50,6 +51,8 @@ static struct object *winstation_lookup_name( struct object *obj, struct unicode unsigned int attr, struct object *root ); static void winstation_destroy( struct object *obj ); static void desktop_dump( struct object *obj, int verbose ); +static struct object *desktop_lookup_name( struct object *obj, struct unicode_str *name, + unsigned int attr, struct object *root ); static int desktop_link_name( struct object *obj, struct object_name *name, struct object *parent ); static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); static void desktop_destroy( struct object *obj ); @@ -123,7 +126,7 @@ static const struct object_ops desktop_ops = default_get_sd, /* get_sd */ default_set_sd, /* set_sd */ default_get_full_name, /* get_full_name */ - no_lookup_name, /* lookup_name */ + desktop_lookup_name, /* lookup_name */ desktop_link_name, /* link_name */ default_unlink_name, /* unlink_name */ no_open_file, /* open_file */ @@ -235,11 +238,22 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->global_hooks = NULL; desktop->close_timeout = NULL; desktop->foreground_input = NULL; + desktop->shared_mapping = NULL; + desktop->shared = NULL; desktop->users = 0; memset( &desktop->cursor, 0, sizeof(desktop->cursor) ); memset( desktop->keystate, 0, sizeof(desktop->keystate) ); list_add_tail( &winstation->desktops, &desktop->entry ); list_init( &desktop->hotkeys ); + + if ((desktop->shared_mapping = create_object_mapping( &desktop->obj, sizeof(*desktop->shared), + 0, NULL, (void **)&desktop->shared ))) + memset( (void *)desktop->shared, 0, sizeof(*desktop->shared) ); + else + { + release_object( desktop ); + return NULL; + } } else { @@ -258,6 +272,25 @@ static void desktop_dump( struct object *obj, int verbose ) desktop->flags, desktop->winstation, desktop->top_window, desktop->global_hooks ); }
+static struct object *desktop_lookup_name( struct object *obj, struct unicode_str *name, + unsigned int attr, struct object *root ) +{ + static const WCHAR object_mappingW[] = {'_','_','w','i','n','e','_','m','a','p','p','i','n','g'}; + struct desktop *desktop = (struct desktop *)obj; + assert( obj->ops == &desktop_ops ); + + if (!name) set_error( STATUS_OBJECT_TYPE_MISMATCH ); + + if (desktop->shared_mapping && name->len == sizeof(object_mappingW) && + !memcmp( name->str, object_mappingW, name->len )) + { + name->len = 0; + return grab_object( desktop->shared_mapping ); + } + + return NULL; +} + static int desktop_link_name( struct object *obj, struct object_name *name, struct object *parent ) { struct winstation *winstation = (struct winstation *)parent; @@ -296,6 +329,8 @@ static void desktop_destroy( struct object *obj ) if (desktop->msg_window) free_window_handle( desktop->msg_window ); if (desktop->global_hooks) release_object( desktop->global_hooks ); if (desktop->close_timeout) remove_timeout_user( desktop->close_timeout ); + if (desktop->shared_mapping) release_object( desktop->shared_mapping ); + if (desktop->shared) munmap( (void *)desktop->shared, sizeof(*desktop->shared) ); list_remove( &desktop->entry ); release_object( desktop->winstation ); } @@ -312,6 +347,7 @@ static void close_desktop_timeout( void *private )
desktop->close_timeout = NULL; unlink_named_object( &desktop->obj ); /* make sure no other process can open it */ + unlink_named_object( desktop->shared_mapping ); post_desktop_message( desktop, WM_CLOSE, 0, 0 ); /* and signal the owner to quit */ }
From: Rémi Bernon rbernon@codeweavers.com
Based on a patch by Huw Davies huw@codeweavers.com. --- server/queue.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/server/queue.c b/server/queue.c index cd913ae03e5..eaeb5d15492 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1887,7 +1887,9 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons WM_MOUSEHWHEEL /* 0x1000 = MOUSEEVENTF_HWHEEL */ };
- desktop->cursor.last_change = get_tick_count(); + /* update last desktop cursor change time */ + update_desktop_cursor_pos( desktop, desktop->cursor.win, desktop->cursor.x, desktop->cursor.y ); + flags = input->mouse.flags; time = input->mouse.time; if (!time) time = desktop->cursor.last_change;
From: Rémi Bernon rbernon@codeweavers.com
Based on a patch by Huw Davies huw@codeweavers.com. --- server/protocol.def | 9 ++++- server/queue.c | 82 +++++++++++++++++++++++++++------------------ server/user.h | 3 -- 3 files changed, 57 insertions(+), 37 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index db039f45255..1f56b8f0bf7 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -893,9 +893,16 @@ typedef struct lparam_t info; } cursor_pos_t;
+struct shared_cursor +{ + int x; /* cursor position */ + int y; + unsigned int last_change; /* time of last position change */ +}; + struct desktop_shared_memory { - int placeholder; + struct shared_cursor cursor; /* global cursor information */ }; typedef volatile struct desktop_shared_memory desktop_shm_t;
diff --git a/server/queue.c b/server/queue.c index eaeb5d15492..7ee96c9325e 100644 --- a/server/queue.c +++ b/server/queue.c @@ -232,6 +232,16 @@ static unsigned int last_input_time; static cursor_pos_t cursor_history[64]; static unsigned int cursor_history_latest;
+#define SHARED_WRITE_BEGIN( object, type ) \ + do { \ + const type *__shared = (object)->shared; \ + type *shared = (type *)__shared; \ + do + +#define SHARED_WRITE_END \ + while(0); \ + } while(0) + static void queue_hardware_message( struct desktop *desktop, struct message *msg, int always_queue ); static void free_message( struct message *msg );
@@ -423,8 +433,8 @@ static void queue_cursor_message( struct desktop *desktop, user_handle_t win, un msg->msg = message; msg->wparam = wparam; msg->lparam = lparam; - msg->x = desktop->cursor.x; - msg->y = desktop->cursor.y; + msg->x = desktop->shared->cursor.x; + msg->y = desktop->shared->cursor.y; if (!(msg->win = win) && (input = desktop->foreground_input)) msg->win = input->active; queue_hardware_message( desktop, msg, 1 ); } @@ -463,13 +473,19 @@ static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win, int x, int y ) { int updated; + unsigned int time = get_tick_count();
x = max( min( x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); y = max( min( y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); - updated = (desktop->cursor.x != x || desktop->cursor.y != y); - desktop->cursor.x = x; - desktop->cursor.y = y; - desktop->cursor.last_change = get_tick_count(); + updated = (desktop->shared->cursor.x != x || desktop->shared->cursor.y != y); + + SHARED_WRITE_BEGIN( desktop, desktop_shm_t ) + { + shared->cursor.x = x; + shared->cursor.y = y; + shared->cursor.last_change = time; + } + SHARED_WRITE_END;
if (!win || !is_window_visible( win ) || is_window_transparent( win )) win = shallow_window_from_point( desktop, x, y ); @@ -515,8 +531,8 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig { struct desktop *desktop = queue->input->desktop;
- *x = desktop->cursor.x; - *y = desktop->cursor.y; + *x = desktop->shared->cursor.x; + *y = desktop->shared->cursor.y; *time = get_tick_count(); }
@@ -540,9 +556,9 @@ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, unsig else desktop->cursor.clip = top_rect;
/* warp the mouse to be inside the clip rect */ - x = max( min( desktop->cursor.x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); - y = max( min( desktop->cursor.y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); - if (x != desktop->cursor.x || y != desktop->cursor.y) set_cursor_pos( desktop, x, y ); + x = max( min( desktop->shared->cursor.x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); + y = max( min( desktop->shared->cursor.y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); + if (x != desktop->shared->cursor.x || y != desktop->shared->cursor.y) set_cursor_pos( desktop, x, y );
/* request clip cursor rectangle reset to the desktop thread */ if (reset) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, flags, FALSE ); @@ -1695,8 +1711,8 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg if (desktop->keystate[VK_XBUTTON2] & 0x80) msg->wparam |= MK_XBUTTON2; break; } - msg->x = desktop->cursor.x; - msg->y = desktop->cursor.y; + msg->x = desktop->shared->cursor.x; + msg->y = desktop->shared->cursor.y;
if (msg->win && (thread = get_window_thread( msg->win ))) { @@ -1888,11 +1904,11 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons };
/* update last desktop cursor change time */ - update_desktop_cursor_pos( desktop, desktop->cursor.win, desktop->cursor.x, desktop->cursor.y ); + update_desktop_cursor_pos( desktop, desktop->cursor.win, desktop->shared->cursor.x, desktop->shared->cursor.y );
flags = input->mouse.flags; time = input->mouse.time; - if (!time) time = desktop->cursor.last_change; + if (!time) time = desktop->shared->cursor.last_change;
if (flags & MOUSEEVENTF_MOVE) { @@ -1901,19 +1917,19 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons x = input->mouse.x; y = input->mouse.y; if (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE) && - x == desktop->cursor.x && y == desktop->cursor.y) + x == desktop->shared->cursor.x && y == desktop->shared->cursor.y) flags &= ~MOUSEEVENTF_MOVE; } else { - x = desktop->cursor.x + input->mouse.x; - y = desktop->cursor.y + input->mouse.y; + x = desktop->shared->cursor.x + input->mouse.x; + y = desktop->shared->cursor.y + input->mouse.y; } } else { - x = desktop->cursor.x; - y = desktop->cursor.y; + x = desktop->shared->cursor.x; + y = desktop->shared->cursor.y; }
if ((foreground = get_foreground_thread( desktop, win ))) @@ -1930,8 +1946,8 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons msg_data->size = sizeof(*msg_data); msg_data->flags = flags; msg_data->rawinput.type = RIM_TYPEMOUSE; - msg_data->rawinput.mouse.x = x - desktop->cursor.x; - msg_data->rawinput.mouse.y = y - desktop->cursor.y; + msg_data->rawinput.mouse.x = x - desktop->shared->cursor.x; + msg_data->rawinput.mouse.y = y - desktop->shared->cursor.y; msg_data->rawinput.mouse.data = input->mouse.data;
enum_processes( queue_rawinput_message, &raw_msg ); @@ -2158,8 +2174,8 @@ static void queue_custom_hardware_message( struct desktop *desktop, user_handle_ msg->msg = input->hw.msg; msg->wparam = 0; msg->lparam = input->hw.lparam; - msg->x = desktop->cursor.x; - msg->y = desktop->cursor.y; + msg->x = desktop->shared->cursor.x; + msg->y = desktop->shared->cursor.y;
queue_hardware_message( desktop, msg, 1 ); } @@ -2680,8 +2696,8 @@ DECL_HANDLER(send_hardware_message) } }
- reply->prev_x = desktop->cursor.x; - reply->prev_y = desktop->cursor.y; + reply->prev_x = desktop->shared->cursor.x; + reply->prev_y = desktop->shared->cursor.y;
switch (req->input.type) { @@ -2699,8 +2715,8 @@ DECL_HANDLER(send_hardware_message) } if (thread) release_object( thread );
- reply->new_x = desktop->cursor.x; - reply->new_y = desktop->cursor.y; + reply->new_x = desktop->shared->cursor.x; + reply->new_y = desktop->shared->cursor.y; release_object( desktop ); }
@@ -3392,8 +3408,8 @@ DECL_HANDLER(set_cursor)
reply->prev_handle = input->cursor; reply->prev_count = input->cursor_count; - reply->prev_x = desktop->cursor.x; - reply->prev_y = desktop->cursor.y; + reply->prev_x = desktop->shared->cursor.x; + reply->prev_y = desktop->shared->cursor.y;
if (req->flags & SET_CURSOR_HANDLE) { @@ -3416,10 +3432,10 @@ DECL_HANDLER(set_cursor) if (req->flags & (SET_CURSOR_HANDLE | SET_CURSOR_COUNT)) update_desktop_cursor_handle( desktop, input );
- reply->new_x = desktop->cursor.x; - reply->new_y = desktop->cursor.y; + reply->new_x = desktop->shared->cursor.x; + reply->new_y = desktop->shared->cursor.y; reply->new_clip = desktop->cursor.clip; - reply->last_change = desktop->cursor.last_change; + reply->last_change = desktop->shared->cursor.last_change; }
/* Get the history of the 64 last cursor positions */ diff --git a/server/user.h b/server/user.h index 446e06ebb0c..6a0e3d65ab7 100644 --- a/server/user.h +++ b/server/user.h @@ -54,10 +54,7 @@ struct winstation
struct global_cursor { - int x; /* cursor position */ - int y; rectangle_t clip; /* cursor clip rectangle */ - unsigned int last_change; /* time of last position change */ user_handle_t win; /* window that contains the cursor */ };
From: Rémi Bernon rbernon@codeweavers.com
The client should check that the lower SEQUENCE_MASK_BITS are zero before reading the data and confirm that the number is unchanged when it's finished.
Based on a patch by Huw Davies huw@codeweavers.com. --- server/protocol.def | 1 + server/queue.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index 1f56b8f0bf7..a82ffe90e5e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -902,6 +902,7 @@ struct shared_cursor
struct desktop_shared_memory { + unsigned int seq; /* sequence number - server updating if (seq & 1) != 0 */ struct shared_cursor cursor; /* global cursor information */ }; typedef volatile struct desktop_shared_memory desktop_shm_t; diff --git a/server/queue.c b/server/queue.c index 7ee96c9325e..8f7c74724e8 100644 --- a/server/queue.c +++ b/server/queue.c @@ -232,14 +232,24 @@ static unsigned int last_input_time; static cursor_pos_t cursor_history[64]; static unsigned int cursor_history_latest;
-#define SHARED_WRITE_BEGIN( object, type ) \ - do { \ - const type *__shared = (object)->shared; \ - type *shared = (type *)__shared; \ +#if defined(__i386__) || defined(__x86_64__) +#define __SHARED_INCREMENT_SEQ( x ) ++(x) +#else +#define __SHARED_INCREMENT_SEQ( x ) __atomic_add_fetch( &(x), 1, __ATOMIC_RELEASE ) +#endif + +#define SHARED_WRITE_BEGIN( object, type ) \ + do { \ + const type *__shared = (object)->shared; \ + type *shared = (type *)__shared; \ + unsigned int __seq = __SHARED_INCREMENT_SEQ( shared->seq ); \ + assert( (__seq & 1) != 0 ); \ do
-#define SHARED_WRITE_END \ - while(0); \ +#define SHARED_WRITE_END \ + while(0); \ + __seq = __SHARED_INCREMENT_SEQ( shared->seq ) - __seq; \ + assert( __seq == 1 ); \ } while(0)
static void queue_hardware_message( struct desktop *desktop, struct message *msg, int always_queue );
From: Rémi Bernon rbernon@codeweavers.com
Based on a patch by Huw Davies huw@codeweavers.com. --- dlls/win32u/input.c | 20 +++++++-------- dlls/win32u/ntuser_private.h | 27 ++++++++++++++++++++ dlls/win32u/sysparams.c | 6 +++++ dlls/win32u/winstation.c | 49 ++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index ef8d564c264..4f78f450798 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -749,25 +749,23 @@ BOOL WINAPI NtUserSetCursorPos( INT x, INT y ) */ BOOL get_cursor_pos( POINT *pt ) { - BOOL ret; + const desktop_shm_t *shared = get_desktop_shared_memory(); DWORD last_change; + BOOL ret = TRUE; UINT dpi;
- if (!pt) return FALSE; + if (!pt || !shared) return FALSE;
- SERVER_START_REQ( set_cursor ) + SHARED_READ_BEGIN( shared, desktop_shm_t ) { - if ((ret = !wine_server_call( req ))) - { - pt->x = reply->new_x; - pt->y = reply->new_y; - last_change = reply->last_change; - } + pt->x = shared->cursor.x; + pt->y = shared->cursor.y; + last_change = shared->cursor.last_change; } - SERVER_END_REQ; + SHARED_READ_END;
/* query new position from graphics driver if we haven't updated recently */ - if (ret && NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt ); + if (NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt ); if (ret && (dpi = get_thread_dpi())) { HMONITOR monitor = monitor_from_point( *pt, MONITOR_DEFAULTTOPRIMARY, 0 ); diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 11bb7f4baf6..2efab7062ec 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -137,6 +137,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 */ + const desktop_shm_t *desktop_shm; /* Ptr to server's desktop shared memory */ };
C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) ); @@ -261,6 +262,9 @@ void release_user_handle_ptr( void *ptr ); void *next_process_user_handle_ptr( HANDLE *handle, unsigned int type ); UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask );
+/* winstation.c */ +extern const desktop_shm_t *get_desktop_shared_memory(void); + static inline UINT win_get_flags( HWND hwnd ) { return win_set_flags( hwnd, 0, 0 ); @@ -270,4 +274,27 @@ WND *get_win_ptr( HWND hwnd ); BOOL is_child( HWND parent, HWND child ); BOOL is_window( HWND hwnd );
+#if defined(__i386__) || defined(__x86_64__) +#define __SHARED_READ_SEQ( x ) (x) +#define __SHARED_READ_FENCE do {} while(0) +#else +#define __SHARED_READ_SEQ( x ) __atomic_load_n( &(x), __ATOMIC_RELAXED ) +#define __SHARED_READ_FENCE __atomic_thread_fence( __ATOMIC_ACQUIRE ) +#endif + +#define SHARED_READ_BEGIN( ptr, type ) \ + do { \ + const type *__shared = (ptr); \ + unsigned int __seq; \ + do { \ + while ((__seq = __SHARED_READ_SEQ( __shared->seq )) & 1) YieldProcessor(); \ + __SHARED_READ_FENCE; \ + do + +#define SHARED_READ_END \ + while (0); \ + __SHARED_READ_FENCE; \ + } while (__SHARED_READ_SEQ( __shared->seq ) != __seq); \ + } while(0) + #endif /* __WINE_NTUSER_PRIVATE_H */ diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index e2c5b10da9e..526f3286885 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6205,6 +6205,12 @@ static void thread_detach(void) cleanup_imm_thread(); NtClose( thread_info->server_queue );
+ if (thread_info->desktop_shm) + { + NtUnmapViewOfSection( GetCurrentProcess(), (void *)thread_info->desktop_shm ); + thread_info->desktop_shm = NULL; + } + exiting_thread_id = 0; }
diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index b187b246941..01dbe2c0b67 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -265,6 +265,11 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ) thread_info->client_info.msg_window = 0; if (key_state_info) key_state_info->time = 0; if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( TRUE ); + if (thread_info->desktop_shm) + { + NtUnmapViewOfSection( GetCurrentProcess(), (void *)thread_info->desktop_shm ); + thread_info->desktop_shm = NULL; + } } return ret; } @@ -607,6 +612,50 @@ static const WCHAR *get_default_desktop( void *buf, size_t buf_size ) return defaultW; }
+static volatile void *map_object_shared_memory( HANDLE object, SIZE_T size ) +{ + static const WCHAR mapping_nameW[] = + { + '_','_','w','i','n','e','_','m','a','p','p','i','n','g',0 + }; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING section_str; + HANDLE handle; + UINT status; + void *ptr; + + RtlInitUnicodeString( §ion_str, mapping_nameW ); + InitializeObjectAttributes( &attr, §ion_str, 0, object, NULL ); + if (!(status = NtOpenSection( &handle, SECTION_ALL_ACCESS, &attr ))) + { + ptr = NULL; + status = NtMapViewOfSection( handle, GetCurrentProcess(), &ptr, 0, 0, NULL, + &size, ViewUnmap, 0, PAGE_READONLY ); + NtClose( handle ); + } + + if (status) + { + ERR( "Failed to map object %p mapping, status %#x\n", object, status ); + return NULL; + } + + return ptr; +} + +const desktop_shm_t *get_desktop_shared_memory(void) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + HANDLE desktop; + + if (thread_info->desktop_shm) return thread_info->desktop_shm; + + desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); + thread_info->desktop_shm = map_object_shared_memory( desktop, sizeof(*thread_info->desktop_shm) ); + + return thread_info->desktop_shm; +} + /*********************************************************************** * winstation_init *
Rebased and refreshed, addressing the comments above.
On Fri Aug 4 16:25:15 2023 +0000, Jinoh Kang wrote:
which is more risky
It may be less risky since intra-object namespace does not have to be enumerable (it can only implement `lookup_name`), in contrast to directory object which has to be *always* enumerable by contract.
than having a wine-specific directory in the WindowsStations directory.
I think it's more likely for an app to mistake `__wine_desktop_mappings` for an actual window station. Currently `user32!EnumWindowStationsW` is implemented as a direct server call in Wine. Windows might as well implement it with `NtQueryDirectoryObject` instead.
It also suggests that desktop is supposed to contain other objects on Windows
I think it's okay since the distinction between leaf objects and directories are blurry on NT. Previously leaf objects can always become a namespace without breaking the ABI.
whereas it would only be a Wine-specific thing.
As long as we keep returning `STATUS_OBJECT_TYPE_MISMATCH` other than for `__wine_desktop_mapping`, we should be fine.
I did that, and I think it ends up being actually nice as it introduces a generic way to have a shared mapping for every server object.
It could perhaps be made even more generic than what's currently done in this MR, by having the shared mapping stored in the object structure, but I didn't want to do that yet. Then, because of this every "mappable" object need to implement a custom `lookup_name` / `link_name` for now, to handle the object mapping, but I think it's not too bad and it could be factored out later if that seem useful.
On Thu Jan 25 12:39:17 2024 +0000, Rémi Bernon wrote:
changed this line in [version 9 of the diff](/wine/wine/-/merge_requests/3103/diffs?diff_id=95824&start_sha=95524ba0157d58db3369a814efb94da137d6a8b7#2b37d5dc1c3bb6a637f043480c038caea64531bb_165_166)
Also did that, I think it works well with the new object mappings. I was initially worried about reused mapping, but that's not the case anymore and even detached thread inputs are going to be destroyed (and another one re-created when threads are detached).
I also updated the https://gitlab.winehq.org/rbernon/wine/-/commits/mr/shared-memories branch with all the commits rebased and with the new mapping scheme. The thread input / foreground thread input mappings have been reworked and I think it's much nicer now.
On Sat Jul 1 04:00:38 2023 +0000, Jinoh Kang wrote:
We could just pass a data offset as part of server requests that
affect or query current desktop. This implies the existence of some shared memory that encompasses such desktops, where each desktop can be located at some "data offset." This leads to four[^5th] sub-problems, which IMHO have minimal impact in my original proposal of "ref-counted mappings:"
- What's the scope of the shared memory?
- **The scope of the shared memory is Winstation-wide:** This sounds
like the most intuitive choice to me, since a desktop has exactly one parent Winstation at any time. Handling `SetProcessWindowStation` will need to get a little more complicated, though. 2. **The scope of the shared memory is wineprefix-wide:** This will make the shared memory management more straightforward, but may end up bloating the shared memory for *all* processes whenever some sandbox-based application tries to create a new station. 2. How many desktops can fit in the initial size of the shared memory?
- **Just one:** This is the least bloated choice, since most
applications (including the `wineboot` dialog) needs just one desktop. 2. **More than one:** This will allow us to delay the 3rd and 4th problem when handling multiple desktops, but will lead to more bloat. 3. Can the shared memory be resized after creation?
- **The size is fixed:** This means that we cannot use the desktop
shared memory when the number of desktops exceed the certain threshold. The CreateDesktop[^createdesktop] documentation states that the "number of desktops that can be created is limited by the size of the system desktop heap," although I'm not sure if the heap is Winstation-wide or system-wide. 2. **The size is variable:** This allows us to delay the 4th problem until when we run out of the host memory. 4. What if we run out of shared memory space?
- **Fail desktop creation:** Simplest solution, but may lead to
regression in existing apps (e.g., Win32 sandboxed apps) if the shared memory is fixed and limited in size (e.g., just one desktop). 2. **Don't fail desktop creation:** This way, we'll have to retain the current desktop-related querying server calls for fallback, which may be additional maintenance burden. Ref-counted mappings avoid these problems entirely, by making each mapping local to the specific desktop object.
And yes, there is an argument to use a different mapping for
more-or-less thread-specific data, Meanwhile, ref-counted mappings do not rely on thread-specific mappings; multiple threads can share the same mapping (the refcount merely has to be incremented).
but desktops are global objects.
I believe that the ambiguity of the word _global_ was the source of confusion. "Global" can mean two things:
- The object being described is associated with some *broader* scope
that subsumes the *immediate* scope in context. This matches the dictionary definition. For example, a C global variable is associated with the *program* scope, which subsumes the *scope of a translation unit* it is defined in. 2. The object is part of some ambient, implicit environment. For example, [ambient authority] is used for Unix/NT APIs that need to check for access permissions, and thread-local variables can be used as implicit parameters to functions. I guess that the meaning was not easily disambiguated since the notion of "broader" and "immediate" scope were not clearly agreed (or thought to be agreed) upon, and the latter does not imply the former. I could be wrong, though, so please ignore it if it was not the case. [^5th]: There's actually the 5th problem, which is a bit theoretical: a process that is given access to the entire shared memory can read the state of a desktop that the process has no access to. Wine is not a security boundary, so let's ignore it. [^createdesktop]: [CreateDesktopA function (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-creat...) [ambient authority]: https://en.wikipedia.org/wiki/Ambient_authority
I didn't try to share the desktop mappings, I think it could perhaps be done but I don't like the idea of pooling the mappings together as I feel it's just going to make things more complicated.
Instead, we could easily add some kind of thread / process desktop deduplication for the most common case where the thread uses its process default desktop, which could be good enough and could be added later.
Jinoh Kang (@iamahuman) commented about server/mapping.c:
return page_mask + 1;
}
+struct object *create_object_mapping( struct object *object, mem_size_t size, unsigned int attr,
const struct security_descriptor *sd, void **ptr )
+{
- static const struct unicode_str name_str = {object_mappingW, sizeof(object_mappingW)};
- static unsigned int access = FILE_READ_DATA | FILE_WRITE_DATA;
- struct mapping *mapping;
- void *tmp;
- if (!(mapping = create_mapping( object, &name_str, attr, size, SEC_COMMIT, 0, access, sd ))) return NULL;
Thanks for your effort for making `__wine_mapping` objects consistent with otber named objects.
However, I don't think it's strictly necessary to make internal objects like the full-blown named objects. In fact, we can simply return an anonymous object from `lookup_name`. For instance, `console_device_lookup_name` constructs objects out of thin air whenever a server object is requested.
Making the object anonymous will also make `mapping_link_name` unnecessary.
(I haven't tested this approach; please feel free to correct me if I did a mistake)