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.
This commit is contained in:
Camilla Löwy 2017-08-29 19:19:00 +02:00
parent 1be81a1540
commit 16bf872117
26 changed files with 387 additions and 28 deletions

View File

@ -136,6 +136,8 @@ information on what to include when reporting a bug.
gamepad mapping (#900) gamepad mapping (#900)
- Added `glfwGetGamepadState` function, `GLFW_GAMEPAD_*` and `GLFWgamepadstate` - Added `glfwGetGamepadState` function, `GLFW_GAMEPAD_*` and `GLFWgamepadstate`
for retrieving gamepad input state (#900) 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 - Added `glfwRequestWindowAttention` function for requesting attention from the
user (#732,#988) user (#732,#988)
- Added `glfwGetKeyScancode` function that allows retrieving platform dependent - Added `glfwGetKeyScancode` function that allows retrieving platform dependent

View File

@ -131,17 +131,34 @@ current _resolution_, i.e. the width and height of its current
[video mode](@ref monitor_modes). [video mode](@ref monitor_modes).
@code @code
int widthMM, heightMM; int width_mm, height_mm;
glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm);
@endcode @endcode
This can, for example, be used together with the current video mode to calculate While this can be used to calculate the raw DPI of a monitor, this is often not
the DPI of a monitor. 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 @code
const double dpi = mode->width / (widthMM / 25.4); float xscale, yscale;
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
@endcode @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 @subsection monitor_pos Virtual position

View File

@ -58,6 +58,15 @@ windows with @ref glfwSetWindowAttrib.
@see @ref window_attribs @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 @subsection news_33_inithint Support for initialization hints
GLFW now supports setting library initialization hints with @ref glfwInitHint GLFW now supports setting library initialization hints with @ref glfwInitHint

View File

@ -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. 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 @subsection window_sizelimits Window size limits
The minimum and maximum size of the client area of a windowed mode window can be The minimum and maximum size of the client area of a windowed mode window can be

View File

@ -1926,6 +1926,36 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos);
*/ */
GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); 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. /*! @brief Returns the name of the specified monitor.
* *
* This function returns a human-readable name, encoded as UTF-8, of the * 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); 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. /*! @brief Iconifies the specified window.
* *
* This function iconifies (minimizes) the specified window if it was * This function iconifies (minimizes) the specified window if it was

View File

@ -229,6 +229,9 @@ void _glfwPollMonitorsNS(void)
displays = calloc(displayCount, sizeof(CGDirectDisplayID)); displays = calloc(displayCount, sizeof(CGDirectDisplayID));
CGGetOnlineDisplayList(displayCount, displays, &displayCount); CGGetOnlineDisplayList(displayCount, displays, &displayCount);
for (i = 0; i < _glfw.monitorCount; i++)
_glfw.monitors[i]->ns.screen = nil;
disconnectedCount = _glfw.monitorCount; disconnectedCount = _glfw.monitorCount;
if (disconnectedCount) if (disconnectedCount)
{ {
@ -371,6 +374,48 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = (int) bounds.origin.y; *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) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
{ {
CFArrayRef modes; CFArrayRef modes;

View File

@ -135,6 +135,7 @@ typedef struct _GLFWmonitorNS
CGDirectDisplayID displayID; CGDirectDisplayID displayID;
CGDisplayModeRef previousMode; CGDisplayModeRef previousMode;
uint32_t unitNumber; uint32_t unitNumber;
id screen;
} _GLFWmonitorNS; } _GLFWmonitorNS;

View File

@ -1288,6 +1288,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
*bottom = contentRect.origin.y - frameRect.origin.y; *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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
[window->ns.object miniaturize:nil]; [window->ns.object miniaturize:nil];

View File

@ -641,6 +641,7 @@ const char* _glfwPlatformGetScancodeName(int scancode);
int _glfwPlatformGetKeyScancode(int key); int _glfwPlatformGetKeyScancode(int key);
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale);
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); 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 _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); 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 _glfwPlatformIconifyWindow(_GLFWwindow* window);
void _glfwPlatformRestoreWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window);
void _glfwPlatformMaximizeWindow(_GLFWwindow* window); void _glfwPlatformMaximizeWindow(_GLFWwindow* window);

View File

@ -88,6 +88,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = monitor->mir.y; *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) static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf)
{ {
switch (pf) switch (pf)

View File

@ -515,6 +515,15 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
*height = window->mir.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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
MirWindowSpec* spec; MirWindowSpec* spec;

View File

@ -314,6 +314,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int*
*heightMM = monitor->heightMM; *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) GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
{ {
_GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFWmonitor* monitor = (_GLFWmonitor*) handle;

View File

@ -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) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{ {
return NULL; return NULL;

View File

@ -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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
} }

View File

@ -151,6 +151,8 @@ static GLFWbool loadLibraries(void)
{ {
_glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness)
GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness");
_glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor)
GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor");
} }
return GLFW_TRUE; return GLFW_TRUE;

View File

@ -60,6 +60,7 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
DISPLAY_DEVICEW* display) DISPLAY_DEVICEW* display)
{ {
_GLFWmonitor* monitor; _GLFWmonitor* monitor;
int widthMM, heightMM;
char* name; char* name;
HDC dc; HDC dc;
DEVMODEW dm; DEVMODEW dm;
@ -72,13 +73,26 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
if (!name) if (!name)
return NULL; 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); dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
monitor = _glfwAllocMonitor(name, if (IsWindows8Point1OrGreater())
GetDeviceCaps(dc, HORZSIZE), {
GetDeviceCaps(dc, VERTSIZE)); 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); DeleteDC(dc);
monitor = _glfwAllocMonitor(name, widthMM, heightMM);
free(name); free(name);
if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
@ -101,10 +115,6 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
NULL, NULL); NULL, NULL);
} }
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
rect.left = dm.dmPosition.x; rect.left = dm.dmPosition.x;
rect.top = dm.dmPosition.y; rect.top = dm.dmPosition.y;
rect.right = dm.dmPosition.x + dm.dmPelsWidth; 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 ////// ////// GLFW platform API //////
@ -324,6 +354,12 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = dm.dmPosition.y; *ypos = dm.dmPosition.y;
} }
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
_glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
{ {
int modeIndex = 0, size = 0; int modeIndex = 0, size = 0;

View File

@ -135,6 +135,13 @@ typedef enum PROCESS_DPI_AWARENESS
PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2 PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS; } 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*/ #endif /*DPI_ENUMS_DECLARED*/
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header // 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 // shcore.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); 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 SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_
#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_
typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
@ -326,6 +335,7 @@ typedef struct _GLFWlibraryWin32
struct { struct {
HINSTANCE instance; HINSTANCE instance;
PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; PFN_SetProcessDpiAwareness SetProcessDpiAwareness_;
PFN_GetDpiForMonitor GetDpiForMonitor_;
} shcore; } shcore;
} _GLFWlibraryWin32; } _GLFWlibraryWin32;
@ -395,4 +405,5 @@ void _glfwInitTimerWin32(void);
void _glfwPollMonitorsWin32(void); void _glfwPollMonitorsWin32(void);
GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor);
void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale);

