From a9cc7c7260c32068a5374ce9d59515df540de970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 7 Feb 2024 20:04:14 +0100 Subject: [PATCH] Add GLFW_SCALE_FRAMEBUFFER window hint This adds the GLFW_SCALE_FRAMEBUFFER window hint, enabling control of framebuffer scaling across Wayland and macOS. On macOS, this window hint is a new name for GLFW_COCOA_RETINA_FRAMEBUFFER, and both hint names will modify the same hint. This is now a more symmetric counterpart to GLFW_SCALE_TO_MONITOR and, weirdly, they each apply neatly to half of the supported platforms. This commit is mostly documentation updates to better integrate and contrast these two scaling mechanisms. --- README.md | 1 + docs/monitor.md | 12 ++------ docs/news.md | 17 ++++++++++++ docs/window.md | 65 +++++++++++++++++++++++++++++++------------- include/GLFW/glfw3.h | 13 +++++++-- src/cocoa_platform.h | 2 +- src/cocoa_window.m | 6 ++-- src/internal.h | 2 +- src/nsgl_context.m | 2 +- src/window.c | 11 ++++---- src/wl_platform.h | 1 + src/wl_window.c | 4 +++ 12 files changed, 92 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index b7758557..a05f62c0 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ information on what to include when reporting a bug. content area (#58) - Added `GLFW_POSITION_X` and `GLFW_POSITION_Y` window hints for initial position (#1603,#1747) + - Added `GLFW_SCALE_FRAMEBUFFER` window hint for Wayland and macOS scaling (#2457) - Added `GLFW_ANY_POSITION` hint value for letting the window manager choose (#1603,#1747) - Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958) - Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692) diff --git a/docs/monitor.md b/docs/monitor.md index d6d83cb3..12d98541 100644 --- a/docs/monitor.md +++ b/docs/monitor.md @@ -150,16 +150,8 @@ float xscale, yscale; glfwGetMonitorContentScale(monitor, &xscale, &yscale); ``` -The content scale is the ratio between the current DPI and the platform's -default DPI. This is especially important for text and any UI elements. If the -pixel dimensions of your UI scaled by this look appropriate on your machine then -it should appear at a reasonable size on other machines regardless of their DPI -and scaling settings. This relies on the system DPI and scaling settings being -somewhat correct. - -The content scale may depend on both the monitor resolution and pixel density -and on user settings. It may be very different from the raw DPI calculated from -the physical size and current resolution. +For more information on what the content scale is and how to use it, see +[window content scale](@ref window_scale). ### Virtual position {#monitor_pos} diff --git a/docs/news.md b/docs/news.md index 0dbbd5ad..7842ddf5 100644 --- a/docs/news.md +++ b/docs/news.md @@ -85,6 +85,22 @@ function pointers corresponding to the standard library functions `malloc`, For more information see @ref init_allocator. +#### Window hint for framebuffer scaling {#scale_framebuffer_34} + +GLFW now allows provides the +[GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) window hint for +controlling framebuffer scaling on platforms that handle scaling by keeping the +window size the same while resizing the framebuffer. The default value is to +allow framebuffer scaling. + +This was already possible on macOS via the +[GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) window +hint. This hint is now another name for +[GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint). + +For more information, see @ref window_scale. + + #### Window hints for initial position {#features_34_position_hint} GLFW now provides the @ref GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints for @@ -288,6 +304,7 @@ then GLFW will fail to initialize. - @ref GLFW_WAYLAND_LIBDECOR - @ref GLFW_WAYLAND_PREFER_LIBDECOR - @ref GLFW_WAYLAND_DISABLE_LIBDECOR + - @ref GLFW_SCALE_FRAMEBUFFER ## Release notes for earlier versions {#news_archive} diff --git a/docs/window.md b/docs/window.md index ecb9c359..f4e153c3 100644 --- a/docs/window.md +++ b/docs/window.md @@ -239,13 +239,28 @@ focus when @ref glfwShowWindow is called. Possible values are `GLFW_TRUE` and @anchor GLFW_SCALE_TO_MONITOR __GLFW_SCALE_TO_MONITOR__ specified whether the window content area should be -resized based on the [monitor content scale](@ref monitor_scale) of any monitor -it is placed on. This includes the initial placement when the window is -created. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. +resized based on [content scale](@ref window_scale) changes. This can be +because of a global user settings change or because the window was moved to +a monitor with different scale settings. This hint only has an effect on platforms where screen coordinates and pixels -always map 1:1 such as Windows and X11. On platforms like macOS the resolution -of the framebuffer is changed independently of the window size. +always map 1:1, such as Windows and X11. On platforms like macOS the resolution +of the framebuffer can change independently of the window size. + +@anchor GLFW_SCALE_FRAMEBUFFER_hint +@anchor GLFW_COCOA_RETINA_FRAMEBUFFER_hint +__GLFW_SCALE_FRAMEBUFFER__ specifies whether the framebuffer should be resized +based on [content scale](@ref window_scale) changes. This can be +because of a global user settings change or because the window was moved to +a monitor with different scale settings. + +This hint only has an effect on platforms where screen coordinates can be scaled +relative to pixel coordinates, such as macOS and Wayland. On platforms like +Windows and X11 the framebuffer and window content area sizes always map 1:1. + +This is the new name, introduced in GLFW 3.4. The older +`GLFW_COCOA_RETINA_FRAMEBUFFER` name is also available for compatibility. Both +names modify the same hint value. @anchor GLFW_MOUSE_PASSTHROUGH_hint __GLFW_MOUSE_PASSTHROUGH__ specifies whether the window is transparent to mouse @@ -474,11 +489,6 @@ GLFW behaves as if this hint was set to `GLFW_FALSE`. Possible values are #### macOS specific hints {#window_hints_osx} -@anchor GLFW_COCOA_RETINA_FRAMEBUFFER_hint -__GLFW_COCOA_RETINA_FRAMEBUFFER__ specifies whether to use full resolution -framebuffers on Retina displays. Possible values are `GLFW_TRUE` and -`GLFW_FALSE`. This is ignored on other platforms. - @anchor GLFW_COCOA_FRAME_NAME_hint __GLFW_COCOA_FRAME_NAME__ specifies the UTF-8 encoded name to use for autosaving the window frame, or if empty disables frame autosaving for the window. This is @@ -533,6 +543,7 @@ GLFW_CENTER_CURSOR | `GLFW_TRUE` | `GLFW_TRUE` or `GL GLFW_TRANSPARENT_FRAMEBUFFER | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_FOCUS_ON_SHOW | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_SCALE_TO_MONITOR | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +GLFW_SCALE_FRAMEBUFFER | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_MOUSE_PASSTHROUGH | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_POSITION_X | `GLFW_ANY_POSITION` | Any valid screen x-coordinate or `GLFW_ANY_POSITION` GLFW_POSITION_Y | `GLFW_ANY_POSITION` | Any valid screen y-coordinate or `GLFW_ANY_POSITION` @@ -563,7 +574,6 @@ GLFW_CONTEXT_DEBUG | `GLFW_FALSE` | `GLFW_TRUE` or `GL GLFW_OPENGL_PROFILE | `GLFW_OPENGL_ANY_PROFILE` | `GLFW_OPENGL_ANY_PROFILE`, `GLFW_OPENGL_COMPAT_PROFILE` or `GLFW_OPENGL_CORE_PROFILE` GLFW_WIN32_KEYBOARD_MENU | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_WIN32_SHOWDEFAULT | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` -GLFW_COCOA_RETINA_FRAMEBUFFER | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_COCOA_FRAME_NAME | `""` | A UTF-8 encoded frame autosave name GLFW_COCOA_GRAPHICS_SWITCHING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_X11_CLASS_NAME | `""` | An ASCII encoded `WM_CLASS` class name @@ -736,16 +746,21 @@ float xscale, yscale; glfwGetWindowContentScale(window, &xscale, &yscale); ``` -The content scale is the ratio between the current DPI and the platform's -default DPI. This is especially important for text and any UI elements. If the -pixel dimensions of your UI scaled by this look appropriate on your machine then -it should appear at a reasonable size on other machines regardless of their DPI -and scaling settings. This relies on the system DPI and scaling settings being -somewhat correct. +The content scale can be thought of as the ratio between the current DPI and the +platform's default DPI. It is intended to be a scaling factor to apply to the +pixel dimensions of text and other UI elements. If the dimensions scaled by +this factor looks appropriate on your machine then it should appear at +a reasonable size on other machines with different DPI and scaling settings. + +This relies on the DPI and scaling settings on both machines being appropriate. + +The content scale may depend on both the monitor resolution and pixel density +and on user settings like DPI or a scaling percentage. It may be very different +from the raw DPI calculated from the physical size and current resolution. On systems where each monitors can have its own content scale, the window -content scale will depend on which monitor the system considers the window to be -on. +content scale will depend on which monitor or monitors the system considers the +window to be "on". If you wish to be notified when the content scale of a window changes, whether because of a system setting change or because it was moved to a monitor with @@ -770,6 +785,18 @@ with a different content scale. To have this done automatically both when the window is created and when its content scale later changes, set the @ref GLFW_SCALE_TO_MONITOR window hint. +On platforms where pixels do not necessarily equal screen coordinates, the +framebuffer will instead need to be sized to provide a full resolution image +for the window. When the window moves between monitors with different content +scales, the window size will remain the same but the framebuffer size will +change. This is done automatically by default. To disable this resizing, set +the @ref GLFW_SCALE_FRAMEBUFFER window hint. + +Both of these hints also apply when the window is created. Every window starts +out with a content scale of one. A window with one or both of these hints set +will adapt to the appropriate scale in the process of being created, set up and +shown. + ### Window size limits {#window_sizelimits} diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 84fa49e2..66edf4fc 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1098,8 +1098,15 @@ extern "C" { * [window hint](@ref GLFW_SCALE_TO_MONITOR). */ #define GLFW_SCALE_TO_MONITOR 0x0002200C -/*! @brief macOS specific - * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). +/*! @brief Window framebuffer scaling + * [window hint](@ref GLFW_SCALE_FRAMEBUFFER_hint). + */ +#define GLFW_SCALE_FRAMEBUFFER 0x0002200D +/*! @brief Legacy name for compatibility. + * + * This is an alias for the + * [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) window hint for + * compatibility with earlier versions. */ #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 /*! @brief macOS specific @@ -3167,7 +3174,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * * @remark @macos On OS X 10.10 and later the window frame will not be rendered * at full resolution on Retina displays unless the - * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the * application bundle's `Info.plist`. For more information, see * [High Resolution Guidelines for OS X][hidpi-guide] in the Mac Developer diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 9f7d191d..3d424ee7 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -145,7 +145,7 @@ typedef struct _GLFWwindowNS GLFWbool maximized; GLFWbool occluded; - GLFWbool retina; + GLFWbool scaleFramebuffer; // Cached window properties to filter out duplicate events int width, height; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index f6639646..7e90cf99 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -513,7 +513,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; if (xscale != window->ns.xscale || yscale != window->ns.yscale) { - if (window->ns.retina && window->ns.layer) + if (window->ns.scaleFramebuffer && window->ns.layer) [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; window->ns.xscale = xscale; @@ -872,7 +872,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)]; window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; - window->ns.retina = wndconfig->ns.retina; + window->ns.scaleFramebuffer = wndconfig->scaleFramebuffer; if (fbconfig->transparent) { @@ -1969,7 +1969,7 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, return VK_ERROR_EXTENSION_NOT_PRESENT; } - if (window->ns.retina) + if (window->ns.scaleFramebuffer) [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; [window->ns.view setLayer:window->ns.layer]; diff --git a/src/internal.h b/src/internal.h index caf93675..c6c856f1 100644 --- a/src/internal.h +++ b/src/internal.h @@ -402,8 +402,8 @@ struct _GLFWwndconfig GLFWbool focusOnShow; GLFWbool mousePassthrough; GLFWbool scaleToMonitor; + GLFWbool scaleFramebuffer; struct { - GLFWbool retina; char frameName[256]; } ns; struct { diff --git a/src/nsgl_context.m b/src/nsgl_context.m index e838caa2..d19622c6 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -333,7 +333,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, forParameter:NSOpenGLContextParameterSurfaceOpacity]; } - [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; + [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.scaleFramebuffer]; [window->context.nsgl.object setView:window->ns.view]; diff --git a/src/window.c b/src/window.c index 0e279336..3b8d706e 100644 --- a/src/window.c +++ b/src/window.c @@ -274,6 +274,7 @@ void glfwDefaultWindowHints(void) _glfw.hints.window.focusOnShow = GLFW_TRUE; _glfw.hints.window.xpos = GLFW_ANY_POSITION; _glfw.hints.window.ypos = GLFW_ANY_POSITION; + _glfw.hints.window.scaleFramebuffer = GLFW_TRUE; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered @@ -288,9 +289,6 @@ void glfwDefaultWindowHints(void) // The default is to select the highest available refresh rate _glfw.hints.refreshRate = GLFW_DONT_CARE; - - // The default is to use full Retina resolution framebuffers - _glfw.hints.window.ns.retina = GLFW_TRUE; } GLFWAPI void glfwWindowHint(int hint, int value) @@ -374,9 +372,6 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_POSITION_Y: _glfw.hints.window.ypos = value; return; - case GLFW_COCOA_RETINA_FRAMEBUFFER: - _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; - return; case GLFW_WIN32_KEYBOARD_MENU: _glfw.hints.window.win32.keymenu = value ? GLFW_TRUE : GLFW_FALSE; return; @@ -389,6 +384,10 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_SCALE_TO_MONITOR: _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_SCALE_FRAMEBUFFER: + case GLFW_COCOA_RETINA_FRAMEBUFFER: + _glfw.hints.window.scaleFramebuffer = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_CENTER_CURSOR: _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; return; diff --git a/src/wl_platform.h b/src/wl_platform.h index 3b9a3a3c..cb9b170f 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -355,6 +355,7 @@ typedef struct _GLFWwindowWayland GLFWbool fullscreen; GLFWbool hovered; GLFWbool transparent; + GLFWbool scaleFramebuffer; struct wl_surface* surface; struct wl_callback* callback; diff --git a/src/wl_window.c b/src/wl_window.c index 06b0256f..0a1554d1 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -369,6 +369,9 @@ void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window) return; } + if (!window->wl.scaleFramebuffer) + return; + // Get the scale factor from the highest scale monitor. int32_t maxScale = 1; @@ -980,6 +983,7 @@ static GLFWbool createNativeSurface(_GLFWwindow* window, window->wl.bufferScale = 1; window->wl.title = _glfw_strdup(wndconfig->title); window->wl.appId = _glfw_strdup(wndconfig->wl.appId); + window->wl.scaleFramebuffer = wndconfig->scaleFramebuffer; window->wl.maximized = wndconfig->maximized;