diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01f191c9..9596eff1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,6 +113,9 @@ if (GLFW_BUILD_WAYLAND) wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "${GLFW_BINARY_DIR}/src/wayland-idle-inhibit-unstable-v1-client-protocol") + wayland_generate( + "${WAYLAND_PROTOCOLS_BASE}/staging/fractional-scale/fractional-scale-v1.xml" + "${GLFW_BINARY_DIR}/src/wayland-fractional-scale-v1-client-protocol") endif() if (WIN32 AND GLFW_BUILD_SHARED_LIBRARY) diff --git a/src/wl_init.c b/src/wl_init.c index 4e6b4294..9fabfca0 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -48,6 +48,7 @@ #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" +#include "wayland-fractional-scale-v1-client-protocol.h" // NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of // wl_interface pointers 'types', making it impossible to combine several unmodified @@ -82,6 +83,10 @@ #include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" #undef types +#define types _glfw_fractional_scale_types +#include "wayland-fractional-scale-v1-client-protocol-code.h" +#undef types + static void wmBaseHandlePing(void* userData, struct xdg_wm_base* wmBase, uint32_t serial) @@ -180,6 +185,13 @@ static void registryHandleGlobal(void* userData, &zwp_idle_inhibit_manager_v1_interface, 1); } + else if (strcmp(interface, "wp_fractional_scale_manager_v1") == 0) + { + _glfw.wl.fractionalScaleManager = + wl_registry_bind(registry, name, + &wp_fractional_scale_manager_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void* userData, diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 3d4fcbb8..cd89ea4f 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -122,7 +122,7 @@ static void outputHandleScale(void* userData, { if (window->wl.monitors[i] == monitor) { - _glfwUpdateContentScaleWayland(window); + _glfwUpdateIntegerContentScaleWayland(window); break; } } diff --git a/src/wl_platform.h b/src/wl_platform.h index 238e1ed4..0254134c 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -212,6 +212,7 @@ typedef struct _GLFWdecorationWayland struct wl_surface* surface; struct wl_subsurface* subsurface; struct wp_viewport* viewport; + struct wp_fractional_scale_v1* fractionalScale; } _GLFWdecorationWayland; typedef struct _GLFWofferWayland @@ -262,7 +263,7 @@ typedef struct _GLFWwindowWayland // We need to track the monitors the window spans on to calculate the // optimal scaling factor. - int scale; + int integerScale; _GLFWmonitor** monitors; int monitorsCount; int monitorsSize; @@ -278,6 +279,9 @@ typedef struct _GLFWwindowWayland _GLFWdecorationWayland top, left, right, bottom; _GLFWdecorationSideWayland focus; } decorations; + + double fractionalScale; + struct wp_fractional_scale_v1* fractionalScaling; } _GLFWwindowWayland; // Wayland-specific global data @@ -300,6 +304,7 @@ typedef struct _GLFWlibraryWayland struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + struct wp_fractional_scale_manager_v1* fractionalScaleManager; _GLFWofferWayland* offers; unsigned int offerCount; @@ -514,7 +519,7 @@ GLFWbool _glfwGetGammaRampWayland(_GLFWmonitor* monitor, GLFWgammaramp* ramp); void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwAddOutputWayland(uint32_t name, uint32_t version); -void _glfwUpdateContentScaleWayland(_GLFWwindow* window); +void _glfwUpdateIntegerContentScaleWayland(_GLFWwindow* window); void _glfwAddSeatListenerWayland(struct wl_seat* seat); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); diff --git a/src/wl_window.c b/src/wl_window.c index c4d097b7..6c61eda2 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -50,6 +50,7 @@ #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" +#include "wayland-fractional-scale-v1-client-protocol.h" #define GLFW_BORDER_SIZE 4 #define GLFW_CAPTION_HEIGHT 24 @@ -192,7 +193,8 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) return buffer; } -static void createFallbackDecoration(_GLFWdecorationWayland* decoration, +static void createFallbackDecoration(_GLFWwindow *window, + _GLFWdecorationWayland* decoration, struct wl_surface* parent, struct wl_buffer* buffer, int x, int y, @@ -203,6 +205,11 @@ static void createFallbackDecoration(_GLFWdecorationWayland* decoration, wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, decoration->surface, parent); wl_subsurface_set_position(decoration->subsurface, x, y); + if (_glfw.wl.fractionalScaleManager) + { + decoration->fractionalScale = wp_fractional_scale_manager_v1_get_fractional_scale(_glfw.wl.fractionalScaleManager, decoration->surface); + wp_fractional_scale_v1_set_scale_factor(decoration->fractionalScale, (uint32_t)(window->wl.fractionalScale * (double)(1UL << 24))); + } decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter, decoration->surface); wp_viewport_set_destination(decoration->viewport, width, height); @@ -228,19 +235,19 @@ static void createFallbackDecorations(_GLFWwindow* window) if (!window->wl.decorations.buffer) return; - createFallbackDecoration(&window->wl.decorations.top, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.top, window->wl.surface, window->wl.decorations.buffer, 0, -GLFW_CAPTION_HEIGHT, window->wl.width, GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.left, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.left, window->wl.surface, window->wl.decorations.buffer, -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.right, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.right, window->wl.surface, window->wl.decorations.buffer, window->wl.width, -GLFW_CAPTION_HEIGHT, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.bottom, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.bottom, window->wl.surface, window->wl.decorations.buffer, -GLFW_BORDER_SIZE, window->wl.height, window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); @@ -306,9 +313,9 @@ static void setContentAreaOpaque(_GLFWwindow* window) static void resizeWindow(_GLFWwindow* window) { - int scale = window->wl.scale; - int scaledWidth = window->wl.width * scale; - int scaledHeight = window->wl.height * scale; + int integerScale = window->wl.integerScale; + int scaledWidth = window->wl.width * integerScale; + int scaledHeight = window->wl.height * integerScale; if (window->wl.egl.window) wl_egl_window_resize(window->wl.egl.window, scaledWidth, scaledHeight, 0, 0); @@ -319,30 +326,44 @@ static void resizeWindow(_GLFWwindow* window) if (!window->wl.decorations.top.surface) return; + const uint32_t scale_8_24 = (uint32_t)(window->wl.fractionalScale * (double)(1UL << 24)); + + wl_subsurface_set_position(window->wl.decorations.top.subsurface, + 0, -GLFW_CAPTION_HEIGHT); + if (window->wl.decorations.top.fractionalScale) + wp_fractional_scale_v1_set_scale_factor(window->wl.decorations.top.fractionalScale, scale_8_24); wp_viewport_set_destination(window->wl.decorations.top.viewport, window->wl.width, GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.top.surface); + wl_subsurface_set_position(window->wl.decorations.left.subsurface, + -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT); + if (window->wl.decorations.left.fractionalScale) + wp_fractional_scale_v1_set_scale_factor(window->wl.decorations.left.fractionalScale, scale_8_24); wp_viewport_set_destination(window->wl.decorations.left.viewport, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.left.surface); wl_subsurface_set_position(window->wl.decorations.right.subsurface, window->wl.width, -GLFW_CAPTION_HEIGHT); + if (window->wl.decorations.right.fractionalScale) + wp_fractional_scale_v1_set_scale_factor(window->wl.decorations.right.fractionalScale, scale_8_24); wp_viewport_set_destination(window->wl.decorations.right.viewport, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.right.surface); wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, -GLFW_BORDER_SIZE, window->wl.height); + if (window->wl.decorations.bottom.fractionalScale) + wp_fractional_scale_v1_set_scale_factor(window->wl.decorations.bottom.fractionalScale, scale_8_24); wp_viewport_set_destination(window->wl.decorations.bottom.viewport, window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); wl_surface_commit(window->wl.decorations.bottom.surface); } -void _glfwUpdateContentScaleWayland(_GLFWwindow* window) +void _glfwUpdateIntegerContentScaleWayland(_GLFWwindow* window) { - if (_glfw.wl.compositorVersion < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + if (_glfw.wl.compositorVersion < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION || window->wl.fractionalScaling) return; // Get the scale factor from the highest scale monitor. @@ -352,9 +373,9 @@ void _glfwUpdateContentScaleWayland(_GLFWwindow* window) maxScale = _glfw_max(window->wl.monitors[i]->wl.scale, maxScale); // Only change the framebuffer size if the scale changed. - if (window->wl.scale != maxScale) + if (window->wl.integerScale != maxScale) { - window->wl.scale = maxScale; + window->wl.integerScale = maxScale; wl_surface_set_buffer_scale(window->wl.surface, maxScale); _glfwInputWindowContentScale(window, maxScale, maxScale); resizeWindow(window); @@ -378,7 +399,7 @@ static void surfaceHandleEnter(void* userData, window->wl.monitors[window->wl.monitorsCount++] = monitor; - _glfwUpdateContentScaleWayland(window); + _glfwUpdateIntegerContentScaleWayland(window); } static void surfaceHandleLeave(void* userData, @@ -398,7 +419,7 @@ static void surfaceHandleLeave(void* userData, } window->wl.monitors[--window->wl.monitorsCount] = NULL; - _glfwUpdateContentScaleWayland(window); + _glfwUpdateIntegerContentScaleWayland(window); } static const struct wl_surface_listener surfaceListener = @@ -407,6 +428,31 @@ static const struct wl_surface_listener surfaceListener = surfaceHandleLeave }; + +void handleScaleFactor(void *userData, + struct wp_fractional_scale_v1 *wp_fractional_scale_v1, + uint32_t scale_8_24) +{ + _GLFWwindow* window = userData; + + const double oldScale = window->wl.fractionalScale; + const double newScale = scale_8_24 / (double)(1UL << 24); + if (newScale != oldScale) { + window->wl.fractionalScale = newScale; + wp_fractional_scale_v1_set_scale_factor(wp_fractional_scale_v1, scale_8_24); + _glfwInputWindowContentScale(window, newScale, newScale); + // keep the effective size of the window the same + window->wl.width = round(window->wl.width * newScale / oldScale); + window->wl.height = round(window->wl.height * newScale / oldScale); + resizeWindow(window); + } +} + +static const struct wp_fractional_scale_v1_listener scaleListener = +{ + handleScaleFactor +}; + static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) { if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager) @@ -731,16 +777,24 @@ static GLFWbool createNativeSurface(_GLFWwindow* window, window->wl.width = wndconfig->width; window->wl.height = wndconfig->height; - window->wl.scale = 1; + window->wl.integerScale = 1; window->wl.title = _glfw_strdup(wndconfig->title); window->wl.appId = _glfw_strdup(wndconfig->wl.appId); + window->wl.fractionalScale = 1; + window->wl.maximized = wndconfig->maximized; window->wl.transparent = fbconfig->transparent; if (!window->wl.transparent) setContentAreaOpaque(window); + if (_glfw.wl.fractionalScaleManager) + { + window->wl.fractionalScaling = wp_fractional_scale_manager_v1_get_fractional_scale(_glfw.wl.fractionalScaleManager, window->wl.surface); + wp_fractional_scale_v1_add_listener(window->wl.fractionalScaling, &scaleListener, window); + } + return GLFW_TRUE; } @@ -758,7 +812,7 @@ static void setCursorImage(_GLFWwindow* window, buffer = cursorWayland->buffer; else { - if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) + if (window->wl.integerScale > 1 && cursorWayland->cursorHiDPI) { wlCursor = cursorWayland->cursorHiDPI; scale = 2; @@ -1106,7 +1160,7 @@ static void setCursor(_GLFWwindow* window, const char* name) struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; int scale = 1; - if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + if (window->wl.integerScale > 1 && _glfw.wl.cursorThemeHiDPI) { // We only support up to scale=2 for now, since libwayland-cursor // requires us to load a different theme for each size. @@ -1995,10 +2049,13 @@ void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom) void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height) { _glfwGetWindowSizeWayland(window, width, height); - if (width) - *width *= window->wl.scale; - if (height) - *height *= window->wl.scale; + if (!window->wl.fractionalScaling) + { + if (width) + *width *= window->wl.integerScale; + if (height) + *height *= window->wl.integerScale; + } } void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, @@ -2021,10 +2078,17 @@ void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, void _glfwGetWindowContentScaleWayland(_GLFWwindow* window, float* xscale, float* yscale) { - if (xscale) - *xscale = (float) window->wl.scale; - if (yscale) - *yscale = (float) window->wl.scale; + if (window->wl.fractionalScaling) { + if (xscale) + *xscale = window->wl.fractionalScale; + if (yscale) + *yscale = window->wl.fractionalScale; + } else { + if (xscale) + *xscale = (float) window->wl.integerScale; + if (yscale) + *yscale = (float) window->wl.integerScale; + } } void _glfwIconifyWindowWayland(_GLFWwindow* window) @@ -2467,13 +2531,13 @@ static void relativePointerHandleRelativeMotion(void* userData, if (window->rawMouseMotion) { - xpos += wl_fixed_to_double(dxUnaccel); - ypos += wl_fixed_to_double(dyUnaccel); + xpos += wl_fixed_to_double(dxUnaccel) / window->wl.fractionalScale; + ypos += wl_fixed_to_double(dyUnaccel) / window->wl.fractionalScale; } else { - xpos += wl_fixed_to_double(dx); - ypos += wl_fixed_to_double(dy); + xpos += wl_fixed_to_double(dx) / window->wl.fractionalScale; + ypos += wl_fixed_to_double(dy) / window->wl.fractionalScale; } _glfwInputCursorPos(window, xpos, ypos);