From 83468535dd147795d70d6df69ef28961f200a2b1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 21 Mar 2024 20:15:23 +0530 Subject: [PATCH] Implement support for preferred buffer scale --- glfw/wl_client_side_decorations.c | 8 +-- glfw/wl_init.c | 11 ++-- glfw/wl_platform.h | 4 +- glfw/wl_text_input.c | 6 ++- glfw/wl_window.c | 85 +++++++++++++++++++++---------- 5 files changed, 75 insertions(+), 39 deletions(-) diff --git a/glfw/wl_client_side_decorations.c b/glfw/wl_client_side_decorations.c index 5f60a79c0..37974426a 100644 --- a/glfw/wl_client_side_decorations.c +++ b/glfw/wl_client_side_decorations.c @@ -282,7 +282,7 @@ render_edges(_GLFWwindow *window) { static bool create_shm_buffers(_GLFWwindow* window) { - const unsigned scale = window->wl.integer_scale >= 1 ? window->wl.integer_scale : 1; + const unsigned scale = _glfwWaylandIntegerWindowScale(window); const size_t vertical_width = decs.metrics.width, vertical_height = window->wl.height + decs.metrics.top; const size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.width + 2 * decs.metrics.width; @@ -371,7 +371,7 @@ ensure_csd_resources(_GLFWwindow *window) { const bool size_changed = ( decs.for_window_state.width != window->wl.width || decs.for_window_state.height != window->wl.height || - decs.for_window_state.scale != window->wl.integer_scale || + decs.for_window_state.scale != _glfwWaylandIntegerWindowScale(window) || !decs.mapping.data ); const bool needs_update = focus_changed || size_changed || !decs.left.surface || decs.buffer_destroyed; @@ -384,7 +384,7 @@ ensure_csd_resources(_GLFWwindow *window) { decs.buffer_destroyed = false; } - int32_t x, y, scale = window->wl.integer_scale < 1 ? 1 : window->wl.integer_scale; + int32_t x, y, scale = _glfwWaylandIntegerWindowScale(window); x = 0; y = -decs.metrics.top; if (!decs.top.surface) create_csd_surfaces(window, &decs.top); position_csd_surface(&decs.top, x, y, scale); @@ -409,7 +409,7 @@ ensure_csd_resources(_GLFWwindow *window) { decs.for_window_state.width = window->wl.width; decs.for_window_state.height = window->wl.height; - decs.for_window_state.scale = window->wl.integer_scale; + decs.for_window_state.scale = _glfwWaylandIntegerWindowScale(window); decs.for_window_state.focused = is_focused; return true; } diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 89d1fca2f..a3d640149 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -132,7 +132,7 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window) struct wl_cursor* cursor; struct wl_cursor_image* image; struct wl_surface* surface = _glfw.wl.cursorSurface; - const int scale = window->wl.integer_scale; + const int scale = _glfwWaylandIntegerWindowScale(window); struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale); if (!theme) return; @@ -632,10 +632,13 @@ static void registryHandleGlobal(void* data UNUSED, #define is(x) strcmp(interface, x##_interface.name) == 0 if (is(wl_compositor)) { +#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION + _glfw.wl.compositorVersion = min(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION, version); + _glfw.wl.has_preferred_buffer_scale = _glfw.wl.compositorVersion >= WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION; +#else _glfw.wl.compositorVersion = min(3, version); - _glfw.wl.compositor = - wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.compositorVersion); +#endif + _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion); } else if (is(wl_subcompositor)) { diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 3690f97d2..0d2f11126 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -174,7 +174,7 @@ typedef struct _GLFWwindowWayland // We need to track the monitors the window spans on to calculate the // optimal scaling factor. - int integer_scale; + struct { uint32_t deduced, preferred; } integer_scale; uint32_t fractional_scale; bool initial_scale_notified; _GLFWmonitor** monitors; @@ -343,6 +343,7 @@ typedef struct _GLFWlibraryWayland EventLoopData eventLoopData; size_t dataOffersCounter; _GLFWWaylandDataOffer dataOffers[8]; + bool has_preferred_buffer_scale; } _GLFWlibraryWayland; // Wayland-specific per-monitor data @@ -380,6 +381,7 @@ void _glfwWaylandAfterBufferSwap(_GLFWwindow *window); void _glfwSetupWaylandDataDevice(void); void _glfwSetupWaylandPrimarySelectionDevice(void); float _glfwWaylandWindowScale(_GLFWwindow*); +int _glfwWaylandIntegerWindowScale(_GLFWwindow*); void animateCursorImage(id_type timer_id, void *data); struct wl_cursor* _glfwLoadCursor(GLFWCursorShape, struct wl_cursor_theme*); void destroy_data_offer(_GLFWWaylandDataOffer*); diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c index 98a4b9b7c..4a9502e78 100644 --- a/glfw/wl_text_input.c +++ b/glfw/wl_text_input.c @@ -172,8 +172,10 @@ _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { commit(); break; case GLFW_IME_UPDATE_CURSOR_POSITION: { - const int scale = w->wl.integer_scale; - const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale; + const float scale = _glfwWaylandWindowScale(w); +#define s(x) (int)roundf((x) / scale) + const int left = s(ev->cursor.left), top = s(ev->cursor.top), width = s(ev->cursor.width), height = s(ev->cursor.height); +#undef s if (left != last_cursor_left || top != last_cursor_top || width != last_cursor_width || height != last_cursor_height) { last_cursor_left = left; last_cursor_top = top; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index ace2f1fb5..e02296152 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -206,7 +206,7 @@ setCursorImage(_GLFWwindow* window, bool on_theme_change) { struct wl_cursor_image* image = NULL; struct wl_buffer* buffer = NULL; struct wl_surface* surface = _glfw.wl.cursorSurface; - const int scale = window->wl.integer_scale; + const int scale = _glfwWaylandIntegerWindowScale(window); if (!_glfw.wl.pointer) return; if (cursorWayland->scale < 0) { @@ -263,9 +263,9 @@ setCursorImage(_GLFWwindow* window, bool on_theme_change) { static bool checkScaleChange(_GLFWwindow* window) { - int scale = 1; + if (window->wl.fractional_scale || window->wl.integer_scale.preferred) return false; + unsigned int scale = 1, monitorScale; int i; - int monitorScale; // Check if we will be able to set the buffer scale or not. if (_glfw.wl.compositorVersion < 3) @@ -281,14 +281,13 @@ checkScaleChange(_GLFWwindow* window) { if (window->wl.monitorsCount < 1 && _glfw.monitorCount > 0) { // The window has not yet been assigned to any monitors, use the primary monitor _GLFWmonitor *m = _glfw.monitors[0]; - if (m && m->wl.scale > scale) scale = m->wl.scale; + if (m && m->wl.scale > (int)scale) scale = m->wl.scale; } // Only change the framebuffer size if the scale changed. - if (scale != window->wl.integer_scale && !window->wl.fractional_scale) + if (scale != window->wl.integer_scale.deduced && !window->wl.fractional_scale) { - window->wl.integer_scale = scale; - wl_surface_set_buffer_scale(window->wl.surface, scale); + window->wl.integer_scale.deduced = scale; setCursorImage(window, false); return true; } @@ -323,9 +322,16 @@ static void setOpaqueRegion(_GLFWwindow* window, bool commit_surface) wl_region_destroy(region); } +int +_glfwWaylandIntegerWindowScale(_GLFWwindow *window) { + int ans = (window->wl.integer_scale.preferred) ? window->wl.integer_scale.preferred : window->wl.integer_scale.deduced; + if (ans < 1) ans = 1; + return ans; +} + float _glfwWaylandWindowScale(_GLFWwindow *window) { - float ans = window->wl.integer_scale; + float ans = _glfwWaylandIntegerWindowScale(window); if (window->wl.fractional_scale) ans = window->wl.fractional_scale / 120.f; return ans; } @@ -363,6 +369,16 @@ clipboard_mime(void) { return buf; } +static void +apply_scale_changes(_GLFWwindow *window, bool resize_framebuffer, bool update_csd) { + float scale = _glfwWaylandWindowScale(window); + if (resize_framebuffer) resizeFramebuffer(window); + _glfwInputWindowContentScale(window, scale, scale); + if (update_csd) ensure_csd_resources(window); + int buffer_scale = window->wl.fractional_scale ? 1 : (int)scale; + wl_surface_set_buffer_scale(window->wl.surface, buffer_scale); +} + static bool dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { bool size_changed = width != window->wl.width || height != window->wl.height; @@ -375,9 +391,8 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height } if (scale_changed) { - debug("Scale changed to %d in dispatchChangesAfterConfigure\n", window->wl.integer_scale); - if (!size_changed) resizeFramebuffer(window); - _glfwInputWindowContentScale(window, window->wl.integer_scale, window->wl.integer_scale); + debug("Scale changed to %.2f in dispatchChangesAfterConfigure\n", _glfwWaylandWindowScale(window)); + apply_scale_changes(window, !size_changed, false); } _glfwInputWindowDamage(window); @@ -429,10 +444,8 @@ static void surfaceHandleEnter(void *data, window->wl.monitors[window->wl.monitorsCount++] = monitor; if (checkScaleChange(window)) { - debug("Scale changed to %d in surface enter event\n", window->wl.integer_scale); - resizeFramebuffer(window); - _glfwInputWindowContentScale(window, window->wl.integer_scale, window->wl.integer_scale); - ensure_csd_resources(window); + debug("Scale changed to %.2f in surfaceHandleEnter\n", _glfwWaylandWindowScale(window)); + apply_scale_changes(window, true, true); } } @@ -455,16 +468,35 @@ static void surfaceHandleLeave(void *data, window->wl.monitors[--window->wl.monitorsCount] = NULL; if (checkScaleChange(window)) { - debug("Scale changed to %d in surface leave event\n", window->wl.integer_scale); - resizeFramebuffer(window); - _glfwInputWindowContentScale(window, window->wl.integer_scale, window->wl.integer_scale); - ensure_csd_resources(window); + debug("Scale changed to %.2f in surfaceHandleLeave\n", _glfwWaylandWindowScale(window)); + apply_scale_changes(window, true, true); } } +#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION +static void +surface_preferred_buffer_scale(void *data, struct wl_surface *surface UNUSED, int32_t scale) { + _GLFWwindow* window = data; + if ((int)window->wl.integer_scale.preferred == scale) return; + debug("Preferred integer buffer scale changed to: %d\n", scale); + window->wl.integer_scale.preferred = scale; + if (!window->wl.fractional_scale) apply_scale_changes(window, true, true); +} + +static void +surface_preferred_buffer_transform(void *data, struct wl_surface *surface, uint32_t transform) { + (void)data; (void)surface; (void)transform; +} +#endif + + static const struct wl_surface_listener surfaceListener = { - surfaceHandleEnter, - surfaceHandleLeave + .enter = surfaceHandleEnter, + .leave = surfaceHandleLeave, +#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION + .preferred_buffer_scale = &surface_preferred_buffer_scale, + .preferred_buffer_transform = &surface_preferred_buffer_transform, +#endif }; static void @@ -473,12 +505,7 @@ fractional_scale_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_f if (scale == window->wl.fractional_scale) return; debug("Fractional scale requested: %u/120 = %.2f\n", scale, scale / 120.); window->wl.fractional_scale = scale; - resizeFramebuffer(window); - inform_compositor_of_window_geometry(window, "FractionalScale"); - wl_surface_set_buffer_scale(window->wl.surface, 1); - float fscale = scale / 120.f; - _glfwInputWindowContentScale(window, fscale, fscale); - ensure_csd_resources(window); + apply_scale_changes(window, true, true); } static const struct wp_fractional_scale_v1_listener fractional_scale_listener = { @@ -517,6 +544,9 @@ static bool createSurface(_GLFWwindow* window, wp_fractional_scale_v1_add_listener(window->wl.wp_fractional_scale_v1, &fractional_scale_listener, window); } + window->wl.integer_scale.deduced = scale; + if (_glfw.wl.has_preferred_buffer_scale) { scale = 1; window->wl.integer_scale.preferred = 1; } + debug("Creating window at size: %dx%d and scale %d\n", wndconfig->width, wndconfig->height, scale); window->wl.native = wl_egl_window_create(window->wl.surface, wndconfig->width * scale, wndconfig->height * scale); if (!window->wl.native) @@ -527,7 +557,6 @@ static bool createSurface(_GLFWwindow* window, window->wl.user_requested_content_size.width = wndconfig->width; window->wl.user_requested_content_size.height = wndconfig->height; - window->wl.integer_scale = scale; if (!window->wl.transparent) setOpaqueRegion(window, false);