|
@@ -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;
|
|
|
-
|
|
|
- dpi = 96;
|
|
|
+ int dpi;
|
|
|
+ UINT frame_dpi;
|
|
|
+
|
|
|
+ /* 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)
|
|
|
{
|