From: Alexandros Frantzis alexandros.frantzis@collabora.com
Use the xdg-output-unstable-v1 protocol to get the position and size of the Wayland outputs in the compositor logical space, and use this information to infer the physical (i.e., native pixel) coordinates of the monitors.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/display.c | 157 +++++++++++++++++++++++--- dlls/winewayland.drv/wayland_output.c | 12 +- dlls/winewayland.drv/waylanddrv.h | 2 + 3 files changed, 157 insertions(+), 14 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index adf981d92e6..fb036d06a0f 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -30,6 +30,8 @@
#include "ntuser.h"
+#include <stdlib.h> + WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
static BOOL force_display_devices_refresh; @@ -47,6 +49,118 @@ void wayland_init_display_devices(void) wayland_refresh_display_devices(); }
+struct output_info +{ + int x, y; + struct wayland_output *output; +}; + +static int output_info_cmp_logical_x(const void *va, const void *vb) +{ + const struct output_info *a = va; + const struct output_info *b = vb; + + if (a->output->logical_x < b->output->logical_x) return -1; + if (a->output->logical_x > b->output->logical_x) return 1; + if (a->output->logical_y < b->output->logical_y) return -1; + if (a->output->logical_y > b->output->logical_y) return 1; + return 0; +} + +static int output_info_cmp_logical_y(const void *va, const void *vb) +{ + const struct output_info *a = va; + const struct output_info *b = vb; + + if (a->output->logical_y < b->output->logical_y) return -1; + if (a->output->logical_y > b->output->logical_y) return 1; + if (a->output->logical_x < b->output->logical_x) return -1; + if (a->output->logical_x > b->output->logical_x) return 1; + return 0; +} + +static int output_info_cmp_x_primary_first(const void *va, const void *vb) +{ + const struct output_info *a = va; + const struct output_info *b = vb; + BOOL a_is_primary = a->x == 0 && a->y == 0; + BOOL b_is_primary = b->x == 0 && b->y == 0; + + if (a_is_primary && !b_is_primary) return -1; + if (!a_is_primary && b_is_primary) return 1; + if (a->x < b->x) return -1; + if (a->x > b->x) return 1; + if (a->y < b->y) return -1; + if (a->y > b->y) return 1; + return 0; +} + +static void output_info_array_arrange_physical_coords(struct wl_array *output_info_array) +{ + struct output_info *cur, *prev; + size_t num_outputs = output_info_array->size / sizeof(struct output_info); + + /* Set default physical coordinates. */ + wl_array_for_each(cur, output_info_array) + { + cur->x = cur->output->logical_x; + cur->y = cur->output->logical_y; + } + + /* Sort and process the outputs from logical left to right. */ + qsort(output_info_array->data, num_outputs, sizeof(struct output_info), + output_info_cmp_logical_x); + + wl_array_for_each(cur, output_info_array) + { + BOOL cur_x_set = FALSE; + wl_array_for_each(prev, output_info_array) + { + if (prev == cur) break; + if (cur->output->logical_x == prev->output->logical_x + + prev->output->logical_w && + prev->output->current_mode) + { + int new_x = prev->x + prev->output->current_mode->width; + if (!cur_x_set || new_x > cur->x) + { + cur->x = new_x; + cur_x_set = TRUE; + } + } + } + } + + /* Sort and process the outputs from logical top to bottom. */ + qsort(output_info_array->data, num_outputs, sizeof(struct output_info), + output_info_cmp_logical_y); + + wl_array_for_each(cur, output_info_array) + { + BOOL cur_y_set = FALSE; + wl_array_for_each(prev, output_info_array) + { + if (prev == cur) break; + if (cur->output->logical_y == prev->output->logical_y + + prev->output->logical_h && + prev->output->current_mode) + { + int new_y = prev->y + prev->output->current_mode->height; + if (!cur_y_set || new_y > cur->y) + { + cur->y = new_y; + cur_y_set = TRUE; + } + } + } + } + + /* Now that we have our physical coordinates, arrange from left to right, + * but ensure the primary output is first. */ + qsort(output_info_array->data, num_outputs, sizeof(struct output_info), + output_info_cmp_x_primary_first); +} + static void wayland_add_device_gpu(const struct gdi_device_manager *device_manager, void *param) { @@ -76,13 +190,13 @@ 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};
- SetRect(&monitor.rc_monitor, 0, 0, - output->current_mode->width, - output->current_mode->height); + 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);
/* We don't have a direct way to get the work area in Wayland. */ monitor.rc_work = monitor.rc_monitor; @@ -90,7 +204,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->output->name, wine_dbgstr_rect(&monitor.rc_monitor), (UINT)monitor.state_flags);
device_manager->add_monitor(&monitor, param); @@ -109,21 +223,22 @@ 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; DEVMODEW mode = {.dmSize = sizeof(mode)};
- RB_FOR_EACH_ENTRY(output_mode, &output->modes, struct wayland_output_mode, entry) + RB_FOR_EACH_ENTRY(output_mode, &output_info->output->modes, + struct wayland_output_mode, entry) { populate_devmode(output_mode, &mode); device_manager->add_mode(&mode, GDI_MODE_NEXT, param); }
- populate_devmode(output->current_mode, &mode); + populate_devmode(output_info->output->current_mode, &mode); mode.dmFields |= DM_POSITION; - mode.dmPosition.x = 0; - mode.dmPosition.y = 0; + mode.dmPosition.x = output_info->x; + mode.dmPosition.y = output_info->y; device_manager->add_mode(&mode, GDI_MODE_REGISTRY_IF_EMPTY, param); device_manager->add_mode(&mode, GDI_MODE_CURRENT, param); } @@ -136,6 +251,8 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage { struct wayland_output *output; INT output_id = 0; + struct wl_array output_info_array; + struct output_info *output_info;
/* For now, update the display devices only when a compositor side update * is received in the desktop window process. */ @@ -145,17 +262,31 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
force_display_devices_refresh = FALSE;
- wayland_add_device_gpu(device_manager, param); + wl_array_init(&output_info_array);
wl_list_for_each(output, &process_wayland->output_list, link) { if (!output->current_mode) continue; + output_info = wl_array_add(&output_info_array, sizeof(*output_info)); + output_info->output = output; + } + + /* Arrange outputs in the physical coordinate space. */ + output_info_array_arrange_physical_coords(&output_info_array); + + /* Populate GDI devices. */ + wayland_add_device_gpu(device_manager, param); + + wl_array_for_each(output_info, &output_info_array) + { 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); + wayland_add_device_monitor(device_manager, param, output_info); + wayland_add_device_modes(device_manager, param, output_info); output_id++; }
+ wl_array_release(&output_info_array); + return TRUE; }
diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index 8eb24fe97f3..4d9033d3f73 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -101,7 +101,9 @@ static void wayland_output_done(struct wayland_output *output) { struct wayland_output_mode *mode;
- TRACE("name=%s\n", output->name); + TRACE("name=%s logical=%d,%d+%dx%d\n", + output->name, output->logical_x, output->logical_y, + output->logical_w, output->logical_h);
RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) { @@ -163,6 +165,10 @@ static void zxdg_output_v1_handle_logical_position(void *data, int32_t x, int32_t y) { + struct wayland_output *output = data; + TRACE("logical_x=%d logical_y=%d\n", x, y); + output->logical_x = x; + output->logical_y = y; }
static void zxdg_output_v1_handle_logical_size(void *data, @@ -170,6 +176,10 @@ static void zxdg_output_v1_handle_logical_size(void *data, int32_t width, int32_t height) { + struct wayland_output *output = data; + TRACE("logical_w=%d logical_h=%d\n", width, height); + output->logical_w = width; + output->logical_h = height; }
static void zxdg_output_v1_handle_done(void *data, diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index f8440d4bfc1..542c1a3b442 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -70,6 +70,8 @@ struct wayland_output struct rb_tree modes; struct wayland_output_mode *current_mode; char *name; + int logical_x, logical_y; /* logical position */ + int logical_w, logical_h; /* logical size */ uint32_t global_id; };