From d4b048735d8c9b4b7c59ece4f95c492ab49617b4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 10 Mar 2022 05:48:00 -0500 Subject: [PATCH 1/4] wayland: Fix CSD size calculations This is a partial revert of e359094cff68ec6cc26af407c2b924e5991eeec1 window->wl.{width, height} and window->wl.current.{width, height} represent different things. The former represents the actual size of the egl surface viewport used for terminal rendering. The former represents the size of the entire window including the CSD (client side decorations). These numbers should be the same if CSD is disabled. --- glfw/wl_client_side_decorations.c | 18 +++++++++--------- glfw/wl_init.c | 4 ++-- glfw/wl_platform.h | 1 + glfw/wl_window.c | 26 ++++++++++++++------------ 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/glfw/wl_client_side_decorations.c b/glfw/wl_client_side_decorations.c index 9d09ce3ae..45f41932e 100644 --- a/glfw/wl_client_side_decorations.c +++ b/glfw/wl_client_side_decorations.c @@ -263,11 +263,11 @@ static bool create_shm_buffers(_GLFWwindow* window) { const unsigned scale = window->wl.scale >= 1 ? window->wl.scale : 1; - const size_t vertical_width = decs.metrics.width, vertical_height = window->wl.current.height + decs.metrics.top; - const size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.current.width + 2 * decs.metrics.width; + 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; decs.mapping.size = 0; - decs.mapping.size += init_buffer_pair(&decs.top.buffer, window->wl.current.width, decs.metrics.top, scale); + decs.mapping.size += init_buffer_pair(&decs.top.buffer, window->wl.width, decs.metrics.top, scale); decs.mapping.size += init_buffer_pair(&decs.left.buffer, vertical_width, vertical_height, scale); decs.mapping.size += init_buffer_pair(&decs.bottom.buffer, horizontal_width, horizontal_height, scale); decs.mapping.size += init_buffer_pair(&decs.right.buffer, vertical_width, vertical_height, scale); @@ -347,8 +347,8 @@ ensure_csd_resources(_GLFWwindow *window) { const bool is_focused = window->id == _glfw.focusedWindowId; const bool focus_changed = is_focused != decs.for_window_state.focused; const bool size_changed = ( - decs.for_window_state.width != window->wl.current.width || - decs.for_window_state.height != window->wl.current.height || + decs.for_window_state.width != window->wl.width || + decs.for_window_state.height != window->wl.height || decs.for_window_state.scale != window->wl.scale || !decs.mapping.data ); @@ -368,11 +368,11 @@ ensure_csd_resources(_GLFWwindow *window) { if (!decs.left.surface) create_csd_surfaces(window, &decs.left); position_csd_surface(&decs.left, x, y, scale); - x = -decs.metrics.width; y = window->wl.current.height; + x = -decs.metrics.width; y = window->wl.height; if (!decs.bottom.surface) create_csd_surfaces(window, &decs.bottom); position_csd_surface(&decs.bottom, x, y, scale); - x = window->wl.current.width; y = -decs.metrics.top; + x = window->wl.width; y = -decs.metrics.top; if (!decs.right.surface) create_csd_surfaces(window, &decs.right); position_csd_surface(&decs.right, x, y, scale); @@ -382,8 +382,8 @@ ensure_csd_resources(_GLFWwindow *window) { damage_csd(bottom, is_focused ? decs.bottom.buffer.front : decs.bottom.buffer.back); damage_csd(right, is_focused ? decs.right.buffer.front : decs.right.buffer.back); - decs.for_window_state.width = window->wl.current.width; - decs.for_window_state.height = window->wl.current.height; + decs.for_window_state.width = window->wl.width; + decs.for_window_state.height = window->wl.height; decs.for_window_state.scale = window->wl.scale; decs.for_window_state.focused = is_focused; return true; diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 1fa6207f5..a43b183a9 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -206,7 +206,7 @@ static void pointerHandleMotion(void* data UNUSED, case BOTTOM_DECORATION: if (x < window->wl.decorations.metrics.width) cursorShape = GLFW_SW_RESIZE_CURSOR; - else if (x > window->wl.current.width + window->wl.decorations.metrics.width) + else if (x > window->wl.width + window->wl.decorations.metrics.width) cursorShape = GLFW_SE_RESIZE_CURSOR; else cursorShape = GLFW_VRESIZE_CURSOR; @@ -274,7 +274,7 @@ static void pointerHandleButton(void* data UNUSED, case BOTTOM_DECORATION: if (x < window->wl.decorations.metrics.width) edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else if (x > window->wl.current.width + window->wl.decorations.metrics.width) + else if (x > window->wl.width + window->wl.decorations.metrics.width) edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 7d8e72200..1aca8eacd 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -132,6 +132,7 @@ static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED // typedef struct _GLFWwindowWayland { + int width, height; bool visible; bool hovered; bool transparent; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 7bcff80db..18327e782 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -206,7 +206,7 @@ static void setOpaqueRegion(_GLFWwindow* window) if (!region) return; - wl_region_add(region, 0, 0, window->wl.current.width, window->wl.current.height); + 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); wl_region_destroy(region); @@ -216,9 +216,9 @@ static void setOpaqueRegion(_GLFWwindow* window) static void resizeFramebuffer(_GLFWwindow* window) { int scale = window->wl.scale; - int scaledWidth = window->wl.current.width * scale; - int scaledHeight = window->wl.current.height * scale; - debug("Resizing framebuffer to: %dx%d at scale: %d\n", window->wl.current.width, window->wl.current.height, scale); + int scaledWidth = window->wl.width * scale; + 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); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); @@ -236,12 +236,12 @@ clipboard_mime(void) { static void dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { - bool size_changed = width != window->wl.current.width || height != window->wl.current.height; + bool size_changed = width != window->wl.width || height != window->wl.height; bool scale_changed = checkScaleChange(window); if (size_changed) { _glfwInputWindowSize(window, width, height); - window->wl.current.width = width; window->wl.current.height = height; + window->wl.width = width; window->wl.height = height; resizeFramebuffer(window); } @@ -384,8 +384,8 @@ static bool createSurface(_GLFWwindow* window, if (!window->wl.native) return false; - window->wl.current.width = wndconfig->width; - window->wl.current.height = wndconfig->height; + window->wl.width = wndconfig->width; + window->wl.height = wndconfig->height; window->wl.user_requested_content_size.width = wndconfig->width; window->wl.user_requested_content_size.height = wndconfig->height; window->wl.scale = 1; @@ -514,6 +514,8 @@ static void xdgSurfaceHandleConfigure(void* data, window->wl.current.toplevel_states = new_states; set_csd_window_geometry(window, &width, &height); dispatchChangesAfterConfigure(window, width, height); + window->wl.current.width = width; + window->wl.current.height = height; debug("final window content size: %dx%d\n", window->wl.current.width, window->wl.current.height); _glfwInputWindowFocus(window, window->wl.current.toplevel_states & TOPLEVEL_STATE_ACTIVATED); ensure_csd_resources(window); @@ -904,19 +906,19 @@ void _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { if (width) - *width = window->wl.current.width; + *width = window->wl.width; if (height) - *height = window->wl.current.height; + *height = window->wl.height; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { - if (width != window->wl.current.width || height != window->wl.current.height) { + if (width != window->wl.width || height != window->wl.height) { window->wl.user_requested_content_size.width = width; window->wl.user_requested_content_size.height = height; int32_t w = 0, h = 0; set_csd_window_geometry(window, &w, &h); - window->wl.current.width = w; window->wl.current.height = h; + window->wl.width = w; window->wl.height = h; resizeFramebuffer(window); ensure_csd_resources(window); wl_surface_commit(window->wl.surface); From 322a80e76ec610080472cf301f34c2fe1d1678ff Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 10 Mar 2022 05:47:38 -0500 Subject: [PATCH 2/4] wayland: track configures through a bit field This will let us add more stuff later. Also, it's a better spot to put the bit field outside of the {current, pending} struct as the bit field doesn't make any sense if it's part of the `current` values. --- glfw/wl_platform.h | 5 ++++- glfw/wl_window.c | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 1aca8eacd..66f214428 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -127,6 +127,9 @@ typedef enum WaylandWindowState { static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM; +enum WaylandWindowPendingState { + PENDING_STATE_TOPLEVEL = 1, +}; // Wayland-specific per-window data // @@ -217,10 +220,10 @@ typedef struct _GLFWwindowWayland unsigned int x, y; } axis_discrete_count; + uint32_t pending_state; struct { int width, height; uint32_t toplevel_states; - bool set; } current, pending; } _GLFWwindowWayland; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 18327e782..a7b80064b 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -479,7 +479,7 @@ xdgToplevelHandleConfigure(void* data, window->wl.pending.toplevel_states = new_states; window->wl.pending.width = width; window->wl.pending.height = height; - window->wl.pending.set = true; + window->wl.pending_state |= PENDING_STATE_TOPLEVEL; } static void xdgToplevelHandleClose(void* data, @@ -500,11 +500,10 @@ static void xdgSurfaceHandleConfigure(void* data, { _GLFWwindow* window = data; xdg_surface_ack_configure(surface, serial); - if (window->wl.pending.set) { + if (window->wl.pending_state & PENDING_STATE_TOPLEVEL) { uint32_t new_states = window->wl.pending.toplevel_states; int width = window->wl.pending.width; int height = window->wl.pending.height; - window->wl.pending.set = false; if (new_states != window->wl.current.toplevel_states || width != window->wl.current.width || @@ -524,6 +523,7 @@ static void xdgSurfaceHandleConfigure(void* data, } } wl_surface_commit(window->wl.surface); + window->wl.pending_state = 0; } static const struct xdg_surface_listener xdgSurfaceListener = { From 39e75e39e8c65ad43e31bafe62d1a1c980197491 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 10 Mar 2022 06:01:50 -0500 Subject: [PATCH 3/4] wayland: Correctly handle xdg decoration configures Much like toplevel configures, xdg decoration configures only take effect once the xdg shell configure comes through. Also, let's get rid of some double computations because we unified the code paths. --- glfw/wl_platform.h | 2 ++ glfw/wl_window.c | 47 +++++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 66f214428..1e31477db 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -129,6 +129,7 @@ static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED enum WaylandWindowPendingState { PENDING_STATE_TOPLEVEL = 1, + PENDING_STATE_DECORATION = 2 }; // Wayland-specific per-window data @@ -224,6 +225,7 @@ typedef struct _GLFWwindowWayland struct { int width, height; uint32_t toplevel_states; + uint32_t decoration_mode; } current, pending; } _GLFWwindowWayland; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index a7b80064b..5ee6ca7f7 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -269,24 +269,8 @@ xdgDecorationHandleConfigure(void* data, uint32_t mode) { _GLFWwindow* window = data; - - bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - debug("XDG decoration configure event received: has_server_side_decorations: %d\n", has_server_side_decorations); - if (has_server_side_decorations == window->wl.decorations.serverSide) return; - window->wl.decorations.serverSide = has_server_side_decorations; - int width = window->wl.current.width, height = window->wl.current.height; - if (window->wl.decorations.serverSide) { - free_csd_surfaces(window); - height += window->wl.decorations.metrics.visible_titlebar_height; - } else { - ensure_csd_resources(window); - } - set_csd_window_geometry(window, &width, &height); - dispatchChangesAfterConfigure(window, width, height); - ensure_csd_resources(window); - wl_surface_commit(window->wl.surface); - debug("final window content size: %dx%d\n", window->wl.current.width, window->wl.current.height); - inform_compositor_of_window_geometry(window, "configure-decorations"); + window->wl.pending.decoration_mode = mode; + window->wl.pending_state |= PENDING_STATE_DECORATION; } static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { @@ -511,17 +495,34 @@ static void xdgSurfaceHandleConfigure(void* data, bool live_resize_done = !(new_states & TOPLEVEL_STATE_RESIZING) && (window->wl.current.toplevel_states & TOPLEVEL_STATE_RESIZING); window->wl.current.toplevel_states = new_states; - set_csd_window_geometry(window, &width, &height); - dispatchChangesAfterConfigure(window, width, height); window->wl.current.width = width; window->wl.current.height = height; - debug("final window content size: %dx%d\n", window->wl.current.width, window->wl.current.height); _glfwInputWindowFocus(window, window->wl.current.toplevel_states & TOPLEVEL_STATE_ACTIVATED); - ensure_csd_resources(window); - inform_compositor_of_window_geometry(window, "configure"); if (live_resize_done) _glfwInputLiveResize(window, false); } } + + if (window->wl.pending_state & PENDING_STATE_DECORATION) { + uint32_t mode = window->wl.pending.decoration_mode; + bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + debug("XDG decoration configure event received: has_server_side_decorations: %d\n", has_server_side_decorations); + window->wl.decorations.serverSide = has_server_side_decorations; + window->wl.current.decoration_mode = mode; + } + + if (window->wl.pending_state) { + int width = window->wl.pending.width, height = window->wl.pending.height; + set_csd_window_geometry(window, &width, &height); + dispatchChangesAfterConfigure(window, width, height); + if (window->wl.decorations.serverSide) { + free_csd_surfaces(window); + } else { + ensure_csd_resources(window); + } + debug("final window content size: %dx%d\n", width, height); + } + + inform_compositor_of_window_geometry(window, "configure"); wl_surface_commit(window->wl.surface); window->wl.pending_state = 0; } From 99a5843595779bbaa51355fc97fbd8afd8e89737 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 10 Mar 2022 06:02:22 -0500 Subject: [PATCH 4/4] wayland: Don't commit the surface on a resize This fixes weirdness with GNOME. On a resize, we will schedule a new frame anyway which will commit the wl_surface for us anyway. If we don't resize, we'll commit to stay true to the wayland spec. --- glfw/wl_window.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 5ee6ca7f7..ff611f332 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -234,7 +234,7 @@ clipboard_mime(void) { return buf; } -static void +static bool dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { bool size_changed = width != window->wl.width || height != window->wl.height; bool scale_changed = checkScaleChange(window); @@ -252,6 +252,8 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height } _glfwInputWindowDamage(window); + + return size_changed || scale_changed; } static void @@ -510,10 +512,11 @@ static void xdgSurfaceHandleConfigure(void* data, window->wl.current.decoration_mode = mode; } + bool resized = false; if (window->wl.pending_state) { int width = window->wl.pending.width, height = window->wl.pending.height; set_csd_window_geometry(window, &width, &height); - dispatchChangesAfterConfigure(window, width, height); + resized = dispatchChangesAfterConfigure(window, width, height); if (window->wl.decorations.serverSide) { free_csd_surfaces(window); } else { @@ -523,7 +526,11 @@ static void xdgSurfaceHandleConfigure(void* data, } inform_compositor_of_window_geometry(window, "configure"); - wl_surface_commit(window->wl.surface); + + if (!resized) { + wl_surface_commit(window->wl.surface); + } + window->wl.pending_state = 0; }