src/core/winrt/SDL_winrtapp_direct3d.cpp
author Sam Lantinga <slouken@libsdl.org>
Mon, 22 Jun 2015 23:36:06 -0700
changeset 9776 952ff8a5076f
parent 9619 b94b6d0bff0f
child 9924 355cccccc7dc
permissions -rw-r--r--
Fixed bug 3030 - SDL_RecreateWindow fails to restore title, icon, etc.

Adam M.

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