diff options
author | Campbell Barton <campbell@blender.org> | 2022-06-15 07:53:33 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-06-15 08:30:44 +0300 |
commit | 2770010224aabd2fa858e8beba94eb07357e9d3c (patch) | |
tree | 4936830b188ab1f28dd1d4568538a8a64abcefaf /intern | |
parent | 08f5219d1c6c5efe76b7b8fb9579eb52f600ad78 (diff) |
GHOST/Wayland: fractional scaling support
GHOST_GetDPIHint now returns a value that takes fractional scaling into
account. Otherwise the integer scale is used since Wayland's API's use
integer scale values exclusively.
Use the same method as SDL to calculate the fractional scale.
Diffstat (limited to 'intern')
-rw-r--r-- | intern/ghost/CMakeLists.txt | 5 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWayland.cpp | 139 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWayland.h | 19 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowWayland.cpp | 71 |
4 files changed, 210 insertions, 24 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 0a6e19f006c..9ef05a99b3c 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -334,6 +334,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) xdg-decoration "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" ) + # xdg-output. + generate_protocol_bindings( + xdg-output + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( pointer-constraints diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index de9c865f163..9dc6f2fd00b 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -31,6 +31,8 @@ #include <relative-pointer-client-protocol.h> #include <tablet-client-protocol.h> #include <wayland-cursor.h> +#include <xdg-output-client-protocol.h> + #include <xkbcommon/xkbcommon.h> #include <fcntl.h> @@ -201,6 +203,7 @@ struct display_t { struct wl_compositor *compositor = nullptr; struct xdg_wm_base *xdg_shell = nullptr; struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; + struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; struct wl_shm *shm = nullptr; std::vector<output_t *> outputs; std::vector<input_t *> inputs; @@ -1815,6 +1818,79 @@ static const struct wl_seat_listener seat_listener = { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Listener (XDG Output), #zxdg_output_v1_listener + * \{ */ + +static void xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t x, + int32_t y) +{ + output_t *output = static_cast<output_t *>(data); + output->position_logical[0] = x; + output->position_logical[1] = y; + output->has_position_logical = true; +} + +static void xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 * /*xdg_output*/, + int32_t width, + int32_t height) +{ + output_t *output = static_cast<output_t *>(data); + + if (output->size_logical[0] != 0 && output->size_logical[1] != 0) { + /* Original comment from SDL. */ + /* FIXME: GNOME has a bug where the logical size does not account for + * scale, resulting in bogus viewport sizes. + * + * Until this is fixed, validate that _some_ kind of scaling is being + * done (we can't match exactly because fractional scaling can't be + * detected otherwise), then override if necessary. + * -flibit + */ + if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) { + GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale"); + return; + } + } + + output->size_logical[0] = width; + output->size_logical[1] = height; + output->has_size_logical = true; +} + +static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/) +{ + /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol. + * `wl-output.done` event will be emitted in version 3 or higher. */ +} + +static void xdg_output_handle_name(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*name*/) +{ + /* Pass. */ +} + +static void xdg_output_handle_description(void * /*data*/, + struct zxdg_output_v1 * /*xdg_output*/, + const char * /*description*/) +{ + /* Pass. */ +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + xdg_output_handle_logical_position, + xdg_output_handle_logical_size, + xdg_output_handle_done, + xdg_output_handle_name, + xdg_output_handle_description, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Listener (Output), #wl_output_listener * \{ */ @@ -1839,14 +1915,24 @@ static void output_geometry(void *data, static void output_mode(void *data, struct wl_output * /*wl_output*/, - uint32_t /*flags*/, + uint32_t flags, int32_t width, int32_t height, int32_t /*refresh*/) { output_t *output = static_cast<output_t *>(data); - output->size_native[0] = width; - output->size_native[1] = height; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + output->size_native[0] = width; + output->size_native[1] = height; + + /* Don't rotate this yet, `wl-output` coordinates are transformed in + * handle_done and `xdg-output` coordinates are pre-transformed. */ + if (!output->has_size_logical) { + output->size_logical[0] = width; + output->size_logical[1] = height; + } + } } /** @@ -1857,8 +1943,31 @@ static void output_mode(void *data, * changes done after that. This allows changes to the output * properties to be seen as atomic, even if they happen via multiple events. */ -static void output_done(void * /*data*/, struct wl_output * /*wl_output*/) +static void output_done(void *data, struct wl_output * /*wl_output*/) { + output_t *output = static_cast<output_t *>(data); + int32_t size_native[2]; + if (output->transform & WL_OUTPUT_TRANSFORM_90) { + size_native[0] = output->size_native[1]; + size_native[1] = output->size_native[1]; + } + else { + size_native[0] = output->size_native[0]; + size_native[1] = output->size_native[1]; + } + + /* If `xdg-output` is present, calculate the true scale of the desktop */ + if (output->has_size_logical) { + + /* NOTE: it's not necessary to divide these values by their greatest-common-denominator + * as even a 64k screen resolution doesn't approach overflowing an `int32_t`. */ + + GHOST_ASSERT(size_native[0] && output->size_logical[0], + "Screen size values were not set when they were expected to be."); + + output->scale_fractional = wl_fixed_from_int(size_native[0]) / output->size_logical[0]; + output->has_scale_fractional = true; + } } static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) @@ -1914,13 +2023,35 @@ static void global_add(void *data, display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>( wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); } + else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) { + display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3)); + for (output_t *output : display->outputs) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } + } else if (!strcmp(interface, wl_output_interface.name)) { output_t *output = new output_t; + output->scale = 1; + output->has_scale_fractional = false; + output->scale_fractional = wl_fixed_from_int(1); + + output->has_size_logical = false; + output->has_position_logical = false; + output->output = static_cast<wl_output *>( wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); display->outputs.push_back(output); wl_output_add_listener(output->output, &output_listener, output); + + if (display->xdg_output_manager) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, + output->output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + } } else if (!strcmp(interface, wl_seat_interface.name)) { input_t *input = new input_t; diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index dad7f38eb31..efd2e2cf439 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -23,12 +23,31 @@ struct display_t; struct output_t { struct wl_output *output; + struct zxdg_output_v1 *xdg_output; /** Dimensions in pixels. */ int32_t size_native[2]; /** Dimensions in millimeter. */ int32_t size_mm[2]; + + int32_t size_logical[2]; + bool has_size_logical; + + int32_t position_logical[2]; + bool has_position_logical; + int transform; int scale; + /** + * The integer `scale` value should be used in almost all cases, + * as this is what is used for most API calls. + * Only use fractional scaling to calculate the DPI. + * + * \note Internally an #wl_fixed_t is used to store the scale of the display, + * so use the same value here (avoid floating point arithmetic in general). + */ + wl_fixed_t scale_fractional; + bool has_scale_fractional; + std::string make; std::string model; }; diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 68d50c597d6..5270c19418d 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -32,8 +32,13 @@ struct window_t { /** The scale value written to #wl_surface_set_buffer_scale. */ int scale; - /** The DPI (currently always `scale * base_dpi`). */ - uint16_t dpi; + /** + * The DPI, either: + * - `scale * base_dpi` + * - `wl_fixed_to_int(scale_fractional * base_dpi)` + * When fractional scaling is available. + */ + uint32_t dpi; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; @@ -54,13 +59,33 @@ struct window_t { * \{ */ static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs, - const int scale_default) + const int32_t scale_default, + uint32_t *r_dpi) { int scale_max = 0; + const output_t *output_max = nullptr; for (const output_t *reg_output : outputs) { - scale_max = std::max(scale_max, reg_output->scale); + if (scale_max < reg_output->scale) { + scale_max = reg_output->scale; + output_max = reg_output; + } } - return scale_max ? scale_max : scale_default; + + if (scale_max != 0) { + if (r_dpi) { + *r_dpi = output_max->has_scale_fractional ? + /* Fractional DPI. */ + wl_fixed_to_int(output_max->scale_fractional * base_dpi) : + /* Simple non-fractional DPI. */ + (scale_max * base_dpi); + } + return scale_max; + } + + if (r_dpi) { + *r_dpi = scale_default * base_dpi; + } + return scale_default; } /** \} */ @@ -242,8 +267,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, * * Using the maximum scale is best as it results in the window first being smaller, * avoiding a large window flashing before it's made smaller. */ - w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1); - w->dpi = w->scale * base_dpi; + w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi); /* Window surfaces. */ w->surface = wl_compositor_create_surface(m_system->compositor()); @@ -356,28 +380,35 @@ output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output) bool GHOST_WindowWayland::outputs_changed_update_scale() { - const int scale_next = outputs_max_scale_or_default(this->m_system->outputs(), 0); + uint32_t dpi_next; + const int scale_next = outputs_max_scale_or_default(this->m_system->outputs(), 0, &dpi_next); if (scale_next == 0) { return false; } + window_t *win = this->w; + const uint32_t dpi_curr = win->dpi; const int scale_curr = win->scale; - if (scale_next == scale_curr) { - return false; - } + bool changed = false; - /* Unlikely but possible there is a pending size change is set. */ - win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; - win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; + if (scale_next != scale_curr) { + /* Unlikely but possible there is a pending size change is set. */ + win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; + win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; - win->scale = scale_next; - wl_surface_set_buffer_scale(this->surface(), scale_next); + win->scale = scale_next; + wl_surface_set_buffer_scale(this->surface(), scale_next); + changed = true; + } - /* Using the real DPI will cause wrong scaling of the UI - * use a multiplier for the default DPI as workaround. */ - win->dpi = scale_next * base_dpi; + if (dpi_next != dpi_curr) { + /* Using the real DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as workaround. */ + win->dpi = dpi_next; + changed = true; + } - return true; + return changed; } uint16_t GHOST_WindowWayland::dpi() |