src/core/winrt/SDL_winrtapp_direct3d.cpp
author David Ludwig <dludwig@pobox.com>
Sun, 23 Mar 2014 11:04:47 -0400
changeset 8666 20d52dc1a24a
parent 8665 b987c561c218
child 8678 7149b02f106b
permissions -rw-r--r--
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   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 /* Standard C++11 includes */
    24 #include <functional>
    25 #include <string>
    26 #include <sstream>
    27 using namespace std;
    28 
    29 
    30 /* Windows includes */
    31 #include "ppltasks.h"
    32 using namespace concurrency;
    33 using namespace Windows::ApplicationModel;
    34 using namespace Windows::ApplicationModel::Core;
    35 using namespace Windows::ApplicationModel::Activation;
    36 using namespace Windows::Devices::Input;
    37 using namespace Windows::Graphics::Display;
    38 using namespace Windows::Foundation;
    39 using namespace Windows::System;
    40 using namespace Windows::UI::Core;
    41 using namespace Windows::UI::Input;
    42 
    43 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
    44 using namespace Windows::Phone::UI::Input;
    45 #endif
    46 
    47 
    48 /* SDL includes */
    49 extern "C" {
    50 #include "../../SDL_internal.h"
    51 #include "SDL_assert.h"
    52 #include "SDL_events.h"
    53 #include "SDL_hints.h"
    54 #include "SDL_log.h"
    55 #include "SDL_main.h"
    56 #include "SDL_stdinc.h"
    57 #include "SDL_render.h"
    58 #include "../../video/SDL_sysvideo.h"
    59 //#include "../../SDL_hints_c.h"
    60 #include "../../events/SDL_events_c.h"
    61 #include "../../events/SDL_keyboard_c.h"
    62 #include "../../events/SDL_mouse_c.h"
    63 #include "../../events/SDL_windowevents_c.h"
    64 #include "../../render/SDL_sysrender.h"
    65 #include "../windows/SDL_windows.h"
    66 }
    67 
    68 #include "../../video/winrt/SDL_winrtevents_c.h"
    69 #include "../../video/winrt/SDL_winrtvideo_cpp.h"
    70 #include "SDL_winrtapp_common.h"
    71 #include "SDL_winrtapp_direct3d.h"
    72 
    73 
    74 // Compile-time debugging options:
    75 // To enable, uncomment; to disable, comment them out.
    76 //#define LOG_POINTER_EVENTS 1
    77 //#define LOG_WINDOW_EVENTS 1
    78 //#define LOG_ORIENTATION_EVENTS 1
    79 
    80 
    81 // HACK, DLudwig: record a reference to the global, WinRT 'app'/view.
    82 // SDL/WinRT will use this throughout its code.
    83 //
    84 // TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something
    85 // non-global, such as something created inside
    86 // SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside
    87 // SDL_CreateWindow().
    88 SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr;
    89 
    90 ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
    91 {
    92 public:
    93     virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
    94 };
    95 
    96 IFrameworkView^ SDLApplicationSource::CreateView()
    97 {
    98     // TODO, WinRT: see if this function (CreateView) can ever get called
    99     // more than once.  For now, just prevent it from ever assigning
   100     // SDL_WinRTGlobalApp more than once.
   101     SDL_assert(!SDL_WinRTGlobalApp);
   102     SDL_WinRTApp ^ app = ref new SDL_WinRTApp();
   103     if (!SDL_WinRTGlobalApp)
   104     {
   105         SDL_WinRTGlobalApp = app;
   106     }
   107     return app;
   108 }
   109 
   110 int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **))
   111 {
   112     WINRT_SDLAppEntryPoint = mainFunction;
   113     auto direct3DApplicationSource = ref new SDLApplicationSource();
   114     CoreApplication::Run(direct3DApplicationSource);
   115     return 0;
   116 }
   117 
   118 static void WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue)
   119 {
   120     SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0);
   121 
   122     // Start with no orientation flags, then add each in as they're parsed
   123     // from newValue.
   124     unsigned int orientationFlags = 0;
   125     if (newValue) {
   126         std::istringstream tokenizer(newValue);
   127         while (!tokenizer.eof()) {
   128             std::string orientationName;
   129             std::getline(tokenizer, orientationName, ' ');
   130             if (orientationName == "LandscapeLeft") {
   131                 orientationFlags |= (unsigned int) DisplayOrientations::LandscapeFlipped;
   132             } else if (orientationName == "LandscapeRight") {
   133                 orientationFlags |= (unsigned int) DisplayOrientations::Landscape;
   134             } else if (orientationName == "Portrait") {
   135                 orientationFlags |= (unsigned int) DisplayOrientations::Portrait;
   136             } else if (orientationName == "PortraitUpsideDown") {
   137                 orientationFlags |= (unsigned int) DisplayOrientations::PortraitFlipped;
   138             }
   139         }
   140     }
   141 
   142     // If no valid orientation flags were specified, use a reasonable set of defaults:
   143     if (!orientationFlags) {
   144         // TODO, WinRT: consider seeing if an app's default orientation flags can be found out via some API call(s).
   145         orientationFlags = (unsigned int) ( \
   146             DisplayOrientations::Landscape |
   147             DisplayOrientations::LandscapeFlipped |
   148             DisplayOrientations::Portrait |
   149             DisplayOrientations::PortraitFlipped);
   150     }
   151 
   152     // Set the orientation/rotation preferences.  Please note that this does
   153     // not constitute a 100%-certain lock of a given set of possible
   154     // orientations.  According to Microsoft's documentation on WinRT [1]
   155     // when a device is not capable of being rotated, Windows may ignore
   156     // the orientation preferences, and stick to what the device is capable of
   157     // displaying.
   158     //
   159     // [1] Documentation on the 'InitialRotationPreference' setting for a
   160     // Windows app's manifest file describes how some orientation/rotation
   161     // preferences may be ignored.  See
   162     // http://msdn.microsoft.com/en-us/library/windows/apps/hh700343.aspx
   163     // for details.  Microsoft's "Display orientation sample" also gives an
   164     // outline of how Windows treats device rotation
   165     // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93).
   166 #if NTDDI_VERSION > NTDDI_WIN8
   167     DisplayInformation::AutoRotationPreferences = (DisplayOrientations) orientationFlags;
   168 #else
   169     DisplayProperties::AutoRotationPreferences = (DisplayOrientations) orientationFlags;
   170 #endif
   171 }
   172 
   173 static void
   174 WINRT_ProcessWindowSizeChange()
   175 {
   176     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   177 
   178     // Make the new window size be the one true fullscreen mode.
   179     // This change was initially done, in part, to allow the Direct3D 11.1
   180     // renderer to receive window-resize events as a device rotates.
   181     // Before, rotating a device from landscape, to portrait, and then
   182     // back to landscape would cause the Direct3D 11.1 swap buffer to
   183     // not get resized appropriately.  SDL would, on the rotation from
   184     // landscape to portrait, re-resize the SDL window to it's initial
   185     // size (landscape).  On the subsequent rotation, SDL would drop the
   186     // window-resize event as it appeared the SDL window didn't change
   187     // size, and the Direct3D 11.1 renderer wouldn't resize its swap
   188     // chain.
   189     SDL_DisplayMode newDisplayMode;
   190     if (WINRT_CalcDisplayModeUsingNativeWindow(&newDisplayMode) != 0) {
   191         return;
   192     }
   193 
   194     // Make note of the old display mode, and it's old driverdata.
   195     SDL_DisplayMode oldDisplayMode;
   196     SDL_zero(oldDisplayMode);
   197     if (_this) {
   198         oldDisplayMode = _this->displays[0].desktop_mode;
   199     }
   200 
   201     // Setup the new display mode in the appropriate spots.
   202     if (_this) {
   203         // Make a full copy of the display mode for display_modes[0],
   204         // one with with a separately malloced 'driverdata' field.
   205         // SDL_VideoQuit(), if called, will attempt to free the driverdata
   206         // fields in 'desktop_mode' and each entry in the 'display_modes'
   207         // array.
   208         if (_this->displays[0].display_modes[0].driverdata) {
   209             // Free the previous mode's memory
   210             SDL_free(_this->displays[0].display_modes[0].driverdata);
   211             _this->displays[0].display_modes[0].driverdata = NULL;
   212         }
   213         if (WINRT_DuplicateDisplayMode(&(_this->displays[0].display_modes[0]), &newDisplayMode) != 0) {
   214             // Uh oh, something went wrong.  A malloc call probably failed.
   215             SDL_free(newDisplayMode.driverdata);
   216             return;
   217         }
   218 
   219         // Install 'newDisplayMode' into 'current_mode' and 'desktop_mode'.
   220         _this->displays[0].current_mode = newDisplayMode;
   221         _this->displays[0].desktop_mode = newDisplayMode;
   222     }
   223 
   224     if (WINRT_GlobalSDLWindow) {
   225         // Send a window-resize event to the rest of SDL, and to apps:
   226         SDL_SendWindowEvent(
   227             WINRT_GlobalSDLWindow,
   228             SDL_WINDOWEVENT_RESIZED,
   229             newDisplayMode.w,
   230             newDisplayMode.h);
   231 
   232 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   233         // HACK: On Windows Phone, make sure that orientation changes from
   234         // Landscape to LandscapeFlipped, Portrait to PortraitFlipped,
   235         // or vice-versa on either of those two, lead to the Direct3D renderer
   236         // getting updated.
   237         const DisplayOrientations oldOrientation = ((SDL_DisplayModeData *)oldDisplayMode.driverdata)->currentOrientation;
   238         const DisplayOrientations newOrientation = ((SDL_DisplayModeData *)newDisplayMode.driverdata)->currentOrientation;
   239 
   240         if ((oldOrientation == DisplayOrientations::Landscape && newOrientation == DisplayOrientations::LandscapeFlipped) ||
   241             (oldOrientation == DisplayOrientations::LandscapeFlipped && newOrientation == DisplayOrientations::Landscape) ||
   242             (oldOrientation == DisplayOrientations::Portrait && newOrientation == DisplayOrientations::PortraitFlipped) ||
   243             (oldOrientation == DisplayOrientations::PortraitFlipped && newOrientation == DisplayOrientations::Portrait))
   244         {
   245             // One of the reasons this event is getting sent out is because SDL
   246             // will ignore requests to send out SDL_WINDOWEVENT_RESIZED events
   247             // if and when the event size doesn't change (and the Direct3D 11.1
   248             // renderer doesn't get the memo).
   249             //
   250             // Make sure that the display/window size really didn't change.  If
   251             // it did, then a SDL_WINDOWEVENT_SIZE_CHANGED event got sent, and
   252             // the Direct3D 11.1 renderer picked it up, presumably.
   253             if (oldDisplayMode.w == newDisplayMode.w &&
   254                 oldDisplayMode.h == newDisplayMode.h)
   255             {
   256                 SDL_SendWindowEvent(
   257                     WINRT_GlobalSDLWindow,
   258                     SDL_WINDOWEVENT_SIZE_CHANGED,
   259                     newDisplayMode.w,
   260                     newDisplayMode.h);
   261             }
   262         }
   263 #endif
   264     }
   265     
   266     // Finally, free the 'driverdata' field of the old 'desktop_mode'.
   267     if (oldDisplayMode.driverdata) {
   268         SDL_free(oldDisplayMode.driverdata);
   269         oldDisplayMode.driverdata = NULL;
   270     }
   271 }
   272 
   273 SDL_WinRTApp::SDL_WinRTApp() :
   274     m_windowClosed(false),
   275     m_windowVisible(true)
   276 {
   277 }
   278 
   279 void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
   280 {
   281     applicationView->Activated +=
   282         ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnActivated);
   283 
   284     CoreApplication::Suspending +=
   285         ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
   286 
   287     CoreApplication::Resuming +=
   288         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
   289 
   290     CoreApplication::Exiting +=
   291         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
   292 }
   293 
   294 #if NTDDI_VERSION > NTDDI_WIN8
   295 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
   296 #else
   297 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
   298 #endif
   299 {
   300 #if LOG_ORIENTATION_EVENTS==1
   301     CoreWindow^ window = CoreWindow::GetForCurrentThread();
   302     if (window) {
   303         SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Size={%f,%f}\n",
   304             __FUNCTION__,
   305             (int)DisplayProperties::CurrentOrientation,
   306             (int)DisplayProperties::NativeOrientation,
   307             (int)DisplayProperties::AutoRotationPreferences,
   308             window->Bounds.Width,
   309             window->Bounds.Height);
   310     } else {
   311         SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
   312             __FUNCTION__,
   313             (int)DisplayProperties::CurrentOrientation,
   314             (int)DisplayProperties::NativeOrientation,
   315             (int)DisplayProperties::AutoRotationPreferences);
   316     }
   317 #endif
   318 
   319 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   320     // On Windows Phone, treat an orientation change as a change in window size.
   321     // The native window's size doesn't seem to change, however SDL will simulate
   322     // a window size change.
   323     WINRT_ProcessWindowSizeChange();
   324 #endif
   325 }
   326 
   327 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
   328 {
   329 #if LOG_WINDOW_EVENTS==1
   330     SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window Size={%f,%f}\n",
   331         __FUNCTION__,
   332         (int)DisplayProperties::CurrentOrientation,
   333         (int)DisplayProperties::NativeOrientation,
   334         (int)DisplayProperties::AutoRotationPreferences,
   335         window->Bounds.Width,
   336         window->Bounds.Height);
   337 #endif
   338 
   339     window->SizeChanged += 
   340         ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
   341 
   342     window->VisibilityChanged +=
   343         ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
   344 
   345     window->Closed += 
   346         ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
   347 
   348 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
   349     window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
   350 #endif
   351 
   352     window->PointerPressed +=
   353         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
   354 
   355     window->PointerMoved +=
   356         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
   357 
   358     window->PointerReleased +=
   359         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
   360 
   361     window->PointerWheelChanged +=
   362         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
   363 
   364 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
   365     // Retrieves relative-only mouse movements:
   366     Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
   367         ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
   368 #endif
   369 
   370     window->KeyDown +=
   371         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
   372 
   373     window->KeyUp +=
   374         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
   375 
   376 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   377     HardwareButtons::BackPressed +=
   378         ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
   379 #endif
   380 
   381 #if NTDDI_VERSION > NTDDI_WIN8
   382     DisplayInformation::GetForCurrentView()->OrientationChanged +=
   383         ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
   384 #else
   385     DisplayProperties::OrientationChanged +=
   386         ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
   387 #endif
   388 
   389     // Register the hint, SDL_HINT_ORIENTATIONS, with SDL.
   390     // TODO, WinRT: see if an app's default orientation can be found out via WinRT API(s), then set the initial value of SDL_HINT_ORIENTATIONS accordingly.
   391     SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
   392 
   393 #if WINAPI_FAMILY == WINAPI_FAMILY_APP  // for Windows 8/8.1/RT apps... (and not Phone apps)
   394     // Make sure we know when a user has opened the app's settings pane.
   395     // This is needed in order to display a privacy policy, which needs
   396     // to be done for network-enabled apps, as per Windows Store requirements.
   397     using namespace Windows::UI::ApplicationSettings;
   398     SettingsPane::GetForCurrentView()->CommandsRequested +=
   399         ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
   400             (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
   401 #endif
   402 }
   403 
   404 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
   405 {
   406 }
   407 
   408 void SDL_WinRTApp::Run()
   409 {
   410     SDL_SetMainReady();
   411     if (WINRT_SDLAppEntryPoint)
   412     {
   413         // TODO, WinRT: pass the C-style main() a reasonably realistic
   414         // representation of command line arguments.
   415         int argc = 0;
   416         char **argv = NULL;
   417         WINRT_SDLAppEntryPoint(argc, argv);
   418     }
   419 }
   420 
   421 static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
   422 {
   423     SDL_Event events[128];
   424     const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
   425     for (int i = 0; i < count; ++i) {
   426         if (events[i].window.event == windowEventID) {
   427             return true;
   428         }
   429     }
   430     return false;
   431 }
   432 
   433 bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
   434 {
   435     /* Don't wait if the app is visible: */
   436     if (m_windowVisible) {
   437         return false;
   438     }
   439     
   440     /* Don't wait until the window-hide events finish processing.
   441      * Do note that if an app-suspend event is sent (as indicated
   442      * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
   443      * events), then this code may be a moot point, as WinRT's
   444      * own event pump (aka ProcessEvents()) will pause regardless
   445      * of what we do here.  This happens on Windows Phone 8, to note.
   446      * Windows 8.x apps, on the other hand, may get a chance to run
   447      * these.
   448      */
   449     if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
   450         return false;
   451     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
   452         return false;
   453     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
   454         return false;
   455     }
   456 
   457     return true;
   458 }
   459 
   460 void SDL_WinRTApp::PumpEvents()
   461 {
   462     if (!m_windowClosed) {
   463         if (!ShouldWaitForAppResumeEvents()) {
   464             /* This is the normal way in which events should be pumped.
   465              * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
   466              * from zero to N events, and will then return.
   467              */
   468             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
   469         } else {
   470             /* This style of event-pumping, with 'ProcessOneAndAllPending',
   471              * will cause anywhere from one to N events to be processed.  If
   472              * at least one event is processed, the call will return.  If
   473              * no events are pending, then the call will wait until one is
   474              * available, and will not return (to the caller) until this
   475              * happens!  This should only occur when the app is hidden.
   476              */
   477             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
   478         }
   479     }
   480 }
   481 
   482 void SDL_WinRTApp::Uninitialize()
   483 {
   484 }
   485 
   486 #if WINAPI_FAMILY == WINAPI_FAMILY_APP
   487 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
   488     Windows::UI::ApplicationSettings::SettingsPane ^p,
   489     Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
   490 {
   491     using namespace Platform;
   492     using namespace Windows::UI::ApplicationSettings;
   493     using namespace Windows::UI::Popups;
   494 
   495     String ^privacyPolicyURL = nullptr;     // a URL to an app's Privacy Policy
   496     String ^privacyPolicyLabel = nullptr;   // label/link text
   497     const char *tmpHintValue = NULL;        // SDL_GetHint-retrieved value, used immediately
   498     wchar_t *tmpStr = NULL;                 // used for UTF8 to UCS2 conversion
   499 
   500     // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
   501     tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
   502     if (tmpHintValue && tmpHintValue[0] != '\0') {
   503         // Convert the privacy policy's URL to UCS2:
   504         tmpStr = WIN_UTF8ToString(tmpHintValue);
   505         privacyPolicyURL = ref new String(tmpStr);
   506         SDL_free(tmpStr);
   507 
   508         // Optionally retrieve custom label-text for the link.  If this isn't
   509         // available, a default value will be used instead.
   510         tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
   511         if (tmpHintValue && tmpHintValue[0] != '\0') {
   512             tmpStr = WIN_UTF8ToString(tmpHintValue);
   513             privacyPolicyLabel = ref new String(tmpStr);
   514             SDL_free(tmpStr);
   515         } else {
   516             privacyPolicyLabel = ref new String(L"Privacy Policy");
   517         }
   518 
   519         // Register the link, along with a handler to be called if and when it is
   520         // clicked:
   521         auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
   522             ref new UICommandInvokedHandler([=](IUICommand ^) {
   523                 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
   524         }));
   525         args->Request->ApplicationCommands->Append(cmd);
   526     }
   527 }
   528 #endif // if WINAPI_FAMILY == WINAPI_FAMILY_APP
   529 
   530 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
   531 {
   532 #if LOG_WINDOW_EVENTS==1
   533     SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n",
   534         __FUNCTION__,
   535         args->Size.Width, args->Size.Height,
   536         (int)DisplayProperties::CurrentOrientation,
   537         (int)DisplayProperties::NativeOrientation,
   538         (int)DisplayProperties::AutoRotationPreferences,
   539         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   540 #endif
   541 
   542     WINRT_ProcessWindowSizeChange();
   543 }
   544 
   545 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
   546 {
   547 #if LOG_WINDOW_EVENTS==1
   548     SDL_Log("%s, visible?=%s, WINRT_GlobalSDLWindow?=%s\n",
   549         __FUNCTION__,
   550         (args->Visible ? "yes" : "no"),
   551         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   552 #endif
   553 
   554     m_windowVisible = args->Visible;
   555     if (WINRT_GlobalSDLWindow) {
   556         SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
   557 
   558         if (args->Visible) {
   559             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
   560             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
   561             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
   562         } else {
   563             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   564             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   565             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   566         }
   567 
   568         // HACK: Prevent SDL's window-hide handling code, which currently
   569         // triggers a fake window resize (possibly erronously), from
   570         // marking the SDL window's surface as invalid.
   571         //
   572         // A better solution to this probably involves figuring out if the
   573         // fake window resize can be prevented.
   574         WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
   575     }
   576 }
   577 
   578 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
   579 {
   580 #if LOG_WINDOW_EVENTS==1
   581     SDL_Log("%s\n", __FUNCTION__);
   582 #endif
   583     m_windowClosed = true;
   584 }
   585 
   586 void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
   587 {
   588     CoreWindow::GetForCurrentThread()->Activate();
   589 }
   590 
   591 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
   592 {
   593     // Save app state asynchronously after requesting a deferral. Holding a deferral
   594     // indicates that the application is busy performing suspending operations. Be
   595     // aware that a deferral may not be held indefinitely. After about five seconds,
   596     // the app will be forced to exit.
   597 
   598     // ... but first, let the app know it's about to go to the background.
   599     // The separation of events may be important, given that the deferral
   600     // runs in a separate thread.  This'll make SDL_APP_WILLENTERBACKGROUND
   601     // the only event among the two that runs in the main thread.  Given
   602     // that a few WinRT operations can only be done from the main thread
   603     // (things that access the WinRT CoreWindow are one example of this),
   604     // this could be important.
   605     SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
   606 
   607     SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
   608     create_task([this, deferral]()
   609     {
   610         // Send an app did-enter-background event immediately to observers.
   611         // CoreDispatcher::ProcessEvents, which is the backbone on which
   612         // SDL_WinRTApp::PumpEvents is built, will not return to its caller
   613         // once it sends out a suspend event.  Any events posted to SDL's
   614         // event queue won't get received until the WinRT app is resumed.
   615         // SDL_AddEventWatch() may be used to receive app-suspend events on
   616         // WinRT.
   617         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
   618 
   619         deferral->Complete();
   620     });
   621 }
   622 
   623 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
   624 {
   625     // Restore any data or state that was unloaded on suspend. By default, data
   626     // and state are persisted when resuming from suspend. Note that these events
   627     // do not occur if the app was previously terminated.
   628     SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
   629     SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
   630 }
   631 
   632 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
   633 {
   634     SDL_SendAppEvent(SDL_APP_TERMINATING);
   635 }
   636 
   637 static void
   638 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
   639 {
   640     Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
   641     SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d\n",
   642         header,
   643         pt->Position.X, pt->Position.Y,
   644         transformedPoint.X, transformedPoint.Y,
   645         pt->Properties->MouseWheelDelta,
   646         pt->FrameId,
   647         pt->PointerId,
   648         WINRT_GetSDLButtonForPointerPoint(pt));
   649 }
   650 
   651 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
   652 {
   653 #if LOG_POINTER_EVENTS
   654     WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   655 #endif
   656 
   657     WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   658 }
   659 
   660 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
   661 {
   662 #if LOG_POINTER_EVENTS
   663     WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   664 #endif
   665 
   666     WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   667 }
   668 
   669 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
   670 {
   671 #if LOG_POINTER_EVENTS
   672     WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   673 #endif
   674 
   675     WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   676 }
   677 
   678 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
   679 {
   680 #if LOG_POINTER_EVENTS
   681     WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   682 #endif
   683 
   684     WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   685 }
   686 
   687 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
   688 {
   689     WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
   690 }
   691 
   692 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   693 {
   694     WINRT_ProcessKeyDownEvent(args);
   695 }
   696 
   697 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   698 {
   699     WINRT_ProcessKeyUpEvent(args);
   700 }
   701 
   702 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   703 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
   704 {
   705     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
   706     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
   707 
   708     const char *hint = SDL_GetHint(SDL_HINT_WINRT_HANDLE_BACK_BUTTON);
   709     if (hint) {
   710         if (*hint == '1') {
   711             args->Handled = true;
   712         }
   713     }
   714 }
   715 #endif
   716