From: Alexandros Frantzis alexandros.frantzis@collabora.com
Whenever the Wayland output display state changes (including during initialization), update the monitor information on the Wine side to reflect these changes.
For now all monitors are placed at 0,0 in the monitor space, and only the current mode is reported. These deficiencies will be addressed in upcoming commits.
We currently support a single GPU, and a single monitor for each adapter.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/Makefile.in | 4 +- dlls/winewayland.drv/display.c | 156 +++++++++++++++++++++++++ dlls/winewayland.drv/wayland.c | 94 ++++++++++++++- dlls/winewayland.drv/wayland_output.c | 134 +++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 47 ++++++++ dlls/winewayland.drv/waylanddrv_main.c | 15 ++- 6 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 dlls/winewayland.drv/display.c create mode 100644 dlls/winewayland.drv/wayland_output.c
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index fc1fe8119ec..90124e66163 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,11 +1,13 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -UNIX_LIBS = $(WAYLAND_CLIENT_LIBS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS)
C_SRCS = \ + display.c \ dllmain.c \ wayland.c \ + wayland_output.c \ waylanddrv_main.c \
RC_SRCS = version.rc diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c new file mode 100644 index 00000000000..74e6450a42b --- /dev/null +++ b/dlls/winewayland.drv/display.c @@ -0,0 +1,156 @@ +/* + * WAYLAND display device functions + * + * Copyright 2020 Alexandros Frantzis for Collabora Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include "waylanddrv.h" + +#include "wine/debug.h" + +#include "ntuser.h" + +WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); + +static BOOL force_display_devices_refresh; + +static void wayland_refresh_display_devices(void) +{ + UINT32 num_path, num_mode; + force_display_devices_refresh = TRUE; + /* Trigger refresh in win32u */ + NtUserGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode); +} + +void wayland_init_display_devices(void) +{ + struct ntuser_thread_info *thread_info = NtUserGetThreadInfo(); + DWORD current_pid = GetCurrentProcessId(); + HWND desktop_hwnd = (HWND)thread_info->top_window; + DWORD desktop_pid = 0; + + if (desktop_hwnd) NtUserGetWindowThread(desktop_hwnd, &desktop_pid); + + /* Refresh devices only from the desktop window process. */ + if (!desktop_pid || current_pid == desktop_pid) + wayland_refresh_display_devices(); +} + +static void wayland_add_device_gpu(const struct gdi_device_manager *device_manager, + void *param) +{ + static const WCHAR wayland_gpuW[] = {'W','a','y','l','a','n','d','G','P','U',0}; + struct gdi_gpu gpu = {0}; + lstrcpyW(gpu.name, wayland_gpuW); + + TRACE("id=0x%s name=%s\n", + wine_dbgstr_longlong(gpu.id), wine_dbgstr_w(gpu.name)); + + device_manager->add_gpu(&gpu, param); +} + +static void wayland_add_device_adapter(const struct gdi_device_manager *device_manager, + void *param, INT output_id) +{ + 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; + + TRACE("id=0x%s state_flags=0x%x\n", + wine_dbgstr_longlong(adapter.id), (UINT)adapter.state_flags); + + device_manager->add_adapter(&adapter, param); +} + +static void wayland_add_device_monitor(const struct gdi_device_manager *device_manager, + void *param, struct wayland_output *output) +{ + struct gdi_monitor monitor = {0}; + + SetRect(&monitor.rc_monitor, 0, 0, + output->current_mode.width, + output->current_mode.height); + + /* We don't have a direct way to get the work area in Wayland. */ + monitor.rc_work = monitor.rc_monitor; + + 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), + (UINT)monitor.state_flags); + + device_manager->add_monitor(&monitor, param); +} + +static void populate_devmode(struct wayland_output_mode *output_mode, DEVMODEW *mode) +{ + mode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | + DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; + mode->dmDisplayOrientation = DMDO_DEFAULT; + mode->dmDisplayFlags = 0; + mode->dmBitsPerPel = 32; + mode->dmPelsWidth = output_mode->width; + mode->dmPelsHeight = output_mode->height; + mode->dmDisplayFrequency = output_mode->refresh / 1000; +} + +static void wayland_add_device_mode(const struct gdi_device_manager *device_manager, + void *param, struct wayland_output *output) +{ + DEVMODEW mode; + populate_devmode(&output->current_mode, &mode); + device_manager->add_mode(&mode, param); +} + +/*********************************************************************** + * UpdateDisplayDevices (WAYLAND.@) + */ +BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, + BOOL force, void *param) +{ + struct wayland *wayland = process_wayland; + struct wayland_output *output; + INT output_id = 0; + + if (!force && !force_display_devices_refresh) return TRUE; + + TRACE("force=%d force_refresh=%d\n", force, force_display_devices_refresh); + + force_display_devices_refresh = FALSE; + + wayland_add_device_gpu(device_manager, param); + + wl_list_for_each(output, &wayland->output_list, link) + { + if (!output->current_mode.refresh) continue; + wayland_add_device_adapter(device_manager, param, output_id); + wayland_add_device_monitor(device_manager, param, output); + wayland_add_device_mode(device_manager, param, output); + output_id++; + } + + return TRUE; +} diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 9b61427c8aa..104a091ea80 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -26,7 +26,55 @@
#include "waylanddrv.h"
+#include "wine/debug.h" + +#include <stdlib.h> + +WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); + struct wl_display *process_wl_display = NULL; +struct wayland *process_wayland = NULL; + +/********************************************************************** + * Registry handling + */ + +static void registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t version) +{ + TRACE("interface=%s version=%u id=%u\n", interface, version, id); + + if (strcmp(interface, "wl_output") == 0) + { + if (!wayland_output_create(id, version)) + ERR("Failed to create wayland_output for global id=%u\n", id); + } +} + +static void registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t id) +{ + struct wayland_output *output, *tmp; + + TRACE("id=%u\n", id); + + wl_list_for_each_safe(output, tmp, &process_wayland->output_list, link) + { + if (output->global_id == id) + { + TRACE("removing output->name=%s\n", output->name); + wayland_output_destroy(output); + wayland_init_display_devices(); + return; + } + } +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +};
/********************************************************************** * wayland_process_init @@ -36,6 +84,50 @@ struct wl_display *process_wl_display = NULL; */ BOOL wayland_process_init(void) { + struct wl_display *wl_display_wrapper; + process_wl_display = wl_display_connect(NULL); - return process_wl_display != NULL; + if (!process_wl_display) + return FALSE; + + process_wayland = calloc(1, sizeof(*process_wayland)); + if (!process_wayland) + return FALSE; + + TRACE("process_wayland=%p wl_display=%p\n", process_wayland, process_wl_display); + + if (!(process_wayland->wl_event_queue = wl_display_create_queue(process_wl_display))) + { + ERR("Failed to create event queue\n"); + return FALSE; + } + + if (!(wl_display_wrapper = wl_proxy_create_wrapper(process_wl_display))) + { + ERR("Failed to create proxy wrapper for wl_display\n"); + return FALSE; + } + wl_proxy_set_queue((struct wl_proxy *) wl_display_wrapper, + process_wayland->wl_event_queue); + + process_wayland->wl_registry = wl_display_get_registry(wl_display_wrapper); + wl_proxy_wrapper_destroy(wl_display_wrapper); + if (!process_wayland->wl_registry) + { + ERR("Failed to get to wayland registry\n"); + return FALSE; + } + + wl_list_init(&process_wayland->output_list); + + /* Populate registry */ + wl_registry_add_listener(process_wayland->wl_registry, ®istry_listener, + process_wayland); + + /* We need two roundtrips. One to get and bind globals, one to handle all + * initial events produced from registering the globals. */ + wl_display_roundtrip_queue(process_wl_display, process_wayland->wl_event_queue); + wl_display_roundtrip_queue(process_wl_display, process_wayland->wl_event_queue); + + return TRUE; } diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c new file mode 100644 index 00000000000..9f9dfc06cb4 --- /dev/null +++ b/dlls/winewayland.drv/wayland_output.c @@ -0,0 +1,134 @@ +/* + * Wayland output handling + * + * Copyright (c) 2020 Alexandros Frantzis for Collabora Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include "waylanddrv.h" + +#include "wine/debug.h" + +#include <stdlib.h> + +WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); + +static const int32_t default_refresh = 60000; +static uint32_t next_output_id = 0; + +/********************************************************************** + * Output handling + */ + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, + int32_t physical_width, int32_t physical_height, + int32_t subpixel, + const char *make, const char *model, + int32_t output_transform) +{ +} + +static void output_handle_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, int32_t height, + int32_t refresh) +{ + struct wayland_output *output = data; + + 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; + + output->current_mode.width = width; + output->current_mode.height = width; + output->current_mode.refresh = refresh; +} + +static void output_handle_done(void *data, struct wl_output *wl_output) +{ + struct wayland_output *output = data; + + TRACE("name=%s mode=%dx%d\n", + output->name, output->current_mode.width, output->current_mode.height); + + wayland_init_display_devices(); +} + +static void output_handle_scale(void *data, struct wl_output *wl_output, + int32_t scale) +{ +} + +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale +}; + +/********************************************************************** + * wayland_output_create + * + * Creates a wayland_output and adds it to the output list. + */ +BOOL wayland_output_create(uint32_t id, uint32_t version) +{ + struct wayland_output *output = calloc(1, sizeof(*output)); + + if (!output) + { + ERR("Couldn't allocate space for wayland_output\n"); + goto err; + } + + output->wl_output = wl_registry_bind(process_wayland->wl_registry, id, + &wl_output_interface, + version < 2 ? version : 2); + output->global_id = id; + wl_output_add_listener(output->wl_output, &output_listener, output); + + wl_list_init(&output->link); + + snprintf(output->name, sizeof(output->name), "WaylandOutput%d", + next_output_id++); + + wl_list_insert(process_wayland->output_list.prev, &output->link); + + return TRUE; + +err: + if (output) wayland_output_destroy(output); + return FALSE; +} + +/********************************************************************** + * wayland_output_destroy + * + * Destroys a wayland_output. + */ +void wayland_output_destroy(struct wayland_output *output) +{ + wl_list_remove(&output->link); + wl_output_destroy(output->wl_output); + free(output); +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 3fd8728d81b..768e22f2caf 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -27,6 +27,10 @@
#include <wayland-client.h>
+#include "windef.h" +#include "winbase.h" +#include "wine/gdi_driver.h" + #include "unixlib.h"
/********************************************************************** @@ -34,11 +38,54 @@ */
extern struct wl_display *process_wl_display DECLSPEC_HIDDEN; +extern struct wayland *process_wayland DECLSPEC_HIDDEN; + +/********************************************************************** + * Definitions for wayland types + */ + +struct wayland +{ + struct wl_event_queue *wl_event_queue; + struct wl_registry *wl_registry; + struct wl_list output_list; +}; + +struct wayland_output_mode +{ + int32_t width; + int32_t height; + int32_t refresh; +}; + +struct wayland_output +{ + struct wl_list link; + struct wl_output *wl_output; + struct wayland_output_mode current_mode; + char name[20]; + uint32_t global_id; +};
/********************************************************************** * Wayland initialization */
BOOL wayland_process_init(void) DECLSPEC_HIDDEN; +void wayland_init_display_devices(void) DECLSPEC_HIDDEN; + +/********************************************************************** + * Wayland output + */ + +BOOL wayland_output_create(uint32_t id, uint32_t version) DECLSPEC_HIDDEN; +void wayland_output_destroy(struct wayland_output *output) DECLSPEC_HIDDEN; + +/********************************************************************** + * USER driver functions + */ + +BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, + BOOL force, void *param) DECLSPEC_HIDDEN;
#endif /* __WINE_WAYLANDDRV_H */ diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index d4018683239..a9297edc500 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -29,11 +29,24 @@
#include "waylanddrv.h"
+static const struct user_driver_funcs waylanddrv_funcs = +{ + .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices, +}; + static NTSTATUS waylanddrv_unix_init(void *arg) { - if (!wayland_process_init()) return STATUS_UNSUCCESSFUL; + /* Set the user driver functions now so that they are available during + * our initialization. We clear them on error. */ + __wine_set_user_driver(&waylanddrv_funcs, WINE_GDI_DRIVER_VERSION); + + if (!wayland_process_init()) goto err;
return 0; + +err: + __wine_set_user_driver(NULL, WINE_GDI_DRIVER_VERSION); + return STATUS_UNSUCCESSFUL; }
const unixlib_entry_t __wine_unix_call_funcs[] =