From: Alexandros Frantzis alexandros.frantzis@collabora.com
Implement the GetCurrentDisplaySettings driver callback by reading display device information from a memory region shared between all processes.
The desktop window process is responsible for maintaining the information in this region, thus providing an authoritative version of display device information, which is also in sync with the Windows configuration, for all other processes to use.
Wayland doesn't provide a mechanism to get the latest output information in a cross-process consistent form from some central point, instead relying on events from which each client builds its own view of the current configuration. Without some single source of truth like the one provided by this shared memory region, it's challenging to maintain consistency between processes in terms of current configuration and mappings from wl_outputs to to Windows monitors.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/Makefile.in | 2 +- dlls/winewayland.drv/display.c | 198 +++++++++++++++++++++++-- dlls/winewayland.drv/waylanddrv.h | 3 + dlls/winewayland.drv/waylanddrv_main.c | 2 + 4 files changed, 188 insertions(+), 17 deletions(-)
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 326a9d765c8..faa6186db21 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS)
C_SRCS = \ display.c \ diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index e72b26a9da0..640829142c1 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -30,10 +30,77 @@
#include "ntuser.h"
+#include <pthread.h> + WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
static BOOL force_display_devices_refresh;
+struct output_info +{ + char name[128]; + struct wayland_output_mode modes[128]; + int num_modes; + int current_mode; +}; + +struct display_devices +{ + struct output_info outputs[16]; + int num_outputs; + pthread_mutex_t mutex; +}; + +static HANDLE display_devices_handle; +static struct display_devices *display_devices; + +BOOL wayland_display_devices_process_init(void) +{ + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER size; + SIZE_T count = 0; + WCHAR name[] = {'\','?','?','\','W','a','y','l','a','n','d', + 'D','i','s','p','l','a','y','D','e','v','i','c','e','s'}; + UNICODE_STRING nameW = {sizeof(name), sizeof(name), name}; + NTSTATUS status; + + size.QuadPart = sizeof(*display_devices); + InitializeObjectAttributes(&attr, &nameW, OBJ_OPENIF, NULL, NULL); + status = NtCreateSection(&display_devices_handle, + SECTION_MAP_READ | SECTION_MAP_WRITE, + &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0); + if (status != 0 && !(status & ERROR_SEVERITY_INFORMATIONAL)) + { + ERR("Failed to create or open WaylandDisplayDevices shared memory\n"); + return FALSE; + } + + if (NtMapViewOfSection(display_devices_handle, GetCurrentProcess(), + (PVOID)&display_devices, 0, 0, + NULL, &count, ViewShare, 0, PAGE_READWRITE)) + { + ERR("Failed to map WaylandDisplayDevices shared memory\n"); + return FALSE; + } + + /* Initialize the shared memory contents if we just created it. */ + if (status == 0) + { + pthread_mutexattr_t mutexattr; + + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_DEFAULT); + pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&display_devices->mutex, &mutexattr); + pthread_mutexattr_destroy(&mutexattr); + } + + TRACE("status=0x%lx handle=%p map=%p\n", + (long)status, display_devices_handle, display_devices); + + return TRUE; +} + static void wayland_refresh_display_devices(void) { UINT32 num_path, num_mode; @@ -85,13 +152,12 @@ static void wayland_add_device_adapter(const struct gdi_device_manager *device_m }
static void wayland_add_device_monitor(const struct gdi_device_manager *device_manager, - void *param, struct wayland_output *output) + void *param, struct output_info *output_info) { struct gdi_monitor monitor = {0}; + struct wayland_output_mode *mode = &output_info->modes[output_info->current_mode];
- SetRect(&monitor.rc_monitor, 0, 0, - output->current_mode->width, - output->current_mode->height); + SetRect(&monitor.rc_monitor, 0, 0, mode->width, mode->height);
/* We don't have a direct way to get the work area in Wayland. */ monitor.rc_work = monitor.rc_monitor; @@ -99,7 +165,7 @@ static void wayland_add_device_monitor(const struct gdi_device_manager *device_m monitor.state_flags = DISPLAY_DEVICE_ATTACHED | DISPLAY_DEVICE_ACTIVE;
TRACE("name=%s rc_monitor=rc_work=%s state_flags=0x%x\n", - output->name, wine_dbgstr_rect(&monitor.rc_monitor), + output_info->name, wine_dbgstr_rect(&monitor.rc_monitor), (UINT)monitor.state_flags);
device_manager->add_monitor(&monitor, param); @@ -118,26 +184,52 @@ 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 wayland_output *output) + void *param, struct output_info *output_info) { - struct wayland_output_mode *output_mode; + int i;
- RB_FOR_EACH_ENTRY(output_mode, &output->modes, struct wayland_output_mode, entry) + for (i = 0; i < output_info->num_modes; i++) { DEVMODEW mode; - populate_devmode(output_mode, &mode); + populate_devmode(&output_info->modes[i], &mode); device_manager->add_mode(&mode, param); } }
+static void display_devices_update_output(struct output_info *output_info, + struct wayland_output *output) +{ + struct wayland_output_mode *mode; + int mode_i = 0; + + lstrcpynA(output_info->name, output->name, sizeof(output_info->name)); + if (strlen(output_info->name) != strlen(output->name)) + WARN("wayland output name %s is too long, truncating\n", output->name); + + RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) + { + if (mode_i >= ARRAYSIZE(output_info->modes)) + { + WARN("wayland reported too many modes for %s, ignoring\n", + output->name); + continue; + } + output_info->modes[mode_i] = *mode; + if (mode == output->current_mode) output_info->current_mode = mode_i; + mode_i++; + } + + output_info->num_modes = mode_i; +} + /*********************************************************************** * UpdateDisplayDevices (WAYLAND.@) */ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) { - struct wayland_output *output; - INT output_id = 0; + INT output_id; + BOOL update_from_wayland = force_display_devices_refresh;
if (!force && !force_display_devices_refresh) return TRUE;
@@ -147,14 +239,88 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wayland_add_device_gpu(device_manager, param);
- wl_list_for_each(output, &process_wayland->output_list, link) + pthread_mutex_lock(&display_devices->mutex); + + /* Update shared display device information from Wayland information. + * This should only happen from the desktop window process. */ + if (update_from_wayland) { - if (!output->current_mode) continue; + struct wayland_output *output; + + output_id = 0; + + wl_list_for_each(output, &process_wayland->output_list, link) + { + if (!output->current_mode) continue; + if (output_id >= ARRAYSIZE(display_devices->outputs)) + { + WARN("wayland reported too many outputs, ignoring %s\n", + output->name); + continue; + } + display_devices_update_output(&display_devices->outputs[output_id], + output); + output_id++; + } + + display_devices->num_outputs = output_id; + } + + /* Update gdi devices from shared display device information. */ + for (output_id = 0; output_id < display_devices->num_outputs; output_id++) + { + struct output_info *output_info = &display_devices->outputs[output_id]; wayland_add_device_adapter(device_manager, param, output_id); - wayland_add_device_monitor(device_manager, param, output); - wayland_add_device_modes(device_manager, param, output); - output_id++; + wayland_add_device_monitor(device_manager, param, output_info); + wayland_add_device_modes(device_manager, param, output_info); }
+ pthread_mutex_unlock(&display_devices->mutex); + + return TRUE; +} + +/*********************************************************************** + * GetCurrentDisplaySettings (WAYLAND.@) + * + */ +BOOL WAYLAND_GetCurrentDisplaySettings(LPCWSTR name, BOOL is_primary, LPDEVMODEW devmode) +{ + int display_index; + WCHAR *end; + struct output_info *output_info; + + TRACE("(%s,%p)\n", debugstr_w(name), devmode); + + display_index = wcstol(name + 11, &end, 10) - 1; + if (*end) + { + ERR("Failed to parse display device name %s\n", wine_dbgstr_w(name)); + return FALSE; + } + + pthread_mutex_lock(&display_devices->mutex); + + if (display_index >= display_devices->num_outputs) + { + pthread_mutex_unlock(&display_devices->mutex); + ERR("Failed to get information for %s\n", wine_dbgstr_w(name)); + return FALSE; + } + + output_info = &display_devices->outputs[display_index]; + populate_devmode(&output_info->modes[output_info->current_mode], devmode); + + pthread_mutex_unlock(&display_devices->mutex); + + devmode->dmFields |= DM_POSITION; + devmode->dmPosition.x = 0; + devmode->dmPosition.y = 0; + + TRACE("=> %d,%d+%ux%u@%u %ubpp\n", + (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, + (UINT)devmode->dmPelsWidth, (UINT)devmode->dmPelsHeight, + (UINT)devmode->dmDisplayFrequency, (UINT)devmode->dmBitsPerPel); + return TRUE; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index da0fccecda9..794cff2b0dd 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -78,6 +78,7 @@ struct wayland_output */
BOOL wayland_process_init(void) DECLSPEC_HIDDEN; +BOOL wayland_display_devices_process_init(void) DECLSPEC_HIDDEN; void wayland_init_display_devices(void) DECLSPEC_HIDDEN;
/********************************************************************** @@ -92,6 +93,8 @@ void wayland_output_use_xdg_extension(struct wayland_output *output) DECLSPEC_HI * USER driver functions */
+BOOL WAYLAND_GetCurrentDisplaySettings(LPCWSTR name, BOOL is_primary, + LPDEVMODEW devmode) DECLSPEC_HIDDEN; BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) DECLSPEC_HIDDEN;
diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index a9297edc500..d83314cd74f 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -31,6 +31,7 @@
static const struct user_driver_funcs waylanddrv_funcs = { + .pGetCurrentDisplaySettings = WAYLAND_GetCurrentDisplaySettings, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices, };
@@ -40,6 +41,7 @@ static NTSTATUS waylanddrv_unix_init(void *arg) * our initialization. We clear them on error. */ __wine_set_user_driver(&waylanddrv_funcs, WINE_GDI_DRIVER_VERSION);
+ if (!wayland_display_devices_process_init()) goto err; if (!wayland_process_init()) goto err;
return 0;