Commit c2243092d7a5248bcdd8624c6b7fc224e67e04c4

David Ludwig 2014-03-23T11:04:47

WinRT: Miscellaneous app-backgrounding/restoring event fixes and additions SDL_WINDOWEVENT_FOCUS_LOST is now sent when an app's native window is hidden. Likewise, SDL_WINDOWEVENT_FOCUS_GAINED is sent when an app's window is shown. This mimicks behavior seen on iOS and Android. SDL_WINDOWEVENT_MINIMIZED and SDL_WINDOWEVENT_RESTORED are now sent when the app's native window is hidden and shown. Previously, these were sent when an app was suspended and resumed. On Windows 8.x/RT, an app may be sent to the background without being suspended, which previously meant that SDL_WINDOWEVENT_MINIMIZED might never have been sent. (On Windows Phone 8, however, this seems to be different, whereby apps sent to the background appear to always get suspended.)

diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index 49880f2..96b539f 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -418,16 +418,62 @@ void SDL_WinRTApp::Run()
     }
 }
 
+static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
+{
+    SDL_Event events[128];
+    const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
+    for (int i = 0; i < count; ++i) {
+        if (events[i].window.event == windowEventID) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
+{
+    /* Don't wait if the app is visible: */
+    if (m_windowVisible) {
+        return false;
+    }
+    
+    /* Don't wait until the window-hide events finish processing.
+     * Do note that if an app-suspend event is sent (as indicated
+     * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
+     * events), then this code may be a moot point, as WinRT's
+     * own event pump (aka ProcessEvents()) will pause regardless
+     * of what we do here.  This happens on Windows Phone 8, to note.
+     * Windows 8.x apps, on the other hand, may get a chance to run
+     * these.
+     */
+    if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
+        return false;
+    } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
+        return false;
+    } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
+        return false;
+    }
+
+    return true;
+}
+
 void SDL_WinRTApp::PumpEvents()
 {
-    if (!m_windowClosed)
-    {
-        if (m_windowVisible)
-        {
+    if (!m_windowClosed) {
+        if (!ShouldWaitForAppResumeEvents()) {
+            /* This is the normal way in which events should be pumped.
+             * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
+             * from zero to N events, and will then return.
+             */
             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
-        }
-        else
-        {
+        } else {
+            /* This style of event-pumping, with 'ProcessOneAndAllPending',
+             * will cause anywhere from one to N events to be processed.  If
+             * at least one event is processed, the call will return.  If
+             * no events are pending, then the call will wait until one is
+             * available, and will not return (to the caller) until this
+             * happens!  This should only occur when the app is hidden.
+             */
             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
         }
     }
@@ -511,8 +557,12 @@ void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEven
 
         if (args->Visible) {
             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
+            SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
+            SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
         } else {
             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
+            SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
+            SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
         }
 
         // HACK: Prevent SDL's window-hide handling code, which currently
@@ -538,26 +588,6 @@ void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedE
     CoreWindow::GetForCurrentThread()->Activate();
 }
 
-static int SDLCALL RemoveAppSuspendAndResumeEvents(void * userdata, SDL_Event * event)
-{
-    if (event->type == SDL_WINDOWEVENT)
-    {
-        switch (event->window.event)
-        {
-            case SDL_WINDOWEVENT_MINIMIZED:
-            case SDL_WINDOWEVENT_RESTORED:
-                // Return 0 to indicate that the event should be removed from the
-                // event queue:
-                return 0;
-            default:
-                break;
-        }
-    }
-
-    // Return 1 to indicate that the event should stay in the event queue:
-    return 1;
-}
-
 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
 {
     // Save app state asynchronously after requesting a deferral. Holding a deferral
@@ -577,24 +607,13 @@ void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ a
     SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
     create_task([this, deferral]()
     {
-        // Send a window-minimized event immediately to observers.
+        // Send an app did-enter-background event immediately to observers.
         // CoreDispatcher::ProcessEvents, which is the backbone on which
         // SDL_WinRTApp::PumpEvents is built, will not return to its caller
         // once it sends out a suspend event.  Any events posted to SDL's
         // event queue won't get received until the WinRT app is resumed.
         // SDL_AddEventWatch() may be used to receive app-suspend events on
         // WinRT.
-        //
-        // In order to prevent app-suspend events from being received twice:
-        // first via a callback passed to SDL_AddEventWatch, and second via
-        // SDL's event queue, the event will be sent to SDL, then immediately
-        // removed from the queue.
-        if (WINRT_GlobalSDLWindow)
-        {
-            SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);   // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
-            SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
-        }
-
         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
 
         deferral->Complete();
@@ -603,24 +622,11 @@ void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ a
 
 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
 {
+    // Restore any data or state that was unloaded on suspend. By default, data
+    // and state are persisted when resuming from suspend. Note that these events
+    // do not occur if the app was previously terminated.
     SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
     SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
-
-    // Restore any data or state that was unloaded on suspend. By default, data
-    // and state are persisted when resuming from suspend. Note that this event
-    // does not occur if the app was previously terminated.
-    if (WINRT_GlobalSDLWindow)
-    {
-        SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);    // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
-
-        // Remove the app-resume event from the queue, as is done with the
-        // app-suspend event.
-        //
-        // TODO, WinRT: consider posting this event to the queue even though
-        // its counterpart, the app-suspend event, effectively has to be
-        // processed immediately.
-        SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
-    }
 }
 
 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.h b/src/core/winrt/SDL_winrtapp_direct3d.h
index 6dc9a6c..714d41c 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.h
+++ b/src/core/winrt/SDL_winrtapp_direct3d.h
@@ -39,6 +39,8 @@ internal:
     void PumpEvents();
 
 protected:
+    bool ShouldWaitForAppResumeEvents();
+
     // Event Handlers.
 
 #if WINAPI_FAMILY == WINAPI_FAMILY_APP  // for Windows 8/8.1/RT apps... (and not Phone apps)