Browse Source

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
Sam Lantinga 1 year ago
parent
commit
02f356439d

+ 18 - 6
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;
 }
 

+ 3 - 2
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_

+ 1 - 1
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

+ 30 - 0
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) {