View File

@ -983,19 +983,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
break; 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: case WM_DROPFILES:
{ {
HDROP drop = (HDROP) wParam; HDROP drop = (HDROP) wParam;
@ -1415,6 +1402,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
*bottom = rect.bottom - height; *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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
ShowWindow(window->win32.handle, SW_MINIMIZE); ShowWindow(window->win32.handle, SW_MINIMIZE);

View File

@ -632,6 +632,21 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle,
_glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); _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) GLFWAPI void glfwIconifyWindow(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;

View File

@ -153,6 +153,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = monitor->wl.y; *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) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{ {
*found = monitor->modeCount; *found = monitor->modeCount;

View File

@ -550,6 +550,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
// implemented, but for now just leave everything as 0. // 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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
// TODO: move to xdg_shell instead of wl_shell. // TODO: move to xdg_shell instead of wl_shell.

View File

@ -743,6 +743,43 @@ static GLFWbool initExtensions(void)
return GLFW_TRUE; 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 // Create a blank cursor for hidden and disabled cursor modes
// //
static Cursor createHiddenCursor(void) static Cursor createHiddenCursor(void)
@ -861,6 +898,7 @@ int _glfwPlatformInit(void)
#endif #endif
XInitThreads(); XInitThreads();
XrmInitialize();
_glfw.x11.display = XOpenDisplay(NULL); _glfw.x11.display = XOpenDisplay(NULL);
if (!_glfw.x11.display) if (!_glfw.x11.display)
@ -884,6 +922,8 @@ int _glfwPlatformInit(void)
_glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
_glfw.x11.context = XUniqueContext(); _glfw.x11.context = XUniqueContext();
getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY);
if (!initExtensions()) if (!initExtensions())
return GLFW_FALSE; return GLFW_FALSE;

View File

@ -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* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
{ {
GLFWvidmode* result; GLFWvidmode* result;

View File

@ -211,6 +211,8 @@ typedef struct _GLFWlibraryX11
int screen; int screen;
Window root; Window root;
// System content scale
float contentScaleX, contentScaleY;
// Helper window for IPC // Helper window for IPC
Window helperWindowHandle; Window helperWindowHandle;
// Invisible cursor for hidden cursor mode // Invisible cursor for hidden cursor mode

View File

@ -2240,6 +2240,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
XFree(extents); 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) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
if (window->x11.overrideRedirect) if (window->x11.overrideRedirect)

View File

@ -92,21 +92,24 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
static void list_modes(GLFWmonitor* monitor) 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* mode = glfwGetVideoMode(monitor);
const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count);
glfwGetMonitorPos(monitor, &x, &y); glfwGetMonitorPos(monitor, &x, &y);
glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm);
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
printf("Name: %s (%s)\n", printf("Name: %s (%s)\n",
glfwGetMonitorName(monitor), glfwGetMonitorName(monitor),
glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary"); glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary");
printf("Current mode: %s\n", format_mode(mode)); printf("Current mode: %s\n", format_mode(mode));
printf("Virtual position: %i %i\n", x, y); 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", 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"); printf("Modes:\n");