2
0
Эх сурвалжийг харах

Wayland: Add support for fractional scaling

This adds basic support for fractional-scale-v1.

Note that this introduces a potential discrepancy between window and
monitor content scales.
Camilla Löwy 1 жил өмнө
parent
commit
2b3f919b60

+ 1 - 0
README.md

@@ -182,6 +182,7 @@ information on what to include when reporting a bug.
    would abort (#1649)
  - [Wayland] Added support for `glfwRequestWindowAttention` (#2287)
  - [Wayland] Added support for `glfwFocusWindow`
+ - [Wayland] Added support for fractional scaling of window contents
  - [Wayland] Added dynamic loading of all Wayland libraries
  - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled
  - [Wayland] Bugfix: `GLFW_HOVERED` was true when the cursor was over any

+ 102 - 0
deps/wayland/fractional-scale-v1.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="fractional_scale_v1">
+  <copyright>
+    Copyright © 2022 Kenny Levinsen
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <description summary="Protocol for requesting fractional surface scales">
+    This protocol allows a compositor to suggest for surfaces to render at
+    fractional scales.
+
+    A client can submit scaled content by utilizing wp_viewport. This is done by
+    creating a wp_viewport object for the surface and setting the destination
+    rectangle to the surface size before the scale factor is applied.
+
+    The buffer size is calculated by multiplying the surface size by the
+    intended scale.
+
+    The wl_surface buffer scale should remain set to 1.
+
+    If a surface has a surface-local size of 100 px by 50 px and wishes to
+    submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should
+    be used and the wp_viewport destination rectangle should be 100 px by 50 px.
+
+    For toplevel surfaces, the size is rounded halfway away from zero. The
+    rounding algorithm for subsurface position and size is not defined.
+  </description>
+
+  <interface name="wp_fractional_scale_manager_v1" version="1">
+    <description summary="fractional surface scale information">
+      A global interface for requesting surfaces to use fractional scales.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="unbind the fractional surface scale interface">
+        Informs the server that the client will not be using this protocol
+        object anymore. This does not affect any other objects,
+        wp_fractional_scale_v1 objects included.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="fractional_scale_exists" value="0"
+        summary="the surface already has a fractional_scale object associated"/>
+    </enum>
+
+    <request name="get_fractional_scale">
+      <description summary="extend surface interface for scale information">
+        Create an add-on object for the the wl_surface to let the compositor
+        request fractional scales. If the given wl_surface already has a
+        wp_fractional_scale_v1 object associated, the fractional_scale_exists
+        protocol error is raised.
+      </description>
+      <arg name="id" type="new_id" interface="wp_fractional_scale_v1"
+           summary="the new surface scale info interface id"/>
+      <arg name="surface" type="object" interface="wl_surface"
+           summary="the surface"/>
+    </request>
+  </interface>
+
+  <interface name="wp_fractional_scale_v1" version="1">
+    <description summary="fractional scale interface to a wl_surface">
+      An additional interface to a wl_surface object which allows the compositor
+      to inform the client of the preferred scale.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove surface scale information for surface">
+        Destroy the fractional scale object. When this object is destroyed,
+        preferred_scale events will no longer be sent.
+      </description>
+    </request>
+
+    <event name="preferred_scale">
+      <description summary="notify of new preferred scale">
+        Notification of a new preferred scale for this surface that the
+        compositor suggests that the client should use.
+
+        The sent scale is the numerator of a fraction with a denominator of 120.
+      </description>
+      <arg name="scale" type="uint" summary="the new preferred scale"/>
+    </event>
+  </interface>
+</protocol>

+ 8 - 0
docs/compat.md

@@ -150,6 +150,14 @@ window focus and attention requests do nothing.
 
 [xdg-activation-v1]: https://wayland.app/protocols/xdg-activation-v1
 
+GLFW uses the [fractional-scale-v1][] protocol to implement fine-grained
+framebuffer scaling.  If the running compositor does not support this protocol,
+the @ref GLFW_SCALE_FRAMEBUFFER window hint will only be able to scale the
+framebuffer by integer scales.  This will typically be the smallest integer not
+less than the actual scale.
+
+[fractional-scale-v1]: https://wayland.app/protocols/fractional-scale-v1
+
 
 ## GLX extensions {#compat_glx}
 

+ 3 - 0
include/GLFW/glfw3.h

@@ -2705,6 +2705,9 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int*
  *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
  *  GLFW_PLATFORM_ERROR.
  *
+ *  @remark @wayland Fractional scaling information is not yet available for
+ *  monitors, so this function only returns integer content scales.
+ *
  *  @thread_safety This function must only be called from the main thread.
  *
  *  @sa @ref monitor_scale

+ 1 - 0
src/CMakeLists.txt

@@ -101,6 +101,7 @@ if (GLFW_BUILD_WAYLAND)
     generate_wayland_protocol("idle-inhibit-unstable-v1.xml")
     generate_wayland_protocol("pointer-constraints-unstable-v1.xml")
     generate_wayland_protocol("relative-pointer-unstable-v1.xml")
+    generate_wayland_protocol("fractional-scale-v1.xml")
     generate_wayland_protocol("xdg-activation-v1.xml")
     generate_wayland_protocol("xdg-decoration-unstable-v1.xml")
 endif()

+ 14 - 0
src/wl_init.c

@@ -46,6 +46,7 @@
 #include "viewporter-client-protocol.h"
 #include "relative-pointer-unstable-v1-client-protocol.h"
 #include "pointer-constraints-unstable-v1-client-protocol.h"
+#include "fractional-scale-v1-client-protocol.h"
 #include "xdg-activation-v1-client-protocol.h"
 #include "idle-inhibit-unstable-v1-client-protocol.h"
 
@@ -78,6 +79,10 @@
 #include "pointer-constraints-unstable-v1-client-protocol-code.h"
 #undef types
 
+#define types _glfw_fractional_scale_types
+#include "fractional-scale-v1-client-protocol-code.h"
+#undef types
+
 #define types _glfw_xdg_activation_types
 #include "xdg-activation-v1-client-protocol-code.h"
 #undef types
@@ -189,6 +194,13 @@ static void registryHandleGlobal(void* userData,
                              &xdg_activation_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,
@@ -969,6 +981,8 @@ void _glfwTerminateWayland(void)
         zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
     if (_glfw.wl.activationManager)
         xdg_activation_v1_destroy(_glfw.wl.activationManager);
+    if (_glfw.wl.fractionalScaleManager)
+        wp_fractional_scale_manager_v1_destroy(_glfw.wl.fractionalScaleManager);
     if (_glfw.wl.registry)
         wl_registry_destroy(_glfw.wl.registry);
     if (_glfw.wl.display)

+ 7 - 0
src/wl_platform.h

@@ -131,6 +131,8 @@ struct wl_output;
 #define xdg_wm_base_interface _glfw_xdg_wm_base_interface
 #define xdg_activation_v1_interface _glfw_xdg_activation_v1_interface
 #define xdg_activation_token_v1_interface _glfw_xdg_activation_token_v1_interface
+#define wl_surface_interface _glfw_wl_surface_interface
+#define wp_fractional_scale_v1_interface _glfw_wp_fractional_scale_v1_interface
 
 #define GLFW_WAYLAND_WINDOW_STATE         _GLFWwindowWayland  wl;
 #define GLFW_WAYLAND_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl;
@@ -395,6 +397,10 @@ typedef struct _GLFWwindowWayland
     size_t                      outputScaleCount;
     size_t                      outputScaleSize;
 
+    struct wp_viewport*             scalingViewport;
+    uint32_t                        scalingNumerator;
+    struct wp_fractional_scale_v1*  fractionalScale;
+
     struct zwp_relative_pointer_v1* relativePointer;
     struct zwp_locked_pointer_v1*   lockedPointer;
     struct zwp_confined_pointer_v1* confinedPointer;
@@ -431,6 +437,7 @@ typedef struct _GLFWlibraryWayland
     struct zwp_pointer_constraints_v1*      pointerConstraints;
     struct zwp_idle_inhibit_manager_v1*     idleInhibitManager;
     struct xdg_activation_v1*               activationManager;
+    struct wp_fractional_scale_manager_v1*  fractionalScaleManager;
 
     _GLFWofferWayland*          offers;
     unsigned int                offerCount;

+ 80 - 7
src/wl_window.c

@@ -50,6 +50,7 @@
 #include "pointer-constraints-unstable-v1-client-protocol.h"
 #include "xdg-activation-v1-client-protocol.h"
 #include "idle-inhibit-unstable-v1-client-protocol.h"
+#include "fractional-scale-v1-client-protocol.h"
 
 #define GLFW_BORDER_SIZE    4
 #define GLFW_CAPTION_HEIGHT 24
@@ -312,8 +313,16 @@ static void setContentAreaOpaque(_GLFWwindow* window)
 
 static void resizeFramebuffer(_GLFWwindow* window)
 {
-    window->wl.fbWidth = window->wl.width * window->wl.bufferScale;
-    window->wl.fbHeight = window->wl.height * window->wl.bufferScale;
+    if (window->wl.fractionalScale)
+    {
+        window->wl.fbWidth = (window->wl.width * window->wl.scalingNumerator) / 120;
+        window->wl.fbHeight = (window->wl.height * window->wl.scalingNumerator) / 120;
+    }
+    else
+    {
+        window->wl.fbWidth = window->wl.width * window->wl.bufferScale;
+        window->wl.fbHeight = window->wl.height * window->wl.bufferScale;
+    }
 
     if (window->wl.egl.window)
     {
@@ -333,6 +342,13 @@ static void resizeWindow(_GLFWwindow* window)
 {
     resizeFramebuffer(window);
 
+    if (window->wl.scalingViewport)
+    {
+        wp_viewport_set_destination(window->wl.scalingViewport,
+                                    window->wl.width,
+                                    window->wl.height);
+    }
+
     if (window->wl.fallback.decorations)
     {
         wp_viewport_set_destination(window->wl.fallback.top.viewport,
@@ -372,6 +388,10 @@ void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window)
     if (!window->wl.scaleFramebuffer)
         return;
 
+    // When using fractional scaling, the buffer scale should remain at 1
+    if (window->wl.fractionalScale)
+        return;
+
     // Get the scale factor from the highest scale monitor.
     int32_t maxScale = 1;
 
@@ -505,6 +525,25 @@ static void releaseMonitor(_GLFWwindow* window)
     }
 }
 
+void fractionalScaleHandlePreferredScale(void* userData,
+                                         struct wp_fractional_scale_v1* fractionalScale,
+                                         uint32_t numerator)
+{
+    _GLFWwindow* window = userData;
+
+    window->wl.scalingNumerator = numerator;
+    _glfwInputWindowContentScale(window, numerator / 120.f, numerator / 120.f);
+    resizeFramebuffer(window);
+
+    if (window->wl.visible)
+        _glfwInputWindowDamage(window);
+}
+
+const struct wp_fractional_scale_v1_listener fractionalScaleListener =
+{
+    fractionalScaleHandlePreferredScale,
+};
+
 static void xdgToplevelHandleConfigure(void* userData,
                                        struct xdg_toplevel* toplevel,
                                        int32_t width,
@@ -980,9 +1019,11 @@ static GLFWbool createNativeSurface(_GLFWwindow* window,
     window->wl.height = wndconfig->height;
     window->wl.fbWidth = wndconfig->width;
     window->wl.fbHeight = wndconfig->height;
-    window->wl.bufferScale = 1;
     window->wl.title = _glfw_strdup(wndconfig->title);
     window->wl.appId = _glfw_strdup(wndconfig->wl.appId);
+
+    window->wl.bufferScale = 1;
+    window->wl.scalingNumerator = 120;
     window->wl.scaleFramebuffer = wndconfig->scaleFramebuffer;
 
     window->wl.maximized = wndconfig->maximized;
@@ -991,6 +1032,28 @@ static GLFWbool createNativeSurface(_GLFWwindow* window,
     if (!window->wl.transparent)
         setContentAreaOpaque(window);
 
+    if (_glfw.wl.fractionalScaleManager)
+    {
+        if (window->wl.scaleFramebuffer)
+        {
+            window->wl.scalingViewport =
+                wp_viewporter_get_viewport(_glfw.wl.viewporter, window->wl.surface);
+
+            wp_viewport_set_destination(window->wl.scalingViewport,
+                                        window->wl.width,
+                                        window->wl.height);
+
+            window->wl.fractionalScale =
+                wp_fractional_scale_manager_v1_get_fractional_scale(
+                    _glfw.wl.fractionalScaleManager,
+                    window->wl.surface);
+
+            wp_fractional_scale_v1_add_listener(window->wl.fractionalScale,
+                                                &fractionalScaleListener,
+                                                window);
+        }
+    }
+
     return GLFW_TRUE;
 }
 
@@ -2315,10 +2378,20 @@ void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window,
 void _glfwGetWindowContentScaleWayland(_GLFWwindow* window,
                                        float* xscale, float* yscale)
 {
-    if (xscale)
-        *xscale = (float) window->wl.bufferScale;
-    if (yscale)
-        *yscale = (float) window->wl.bufferScale;
+    if (window->wl.fractionalScale)
+    {
+        if (xscale)
+            *xscale = (float) window->wl.scalingNumerator / 120.f;
+        if (yscale)
+            *yscale = (float) window->wl.scalingNumerator / 120.f;
+    }
+    else
+    {
+        if (xscale)
+            *xscale = (float) window->wl.bufferScale;
+        if (yscale)
+            *yscale = (float) window->wl.bufferScale;
+    }
 }
 
 void _glfwIconifyWindowWayland(_GLFWwindow* window)