From 134b57f6f55052becc1465b0bc4697a7155b3303 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 21 Mar 2025 16:29:11 -0700
Subject: [PATCH] Improve move/resize visual smoothness on Windows
Fixes https://github.com/libsdl-org/SDL/issues/12528
(cherry picked from commit 6b13d69105721ac54cd7224b0493065c57776c89)
---
src/video/windows/SDL_windowsevents.c | 7 ++
src/video/windows/SDL_windowsvideo.c | 14 ++++
src/video/windows/SDL_windowsvideo.h | 44 +++++++++++
src/video/windows/SDL_windowswindow.c | 110 ++++++--------------------
4 files changed, 91 insertions(+), 84 deletions(-)
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 9baa9e4222ca6..e6f10480d72fc 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1859,6 +1859,13 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
{
if (wParam == (UINT_PTR)SDL_IterateMainCallbacks) {
SDL_OnWindowLiveResizeUpdate(data->window);
+
+#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+ // Make sure graphics operations are complete for smooth refresh
+ if (data->videodata->DwmFlush) {
+ data->videodata->DwmFlush();
+ }
+#endif
return 0;
}
} break;
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 04415b548101f..6103d52334e1d 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -108,6 +108,9 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
if (data->shcoreDLL) {
SDL_UnloadObject(data->shcoreDLL);
}
+ if (data->dwmapiDLL) {
+ SDL_UnloadObject(data->dwmapiDLL);
+ }
#endif
#ifdef HAVE_DXGI_H
if (data->pDXGIFactory) {
@@ -184,6 +187,17 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
} else {
SDL_ClearError();
}
+
+ data->dwmapiDLL = SDL_LoadObject("DWMAPI.DLL");
+ if (data->dwmapiDLL) {
+ /* *INDENT-OFF* */ // clang-format off
+ data->DwmFlush = (HRESULT (WINAPI *)(void))SDL_LoadFunction(data->dwmapiDLL, "DwmFlush");
+ data->DwmEnableBlurBehindWindow = (HRESULT (WINAPI *)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind))SDL_LoadFunction(data->dwmapiDLL, "DwmEnableBlurBehindWindow");
+ data->DwmSetWindowAttribute = (HRESULT (WINAPI *)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute))SDL_LoadFunction(data->dwmapiDLL, "DwmSetWindowAttribute");
+ /* *INDENT-ON* */ // clang-format on
+ } else {
+ SDL_ClearError();
+ }
#endif // #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifdef HAVE_DXGI_H
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index 53cfbadd7220c..c80a721a83620 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -374,6 +374,45 @@ typedef struct tagINPUTCONTEXT2
} INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2;
#endif
+// Corner rounding support (Win 11+)
+#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
+#define DWMWA_WINDOW_CORNER_PREFERENCE 33
+#endif
+typedef enum {
+ DWMWCP_DEFAULT = 0,
+ DWMWCP_DONOTROUND = 1,
+ DWMWCP_ROUND = 2,
+ DWMWCP_ROUNDSMALL = 3
+} DWM_WINDOW_CORNER_PREFERENCE;
+
+// Border Color support (Win 11+)
+#ifndef DWMWA_BORDER_COLOR
+#define DWMWA_BORDER_COLOR 34
+#endif
+
+#ifndef DWMWA_COLOR_DEFAULT
+#define DWMWA_COLOR_DEFAULT 0xFFFFFFFF
+#endif
+
+#ifndef DWMWA_COLOR_NONE
+#define DWMWA_COLOR_NONE 0xFFFFFFFE
+#endif
+
+// Transparent window support
+#ifndef DWM_BB_ENABLE
+#define DWM_BB_ENABLE 0x00000001
+#endif
+#ifndef DWM_BB_BLURREGION
+#define DWM_BB_BLURREGION 0x00000002
+#endif
+typedef struct
+{
+ DWORD flags;
+ BOOL enable;
+ HRGN blur_region;
+ BOOL transition_on_maxed;
+} DWM_BLURBEHIND;
+
// Private display data
struct SDL_VideoData
@@ -420,6 +459,11 @@ struct SDL_VideoData
BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
+ SDL_SharedObject *dwmapiDLL;
+ /* *INDENT-OFF* */ // clang-format off
+ HRESULT (WINAPI *DwmFlush)(void);
+ HRESULT (WINAPI *DwmEnableBlurBehindWindow)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
+ HRESULT (WINAPI *DwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
/* *INDENT-ON* */ // clang-format on
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index ea23cf59fa893..18820f1d6cb13 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -38,10 +38,6 @@
// Dropfile support
#include <shellapi.h>
-// DWM setting support
-typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
-typedef HRESULT (WINAPI *DwmGetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);
-
// Dark mode support
typedef enum {
UXTHEME_APPMODE_DEFAULT,
@@ -80,46 +76,6 @@ typedef UxthemePreferredAppMode (WINAPI *SetPreferredAppMode_t)(UxthemePreferred
typedef BOOL (WINAPI *SetWindowCompositionAttribute_t)(HWND, const WINDOWCOMPOSITIONATTRIBDATA *);
typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *);
-// Corner rounding support (Win 11+)
-#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
-#define DWMWA_WINDOW_CORNER_PREFERENCE 33
-#endif
-typedef enum {
- DWMWCP_DEFAULT = 0,
- DWMWCP_DONOTROUND = 1,
- DWMWCP_ROUND = 2,
- DWMWCP_ROUNDSMALL = 3
-} DWM_WINDOW_CORNER_PREFERENCE;
-
-// Border Color support (Win 11+)
-#ifndef DWMWA_BORDER_COLOR
-#define DWMWA_BORDER_COLOR 34
-#endif
-
-#ifndef DWMWA_COLOR_DEFAULT
-#define DWMWA_COLOR_DEFAULT 0xFFFFFFFF
-#endif
-
-#ifndef DWMWA_COLOR_NONE
-#define DWMWA_COLOR_NONE 0xFFFFFFFE
-#endif
-
-// Transparent window support
-#ifndef DWM_BB_ENABLE
-#define DWM_BB_ENABLE 0x00000001
-#endif
-#ifndef DWM_BB_BLURREGION
-#define DWM_BB_BLURREGION 0x00000002
-#endif
-typedef struct
-{
- DWORD flags;
- BOOL enable;
- HRGN blur_region;
- BOOL transition_on_maxed;
-} DWM_BLURBEHIND;
-typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
-
// Windows CE compatibility
#ifndef SWP_NOCOPYBITS
#define SWP_NOCOPYBITS 0
@@ -744,6 +700,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
{
+ SDL_VideoData *videodata = _this->internal;
HWND hwnd = (HWND)SDL_GetPointerProperty(create_props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL));
HWND parent = NULL;
if (hwnd) {
@@ -805,24 +762,19 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
// FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs)
if (window->flags & SDL_WINDOW_TRANSPARENT) {
- SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
- if (handle) {
- DwmEnableBlurBehindWindow_t DwmEnableBlurBehindWindowFunc = (DwmEnableBlurBehindWindow_t)SDL_LoadFunction(handle, "DwmEnableBlurBehindWindow");
- if (DwmEnableBlurBehindWindowFunc) {
- /* The region indicates which part of the window will be blurred and rest will be transparent. This
- is because the alpha value of the window will be used for non-blurred areas
- We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
- */
- HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
- DWM_BLURBEHIND bb;
- bb.flags = (DWM_BB_ENABLE | DWM_BB_BLURREGION);
- bb.enable = TRUE;
- bb.blur_region = rgn;
- bb.transition_on_maxed = FALSE;
- DwmEnableBlurBehindWindowFunc(hwnd, &bb);
- DeleteObject(rgn);
- }
- SDL_UnloadObject(handle);
+ if (videodata->DwmEnableBlurBehindWindow) {
+ /* The region indicates which part of the window will be blurred and rest will be transparent. This
+ is because the alpha value of the window will be used for non-blurred areas
+ We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
+ */
+ HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
+ DWM_BLURBEHIND bb;
+ bb.flags = (DWM_BB_ENABLE | DWM_BB_BLURREGION);
+ bb.enable = TRUE;
+ bb.blur_region = rgn;
+ bb.transition_on_maxed = FALSE;
+ videodata->DwmEnableBlurBehindWindow(hwnd, &bb);
+ DeleteObject(rgn);
}
}
@@ -1264,29 +1216,19 @@ void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
-static void WIN_UpdateCornerRoundingForHWND(HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref)
+static void WIN_UpdateCornerRoundingForHWND(SDL_VideoDevice *_this, HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref)
{
- SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
- if (handle) {
- DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute");
- if (DwmSetWindowAttributeFunc) {
- DwmSetWindowAttributeFunc(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
- }
-
- SDL_UnloadObject(handle);
+ SDL_VideoData *videodata = _this->internal;
+ if (videodata->DwmSetWindowAttribute) {
+ videodata->DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
}
}
-static void WIN_UpdateBorderColorForHWND(HWND hwnd, COLORREF colorRef)
+static void WIN_UpdateBorderColorForHWND(SDL_VideoDevice *_this, HWND hwnd, COLORREF colorRef)
{
- SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
- if (handle) {
- DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute");
- if (DwmSetWindowAttributeFunc) {
- DwmSetWindowAttributeFunc(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef));
- }
-
- SDL_UnloadObject(handle);
+ SDL_VideoData *videodata = _this->internal;
+ if (videodata->DwmSetWindowAttribute) {
+ videodata->DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef));
}
}
@@ -1353,13 +1295,13 @@ SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window
}
// Disable corner rounding & border color (Windows 11+) so the window fills the full screen
- WIN_UpdateCornerRoundingForHWND(hwnd, DWMWCP_DONOTROUND);
- WIN_UpdateBorderColorForHWND(hwnd, DWMWA_COLOR_NONE);
+ WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DONOTROUND);
+ WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_NONE);
} else {
BOOL menu;
- WIN_UpdateCornerRoundingForHWND(hwnd, DWMWCP_DEFAULT);
- WIN_UpdateBorderColorForHWND(hwnd, DWMWA_COLOR_DEFAULT);
+ WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DEFAULT);
+ WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_DEFAULT);
/* Restore window-maximization state, as applicable.
Special care is taken to *not* do this if and when we're