mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-17 23:31:45 +00:00
Windows DPI scaling/highdpi support
Adds hint "SDL_WINDOWS_DPI_SCALING" which can be set to "1" to change the SDL coordinate system units to be DPI-scaled points, rather than pixels everywhere. This means windows will be appropriately sized, even when created on high-DPI displays with scaling. e.g. requesting a 640x480 window from SDL, on a display with 125% scaling in Windows display settings, will create a window with an 800x600 client area (in pixels). Setting this to "1" implicitly requests process DPI awareness (setting SDL_WINDOWS_DPI_AWARENESS is unnecessary), and forces SDL_WINDOW_ALLOW_HIGHDPI on all windows.
This commit is contained in:

committed by
Sam Lantinga

parent
df36f926fc
commit
ab81a559f4
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
|
||||
#include "SDL_log.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../SDL_pixels_c.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
@@ -115,19 +116,45 @@ GetWindowStyle(SDL_Window * window)
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns arguments to pass to SetWindowPos - the window rect, including frame, in Windows coordinates.
|
||||
*
|
||||
* Can be called before we have a HWND.
|
||||
*/
|
||||
static void
|
||||
WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current)
|
||||
{
|
||||
SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
|
||||
SDL_VideoData* videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL;
|
||||
RECT rect;
|
||||
UINT dpi;
|
||||
int dpi;
|
||||
UINT frame_dpi;
|
||||
|
||||
dpi = 96;
|
||||
/* Client rect, in SDL screen coordinates */
|
||||
*x = (use_current ? window->x : window->windowed.x);
|
||||
*y = (use_current ? window->y : window->windowed.y);
|
||||
*width = (use_current ? window->w : window->windowed.w);
|
||||
*height = (use_current ? window->h : window->windowed.h);
|
||||
|
||||
/* Convert client rect from SDL coordinates to pixels (no-op if DPI scaling not enabled) */
|
||||
WIN_ScreenPointFromSDL(x, y, &dpi);
|
||||
/* Note, use the guessed DPI returned from WIN_ScreenPointFromSDL rather than the cached one in
|
||||
data->scaling_dpi.
|
||||
|
||||
- This is called before the window is created, so we can't rely on data->scaling_dpi
|
||||
- Bug workaround: when leaving exclusive fullscreen, the cached DPI and window DPI reported
|
||||
by GetDpiForWindow will be wrong, and would cause windows shrinking slightly when
|
||||
going from exclusive fullscreen to windowed on a HighDPI monitor with scaling if we used them.
|
||||
*/
|
||||
*width = MulDiv(*width, dpi, 96);
|
||||
*height = MulDiv(*height, dpi, 96);
|
||||
|
||||
/* Copy the client size in pixels into this rect structure,
|
||||
which we'll then adjust with AdjustWindowRectEx */
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = (use_current ? window->w : window->windowed.w);
|
||||
rect.bottom = (use_current ? window->h : window->windowed.h);
|
||||
rect.right = *width;
|
||||
rect.bottom = *height;
|
||||
|
||||
/* borderless windows will have WM_NCCALCSIZE return 0 for the non-client area. When this happens, it looks like windows will send a resize message
|
||||
expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles.
|
||||
@@ -141,26 +168,27 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x
|
||||
RECT screen_rect;
|
||||
HMONITOR mon;
|
||||
|
||||
screen_rect.left = (use_current ? window->x : window->windowed.x);
|
||||
screen_rect.top = (use_current ? window->y : window->windowed.y);
|
||||
screen_rect.right = screen_rect.left + (use_current ? window->w : window->windowed.w);
|
||||
screen_rect.bottom = screen_rect.top + (use_current ? window->h : window->windowed.h);
|
||||
screen_rect.left = *x;
|
||||
screen_rect.top = *y;
|
||||
screen_rect.right = *x + *width;
|
||||
screen_rect.bottom = *y + *height;
|
||||
|
||||
mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
/* GetDpiForMonitor docs promise to return the same hdpi / vdpi */
|
||||
if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpi, &unused) != S_OK) {
|
||||
dpi = 96;
|
||||
if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &frame_dpi, &unused) != S_OK) {
|
||||
frame_dpi = 96;
|
||||
}
|
||||
|
||||
videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi);
|
||||
videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, frame_dpi);
|
||||
} else {
|
||||
AdjustWindowRectEx(&rect, style, menu, 0);
|
||||
}
|
||||
}
|
||||
|
||||
*x = (use_current ? window->x : window->windowed.x) + rect.left;
|
||||
*y = (use_current ? window->y : window->windowed.y) + rect.top;
|
||||
/* Final rect in Windows screen space, including the frame */
|
||||
*x += rect.left;
|
||||
*y += rect.top;
|
||||
*width = (rect.right - rect.left);
|
||||
*height = (rect.bottom - rect.top);
|
||||
|
||||
@@ -170,7 +198,7 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x
|
||||
(use_current ? window->y : window->windowed.y),
|
||||
(use_current ? window->w : window->windowed.w),
|
||||
(use_current ? window->h : window->windowed.h),
|
||||
*x, *y, *width, *height, dpi);
|
||||
*x, *y, *width, *height, frame_dpi);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -217,6 +245,43 @@ WIN_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char
|
||||
data->mouse_relative_mode_center = SDL_GetStringBoolean(hint, SDL_TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
WIN_GetScalingDPIForHWND(const SDL_VideoData *videodata, HWND hwnd)
|
||||
{
|
||||
/* DPI scaling not requested? */
|
||||
if (!videodata->dpi_scaling_enabled) {
|
||||
return 96;
|
||||
}
|
||||
|
||||
/* Window 10+ */
|
||||
if (videodata->GetDpiForWindow) {
|
||||
return videodata->GetDpiForWindow(hwnd);
|
||||
}
|
||||
|
||||
/* Window 8.1+ */
|
||||
if (videodata->GetDpiForMonitor) {
|
||||
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if (monitor) {
|
||||
UINT dpi_uint, unused;
|
||||
if (S_OK == videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_uint, &unused)) {
|
||||
return (int)dpi_uint;
|
||||
}
|
||||
}
|
||||
return 96;
|
||||
}
|
||||
|
||||
/* Windows Vista-8.0 */
|
||||
{
|
||||
HDC hdc = GetDC(NULL);
|
||||
if (hdc) {
|
||||
int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
ReleaseDC(NULL, hdc);
|
||||
return dpi;
|
||||
}
|
||||
return 96;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool created)
|
||||
{
|
||||
@@ -239,6 +304,11 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre
|
||||
data->last_pointer_update = (LPARAM)-1;
|
||||
data->videodata = videodata;
|
||||
data->initializing = SDL_TRUE;
|
||||
data->scaling_dpi = WIN_GetScalingDPIForHWND(videodata, hwnd);
|
||||
|
||||
#ifdef HIGHDPI_DEBUG
|
||||
SDL_Log("SetupWindowData: initialized data->scaling_dpi to %d", data->scaling_dpi);
|
||||
#endif
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
|
||||
|
||||
@@ -274,6 +344,8 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre
|
||||
if (GetClientRect(hwnd, &rect)) {
|
||||
int w = rect.right;
|
||||
int h = rect.bottom;
|
||||
|
||||
WIN_ClientPointToSDL(window, &w, &h);
|
||||
if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) {
|
||||
/* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */
|
||||
int x, y;
|
||||
@@ -293,8 +365,11 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre
|
||||
point.x = 0;
|
||||
point.y = 0;
|
||||
if (ClientToScreen(hwnd, &point)) {
|
||||
window->x = point.x;
|
||||
window->y = point.y;
|
||||
int x = point.x;
|
||||
int y = point.y;
|
||||
WIN_ScreenPointToSDL(&x, &y);
|
||||
window->x = x;
|
||||
window->y = y;
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -342,6 +417,11 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre
|
||||
videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH|TWF_WANTPALM));
|
||||
}
|
||||
|
||||
/* Force the SDL_WINDOW_ALLOW_HIGHDPI window flag if we are doing DPI scaling */
|
||||
if (videodata->dpi_scaling_enabled) {
|
||||
window->flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
}
|
||||
|
||||
data->initializing = SDL_FALSE;
|
||||
|
||||
/* All done! */
|
||||
@@ -767,12 +847,17 @@ WIN_RestoreWindow(_THIS, SDL_Window * window)
|
||||
data->expected_resize = SDL_FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures the window to fill the given display, if fullscreen is true, otherwise restores the window.
|
||||
*/
|
||||
void
|
||||
WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
|
||||
{
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
|
||||
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
|
||||
SDL_VideoData *videodata = data->videodata;
|
||||
HWND hwnd = data->hwnd;
|
||||
SDL_Rect bounds;
|
||||
MONITORINFO minfo;
|
||||
DWORD style;
|
||||
HWND top;
|
||||
int x, y;
|
||||
@@ -785,6 +870,15 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HIGHDPI_DEBUG
|
||||
SDL_Log("WIN_SetWindowFullscreen: %d", (int)fullscreen);
|
||||
#endif
|
||||
|
||||
/* Clear the window size, to force SDL_SendWindowEvent to send a SDL_WINDOWEVENT_RESIZED
|
||||
event in WM_WINDOWPOSCHANGED. */
|
||||
data->window->w = 0;
|
||||
data->window->h = 0;
|
||||
|
||||
if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) {
|
||||
top = HWND_TOPMOST;
|
||||
} else {
|
||||
@@ -795,13 +889,20 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display,
|
||||
style &= ~STYLE_MASK;
|
||||
style |= GetWindowStyle(window);
|
||||
|
||||
WIN_GetDisplayBounds(_this, display, &bounds);
|
||||
/* Use GetMonitorInfo instead of WIN_GetDisplayBounds because we want the
|
||||
monitor bounds in Windows coordinates (pixels) rather than SDL coordinates (points). */
|
||||
SDL_zero(minfo);
|
||||
minfo.cbSize = sizeof(MONITORINFO);
|
||||
if (!GetMonitorInfo(displaydata->MonitorHandle, &minfo)) {
|
||||
SDL_SetError("GetMonitorInfo failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
x = bounds.x;
|
||||
y = bounds.y;
|
||||
w = bounds.w;
|
||||
h = bounds.h;
|
||||
x = minfo.rcMonitor.left;
|
||||
y = minfo.rcMonitor.top;
|
||||
w = minfo.rcMonitor.right - minfo.rcMonitor.left;
|
||||
h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
|
||||
|
||||
/* Unset the maximized flag. This fixes
|
||||
https://bugzilla.libsdl.org/show_bug.cgi?id=3215
|
||||
@@ -831,6 +932,10 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display,
|
||||
data->expected_resize = SDL_TRUE;
|
||||
SetWindowPos(hwnd, top, x, y, w, h, SWP_NOCOPYBITS | SWP_NOACTIVATE);
|
||||
data->expected_resize = SDL_FALSE;
|
||||
|
||||
#ifdef HIGHDPI_DEBUG
|
||||
SDL_Log("WIN_SetWindowFullscreen: %d finished. Set window to %d,%d, %dx%d", (int)fullscreen, x, y, w, h);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1122,12 +1227,19 @@ WIN_UpdateClipCursor(SDL_Window *window)
|
||||
ClientToScreen(data->hwnd, (LPPOINT) & rect);
|
||||
ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
|
||||
if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) {
|
||||
SDL_Rect mouse_rect_win_client;
|
||||
RECT mouse_rect, intersection;
|
||||
|
||||
mouse_rect.left = rect.left + window->mouse_rect.x;
|
||||
mouse_rect.top = rect.top + window->mouse_rect.y;
|
||||
mouse_rect.right = mouse_rect.left + window->mouse_rect.w - 1;
|
||||
mouse_rect.bottom = mouse_rect.top + window->mouse_rect.h - 1;
|
||||
/* mouse_rect_win_client is the mouse rect in Windows client space */
|
||||
mouse_rect_win_client = window->mouse_rect;
|
||||
WIN_ClientPointFromSDL(window, &mouse_rect_win_client.x, &mouse_rect_win_client.y);
|
||||
WIN_ClientPointFromSDL(window, &mouse_rect_win_client.w, &mouse_rect_win_client.h);
|
||||
|
||||
/* mouse_rect is the rect in Windows screen space */
|
||||
mouse_rect.left = rect.left + mouse_rect_win_client.x;
|
||||
mouse_rect.top = rect.top + mouse_rect_win_client.y;
|
||||
mouse_rect.right = mouse_rect.left + mouse_rect_win_client.w - 1;
|
||||
mouse_rect.bottom = mouse_rect.top + mouse_rect_win_client.h - 1;
|
||||
if (IntersectRect(&intersection, &rect, &mouse_rect)) {
|
||||
SDL_memcpy(&rect, &intersection, sizeof(rect));
|
||||
} else if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0) {
|
||||
@@ -1203,6 +1315,61 @@ WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drawable size in pixels (GetClientRect).
|
||||
*/
|
||||
void
|
||||
WIN_GetDrawableSize(const SDL_Window *window, int *w, int *h)
|
||||
{
|
||||
const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata);
|
||||
HWND hwnd = data->hwnd;
|
||||
RECT rect;
|
||||
|
||||
if (GetClientRect(hwnd, &rect)) {
|
||||
*w = rect.right;
|
||||
*h = rect.bottom;
|
||||
} else {
|
||||
*w = 0;
|
||||
*h = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a point in the client area from pixels to DPI-scaled points.
|
||||
*
|
||||
* No-op if DPI scaling is not enabled.
|
||||
*/
|
||||
void
|
||||
WIN_ClientPointToSDL(const SDL_Window *window, int *x, int *y)
|
||||
{
|
||||
const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata);
|
||||
const SDL_VideoData *videodata = data->videodata;
|
||||
|
||||
if (!videodata->dpi_scaling_enabled)
|
||||
return;
|
||||
|
||||
*x = MulDiv(*x, 96, data->scaling_dpi);
|
||||
*y = MulDiv(*y, 96, data->scaling_dpi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a point in the client area from DPI-scaled points to pixels.
|
||||
*
|
||||
* No-op if DPI scaling is not enabled.
|
||||
*/
|
||||
void
|
||||
WIN_ClientPointFromSDL(const SDL_Window *window, int *x, int *y)
|
||||
{
|
||||
const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata);
|
||||
const SDL_VideoData *videodata = data->videodata;
|
||||
|
||||
if (!videodata->dpi_scaling_enabled)
|
||||
return;
|
||||
|
||||
*x = MulDiv(*x, data->scaling_dpi, 96);
|
||||
*y = MulDiv(*y, data->scaling_dpi, 96);
|
||||
}
|
||||
|
||||
void
|
||||
WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
|
||||
{
|
||||
|
Reference in New Issue
Block a user