From 16bf872117896fb88493403157fcd367303fd1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 29 Aug 2017 19:19:00 +0200 Subject: [PATCH] Add content scale queries This adds glfwGetWindowContentScale and glfwGetMonitorContentScale for querying the recommended drawing scale factor for DPI-aware rendering. Parts of this patch are based on code by @ferreiradaselva. Fixes #235. Fixes #439. Fixes #677. Fixes #845. Fixes #898. --- README.md | 2 ++ docs/monitor.dox | 27 ++++++++++++++++---- docs/news.dox | 9 +++++++ docs/window.dox | 20 +++++++++++++++ include/GLFW/glfw3.h | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/cocoa_monitor.m | 45 +++++++++++++++++++++++++++++++++ src/cocoa_platform.h | 1 + src/cocoa_window.m | 12 +++++++++ src/internal.h | 2 ++ src/mir_monitor.c | 9 +++++++ src/mir_window.c | 9 +++++++ src/monitor.c | 15 +++++++++++ src/null_monitor.c | 9 +++++++ src/null_window.c | 9 +++++++ src/win32_init.c | 2 ++ src/win32_monitor.c | 50 ++++++++++++++++++++++++++++++------ src/win32_platform.h | 11 ++++++++ src/win32_window.c | 21 ++++++---------- src/window.c | 15 +++++++++++ src/wl_monitor.c | 9 +++++++ src/wl_window.c | 9 +++++++ src/x11_init.c | 40 +++++++++++++++++++++++++++++ src/x11_monitor.c | 9 +++++++ src/x11_platform.h | 2 ++ src/x11_window.c | 9 +++++++ tests/monitors.c | 9 ++++--- 26 files changed, 387 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 01b159c9..a890ac09 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,8 @@ information on what to include when reporting a bug. gamepad mapping (#900) - Added `glfwGetGamepadState` function, `GLFW_GAMEPAD_*` and `GLFWgamepadstate` for retrieving gamepad input state (#900) +- Added `glfwGetWindowContentScale` and `glfwGetMonitorContentScale` for + DPI-aware rendering (#235,#439,#677,#845,#898) - Added `glfwRequestWindowAttention` function for requesting attention from the user (#732,#988) - Added `glfwGetKeyScancode` function that allows retrieving platform dependent diff --git a/docs/monitor.dox b/docs/monitor.dox index 708461f5..7245f70c 100644 --- a/docs/monitor.dox +++ b/docs/monitor.dox @@ -131,17 +131,34 @@ current _resolution_, i.e. the width and height of its current [video mode](@ref monitor_modes). @code -int widthMM, heightMM; -glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); +int width_mm, height_mm; +glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); @endcode -This can, for example, be used together with the current video mode to calculate -the DPI of a monitor. +While this can be used to calculate the raw DPI of a monitor, this is often not +useful. Instead use the [monitor content scale](@ref monitor_scale) and +[window content scale](@ref window_scale) to scale your content. + + +@subsection monitor_scale Content scale + +The content scale for a monitor can be retrieved with @ref +glfwGetMonitorContentScale. @code -const double dpi = mode->width / (widthMM / 25.4); +float xscale, yscale; +glfwGetMonitorContentScale(monitor, &xscale, &yscale); @endcode +The content scale is the ratio between the current DPI and the platform's +default DPI. If you scale all pixel dimensions by this scale then your content +should appear at an appropriate size. This is especially important for text and +any UI elements. + +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. + @subsection monitor_pos Virtual position diff --git a/docs/news.dox b/docs/news.dox index 86b68c41..03a9c8dc 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -58,6 +58,15 @@ windows with @ref glfwSetWindowAttrib. @see @ref window_attribs +@subsection news_33_contentscale Content scale queries for DPI-aware rendering + +GLFW now supports querying the window and monitor content scale, i.e. the ratio +between the current DPI and the platform's default DPI, with @ref +glfwGetWindowContentScale and @ref glfwGetMonitorContentScale. + +@see @ref window_scale + + @subsection news_33_inithint Support for initialization hints GLFW now supports setting library initialization hints with @ref glfwInitHint diff --git a/docs/window.dox b/docs/window.dox index 85663e77..7961e028 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -663,6 +663,26 @@ The size of a framebuffer may change independently of the size of a window, for example if the window is dragged between a regular monitor and a high-DPI one. +@subsection window_scale Window content scale + +The content scale for a window can be retrieved with @ref +glfwGetWindowContentScale. + +@code +float xscale, yscale; +glfwGetWindowContentScale(window, &xscale, &yscale); +@endcode + +The content scale of a window is the ratio between the current DPI and the +platform's default DPI. If you scale all pixel dimensions by this scale then +your content should appear at an appropriate size. This is especially important +for text and any UI elements. + +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. + + @subsection window_sizelimits Window size limits The minimum and maximum size of the client area of a windowed mode window can be diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 8e6bc42c..0ba409b0 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1926,6 +1926,36 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); */ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * 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. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + /*! @brief Returns the name of the specified monitor. * * This function returns a human-readable name, encoded as UTF-8, of the @@ -2774,6 +2804,36 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) */ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * 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. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + /*! @brief Iconifies the specified window. * * This function iconifies (minimizes) the specified window if it was diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 6020599f..6108342c 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -229,6 +229,9 @@ void _glfwPollMonitorsNS(void) displays = calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); + for (i = 0; i < _glfw.monitorCount; i++) + _glfw.monitors[i]->ns.screen = nil; + disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { @@ -371,6 +374,48 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = (int) bounds.origin.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (!monitor->ns.screen) + { + NSUInteger i; + NSArray* screens = [NSScreen screens]; + + for (i = 0; i < [screens count]; i++) + { + NSScreen* screen = [screens objectAtIndex:i]; + NSNumber* displayID = + [[screen deviceDescription] objectForKey:@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (monitor->ns.unitNumber == + CGDisplayUnitNumber([displayID unsignedIntValue])) + { + monitor->ns.screen = screen; + break; + } + } + + if (i == [screens count]) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find a screen for monitor"); + return; + } + } + + const NSRect points = [monitor->ns.screen frame]; + const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { CFArrayRef modes; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index f0ba4e85..61d0ee91 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -135,6 +135,7 @@ typedef struct _GLFWmonitorNS CGDirectDisplayID displayID; CGDisplayModeRef previousMode; uint32_t unitNumber; + id screen; } _GLFWmonitorNS; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 0a3bcd54..4c9dea90 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1288,6 +1288,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = contentRect.origin.y - frameRect.origin.y; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const NSRect points = [window->ns.view frame]; + const NSRect pixels = [window->ns.view convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { [window->ns.object miniaturize:nil]; diff --git a/src/internal.h b/src/internal.h index b9f55da7..39494340 100644 --- a/src/internal.h +++ b/src/internal.h @@ -641,6 +641,7 @@ const char* _glfwPlatformGetScancodeName(int scancode); int _glfwPlatformGetKeyScancode(int key); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); @@ -670,6 +671,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int min void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale); void _glfwPlatformIconifyWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window); void _glfwPlatformMaximizeWindow(_GLFWwindow* window); diff --git a/src/mir_monitor.c b/src/mir_monitor.c index 26848506..b300cac0 100644 --- a/src/mir_monitor.c +++ b/src/mir_monitor.c @@ -88,6 +88,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->mir.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) { switch (pf) diff --git a/src/mir_window.c b/src/mir_window.c index 8b763de1..c752cdb4 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -515,6 +515,15 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = window->mir.height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { MirWindowSpec* spec; diff --git a/src/monitor.c b/src/monitor.c index 4acecd58..40b24132 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -314,6 +314,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* *heightMM = monitor->heightMM; } +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, + float* xscale, float* yscale) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); +} + GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; diff --git a/src/null_monitor.c b/src/null_monitor.c index f5678bbc..007dd1aa 100644 --- a/src/null_monitor.c +++ b/src/null_monitor.c @@ -36,6 +36,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { return NULL; diff --git a/src/null_window.c b/src/null_window.c index 33ff6c33..3cc3905d 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -139,6 +139,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, { } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { } diff --git a/src/win32_init.c b/src/win32_init.c index 0531ff13..ee7ccfda 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -151,6 +151,8 @@ static GLFWbool loadLibraries(void) { _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) + GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } return GLFW_TRUE; diff --git a/src/win32_monitor.c b/src/win32_monitor.c index 76fbc0d8..74dd8252 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -60,6 +60,7 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) { _GLFWmonitor* monitor; + int widthMM, heightMM; char* name; HDC dc; DEVMODEW dm; @@ -72,13 +73,26 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, if (!name) return NULL; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); + dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); - monitor = _glfwAllocMonitor(name, - GetDeviceCaps(dc, HORZSIZE), - GetDeviceCaps(dc, VERTSIZE)); + if (IsWindows8Point1OrGreater()) + { + widthMM = GetDeviceCaps(dc, HORZSIZE); + heightMM = GetDeviceCaps(dc, VERTSIZE); + } + else + { + widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); + heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); + } DeleteDC(dc); + + monitor = _glfwAllocMonitor(name, widthMM, heightMM); free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) @@ -101,10 +115,6 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, NULL, NULL); } - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); - rect.left = dm.dmPosition.x; rect.top = dm.dmPosition.y; rect.right = dm.dmPosition.x + dm.dmPelsWidth; @@ -302,6 +312,26 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) } } +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) +{ + UINT xdpi, ydpi; + + if (IsWindows8Point1OrGreater()) + GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + else + { + const HDC dc = GetDC(NULL); + xdpi = GetDeviceCaps(dc, LOGPIXELSX); + ydpi = GetDeviceCaps(dc, LOGPIXELSY); + ReleaseDC(NULL, dc); + } + + if (xscale) + *xscale = xdpi / 96.f; + if (yscale) + *yscale = ydpi / 96.f; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -324,6 +354,12 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = dm.dmPosition.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { int modeIndex = 0, size = 0; diff --git a/src/win32_platform.h b/src/win32_platform.h index acbb5ca2..1bfc638f 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -135,6 +135,13 @@ typedef enum PROCESS_DPI_AWARENESS PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum MONITOR_DPI_TYPE +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ // HACK: Define versionhelpers.h functions manually as MinGW lacks the header @@ -216,7 +223,9 @@ typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIN // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); #define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ +#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; @@ -326,6 +335,7 @@ typedef struct _GLFWlibraryWin32 struct { HINSTANCE instance; PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; + PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; } _GLFWlibraryWin32; @@ -395,4 +405,5 @@ void _glfwInitTimerWin32(void); void _glfwPollMonitorsWin32(void); GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); diff --git a/src/win32_window.c b/src/win32_window.c index 490a75d2..394a24da 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -983,19 +983,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } - case WM_DPICHANGED: - { - RECT* rect = (RECT*) lParam; - SetWindowPos(window->win32.handle, - HWND_TOP, - rect->left, - rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - SWP_NOACTIVATE | SWP_NOZORDER); - break; - } - case WM_DROPFILES: { HDROP drop = (HDROP) wParam; @@ -1415,6 +1402,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = rect.bottom - height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const HANDLE handle = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); diff --git a/src/window.c b/src/window.c index 57845579..c91ef005 100644 --- a/src/window.c +++ b/src/window.c @@ -632,6 +632,21 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); } +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, + float* xscale, float* yscale) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowContentScale(window, xscale, yscale); +} + GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 24b0b39a..10ef0e12 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -153,6 +153,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->wl.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) monitor->wl.scale; + if (yscale) + *yscale = (float) monitor->wl.scale; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { *found = monitor->modeCount; diff --git a/src/wl_window.c b/src/wl_window.c index 32d5f404..649ed7e9 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -550,6 +550,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, // implemented, but for now just leave everything as 0. } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) window->wl.scale; + if (yscale) + *yscale = (float) window->wl.scale; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { // TODO: move to xdg_shell instead of wl_shell. diff --git a/src/x11_init.c b/src/x11_init.c index 526d9ca2..b603a15f 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -743,6 +743,43 @@ static GLFWbool initExtensions(void) return GLFW_TRUE; } +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + // NOTE: Default to the display-wide DPI as we don't currently have a policy + // for which monitor a window is considered to be on + float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + // NOTE: Basing the scale on Xft.dpi where available should provide the most + // consistent user experience (matches Qt, Gtk, etc), although not + // always the most accurate one + char* rms = XResourceManagerString(_glfw.x11.display); + if (rms) + { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) + { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) + { + if (type && strcmp(type, "String") == 0) + xdpi = ydpi = atof(value.addr); + } + + XrmDestroyDatabase(db); + } + } + + *xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; +} + // Create a blank cursor for hidden and disabled cursor modes // static Cursor createHiddenCursor(void) @@ -861,6 +898,7 @@ int _glfwPlatformInit(void) #endif XInitThreads(); + XrmInitialize(); _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) @@ -884,6 +922,8 @@ int _glfwPlatformInit(void) _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); + getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!initExtensions()) return GLFW_FALSE; diff --git a/src/x11_monitor.c b/src/x11_monitor.c index 5c0516e6..d68c5888 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -338,6 +338,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; diff --git a/src/x11_platform.h b/src/x11_platform.h index 2b1c0c62..7eba441d 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -211,6 +211,8 @@ typedef struct _GLFWlibraryX11 int screen; Window root; + // System content scale + float contentScaleX, contentScaleY; // Helper window for IPC Window helperWindowHandle; // Invisible cursor for hidden cursor mode diff --git a/src/x11_window.c b/src/x11_window.c index cf5483b0..7c407cc5 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2240,6 +2240,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, XFree(extents); } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) diff --git a/tests/monitors.c b/tests/monitors.c index fc54c319..c7fd5c5c 100644 --- a/tests/monitors.c +++ b/tests/monitors.c @@ -92,21 +92,24 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, static void list_modes(GLFWmonitor* monitor) { - int count, x, y, widthMM, heightMM, i; + int count, x, y, width_mm, height_mm, i; + float xscale, yscale; const GLFWvidmode* mode = glfwGetVideoMode(monitor); const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); glfwGetMonitorPos(monitor, &x, &y); - glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); + glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); + glfwGetMonitorContentScale(monitor, &xscale, &yscale); printf("Name: %s (%s)\n", glfwGetMonitorName(monitor), glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary"); printf("Current mode: %s\n", format_mode(mode)); printf("Virtual position: %i %i\n", x, y); + printf("Content scale: %f %f\n", xscale, yscale); printf("Physical size: %i x %i mm (%0.2f dpi)\n", - widthMM, heightMM, mode->width * 25.4f / widthMM); + width_mm, height_mm, mode->width * 25.4f / width_mm); printf("Modes:\n");