From: Alexandros Frantzis alexandros.frantzis@collabora.com
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/display.c | 23 +++++--- dlls/winewayland.drv/wayland_output.c | 84 ++++++++++++++++++++++++--- dlls/winewayland.drv/waylanddrv.h | 5 +- 3 files changed, 95 insertions(+), 17 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 5144b33eb77..287aa374113 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -90,8 +90,8 @@ static void wayland_add_device_monitor(const struct gdi_device_manager *device_m struct gdi_monitor monitor = {0};
SetRect(&monitor.rc_monitor, 0, 0, - output->current_mode.width, - output->current_mode.height); + 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; @@ -117,12 +117,17 @@ static void populate_devmode(struct wayland_output_mode *output_mode, DEVMODEW * 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) +static void wayland_add_device_modes(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); + struct wayland_output_mode *output_mode; + + RB_FOR_EACH_ENTRY(output_mode, &output->modes, struct wayland_output_mode, entry) + { + DEVMODEW mode; + populate_devmode(output_mode, &mode); + device_manager->add_mode(&mode, param); + } }
/*********************************************************************** @@ -145,10 +150,10 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_list_for_each(output, &wayland->output_list, link) { - if (!output->current_mode.refresh) continue; + if (!output->current_mode) 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); + wayland_add_device_modes(device_manager, param, output); output_id++; }
diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index c8192f94fc7..af32e66acb2 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -39,6 +39,65 @@ 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 = + (const struct wayland_output_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_add_mode(struct wayland_output *output, + 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(&output->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(&output->modes, mode, &mode->entry); + } + + if (current) output->current_mode = mode; +} + 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, @@ -54,22 +113,26 @@ static void output_handle_mode(void *data, struct wl_output *wl_output, { 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; + wayland_output_add_mode(output, width, height, refresh, + (flags & WL_OUTPUT_MODE_CURRENT)); }
static void output_handle_done(void *data, struct wl_output *wl_output) { struct wayland_output *output = data; + struct wayland_output_mode *mode; + + TRACE("name=%s\n", output->name);
- TRACE("name=%s mode=%dx%d\n", - output->name, output->current_mode.width, output->current_mode.height); + RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) + { + TRACE("mode %dx%d @ %d %s\n", + mode->width, mode->height, mode->refresh, + output->current_mode == mode ? "*" : ""); + }
wayland_init_display_devices(); } @@ -108,6 +171,7 @@ 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->modes, wayland_output_mode_cmp_rb);
snprintf(output->name, sizeof(output->name), "WaylandOutput%d", next_output_id++); @@ -121,6 +185,11 @@ err: return FALSE; }
+static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) +{ + free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry)); +} + /********************************************************************** * wayland_output_destroy * @@ -128,6 +197,7 @@ err: */ void wayland_output_destroy(struct wayland_output *output) { + rb_destroy(&output->modes, wayland_output_mode_free_rb, NULL); 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 768e22f2caf..8458ed17df2 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -30,6 +30,7 @@ #include "windef.h" #include "winbase.h" #include "wine/gdi_driver.h" +#include "wine/rbtree.h"
#include "unixlib.h"
@@ -53,6 +54,7 @@ struct wayland
struct wayland_output_mode { + struct rb_entry entry; int32_t width; int32_t height; int32_t refresh; @@ -62,7 +64,8 @@ struct wayland_output { struct wl_list link; struct wl_output *wl_output; - struct wayland_output_mode current_mode; + struct rb_tree modes; + struct wayland_output_mode *current_mode; char name[20]; uint32_t global_id; };