From 29b3a92ffc155840f0f9134b7210d132dd8fb9d4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Sep 2022 15:09:46 +0530 Subject: [PATCH] Wayland: swap buffers immediately on scale change to ensure attached buffer is a multiple of the new scale Earlier we were only swapping buffers when ready to draw, but the Wayland protocol requires the attached buffer to be a multiple of the scale. We cannot guarantee an application side swap will be triggered before the next commit, so instead we blank the new buffer swap it in the GLFW backend itself. Fixes #5467 --- docs/changelog.rst | 2 ++ glfw/wl_window.c | 44 +++++++++++++++++++++++++++----------------- kitty/glfw.c | 8 ++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 17a690304..b40d926f9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -49,6 +49,8 @@ Detailed list of changes - Wayland: Fix for a bug that could cause kitty to become non-responsive when using multiple OS windows in a single instance on some compositors (:iss:`5495`) +- Wayland: Fix for a bug preventing kitty from starting on Hyprland when using a non-unit scale (:iss:`5467`) + 0.26.2 [2022-09-05] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/glfw/wl_window.c b/glfw/wl_window.c index c67c69a97..cd21e465d 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -199,7 +199,7 @@ static bool checkScaleChange(_GLFWwindow* window) } // Makes the surface considered as XRGB instead of ARGB. -static void setOpaqueRegion(_GLFWwindow* window) +static void setOpaqueRegion(_GLFWwindow* window, bool commit_surface) { struct wl_region* region; @@ -209,10 +209,22 @@ static void setOpaqueRegion(_GLFWwindow* window) wl_region_add(region, 0, 0, window->wl.width, window->wl.height); wl_surface_set_opaque_region(window->wl.surface, region); - wl_surface_commit(window->wl.surface); + if (commit_surface) wl_surface_commit(window->wl.surface); wl_region_destroy(region); } +static void +swap_buffers(_GLFWwindow *window) { + // this will attach the buffer to the surface, + // the client is responsible for clearing the buffer to an appropriate blank + window->swaps_disallowed = false; + GLFWwindow *current = glfwGetCurrentContext(); + bool context_is_current = ((_GLFWwindow*)current)->id == window->id; + if (!context_is_current) glfwMakeContextCurrent((GLFWwindow*)window); + window->context.swapBuffers(window); + if (!context_is_current) glfwMakeContextCurrent(current); +} + static void resizeFramebuffer(_GLFWwindow* window) { @@ -221,7 +233,7 @@ resizeFramebuffer(_GLFWwindow* window) { int scaledHeight = window->wl.height * scale; debug("Resizing framebuffer to: %dx%d at scale: %d\n", window->wl.width, window->wl.height, scale); wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); - if (!window->wl.transparent) setOpaqueRegion(window); + if (!window->wl.transparent) setOpaqueRegion(window, false); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } @@ -236,9 +248,9 @@ clipboard_mime(void) { } static bool -dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { +dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height, bool *scale_changed) { bool size_changed = width != window->wl.width || height != window->wl.height; - bool scale_changed = checkScaleChange(window); + *scale_changed = checkScaleChange(window); if (size_changed) { _glfwInputWindowSize(window, width, height); @@ -246,7 +258,7 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height resizeFramebuffer(window); } - if (scale_changed) { + if (*scale_changed) { debug("Scale changed to %d in dispatchChangesAfterConfigure\n", window->wl.scale); if (!size_changed) resizeFramebuffer(window); _glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale); @@ -254,7 +266,7 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height _glfwInputWindowDamage(window); - return size_changed || scale_changed; + return size_changed || *scale_changed; } static void @@ -383,7 +395,7 @@ static bool createSurface(_GLFWwindow* window, window->wl.scale = 1; if (!window->wl.transparent) - setOpaqueRegion(window); + setOpaqueRegion(window, false); return true; } @@ -498,14 +510,7 @@ static void xdgSurfaceHandleConfigure(void* data, int height = window->wl.pending.height; if (!window->wl.surface_configured_once) { window->wl.surface_configured_once = true; - // this will attach the buffer to the surface, the client is responsible for clearing the buffer to an appropriate blank - window->swaps_disallowed = false; - GLFWwindow *current = glfwGetCurrentContext(); - bool context_is_current = ((_GLFWwindow*)current)->id == window->id; - if (!context_is_current) glfwMakeContextCurrent(data); - window->context.swapBuffers(window); - if (!context_is_current) glfwMakeContextCurrent(current); - + swap_buffers(window); if (!width && !height && !new_states && !window->wl.decorations.serverSide && getenv("XAUTHORITY") && strstr(getenv("XAUTHORITY"), "mutter")) { // https://github.com/kovidgoyal/kitty/issues/4802 debug("Ignoring first empty surface configure event on mutter.\n"); @@ -534,10 +539,11 @@ static void xdgSurfaceHandleConfigure(void* data, } bool resized = false; + bool scale_changed = false; if (window->wl.pending_state) { int width = window->wl.pending.width, height = window->wl.pending.height; set_csd_window_geometry(window, &width, &height); - resized = dispatchChangesAfterConfigure(window, width, height); + resized = dispatchChangesAfterConfigure(window, width, height, &scale_changed); if (window->wl.decorations.serverSide) { free_csd_surfaces(window); } else { @@ -548,6 +554,10 @@ static void xdgSurfaceHandleConfigure(void* data, inform_compositor_of_window_geometry(window, "configure"); + // we need to swap buffers here to ensure the buffer attached to the surface is a multiple + // of the new scale. See https://github.com/kovidgoyal/kitty/issues/5467 + if (scale_changed) swap_buffers(window); + // if a resize happened there will be a commit at the next render frame so // dont commit here, GNOME doesnt like it and its not really needed anyway if (!resized) wl_surface_commit(window->wl.surface); diff --git a/kitty/glfw.c b/kitty/glfw.c index 1284ff5a2..6193d684a 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -312,6 +312,14 @@ dpi_change_callback(GLFWwindow *w, float x_scale UNUSED, float y_scale UNUSED) { window->live_resize.last_resize_event_at = monotonic(); global_state.callback_os_window = NULL; request_tick_callback(); + if (global_state.is_wayland) { + // because of the stupidity of Wayland design, the GLFW wayland backend + // will need to swap buffers immediately after a scale change to ensure + // the buffer size is a multiple of the scale. So blank the new buffer to + // ensure we dont leak any unintialized pixels to the screen. The OpenGL viewport + // will already have been resized to its new size in framebuffer_size_callback + blank_os_window(window); + } } static void