diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index a316838654..f0d96ed44b 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -3552,6 +3552,22 @@ extern "C" { */ #define SDL_HINT_WINDOWS_USE_D3D9EX "SDL_WINDOWS_USE_D3D9EX" +/** + * A variable controlling whether SDL will clear the window contents when + * the WM_ERASEBKGND message is received. + * + * The variable can be set to the following values: + * + * - "0"/"never": Never clear the window. + * - "1"/"initial": Clear the window when the first WM_ERASEBKGND event fires. (default) + * - "2"/"always": Clear the window on every WM_ERASEBKGND event. + * + * This hint should be set before creating a window. + * + * \since This hint is available since SDL 3.0.0. + */ +#define SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE "SDL_WINDOWS_ERASE_BACKGROUND_MODE" + /** * A variable controlling whether back-button-press events on Windows Phone to * be marked as handled. diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 3ac4912d40..6715178292 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -369,6 +369,21 @@ static SDL_bool ShouldGenerateWindowCloseOnAltF4(void) return SDL_GetHintBoolean(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, SDL_TRUE); } +static SDL_bool ShouldClearWindowOnEraseBackground(SDL_WindowData *data) +{ + switch (data->hint_erase_background_mode) { + case SDL_ERASEBACKGROUNDMODE_NEVER: + return SDL_FALSE; + case SDL_ERASEBACKGROUNDMODE_INITIAL: + return !data->videodata->cleared; + case SDL_ERASEBACKGROUNDMODE_ALWAYS: + return SDL_TRUE; + default: + // Unexpected value, fallback to default behaviour + return !data->videodata->cleared; + } +} + #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) /* We want to generate mouse events from mouse and pen, and touch events from touchscreens */ #define MI_WP_SIGNATURE 0xFF515700 @@ -1689,7 +1704,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara /* We'll do our own drawing, prevent flicker */ case WM_ERASEBKGND: - if (!data->videodata->cleared) { + if (ShouldClearWindowOnEraseBackground(data)) { RECT client_rect; HBRUSH brush; data->videodata->cleared = SDL_TRUE; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d42be71cf7..014a9207ca 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -352,6 +352,30 @@ static void SDLCALL WIN_MouseRelativeModeCenterChanged(void *userdata, const cha data->mouse_relative_mode_center = SDL_GetStringBoolean(hint, SDL_TRUE); } +static SDL_WindowEraseBackgroundMode GetEraseBackgroundModeHint() +{ + const char *hint = SDL_GetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE); + if (!hint) + return SDL_ERASEBACKGROUNDMODE_INITIAL; + + if (SDL_strstr(hint, "never")) + return SDL_ERASEBACKGROUNDMODE_NEVER; + + if (SDL_strstr(hint, "initial")) + return SDL_ERASEBACKGROUNDMODE_INITIAL; + + if (SDL_strstr(hint, "always")) + return SDL_ERASEBACKGROUNDMODE_ALWAYS; + + int mode = SDL_GetStringInteger(hint, 1); + if (mode < 0 || mode > 2) { + SDL_Log("GetEraseBackgroundModeHint: invalid value for SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE. Fallback to default"); + return SDL_ERASEBACKGROUNDMODE_INITIAL; + } + + return mode; +} + static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd, HWND parent) { SDL_VideoData *videodata = _this->driverdata; @@ -377,6 +401,7 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd data->initializing = SDL_TRUE; data->last_displayID = window->last_displayID; data->dwma_border_color = DWMWA_COLOR_DEFAULT; + data->hint_erase_background_mode = GetEraseBackgroundModeHint(); if (SDL_GetHintBoolean("SDL_WINDOW_RETAIN_CONTENT", SDL_FALSE)) { data->copybits_flag = 0; diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index f1f142784b..3cf0f833e3 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -41,6 +41,13 @@ typedef enum SDL_WindowRect SDL_WINDOWRECT_FLOATING } SDL_WindowRect; +typedef enum SDL_WindowEraseBackgroundMode +{ + SDL_ERASEBACKGROUNDMODE_NEVER, + SDL_ERASEBACKGROUNDMODE_INITIAL, + SDL_ERASEBACKGROUNDMODE_ALWAYS, +} SDL_WindowEraseBackgroundMode; + struct SDL_WindowData { SDL_Window *window; @@ -74,6 +81,7 @@ struct SDL_WindowData SDL_DisplayID last_displayID; WCHAR *ICMFileName; SDL_Window *keyboard_focus; + SDL_WindowEraseBackgroundMode hint_erase_background_mode; struct SDL_VideoData *videodata; #ifdef SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface;