This WIP branch prototypes the usage of virtual display devices (through "virtual desktop" mode at the moment) to support display mode emulation in the Wayland driver.
It works by allowing the driver to mark each host adapter with a virtual_id, which, if present, signals to win32u that it should include the adapter in the virtual display configuration. The virtual configuration (along with the virtual ids) is reported back to the driver though a new user driver function. If none of the host adapters is marked with a virtual id (i.e., all other drivers) we fall back to the single virtual display mode as before.
Some deficiencies and open questions with the prototype:
1. Virtual desktop mode implies a lot of behavior that we don't want at this point the Wayland driver (e.g., taskbar window, changing the display mode to the virtual desktop resolution at startup, some changes in fsclip behavior). One way forward would be to have a "virtual display devices" mode separate from the "virtual desktop" mode, so that drivers can opt in only to the desired behavior (virtual desktop would imply virtual devices, to maintain the current behavior). 2. It's not clear to me what would be the best way for a driver to opt in or out of certain core behavior. Perhaps the driver would change some volatile registry key which other components would consult? Or introduce a user driver function for core to query the driver about such features on demand? Is there some precedence I can get inspiration from? 3. At the moment, the prototype calls the new "NotifyVirtualDevices" user driver callback only in the process which the display update occurred. This means that other processes don't get notified of the virtual devices mapping to host devices, so, in the Wayland driver case, we are not able to scale windows from those processes properly. One approach would be for core to ensure that the NotifyVirtualDevices callback is called once in all processes, but I am not sure what's the best way to achieve this (or even if this is the preferred approach). Another way would be to have a mechanism for the driver to query the virtual device info (+mappings) on demand, and just broadcast a message to all windows. In this case it could even be done internally by the driver (so more in line with what the part-12 MR is doing), if we don't want to introduce a more global behavior. Perhaps there is some even better mechanism, which would also be more fitting to how we envision the virtual display/devic es to evolve going forward.
Let me know what you think!
From: Alexandros Frantzis alexandros.frantzis@collabora.com
The reporting of non-current wl_output modes is deprecated, and most compositors now report only the current display mode. --- dlls/winewayland.drv/display.c | 44 +++++----- dlls/winewayland.drv/wayland_output.c | 112 ++++---------------------- dlls/winewayland.drv/waylanddrv.h | 4 +- 3 files changed, 36 insertions(+), 124 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index ebe151ffab0..99dc92e0e5a 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -71,10 +71,10 @@ static int output_info_cmp_primary_x_y(const void *va, const void *vb)
static inline BOOL output_info_overlap(struct output_info *a, struct output_info *b) { - return b->x < a->x + a->output->current_mode->width && - b->x + b->output->current_mode->width > a->x && - b->y < a->y + a->output->current_mode->height && - b->y + b->output->current_mode->height > a->y; + return b->x < a->x + a->output->mode.width && + b->x + b->output->mode.width > a->x && + b->y < a->y + a->output->mode.height && + b->y + b->output->mode.height > a->y; }
/* Map a point to one of the four quadrants of our 2d coordinate space: @@ -156,16 +156,16 @@ static BOOL output_info_array_resolve_overlaps(struct wl_array *output_info_arra rel_x = (move->output->logical_x - anchor->output->logical_x + (x_use_end ? move->output->logical_w : 0)) / (double)anchor->output->logical_w; - move->x = anchor->x + anchor->output->current_mode->width * rel_x - - (x_use_end ? move->output->current_mode->width : 0); + move->x = anchor->x + anchor->output->mode.width * rel_x - + (x_use_end ? move->output->mode.width : 0);
/* Similarly for the Y axis. */ y_use_end = move->output->logical_y < anchor->output->logical_y; rel_y = (move->output->logical_y - anchor->output->logical_y + (y_use_end ? move->output->logical_h : 0)) / (double)anchor->output->logical_h; - move->y = anchor->y + anchor->output->current_mode->height * rel_y - - (y_use_end ? move->output->current_mode->height : 0); + move->y = anchor->y + anchor->output->mode.height * rel_y - + (y_use_end ? move->output->mode.height : 0); } }
@@ -231,8 +231,8 @@ static void wayland_add_device_monitor(const struct gdi_device_manager *device_m struct gdi_monitor monitor = {0};
SetRect(&monitor.rc_monitor, output_info->x, output_info->y, - output_info->x + output_info->output->current_mode->width, - output_info->y + output_info->output->current_mode->height); + output_info->x + output_info->output->mode.width, + output_info->y + output_info->output->mode.height);
/* We don't have a direct way to get the work area in Wayland. */ monitor.rc_work = monitor.rc_monitor; @@ -261,22 +261,14 @@ static void populate_devmode(struct wayland_output_mode *output_mode, DEVMODEW * static void wayland_add_device_modes(const struct gdi_device_manager *device_manager, void *param, struct output_info *output_info) { - struct wayland_output_mode *output_mode; + DEVMODEW mode = {.dmSize = sizeof(mode)};
- RB_FOR_EACH_ENTRY(output_mode, &output_info->output->modes, - struct wayland_output_mode, entry) - { - DEVMODEW mode = {.dmSize = sizeof(mode)}; - BOOL mode_is_current = output_mode == output_info->output->current_mode; - populate_devmode(output_mode, &mode); - if (mode_is_current) - { - mode.dmFields |= DM_POSITION; - mode.dmPosition.x = output_info->x; - mode.dmPosition.y = output_info->y; - } - device_manager->add_mode(&mode, mode_is_current, param); - } + populate_devmode(&output_info->output->mode, &mode); + mode.dmFields |= DM_POSITION; + mode.dmPosition.x = output_info->x; + mode.dmPosition.y = output_info->y; + + device_manager->add_mode(&mode, TRUE, param); }
/*********************************************************************** @@ -302,7 +294,7 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_list_for_each(output, &process_wayland.output_list, link) { - if (!output->current.current_mode) continue; + if (!output->current.mode.width || !output->current.mode.height) continue; output_info = wl_array_add(&output_info_array, sizeof(*output_info)); if (output_info) output_info->output = &output->current; else ERR("Failed to allocate space for output_info\n"); diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index f5941c10f6f..656e59edbec 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -35,7 +35,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); static const int32_t default_refresh = 60000; static uint32_t next_output_id = 0;
-#define WAYLAND_OUTPUT_CHANGED_MODES 0x01 +#define WAYLAND_OUTPUT_CHANGED_MODE 0x01 #define WAYLAND_OUTPUT_CHANGED_NAME 0x02 #define WAYLAND_OUTPUT_CHANGED_LOGICAL_XY 0x04 #define WAYLAND_OUTPUT_CHANGED_LOGICAL_WH 0x08 @@ -44,64 +44,6 @@ static uint32_t next_output_id = 0; * Output handling */
-/* Compare a mode rb_tree key with the provided mode rb_entry and return -1 if - * the key compares less than the entry, 0 if the key compares equal to the - * entry, and 1 if the key compares greater than the entry. - * - * The comparison is based on comparing the width, height and refresh in that - * order. */ -static int wayland_output_mode_cmp_rb(const void *key, - const struct rb_entry *entry) -{ - const struct wayland_output_mode *key_mode = key; - const struct wayland_output_mode *entry_mode = - RB_ENTRY_VALUE(entry, const struct wayland_output_mode, entry); - - if (key_mode->width < entry_mode->width) return -1; - if (key_mode->width > entry_mode->width) return 1; - if (key_mode->height < entry_mode->height) return -1; - if (key_mode->height > entry_mode->height) return 1; - if (key_mode->refresh < entry_mode->refresh) return -1; - if (key_mode->refresh > entry_mode->refresh) return 1; - - return 0; -} - -static void wayland_output_state_add_mode(struct wayland_output_state *state, - int32_t width, int32_t height, - int32_t refresh, BOOL current) -{ - struct rb_entry *mode_entry; - struct wayland_output_mode *mode; - struct wayland_output_mode key = - { - .width = width, - .height = height, - .refresh = refresh, - }; - - mode_entry = rb_get(&state->modes, &key); - if (mode_entry) - { - mode = RB_ENTRY_VALUE(mode_entry, struct wayland_output_mode, entry); - } - else - { - mode = calloc(1, sizeof(*mode)); - if (!mode) - { - ERR("Failed to allocate space for wayland_output_mode\n"); - return; - } - mode->width = width; - mode->height = height; - mode->refresh = refresh; - rb_put(&state->modes, mode, &mode->entry); - } - - if (current) state->current_mode = mode; -} - static void maybe_init_display_devices(void) { DWORD desktop_pid = 0; @@ -122,29 +64,13 @@ static void maybe_init_display_devices(void) NtUserPostMessage(desktop_hwnd, WM_WAYLAND_INIT_DISPLAY_DEVICES, 0, 0); }
-static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) -{ - free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry)); -} - static void wayland_output_done(struct wayland_output *output) { - struct wayland_output_mode *mode; - /* Update current state from pending state. */ pthread_mutex_lock(&process_wayland.output_mutex);
- if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_MODES) - { - RB_FOR_EACH_ENTRY(mode, &output->pending.modes, struct wayland_output_mode, entry) - { - wayland_output_state_add_mode(&output->current, - mode->width, mode->height, mode->refresh, - mode == output->pending.current_mode); - } - rb_destroy(&output->pending.modes, wayland_output_mode_free_rb, NULL); - rb_init(&output->pending.modes, wayland_output_mode_cmp_rb); - } + if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_MODE) + output->current.mode = output->pending.mode;
if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_NAME) { @@ -169,24 +95,19 @@ static void wayland_output_done(struct wayland_output *output)
/* Ensure the logical dimensions have sane values. */ if ((!output->current.logical_w || !output->current.logical_h) && - output->current.current_mode) + output->current.mode.width && output->current.mode.height) { - output->current.logical_w = output->current.current_mode->width; - output->current.logical_h = output->current.current_mode->height; + output->current.logical_w = output->current.mode.width; + output->current.logical_h = output->current.mode.height; }
pthread_mutex_unlock(&process_wayland.output_mutex);
- TRACE("name=%s logical=%d,%d+%dx%d\n", + TRACE("name=%s logical=%d,%d+%dx%d mode=%dx%d@%d\n", output->current.name, output->current.logical_x, output->current.logical_y, - output->current.logical_w, output->current.logical_h); - - RB_FOR_EACH_ENTRY(mode, &output->current.modes, struct wayland_output_mode, entry) - { - TRACE("mode %dx%d @ %d %s\n", - mode->width, mode->height, mode->refresh, - output->current.current_mode == mode ? "*" : ""); - } + output->current.logical_w, output->current.logical_h, + output->current.mode.width, output->current.mode.height, + output->current.mode.refresh);
maybe_init_display_devices(); } @@ -206,13 +127,17 @@ static void output_handle_mode(void *data, struct wl_output *wl_output, { struct wayland_output *output = data;
+ /* Non-current output modes are deprecated. */ + if (!(flags & WL_OUTPUT_MODE_CURRENT)) return; + /* Windows apps don't expect a zero refresh rate, so use a default value. */ if (refresh == 0) refresh = default_refresh;
- wayland_output_state_add_mode(&output->pending, width, height, refresh, - (flags & WL_OUTPUT_MODE_CURRENT)); + output->pending.mode.width = width; + output->pending.mode.height = height; + output->pending.mode.refresh = refresh;
- output->pending_flags |= WAYLAND_OUTPUT_CHANGED_MODES; + output->pending_flags |= WAYLAND_OUTPUT_CHANGED_MODE; }
static void output_handle_done(void *data, struct wl_output *wl_output) @@ -320,8 +245,6 @@ BOOL wayland_output_create(uint32_t id, uint32_t version) wl_output_add_listener(output->wl_output, &output_listener, output);
wl_list_init(&output->link); - rb_init(&output->pending.modes, wayland_output_mode_cmp_rb); - rb_init(&output->current.modes, wayland_output_mode_cmp_rb);
/* Have a fallback while we don't have compositor given name. */ name_len = snprintf(NULL, 0, "WaylandOutput%d", next_output_id); @@ -352,7 +275,6 @@ err:
static void wayland_output_state_deinit(struct wayland_output_state *state) { - rb_destroy(&state->modes, wayland_output_mode_free_rb, NULL); free(state->name); }
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index f030f6fc6a0..995275768a5 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -134,7 +134,6 @@ struct wayland
struct wayland_output_mode { - struct rb_entry entry; int32_t width; int32_t height; int32_t refresh; @@ -142,8 +141,7 @@ struct wayland_output_mode
struct wayland_output_state { - struct rb_tree modes; - struct wayland_output_mode *current_mode; + struct wayland_output_mode mode; char *name; int logical_x, logical_y; int logical_w, logical_h;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/win32u/sysparams.c | 67 +++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 13 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 9a43de8fd76..321f42fdc46 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1132,6 +1132,14 @@ static unsigned int format_date( WCHAR *bufferW, LONGLONG time ) return asciiz_to_unicode( bufferW, buffer ); }
+struct host_adapter +{ + struct gdi_adapter gdi; + UINT width; + UINT height; + UINT bpp; +}; + struct device_manager_ctx { unsigned int gpu_count; @@ -1146,10 +1154,7 @@ struct device_manager_ctx LUID gpu_luid; HKEY adapter_key; /* for the virtual desktop settings */ - BOOL is_primary; - UINT primary_bpp; - UINT primary_width; - UINT primary_height; + struct host_adapter *host_adapters; };
static void link_device( const WCHAR *instance, const WCHAR *class ) @@ -1652,6 +1657,7 @@ static void reset_display_manager_ctx( struct device_manager_ctx *ctx ) last_query_display_time = 0; } if (ctx->gpu_count) cleanup_devices(); + free( ctx->host_adapters );
memset( ctx, 0, sizeof(*ctx) ); if ((ctx->mutex = mutex)) prepare_devices(); @@ -1880,7 +1886,19 @@ static void desktop_add_gpu( const struct gdi_gpu *gpu, void *param ) static void desktop_add_adapter( const struct gdi_adapter *adapter, void *param ) { struct device_manager_ctx *ctx = param; - ctx->is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); + struct host_adapter *new_adapters, *host_adapter; + + if (!(adapter->state_flags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) return; + + new_adapters = realloc( ctx->host_adapters, + sizeof(*ctx->host_adapters) * (ctx->adapter_count + 1) ); + if (!new_adapters) return; + ctx->host_adapters = new_adapters; + ++ctx->adapter_count; + host_adapter = &ctx->host_adapters[ctx->adapter_count - 1]; + + memset( host_adapter, 0, sizeof(*host_adapter) ); + host_adapter->gdi = *adapter; }
static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param ) @@ -1890,12 +1908,16 @@ static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param static void desktop_add_mode( const DEVMODEW *mode, BOOL current, void *param ) { struct device_manager_ctx *ctx = param; + struct host_adapter *host_adapter; + + if (!ctx->adapter_count) return; + host_adapter = &ctx->host_adapters[ctx->adapter_count - 1];
- if (ctx->is_primary && current) + if (current) { - ctx->primary_bpp = mode->dmBitsPerPel; - ctx->primary_width = mode->dmPelsWidth; - ctx->primary_height = mode->dmPelsHeight; + host_adapter->bpp = mode->dmBitsPerPel; + host_adapter->width = mode->dmPelsWidth; + host_adapter->height = mode->dmPelsHeight; } }
@@ -1907,6 +1929,17 @@ static const struct gdi_device_manager desktop_device_manager = desktop_add_mode, };
+static struct host_adapter *get_primary_host_adapter( struct device_manager_ctx *ctx ) +{ + UINT i; + for (i = 0; i < ctx->adapter_count; i++) + { + if (ctx->host_adapters[i].gdi.state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE) + return &ctx->host_adapters[i]; + } + return NULL; +} + static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx ) { static const struct gdi_gpu gpu; @@ -1957,6 +1990,7 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct };
struct device_manager_ctx desktop_ctx = {0}; + struct host_adapter *primary; UINT screen_width, screen_height, max_width, max_height; unsigned int depths[] = {8, 16, 0}; DEVMODEW current, mode = @@ -1970,9 +2004,16 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */ if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE;
- max_width = desktop_ctx.primary_width; - max_height = desktop_ctx.primary_height; - depths[ARRAY_SIZE(depths) - 1] = desktop_ctx.primary_bpp; + if ((primary = get_primary_host_adapter( &desktop_ctx ))) + { + max_width = primary->width; + max_height = primary->height; + depths[ARRAY_SIZE(depths) - 1] = primary->bpp; + } + else + { + max_width = max_height = 0; + }
if (!get_default_desktop_size( &screen_width, &screen_height )) { @@ -1986,7 +2027,7 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct { current = mode; current.dmFields |= DM_POSITION; - current.dmBitsPerPel = desktop_ctx.primary_bpp; + current.dmBitsPerPel = primary ? primary->bpp : 0; current.dmPelsWidth = screen_width; current.dmPelsHeight = screen_height; }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/win32u/sysparams.c | 93 +++++++++++++++++++++++---------------- include/wine/gdi_driver.h | 1 + 2 files changed, 56 insertions(+), 38 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 321f42fdc46..f9207465288 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1940,12 +1940,14 @@ static struct host_adapter *get_primary_host_adapter( struct device_manager_ctx return NULL; }
-static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx ) +static void desktop_add_host_adapter( struct host_adapter *host_adapter, + UINT screen_width, UINT screen_height, + struct device_manager_ctx *ctx ) { - static const struct gdi_gpu gpu; - static const struct gdi_adapter adapter = + DEVMODEW current, mode = { - .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE, + .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, + .dmDisplayFrequency = 60, }; struct gdi_monitor monitor = { @@ -1988,46 +1990,16 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct {1920, 1200}, {2560, 1600} }; - - struct device_manager_ctx desktop_ctx = {0}; - struct host_adapter *primary; - UINT screen_width, screen_height, max_width, max_height; - unsigned int depths[] = {8, 16, 0}; - DEVMODEW current, mode = - { - .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, - .dmDisplayFrequency = 60, - }; + unsigned int depths[] = {8, 16, host_adapter->bpp}; + UINT max_width = host_adapter->width, max_height = host_adapter->height; UINT i, j;
- if (!force) return TRUE; - /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */ - if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE; - - if ((primary = get_primary_host_adapter( &desktop_ctx ))) - { - max_width = primary->width; - max_height = primary->height; - depths[ARRAY_SIZE(depths) - 1] = primary->bpp; - } - else - { - max_width = max_height = 0; - } - - if (!get_default_desktop_size( &screen_width, &screen_height )) - { - screen_width = max_width; - screen_height = max_height; - } - - add_gpu( &gpu, ctx ); - add_adapter( &adapter, ctx ); + add_adapter( &host_adapter->gdi, ctx ); if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, ¤t )) { current = mode; current.dmFields |= DM_POSITION; - current.dmBitsPerPel = primary ? primary->bpp : 0; + current.dmBitsPerPel = host_adapter->bpp; current.dmPelsWidth = screen_width; current.dmPelsHeight = screen_height; } @@ -2068,6 +2040,51 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct else add_mode( &mode, FALSE, ctx ); } } +} + +static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx ) +{ + static const struct gdi_gpu gpu; + + struct device_manager_ctx desktop_ctx = {0}; + UINT i; + + if (!force) return TRUE; + /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */ + if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE; + + add_gpu( &gpu, ctx ); + + for (i = 0; i < desktop_ctx.adapter_count; ++i) + { + struct host_adapter *host_adapter = &desktop_ctx.host_adapters[i]; + if (!host_adapter->gdi.virtual_id[0]) continue; + desktop_add_host_adapter( host_adapter, host_adapter->width, host_adapter->height, ctx ); + } + + /* If the driver has not specified any adapters to pass through to the visual + * configuration, use a single virtual desktop adapter. */ + if (ctx->adapter_count == 0) + { + struct host_adapter *primary = get_primary_host_adapter( &desktop_ctx ); + struct host_adapter default_primary = + { + .gdi.state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | + DISPLAY_DEVICE_PRIMARY_DEVICE | + DISPLAY_DEVICE_VGA_COMPATIBLE, + }; + UINT screen_width, screen_height; + + if (!primary) primary = &default_primary; + + if (!get_default_desktop_size( &screen_width, &screen_height )) + { + screen_width = primary->width; + screen_height = primary->height; + } + + desktop_add_host_adapter( primary, screen_width, screen_height, ctx ); + }
return TRUE; } diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index bd827c31cb1..bd6c1699f13 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -258,6 +258,7 @@ struct gdi_adapter { ULONG_PTR id; DWORD state_flags; + WCHAR virtual_id[128]; };
struct gdi_monitor
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/win32u/sysparams.c | 60 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index f9207465288..c66371f1b23 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -183,6 +183,7 @@ static const WCHAR linkedW[] = {'L','i','n','k','e','d',0}; static const WCHAR symbolic_link_valueW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0}; static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0}; +static const WCHAR virtual_idW[] = {'V','i','r','t','u','a','l','I','D',0}; static const WCHAR gpu_idW[] = {'G','P','U','I','D',0}; static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0}; static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0}; @@ -243,6 +244,7 @@ struct adapter const WCHAR *config_key; unsigned int mode_count; DEVMODEW *modes; + WCHAR virtual_id[128]; };
#define MONITOR_INFO_HAS_MONITOR_ID 0x00000001 @@ -760,6 +762,10 @@ static BOOL read_display_adapter_settings( unsigned int index, struct adapter *i if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD) info->dev.state_flags = *(const DWORD *)value->Data;
+ /* VirtualID */ + if (query_reg_value( hkey, virtual_idW, value, sizeof(buffer) ) && value->Type == REG_SZ) + memcpy( info->virtual_id, value_str, value->DataLength ); + /* Interface name */ info->dev.interface_name[0] = 0;
@@ -1484,6 +1490,8 @@ static void add_adapter( const struct gdi_adapter *adapter, void *param ) (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) ); set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags, sizeof(adapter->state_flags) ); + set_reg_value( ctx->adapter_key, virtual_idW, REG_SZ , adapter->virtual_id, + (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) ); }
static void add_monitor( const struct gdi_monitor *monitor, void *param ) @@ -1940,6 +1948,48 @@ static struct host_adapter *get_primary_host_adapter( struct device_manager_ctx return NULL; }
+static HKEY get_adapter_key_for_virtual_id( WCHAR *virtual_id ) +{ + char buffer[4096]; + KEY_NAME_INFORMATION *key = (KEY_NAME_INFORMATION *)buffer; + KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + WCHAR bufferW[MAX_PATH]; + DWORD len, i, j; + HKEY hkey, gpu_key = NULL, adapter_key = NULL; + + len = asciiz_to_unicode( bufferW, "System\CurrentControlSet\Control\Video" ) / sizeof(WCHAR) - 1; + hkey = reg_open_key( config_key, bufferW, len * sizeof(WCHAR) ); + i = 0; + + while (!NtEnumerateKey( hkey, i++, KeyNameInformation, key, sizeof(buffer), &len )) + { + gpu_key = reg_open_key( NULL, key->Name, key->NameLength ); + j = 0; + + while (!NtEnumerateKey( gpu_key, j++, KeyNameInformation, key, sizeof(buffer), &len )) + { + adapter_key = reg_open_key( NULL, key->Name, key->NameLength ); + + if (query_reg_value( adapter_key, virtual_idW, value, sizeof(buffer) ) && + value->Type == REG_SZ && !wcscmp( (WCHAR *)value->Data, virtual_id )) + { + goto done; + } + + NtClose( adapter_key ); + adapter_key = NULL; + } + + NtClose( gpu_key ); + gpu_key = NULL; + } + +done: + NtClose( gpu_key ); + NtClose( hkey ); + return adapter_key; +} + static void desktop_add_host_adapter( struct host_adapter *host_adapter, UINT screen_width, UINT screen_height, struct device_manager_ctx *ctx ) @@ -1993,9 +2043,16 @@ static void desktop_add_host_adapter( struct host_adapter *host_adapter, unsigned int depths[] = {8, 16, host_adapter->bpp}; UINT max_width = host_adapter->width, max_height = host_adapter->height; UINT i, j; + HKEY adapter_key = NULL; + + if (host_adapter->gdi.virtual_id[0]) + adapter_key = get_adapter_key_for_virtual_id( host_adapter->gdi.virtual_id );
add_adapter( &host_adapter->gdi, ctx ); - if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, ¤t )) + + if (!adapter_key) adapter_key = ctx->adapter_key; + + if (!read_adapter_mode( adapter_key, ENUM_CURRENT_SETTINGS, ¤t )) { current = mode; current.dmFields |= DM_POSITION; @@ -2003,6 +2060,7 @@ static void desktop_add_host_adapter( struct host_adapter *host_adapter, current.dmPelsWidth = screen_width; current.dmPelsHeight = screen_height; } + if (adapter_key != ctx->adapter_key) NtClose( adapter_key );
monitor.rc_monitor.right = current.dmPelsWidth; monitor.rc_monitor.bottom = current.dmPelsHeight;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/display.c | 10 ++++++++-- dlls/winewayland.drv/waylanddrv.h | 11 +++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 99dc92e0e5a..f94ad73f1dc 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -211,13 +211,19 @@ static void wayland_add_device_gpu(const struct gdi_device_manager *device_manag }
static void wayland_add_device_adapter(const struct gdi_device_manager *device_manager, - void *param, INT output_id) + void *param, INT output_id, + struct output_info *output_info) { struct gdi_adapter adapter; adapter.id = output_id; adapter.state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP; if (output_id == 0) adapter.state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE; + if (!asciiz_to_unicodez(adapter.virtual_id, output_info->output->name, + sizeof(adapter.virtual_id) / sizeof(WCHAR))) + { + WARN("output name too long, truncating\n"); + }
TRACE("id=0x%s state_flags=0x%x\n", wine_dbgstr_longlong(adapter.id), (UINT)adapter.state_flags); @@ -307,7 +313,7 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_array_for_each(output_info, &output_info_array) { - wayland_add_device_adapter(device_manager, param, output_id); + wayland_add_device_adapter(device_manager, param, output_id, output_info); wayland_add_device_monitor(device_manager, param, output_info); wayland_add_device_modes(device_manager, param, output_info); output_id++; diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 995275768a5..3dfe341b623 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -310,6 +310,17 @@ static inline LRESULT send_message(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp
RGNDATA *get_region_data(HRGN region);
+/* Returns TRUE if dst contains a complete unicode copy of src, FALSE if truncation + * occurred. If n > 0 dst is always NULL terminated. */ +static inline BOOL asciiz_to_unicodez(WCHAR *dst, const char *src, size_t n) +{ + WCHAR *p = dst; + if (!n) return FALSE; + while (n && *src) { *p++ = *src++; n--; } + if (!n) p[-1] = 0; else *p = 0; + return !!n; +} + /********************************************************************** * USER driver functions */
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Add a user driver function to provide information about the virtual display devices and their mappings to host display devices. --- dlls/win32u/driver.c | 11 +++++++++++ dlls/win32u/sysparams.c | 38 ++++++++++++++++++++++++++++++++++++++ include/wine/gdi_driver.h | 7 +++++++ 3 files changed, 56 insertions(+)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 7fcdbc4c383..33e82b7bf5e 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -813,6 +813,10 @@ static BOOL nulldrv_UpdateDisplayDevices( const struct gdi_device_manager *manag return FALSE; }
+static void nulldrv_NotifyVirtualDevices( const struct gdi_virtual *virtual ) +{ +} + static BOOL nulldrv_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { return TRUE; @@ -1218,6 +1222,11 @@ static BOOL loaderdrv_UpdateDisplayDevices( const struct gdi_device_manager *man return load_driver()->pUpdateDisplayDevices( manager, force, param ); }
+static void loaderdrv_NotifyVirtualDevices( const struct gdi_virtual *virtual ) +{ + load_driver()->pNotifyVirtualDevices( virtual ); +} + static BOOL loaderdrv_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { return load_driver()->pCreateDesktop( name, width, height ); @@ -1303,6 +1312,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_GetCurrentDisplaySettings, loaderdrv_GetDisplayDepth, loaderdrv_UpdateDisplayDevices, + loaderdrv_NotifyVirtualDevices, /* windowing functions */ loaderdrv_CreateDesktop, loaderdrv_CreateWindow, @@ -1390,6 +1400,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(GetCurrentDisplaySettings); SET_USER_FUNC(GetDisplayDepth); SET_USER_FUNC(UpdateDisplayDevices); + SET_USER_FUNC(NotifyVirtualDevices); SET_USER_FUNC(CreateDesktop); SET_USER_FUNC(CreateWindow); SET_USER_FUNC(DesktopWindowProc); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index c66371f1b23..ca3e4c941c6 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2147,6 +2147,32 @@ static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ct return TRUE; }
+static struct gdi_virtual *get_virtual_settings( void ) +{ + struct gdi_virtual *virtual, *settings; + struct adapter *adapter; + + /* allocate an extra entry for easier iteration */ + if (!(settings = calloc( list_count( &adapters ) + 1, sizeof(*settings) ))) return NULL; + virtual = settings; + + LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) + { + virtual->mode.dmSize = sizeof(DEVMODEW); + if (!adapter_get_current_settings( adapter, &virtual->mode )) + { + free( settings ); + return NULL; + } + + lstrcpyW( virtual->mode.dmDeviceName, adapter->dev.device_name ); + memcpy( virtual->virtual_id, adapter->virtual_id, sizeof(virtual->virtual_id) ); + ++virtual; + } + + return settings; +} + BOOL update_display_cache( BOOL force ) { static const WCHAR wine_service_station_name[] = @@ -2155,6 +2181,7 @@ BOOL update_display_cache( BOOL force ) struct device_manager_ctx ctx = {0}; BOOL was_virtual_desktop, ret; WCHAR name[MAX_PATH]; + BOOL force_desktop_update = FALSE;
/* services do not have any adapters, only a virtual monitor */ if (NtUserGetObjectInformation( winstation, UOI_NAME, name, sizeof(name), NULL ) @@ -2174,6 +2201,7 @@ BOOL update_display_cache( BOOL force ) if (ret && is_virtual_desktop()) { reset_display_manager_ctx( &ctx ); + force_desktop_update = force || !was_virtual_desktop; ret = desktop_update_display_devices( force || !was_virtual_desktop, &ctx ); }
@@ -2197,6 +2225,16 @@ BOOL update_display_cache( BOOL force ) return update_display_cache( TRUE ); }
+ if (force_desktop_update) + { + struct gdi_virtual *settings; + pthread_mutex_lock( &display_lock ); + settings = get_virtual_settings(); + if (settings) user_driver->pNotifyVirtualDevices( settings ); + pthread_mutex_unlock( &display_lock ); + free(settings); + } + return TRUE; }
diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index bd6c1699f13..8aee5c65643 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -278,6 +278,12 @@ struct gdi_device_manager void (*add_mode)( const DEVMODEW *mode, BOOL current, void *param ); };
+struct gdi_virtual +{ + DEVMODEW mode; + WCHAR virtual_id[128]; +}; + #define WINE_DM_UNSUPPORTED 0x80000000
struct tagUPDATELAYEREDWINDOWINFO; @@ -322,6 +328,7 @@ struct user_driver_funcs BOOL (*pGetCurrentDisplaySettings)(LPCWSTR,BOOL,LPDEVMODEW); INT (*pGetDisplayDepth)(LPCWSTR,BOOL); BOOL (*pUpdateDisplayDevices)(const struct gdi_device_manager *,BOOL,void*); + void (*pNotifyVirtualDevices)(const struct gdi_virtual *); /* windowing functions */ BOOL (*pCreateDesktop)(const WCHAR *,UINT,UINT); BOOL (*pCreateWindow)(HWND);
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Emulate the visual effect of a display mode change, by scaling the window according to the ratios of the native vs current mode.
We find the proper native and current mode by utilizing the virtual display device information. --- dlls/winewayland.drv/display.c | 20 ++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 2 ++ dlls/winewayland.drv/waylanddrv_main.c | 1 + dlls/winewayland.drv/window.c | 37 ++++++++++++++++++++++++++ 4 files changed, 60 insertions(+)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index f94ad73f1dc..1f4148ced57 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -325,3 +325,23 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
return TRUE; } + +/*********************************************************************** + * NotifyVirtualDevices (WAYLAND.@) + */ +void WAYLAND_NotifyVirtualDevices(const struct gdi_virtual *virtual) +{ + const struct gdi_virtual *v = virtual; + size_t virtual_size; + + for (v = virtual; v->mode.dmSize; ++v) + TRACE("%s => %s\n", wine_dbgstr_w(v->mode.dmDeviceName), wine_dbgstr_w(v->virtual_id)); + + virtual_size = (v - virtual + 1) * sizeof(*v); + + pthread_mutex_lock(&process_wayland.output_mutex); + free(process_wayland.virtual); + process_wayland.virtual = malloc(virtual_size); + if (process_wayland.virtual) memcpy(process_wayland.virtual, virtual, virtual_size); + pthread_mutex_unlock(&process_wayland.output_mutex); +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 3dfe341b623..32928575629 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -128,6 +128,7 @@ struct wayland struct wayland_keyboard keyboard; struct wayland_pointer pointer; struct wl_list output_list; + struct gdi_virtual *virtual; /* Protects the output_list and the wayland_output.current states. */ pthread_mutex_t output_mutex; }; @@ -328,6 +329,7 @@ static inline BOOL asciiz_to_unicodez(WCHAR *dst, const char *src, size_t n) BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset); LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); void WAYLAND_DestroyWindow(HWND hwnd); +void WAYLAND_NotifyVirtualDevices(const struct gdi_virtual *virtual); void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor); LRESULT WAYLAND_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam); BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index b60d282aacb..26279905dd6 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -35,6 +35,7 @@ static const struct user_driver_funcs waylanddrv_funcs = .pDesktopWindowProc = WAYLAND_DesktopWindowProc, .pDestroyWindow = WAYLAND_DestroyWindow, .pKbdLayerDescriptor = WAYLAND_KbdLayerDescriptor, + .pNotifyVirtualDevices = WAYLAND_NotifyVirtualDevices, .pReleaseKbdTables = WAYLAND_ReleaseKbdTables, .pSetCursor = WAYLAND_SetCursor, .pSysCommand = WAYLAND_SysCommand, diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index ac5da371e5c..2b044563a8d 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -158,6 +158,41 @@ static void wayland_win_data_release(struct wayland_win_data *data) pthread_mutex_unlock(&win_data_mutex); }
+static double get_window_mode_change_scale(HWND hwnd) +{ + MONITORINFOEXW mi = {.cbSize = sizeof(mi)}; + HMONITOR hmon; + struct gdi_virtual *virtual; + struct wayland_output *output; + double scale = 1.0; + WCHAR name[128]; + + if (!(hmon = NtUserMonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY)) || + !NtUserGetMonitorInfo(hmon, (MONITORINFO *)&mi)) + return 1.0; + + pthread_mutex_lock(&process_wayland.output_mutex); + + for (virtual = process_wayland.virtual; virtual && virtual->mode.dmSize; ++virtual) + { + if (wcscmp(virtual->mode.dmDeviceName, mi.szDevice)) continue; + wl_list_for_each(output, &process_wayland.output_list, link) + { + asciiz_to_unicodez(name, output->current.name, ARRAY_SIZE(name)); + if (wcscmp(name, virtual->virtual_id)) continue; + if (!output->current.mode.width || !output->current.mode.height) continue; + scale = min(output->current.mode.width / (double)virtual->mode.dmPelsWidth, + output->current.mode.height / (double)virtual->mode.dmPelsHeight); + break; + } + break; + } + + pthread_mutex_unlock(&process_wayland.output_mutex); + + return scale; +} + static void wayland_win_data_get_config(struct wayland_win_data *data, struct wayland_window_config *conf) { @@ -185,6 +220,8 @@ static void wayland_win_data_get_config(struct wayland_win_data *data,
conf->state = window_state; conf->scale = NtUserGetDpiForWindow(data->hwnd) / 96.0; + /* Adjust the window scale for the current display mode. */ + conf->scale /= get_window_mode_change_scale(data->hwnd); conf->visible = (style & WS_VISIBLE) == WS_VISIBLE; conf->managed = data->managed; }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Since a display reconfiguration may affect the compositor side scaling which we apply to a surface, instruct all surfaces to refresh themselves by committing an updated state based on the latest window state. --- dlls/winewayland.drv/display.c | 9 ++++++ dlls/winewayland.drv/waylanddrv.h | 3 ++ dlls/winewayland.drv/window.c | 52 +++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 1f4148ced57..d8168f06774 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -326,6 +326,11 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage return TRUE; }
+static void refresh_hwnd(HWND hwnd) +{ + NtUserPostMessage(hwnd, WM_WAYLAND_REFRESH, 0, 0); +} + /*********************************************************************** * NotifyVirtualDevices (WAYLAND.@) */ @@ -344,4 +349,8 @@ void WAYLAND_NotifyVirtualDevices(const struct gdi_virtual *virtual) process_wayland.virtual = malloc(virtual_size); if (process_wayland.virtual) memcpy(process_wayland.virtual, virtual, virtual_size); pthread_mutex_unlock(&process_wayland.output_mutex); + + /* Refresh all windows to ensure they have been committed with proper + * scaling applied. */ + enum_process_windows(refresh_hwnd); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 32928575629..bc845d7c230 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -62,6 +62,7 @@ enum wayland_window_message WM_WAYLAND_INIT_DISPLAY_DEVICES = WM_WINE_FIRST_DRIVER_MSG, WM_WAYLAND_CONFIGURE, WM_WAYLAND_SET_FOREGROUND, + WM_WAYLAND_REFRESH, };
enum wayland_surface_config_state @@ -322,6 +323,8 @@ static inline BOOL asciiz_to_unicodez(WCHAR *dst, const char *src, size_t n) return !!n; }
+void enum_process_windows(void (*cb)(HWND hwnd)); + /********************************************************************** * USER driver functions */ diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 2b044563a8d..f4443584ddb 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -651,6 +651,24 @@ static void wayland_configure_window(HWND hwnd) NtUserSetWindowPos(hwnd, 0, 0, 0, window_width, window_height, flags); }
+static void wayland_refresh_window(HWND hwnd) +{ + struct wayland_win_data *data; + + if (!(data = wayland_win_data_get(hwnd))) return; + + if (data->wayland_surface) + { + pthread_mutex_lock(&data->wayland_surface->mutex); + wayland_win_data_get_config(data, &data->wayland_surface->window); + if (wayland_surface_reconfigure(data->wayland_surface)) + wl_surface_commit(data->wayland_surface->wl_surface); + pthread_mutex_unlock(&data->wayland_surface->mutex); + } + + wayland_win_data_release(data); +} + /********************************************************************** * WAYLAND_WindowMessage */ @@ -668,6 +686,9 @@ LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) case WM_WAYLAND_SET_FOREGROUND: NtUserSetForegroundWindow(hwnd); return 0; + case WM_WAYLAND_REFRESH: + wayland_refresh_window(hwnd); + return 0; default: FIXME("got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, (long)wp, lp); return 0; @@ -787,3 +808,34 @@ struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd)
return surface; } + +/********************************************************************** + * enum_process_windows + */ +void enum_process_windows(void (*cb)(HWND hwnd)) +{ + struct wayland_win_data *data; + HWND *hwnds = NULL; + UINT num_hwnds = 0, i = 0; + + pthread_mutex_lock(&win_data_mutex); + + RB_FOR_EACH_ENTRY(data, &win_data_rb, struct wayland_win_data, entry) + ++num_hwnds; + + if (num_hwnds && (hwnds = malloc(num_hwnds * sizeof(*hwnds)))) + { + RB_FOR_EACH_ENTRY(data, &win_data_rb, struct wayland_win_data, entry) + hwnds[i++] = data->hwnd; + } + else + { + num_hwnds = 0; + } + + pthread_mutex_unlock(&win_data_mutex); + + for (i = 0; i < num_hwnds; i++) cb(hwnds[i]); + + free(hwnds); +}
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Depending on the display mode emulation and DPI settings, the window scale factor may be less than 0.5 which will be rounded to 0. Since 0 is not acceptable value for the buffer scale, use 1 instead. --- dlls/winewayland.drv/wayland_pointer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 54dd7931b6e..6f5ccfe14e3 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -630,7 +630,7 @@ static void wayland_pointer_update_cursor_surface(double scale) * support wp_viewport for cursor surfaces, so also set the buffer * scale. Note that setting the viewport destination overrides * the buffer scale, so it's fine to set both. */ - wl_surface_set_buffer_scale(cursor->wl_surface, round(scale)); + wl_surface_set_buffer_scale(cursor->wl_surface, max(1, round(scale))); if (cursor->wp_viewport) { wp_viewport_set_destination(cursor->wp_viewport,
What would you think of something like that instead: https://gitlab.winehq.org/rbernon/wine/-/commits/wip/virtual-desktop?
I'm not 100% sure about using the per-monitor DPI for that, but I also don't know much about DPI at all, and it seems to be almost a perfect fit and seems to be working alright for your needs. In any case, this is only a minor implementation detail and we could expose the virtual mode scaling a different way if we don't want to mess with DPI.
Hey, thanks for your beautiful work arround the wayland driver! I have been following the development since a while and patiently waiting, but one big issue i have still isn't fixed. When playing ManiaPlanet or TrackMania United Forever (i didn't test with others but these 2 have the problem for me which basically makes the wayland driver unusable for me), the screen resolution gets picked up from my second 1080p monitor, and not my 1440p main monitor. This way i can't play at the full resolution no matter what i do: ![image](/uploads/f78f4552c0c2d719bebfd20311f63add/image.png) ![image](/uploads/ee48bf84e29b295c842446280410b1b3/image.png) is there any way to fix this or is that a shortcoming of the wayland driver? It works fine on winex11. I didn't try to compile this MR yet, but running the game in a virtual desktop isn't a real solution for me. Thanks for your work Alexandros!
On Mon Feb 12 21:59:02 2024 +0000, AroPix wrote:
Hey, thanks for your beautiful work arround the wayland driver! I have been following the development since a while and patiently waiting, but one big issue i have still isn't fixed. When playing ManiaPlanet or TrackMania United Forever (i didn't test with others but these 2 have the problem for me which basically makes the wayland driver unusable for me), the screen resolution gets picked up from my second 1080p monitor, and not my 1440p main monitor. This way i can't play at the full resolution no matter what i do: ![image](/uploads/f78f4552c0c2d719bebfd20311f63add/image.png) ![image](/uploads/ee48bf84e29b295c842446280410b1b3/image.png) is there any way to fix this or is that a shortcoming of the wayland driver? It works fine on winex11. I didn't try to compile this MR yet, but running the game in a virtual desktop isn't a real solution for me. Thanks for your work Alexandros!
Please, do not use merge requests for unrelated discussions, this only creates noise -especially with large screenshots- and is *not* helpful. If you encounter a bug file a report on https://bugs.winehq.org and discuss it there.
On Tue Feb 13 10:57:43 2024 +0000, Rémi Bernon wrote:
What would you think of something like that instead: https://gitlab.winehq.org/rbernon/wine/-/commits/wip/virtual-desktop? I'm not 100% sure about using the per-monitor DPI for that, but I also don't know much about DPI at all, and it seems to be almost a perfect fit and seems to be working alright for your needs. In any case, this is only a minor implementation detail and we could expose the virtual mode scaling a different way if we don't want to mess with DPI.
@rbernon Thanks for the virtual-desktop branch! I have pushed my own proposed changes/fixes on top of your branch at https://gitlab.winehq.org/afrantzis/wine/-/commits/virtual-desktop. With those changes the branch works well for me.
Here are some notes:
1. The virtual-desktop branch successfully virtualizes the adapter modes, but leaves the monitor rectangles as they are, which causes issues with a lot of calculation and checks (e.g., a fullscreen window will not be detected by `NtUserIsWindowRectFullScreen`). The part-12 MR was able to provide consistent monitor information/placement since the virtualization was done by the driver itself. The current WIP MR works around the issue by ignoring the host monitors and arbitrarily creates a virtual monitor with the proper rectangles for each virtual adapter. In my proposed virtual-desktop changes I have tried to solve this by automatically scaling the monitor rectangles. 2. Concerning integrating the scale factor with the DPI, the proposed approach does make sense to me, insofar as it reflects the actual (relative) DPI. However, I am skeptical because we will end up returning DPI values less than 96, and I am concerned that this may cause issues in applications in which 96 is assumed to be a minimum. As you note, the alternative would be to allow each driver to somehow get or calculate the mode scale factor and apply it internally as it sees fit (so I guess something closer to part-12 MR in this respect, at least in how the driver handles the scaling, if not in the actual mechanism to communicate the info). 3. The virtual-desktop branch uses the adapter_key to get the current settings from the registry during updates, which depends on the driver advertising adapters with the same order across processes. The Wayland protocol doesn't guarantee such ordering, but the Wayland driver mostly alleviates this since it sorts the adapters internally before sending them to win32u. 4. In the future we will need a way to associate a wl_output (by name, which is guaranteed to be cross-process consistent) to each adapter (e.g., so that if an app requests to become fullscreen on a particula monitor/adapter, we know that this corresponds to a particular wl_output and request it from the compositor). My proposed MRs implement such an association (each in a different way), but the virtual-desktop branch doesn't at the moment. We don't need to resolve this now if it's not directly needed by the approach we choose for the display mode changes, but I am mentioning it so that it's taken into account for any design evolution.
---
One concern (related to (3) and (4), although I think non-critical, so we can leave it for later), that hasn't been resolved in any of the MRs so far, is what happens when processes transiently disagree about the adapters when a user adds/removes an monitor (and the corresponding wl_output addition/removal has been handled in one process but not another). For the Wayland driver the desktop process is the authoritative source of display info (i.e., the one handles UpdateDisplayDevices), but ChangeDisplaySettings does force an UpdateDisplayDevices invocation in other processes. In a worst case scenario:
1. Desktop process handles wl_output event and updates display devices. 2. Another process, which hasn't handled the wl_output event yet, calls ChangeDisplaySettings which may update the devices with stale information.
Ideally, to avoid problems, I wouldn't want to update the display devices during a change display settings sequence, just let the process act on the existing registry information. Perhaps we can have an UpdateDisplayDevices parameter to signal when the update is part of a settings change, or I can track that manually in the driver (record it with trivial ChangeDisplaySettings handler). Another thought would be for the driver to only act on UpdateDisplayDevices in the desktop process (but I remember we tried something similar in the early Wayland driver MRs and that was causing issues).
On Tue Feb 13 10:57:43 2024 +0000, Alexandros Frantzis wrote:
@rbernon Thanks for the virtual-desktop branch! I have pushed my own proposed changes/fixes on top of your branch at https://gitlab.winehq.org/afrantzis/wine/-/commits/virtual-desktop. With those changes the branch works well for me. Here are some notes:
- The virtual-desktop branch successfully virtualizes the adapter
modes, but leaves the monitor rectangles as they are, which causes issues with a lot of calculation and checks (e.g., a fullscreen window will not be detected by `NtUserIsWindowRectFullScreen`). The part-12 MR was able to provide consistent monitor information/placement since the virtualization was done by the driver itself. The current WIP MR works around the issue by ignoring the host monitors and arbitrarily creates a virtual monitor with the proper rectangles for each virtual adapter. In my proposed virtual-desktop changes I have tried to solve this by automatically scaling the monitor rectangles. 2. Concerning integrating the scale factor with the DPI, the proposed approach does make sense to me, insofar as it reflects the actual (relative) DPI. However, I am skeptical because we will end up returning DPI values less than 96, and I am concerned that this may cause issues in applications in which 96 is assumed to be a minimum. As you note, the alternative would be to allow each driver to somehow get or calculate the mode scale factor and apply it internally as it sees fit (so I guess something closer to part-12 MR in this respect, at least in how the driver handles the scaling, if not in the actual mechanism to communicate the info). 3. The virtual-desktop branch uses the adapter_key to get the current settings from the registry during updates, which depends on the driver advertising adapters with the same order across processes. The Wayland protocol doesn't guarantee such ordering, but the Wayland driver mostly alleviates this since it sorts the adapters internally before sending them to win32u. 4. In the future we will need a way to associate a wl_output (by name, which is guaranteed to be cross-process consistent) to each adapter (e.g., so that if an app requests to become fullscreen on a particula monitor/adapter, we know that this corresponds to a particular wl_output and request it from the compositor). My proposed MRs implement such an association (each in a different way), but the virtual-desktop branch doesn't at the moment. We don't need to resolve this now if it's not directly needed by the approach we choose for the display mode changes, but I am mentioning it so that it's taken into account for any design evolution.
One concern (related to (3) and (4), although I think non-critical, so we can leave it for later), that hasn't been resolved in any of the MRs so far, is what happens when processes transiently disagree about the adapters when a user adds/removes an monitor (and the corresponding wl_output addition/removal has been handled in one process but not another). For the Wayland driver the desktop process is the authoritative source of display info (i.e., the one handles UpdateDisplayDevices), but ChangeDisplaySettings does force an UpdateDisplayDevices invocation in other processes. In a worst case scenario:
- Desktop process handles wl_output event and updates display devices.
- Another process, which hasn't handled the wl_output event yet, calls
ChangeDisplaySettings which may update the devices with stale information. Ideally, to avoid problems, I wouldn't want to update the display devices during a change display settings sequence, just let the process act on the existing registry information. Perhaps we can have an UpdateDisplayDevices parameter to signal when the update is part of a settings change, or I can track that manually in the driver (record it with trivial ChangeDisplaySettings handler). Another thought would be for the driver to only act on UpdateDisplayDevices in the desktop process (but I remember we tried something similar in the early Wayland driver MRs and that was causing issues).
Thanks for the feedback, I'll have a look at your fixes and think a bit about 3)/4) and the last parts of your comment.
Regarding 1), I was expecting monitor rects to be correct (as in matching the virtual mode), but maybe I missed something. There's some DPI conversion in the NtUserGetMonitorInfo which I'm not completely sure it is doing. There's also some issues with `get_win_monitor_dpi` / `get_dpi_for_window` which will need to be sorted out. I tried to break up some recursive dependency in one of the commit but I later saw that it's was not enough and triggered some user lock asserts.
Regarding 2), what about increasing the system_dpi when virtual modes are used so that the smallest mode ends up with 96dpi? Not entirely sure how to do that, and it will probably also require changing the wayland driver scaling computation.
Regarding 2), what about increasing the system_dpi when virtual modes are used so that the smallest mode ends up with 96dpi? Not entirely sure how to do that, and it will probably also require changing the wayland driver scaling computation.
Or maybe instead of changing system_dpi which *will* be seen by every application, decide that Wine virtualized monitor dpi will always be relative to 96dpi at 640x480, the minimum supported resolution, and let driver adjust their scaling. Only per-monitor DPI aware applications should be able to read that, and we can only hope that they'll be happy about it.
Still remains the question to whether changing DPI dynamically is a good idea, I can very well imagine per-monitor DPI aware app deciding to adjust things based on that after they changed display modes.
I was expecting monitor rects to be correct (as in matching the virtual mode), but maybe I missed something. There's some DPI conversion in the NtUserGetMonitorInfo which I'm not completely sure it is doing.
Here is an attempt to summarize my current understanding:
* physical dpi: The "real" dpi of the adapter, currently assumed to match `system_dpi` since we don't have per-adapter dpi reporting. * virtual dpi: The dpi of the adapter after a virtual mode change (matches physical dpi if there is no virtual mode change) * adapter dpi: Previously reported physical dpi (= `system_dpi` ), now virtual dpi. * monitor dpi: At the moment returns the respective adapter dpi (so now also returning virtual dpi)
The drivers (through `UpdateDisplayDevices`) report monitor rect information in physical dpi.
Your proposal has uncovered a few pre-existing inconsistencies in how monitor rects are treated. Some functions assume that the monitor rects are expressed in `system_dpi` (the get_*_rect helper functions in sysparams.c) whereas others assume they are expressed in their respective adapter dpi. That was OK before because all those dpi values were the same.
The change proposed in my virtual-desktop branch transform the monitor rects to account for the virtual mode change. This involves mapping from physical dpi to virtual dpi, but the transformation is done relative to the adapter origin coordinates. This means that the functions that assumed the rects are in adapter dpi now work, but the functions that make different assumptions now need fixing.
Regarding (2)... Or maybe instead of changing system_dpi which _will_ be seen by every application, decide that Wine virtualized monitor dpi will always be relative to 96dpi at 640x480, the minimum supported resolution, and let driver adjust their scaling
Right, so we can play with the monitor dpi value reported to applications (i.e., dissociate from the real "virtual dpi") in various ways. I am not sure I understand how the 96dpi at 640x480 approach would work, though, can you explain that a bit more?
I was thinking that one way would be for the `get_monitor_dpi` sysparam.c function to return `max(96, real_monitor_dpi)` (so that's the value all public APIs will get), but then allow the driver to somehow know (or figure out) the `real_monitor_dpi` value and apply the residual scaling as required.
I am not sure I understand how the 96dpi at 640x480 approach would work, though, can you explain that a bit more?
I meant that every monitor would always report 96dpi when their virtual mode is 640x480, and then adjusted virtual dpi for higher virtual modes. This way we would never report a monitor dpi < 96, and break applications that don't expect it to be possible.
Still remains the question to whether changing DPI dynamically is a good idea, I can very well imagine per-monitor DPI aware app deciding to adjust things based on that after they changed display modes.
This is a pertinent question, since, from what I understand, display mode changes under Windows don't lead to any changes in the reported DPI. I don't know if/how applications may be confused by that. In the model from my previous post, we just return the physical dpi (currently =`system_dpi`) as the monitor dpi and the driver will then apply all the needed scaling (so effectively what part-12 and this draft are doing).