From 02f356439d1fedf0b4e5f96cd23c2d570e2f7be2 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 0c091323f03e..0e7fb8d2895f 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 9df171a99c13..229fec7766a0 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 1fcb7d767f8f..fa18e80002c5 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 57abda4fb1bc..cc6b1f63b0ee 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 <shellapi.h>
@@ -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) {