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 | 40 +++++++-- dlls/winewayland.drv/wayland_output.c | 125 +++++++++++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 3 + 3 files changed, 160 insertions(+), 8 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 354e10162ca..e9fac1dad47 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -42,6 +42,7 @@ struct output_info struct wayland_output_mode modes[128]; int num_modes; int current_mode; + int x, y; };
struct display_devices @@ -101,6 +102,19 @@ BOOL wayland_display_devices_process_init(void) return TRUE; }
+static struct wayland_output *wayland_get_primary_output(void) +{ + struct wayland_output *output; + + wl_list_for_each(output, &process_wayland->output_list, link) + { + if (output->current_mode && output->x == 0 && output->y == 0) + return output; + } + + return NULL; +} + static void wayland_refresh_display_devices(void) { UINT32 num_path, num_mode; @@ -148,7 +162,8 @@ static void wayland_add_device_monitor(const struct gdi_device_manager *device_m struct gdi_monitor monitor = {0}; struct wayland_output_mode *mode = &output_info->modes[output_info->current_mode];
- SetRect(&monitor.rc_monitor, 0, 0, mode->width, mode->height); + SetRect(&monitor.rc_monitor, output_info->x, output_info->y, + output_info->x + mode->width, output_info->y + mode->height);
/* We don't have a direct way to get the work area in Wayland. */ monitor.rc_work = monitor.rc_monitor; @@ -211,6 +226,9 @@ static void display_devices_update_output(struct output_info *output_info, }
output_info->num_modes = mode_i; + + output_info->x = output->x; + output_info->y = output->y; }
/*********************************************************************** @@ -236,13 +254,21 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage * This should only happen from the desktop window process. */ if (update_from_wayland) { - struct wayland_output *output; + struct wayland_output *output, *primary;
output_id = 0;
+ /* Add the primary output (i.e., positioned at 0,0) first. */ + if ((primary = wayland_get_primary_output())) + { + display_devices_update_output(&display_devices->outputs[output_id], + primary); + output_id++; + } + wl_list_for_each(output, &process_wayland->output_list, link) { - if (!output->current_mode) continue; + if (!output->current_mode || output == primary) continue; if (output_id >= ARRAYSIZE(display_devices->outputs)) { WARN("wayland reported too many outputs, ignoring %s\n", @@ -302,11 +328,11 @@ BOOL WAYLAND_GetCurrentDisplaySettings(LPCWSTR name, BOOL is_primary, LPDEVMODEW 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; + devmode->dmPosition.x = output_info->x; + devmode->dmPosition.y = output_info->y; + + pthread_mutex_unlock(&display_devices->mutex);
TRACE("=> %d,%d+%ux%u@%u %ubpp\n", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index 8eb24fe97f3..717341d678e 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -97,11 +97,126 @@ static void wayland_output_add_mode(struct wayland_output *output, if (current) output->current_mode = mode; }
+static int wayland_output_cmp_x(const void *va, const void *vb) +{ + const struct wayland_output *a = va; + const struct wayland_output *b = vb; + + if (a->logical_x < b->logical_x) return -1; + if (a->logical_x > b->logical_x) return 1; + if (a->logical_y < b->logical_y) return -1; + if (a->logical_y > b->logical_y) return 1; + return 0; +} + +static int wayland_output_cmp_y(const void *va, const void *vb) +{ + const struct wayland_output *a = va; + const struct wayland_output *b = vb; + + if (a->logical_y < b->logical_y) return -1; + if (a->logical_y > b->logical_y) return 1; + if (a->logical_x < b->logical_x) return -1; + if (a->logical_x > b->logical_x) return 1; + return 0; +} + +static struct wayland_output** wayland_output_list_sorted(struct wl_list *output_list, + int (*cmp)(const void *, const void *)) +{ + int num_outputs = wl_list_length(output_list); + struct wayland_output **sorted; + struct wayland_output *o; + int i = 0; + + sorted = malloc(sizeof(*sorted) * (num_outputs + 1)); + if (!sorted) + { + ERR("Couldn't allocate space for sorted outputs\n"); + return NULL; + } + + wl_list_for_each(o, output_list, link) sorted[i++] = o; + + qsort(sorted, num_outputs, sizeof(*sorted), cmp); + + sorted[num_outputs] = NULL; + return sorted; +} + +static void wayland_output_list_update_physical_coords(struct wl_list *output_list) +{ + struct wayland_output **sorted_x, **sorted_y; + struct wayland_output **cur_p, **prev_p; + struct wayland_output *cur, *prev; + + /* Set default physical coordinates. */ + wl_list_for_each(cur, output_list, link) + { + cur->x = cur->logical_x; + cur->y = cur->logical_y; + } + + /* Sort and process the outputs from left to right. */ + cur_p = sorted_x = wayland_output_list_sorted(output_list, wayland_output_cmp_x); + if (!sorted_x) return; + + while ((cur = *cur_p)) + { + /* Update output->x based on other outputs that are to to the left. */ + prev_p = sorted_x; + while ((prev = *prev_p) != cur) + { + if (cur->logical_x == prev->logical_x + prev->logical_w && + prev->current_mode) + { + int new_x = prev->x + prev->current_mode->width; + if (new_x > cur->x) cur->x = new_x; + } + prev_p++; + } + + cur_p++; + } + + free(sorted_x); + + /* Now sort and process the outputs from top to bottom. */ + cur_p = sorted_y = wayland_output_list_sorted(output_list, wayland_output_cmp_y); + if (!sorted_y) return; + + while ((cur = *cur_p)) + { + /* Update output->y based on other outputs that are above. */ + prev_p = sorted_y; + while ((prev = *prev_p) != cur) + { + if (cur->logical_y == prev->logical_y + prev->logical_h && + prev->current_mode) + { + int new_y = prev->y + prev->current_mode->height; + if (new_y > cur->y) cur->y = new_y; + } + prev_p++; + } + + cur_p++; + } + + free(sorted_y); +} + 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 physical=%d,%d+%dx%d\n", + output->name, output->logical_x, output->logical_y, + output->logical_w, output->logical_h, + output->x, output->y, + output->current_mode->width, output->current_mode->height); + + wayland_output_list_update_physical_coords(&process_wayland->output_list);
RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) { @@ -163,6 +278,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 +289,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 2f1a83ee542..492976a5070 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -70,6 +70,9 @@ 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 */ + int x, y; /* position in physical pixel coordinate space */ uint32_t global_id; };