From 57504385e09012ef0eb645ee58d6945304a9e894 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 28 May 2024 17:59:04 -0400 Subject: [PATCH] wayland: Maintain aspect ratio while resizing the window Note that Wayland places a restriction on windows being resized, where the requested size passed to the configuration event is a maximum, and attempting to exceed it is a protocol violation, so trying to grow the window by dragging the sides only vertically or horizontally is limited, as the provided dimensions can't be exceeded. In practice, nothing seems to kill clients that do this, but the more immediate problem is that doing so causes GNOME to glitch out. --- src/video/wayland/SDL_waylandwindow.c | 66 +++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 61d8b64c14..19c6897e1c 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -818,11 +818,18 @@ static void handle_configure_xdg_toplevel(void *data, } } - /* The content limits are only a hint, which the compositor is free to ignore, - * so apply them manually when appropriate. + /* Notes on the spec: * - * Per the spec, maximized windows must have their exact dimensions respected, - * thus they must not be resized, or a protocol violation can occur. + * - The content limits are only a hint, which the compositor is free to ignore, + * so apply them manually when appropriate. + * + * - Maximized windows must have their exact dimensions respected, thus they must + * not be resized, or a protocol violation can occur. + * + * - When resizing a window, the width/height are maximum values, so aspect ratio + * correction can't resize beyond the existing dimensions, or a protocol violation + * can occur. In practice, nothing seems to kill clients that do this, but doing + * so causes GNOME to glitch out. */ if (!maximized) { if (!wind->scale_to_display) { @@ -835,6 +842,15 @@ static void handle_configure_xdg_toplevel(void *data, wind->requested.logical_height = SDL_min(wind->requested.logical_height, window->max_h); } wind->requested.logical_height = SDL_max(wind->requested.logical_height, window->min_h); + + /* Aspect correction. */ + const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height; + + if (window->min_aspect && aspect < window->min_aspect) { + wind->requested.logical_height = SDL_roundf((float)wind->requested.logical_width / window->min_aspect); + } else if (window->max_aspect && aspect > window->max_aspect) { + wind->requested.logical_width = SDL_roundf((float)wind->requested.logical_height * window->max_aspect); + } } else { if (window->max_w > 0) { wind->requested.pixel_width = SDL_min(wind->requested.pixel_width, window->max_w); @@ -846,6 +862,15 @@ static void handle_configure_xdg_toplevel(void *data, } wind->requested.pixel_height = SDL_max(wind->requested.pixel_height, window->min_h); + /* Aspect correction. */ + const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height; + + if (window->min_aspect && aspect < window->min_aspect) { + wind->requested.pixel_height = SDL_roundf((float)wind->requested.pixel_width / window->min_aspect); + } else if (window->max_aspect && aspect > window->max_aspect) { + wind->requested.pixel_width = SDL_roundf((float)wind->requested.pixel_height * window->max_aspect); + } + wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width); wind->requested.logical_height = PixelToPoint(window, wind->requested.pixel_height); } @@ -1175,11 +1200,18 @@ static void decoration_frame_configure(struct libdecor_frame *frame, } } - /* The content limits are only a hint, which the compositor is free to ignore, - * so apply them manually when appropriate. + /* Notes on the spec: * - * Per the spec, maximized windows must have their exact dimensions respected, - * thus they must not be resized, or a protocol violation can occur. + * - The content limits are only a hint, which the compositor is free to ignore, + * so apply them manually when appropriate. + * + * - Maximized windows must have their exact dimensions respected, thus they must + * not be resized, or a protocol violation can occur. + * + * - When resizing a window, the width/height are maximum values, so aspect ratio + * correction can't resize beyond the existing dimensions, or a protocol violation + * can occur. In practice, nothing seems to kill clients that do this, but doing + * so causes GNOME to glitch out. */ if (!maximized) { if (!wind->scale_to_display) { @@ -1192,6 +1224,15 @@ static void decoration_frame_configure(struct libdecor_frame *frame, wind->requested.logical_height = SDL_min(wind->requested.logical_height, window->max_h); } wind->requested.logical_height = SDL_max(wind->requested.logical_height, window->min_h); + + /* Aspect correction. */ + const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height; + + if (window->min_aspect && aspect < window->min_aspect) { + wind->requested.logical_height = SDL_roundf((float)wind->requested.logical_width / window->min_aspect); + } else if (window->max_aspect && aspect > window->max_aspect) { + wind->requested.logical_width = SDL_roundf((float)wind->requested.logical_height * window->max_aspect); + } } else { if (window->max_w > 0) { wind->requested.pixel_width = SDL_min(wind->requested.pixel_width, window->max_w); @@ -1203,6 +1244,15 @@ static void decoration_frame_configure(struct libdecor_frame *frame, } wind->requested.pixel_height = SDL_max(wind->requested.pixel_height, window->min_h); + /* Aspect correction. */ + const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height; + + if (window->min_aspect && aspect < window->min_aspect) { + wind->requested.pixel_height = SDL_roundf((float)wind->requested.pixel_width / window->min_aspect); + } else if (window->max_aspect && aspect > window->max_aspect) { + wind->requested.pixel_width = SDL_roundf((float)wind->requested.pixel_height * window->max_aspect); + } + wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width); wind->requested.logical_height = PixelToPoint(window, wind->requested.pixel_height); }