From 02f356439d1fedf0b4e5f96cd23c2d570e2f7be2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 8 Nov 2023 13:11:21 -0800 Subject: [PATCH] Allow the application to draw while Windows is in a modal move/resize loop If you're using the application main callbacks, your SDL_AppIterate() function will be called while Windows is moving and resizing your window. If not, then SDL will send an SDL_EVENT_WINDOW_EXPOSED event for your window and you can use an event watcher to redraw your window directly from the callback. Fixes https://github.com/libsdl-org/SDL/issues/1059 Closes https://github.com/libsdl-org/SDL/pull/4836 --- src/main/SDL_main_callbacks.c | 24 ++++++++++++++----- src/main/SDL_main_callbacks.h | 5 ++-- src/main/generic/SDL_sysmain_callbacks.c | 2 +- src/video/windows/SDL_windowsevents.c | 30 ++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/main/SDL_main_callbacks.c b/src/main/SDL_main_callbacks.c index 0c091323f..0e7fb8d28 100644 --- a/src/main/SDL_main_callbacks.c +++ b/src/main/SDL_main_callbacks.c @@ -80,6 +80,14 @@ static int SDLCALL SDL_MainCallbackEventWatcher(void *userdata, SDL_Event *event return 0; } +SDL_bool SDL_HasMainCallbacks() +{ + if (SDL_main_iteration_callback) { + return SDL_TRUE; + } + return SDL_FALSE; +} + int SDL_InitMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, SDL_AppIterate_func appiter, SDL_AppEvent_func appevent, SDL_AppQuit_func appquit) { SDL_main_iteration_callback = appiter; @@ -104,16 +112,20 @@ int SDL_InitMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, SDL_ return SDL_AtomicGet(&apprc); } -int SDL_IterateMainCallbacks(void) +int SDL_IterateMainCallbacks(SDL_bool pump_events) { - SDL_PumpEvents(); + if (pump_events) { + SDL_PumpEvents(); + } SDL_DispatchMainCallbackEvents(); - int rc = SDL_main_iteration_callback(); - if (!SDL_AtomicCAS(&apprc, 0, rc)) { - rc = SDL_AtomicGet(&apprc); // something else already set a quit result, keep that. + int rc = SDL_AtomicGet(&apprc); + if (rc == 0) { + rc = SDL_main_iteration_callback(); + if (!SDL_AtomicCAS(&apprc, 0, rc)) { + rc = SDL_AtomicGet(&apprc); // something else already set a quit result, keep that. + } } - return rc; } diff --git a/src/main/SDL_main_callbacks.h b/src/main/SDL_main_callbacks.h index 9df171a99..229fec776 100644 --- a/src/main/SDL_main_callbacks.h +++ b/src/main/SDL_main_callbacks.h @@ -22,8 +22,9 @@ #ifndef SDL_main_callbacks_h_ #define SDL_main_callbacks_h_ -int SDL_InitMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, SDL_AppIterate_func _appiter, SDL_AppEvent_func _appevent, SDL_AppQuit_func _appquit); -int SDL_IterateMainCallbacks(void); +SDL_bool SDL_HasMainCallbacks(); +int SDL_InitMainCallbacks(int argc, char *argv[], SDL_AppInit_func appinit, SDL_AppIterate_func _appiter, SDL_AppEvent_func _appevent, SDL_AppQuit_func _appquit); +int SDL_IterateMainCallbacks(SDL_bool pump_events); void SDL_QuitMainCallbacks(void); #endif // SDL_main_callbacks_h_ diff --git a/src/main/generic/SDL_sysmain_callbacks.c b/src/main/generic/SDL_sysmain_callbacks.c index 1fcb7d767..fa18e8000 100644 --- a/src/main/generic/SDL_sysmain_callbacks.c +++ b/src/main/generic/SDL_sysmain_callbacks.c @@ -45,7 +45,7 @@ int SDL_EnterAppMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, Uint64 next_iteration = callback_rate_increment ? (SDL_GetTicksNS() + callback_rate_increment) : 0; - while ((rc = SDL_IterateMainCallbacks()) == 0) { + while ((rc = SDL_IterateMainCallbacks(SDL_TRUE)) == 0) { // !!! FIXME: this can be made more complicated if we decide to // !!! FIXME: optionally hand off callback responsibility to the // !!! FIXME: video subsystem (for example, if Wayland has a diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 57abda4fb..cc6b1f63b 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -28,6 +28,7 @@ #include "../../events/SDL_events_c.h" #include "../../events/SDL_touch_c.h" #include "../../events/scancodes_windows.h" +#include "../../main/SDL_main_callbacks.h" /* Dropfile support */ #include @@ -106,6 +107,10 @@ #define IS_SURROGATE_PAIR(h, l) (IS_HIGH_SURROGATE(h) && IS_LOW_SURROGATE(l)) #endif +#ifndef USER_TIMER_MINIMUM +#define USER_TIMER_MINIMUM 0x0000000A +#endif + /* Used to compare Windows message timestamps */ #define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) @@ -1283,6 +1288,31 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } break; + case WM_ENTERSIZEMOVE: + case WM_ENTERMENULOOP: + { + SetTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks, USER_TIMER_MINIMUM, NULL); + } break; + + case WM_TIMER: + { + if (wParam == (UINT_PTR)SDL_IterateMainCallbacks) { + if (SDL_HasMainCallbacks()) { + SDL_IterateMainCallbacks(SDL_FALSE); + } else { + // Send an expose event so the application can redraw + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); + } + return 0; + } + } break; + + case WM_EXITSIZEMOVE: + case WM_EXITMENULOOP: + { + KillTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks); + } break; + case WM_SIZE: { switch (wParam) {