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.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
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)