src/core/winrt/SDL_winrtapp_direct3d.cpp
author David Ludwig <dludwig@pobox.com>
Sat, 15 Mar 2014 14:54:23 -0400
changeset 8621 5252788cb448
parent 8616 ec011c16e2fd
child 8664 565698ab1c40
permissions -rw-r--r--
Fixed broken rotation detection routines on WinRT

Rotation detection and handling should now work across all, publicly-released,
WinRT-based platforms (Windows 8.0, Windows 8.1, and Windows Phone 8.0).
     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     // Make the new window size be the one true fullscreen mode.
   177     // This change was initially done, in part, to allow the Direct3D 11.1
   178     // renderer to receive window-resize events as a device rotates.
   179     // Before, rotating a device from landscape, to portrait, and then
   180     // back to landscape would cause the Direct3D 11.1 swap buffer to
   181     // not get resized appropriately.  SDL would, on the rotation from
   182     // landscape to portrait, re-resize the SDL window to it's initial
   183     // size (landscape).  On the subsequent rotation, SDL would drop the
   184     // window-resize event as it appeared the SDL window didn't change
   185     // size, and the Direct3D 11.1 renderer wouldn't resize its swap
   186     // chain.
   187     SDL_DisplayMode newDisplayMode;
   188     if (WINRT_CalcDisplayModeUsingNativeWindow(&newDisplayMode) != 0) {
   189         return;
   190     }
   191 
   192     // Make note of the old display mode, and it's old driverdata.
   193     SDL_DisplayMode oldDisplayMode;
   194     SDL_zero(oldDisplayMode);
   195     if (WINRT_GlobalSDLVideoDevice) {
   196         oldDisplayMode = WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode;
   197     }
   198 
   199     // Setup the new display mode in the appropriate spots.
   200     if (WINRT_GlobalSDLVideoDevice) {
   201         // Make a full copy of the display mode for display_modes[0],
   202         // one with with a separately malloced 'driverdata' field.
   203         // SDL_VideoQuit(), if called, will attempt to free the driverdata
   204         // fields in 'desktop_mode' and each entry in the 'display_modes'
   205         // array.
   206         if (WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata) {
   207             // Free the previous mode's memory
   208             SDL_free(WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata);
   209             WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata = NULL;
   210         }
   211         if (WINRT_DuplicateDisplayMode(&(WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0]), &newDisplayMode) != 0) {
   212             // Uh oh, something went wrong.  A malloc call probably failed.
   213             SDL_free(newDisplayMode.driverdata);
   214             return;
   215         }
   216 
   217         // Install 'newDisplayMode' into 'current_mode' and 'desktop_mode'.
   218         WINRT_GlobalSDLVideoDevice->displays[0].current_mode = newDisplayMode;
   219         WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode = newDisplayMode;
   220     }
   221 
   222     if (WINRT_GlobalSDLWindow) {
   223         // Send a window-resize event to the rest of SDL, and to apps:
   224         SDL_SendWindowEvent(
   225             WINRT_GlobalSDLWindow,
   226             SDL_WINDOWEVENT_RESIZED,
   227             newDisplayMode.w,
   228             newDisplayMode.h);
   229 
   230 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   231         // HACK: On Windows Phone, make sure that orientation changes from
   232         // Landscape to LandscapeFlipped, Portrait to PortraitFlipped,
   233         // or vice-versa on either of those two, lead to the Direct3D renderer
   234         // getting updated.
   235         const DisplayOrientations oldOrientation = ((SDL_DisplayModeData *)oldDisplayMode.driverdata)->currentOrientation;
   236         const DisplayOrientations newOrientation = ((SDL_DisplayModeData *)newDisplayMode.driverdata)->currentOrientation;
   237 
   238         if ((oldOrientation == DisplayOrientations::Landscape && newOrientation == DisplayOrientations::LandscapeFlipped) ||
   239             (oldOrientation == DisplayOrientations::LandscapeFlipped && newOrientation == DisplayOrientations::Landscape) ||
   240             (oldOrientation == DisplayOrientations::Portrait && newOrientation == DisplayOrientations::PortraitFlipped) ||
   241             (oldOrientation == DisplayOrientations::PortraitFlipped && newOrientation == DisplayOrientations::Portrait))
   242         {
   243             // One of the reasons this event is getting sent out is because SDL
   244             // will ignore requests to send out SDL_WINDOWEVENT_RESIZED events
   245             // if and when the event size doesn't change (and the Direct3D 11.1
   246             // renderer doesn't get the memo).
   247             //
   248             // Make sure that the display/window size really didn't change.  If
   249             // it did, then a SDL_WINDOWEVENT_SIZE_CHANGED event got sent, and
   250             // the Direct3D 11.1 renderer picked it up, presumably.
   251             if (oldDisplayMode.w == newDisplayMode.w &&
   252                 oldDisplayMode.h == newDisplayMode.h)
   253             {
   254                 SDL_SendWindowEvent(
   255                     WINRT_GlobalSDLWindow,
   256                     SDL_WINDOWEVENT_SIZE_CHANGED,
   257                     newDisplayMode.w,
   258                     newDisplayMode.h);
   259             }
   260         }
   261 #endif
   262     }
   263     
   264     // Finally, free the 'driverdata' field of the old 'desktop_mode'.
   265     if (oldDisplayMode.driverdata) {
   266         SDL_free(oldDisplayMode.driverdata);
   267         oldDisplayMode.driverdata = NULL;
   268     }
   269 }
   270 
   271 SDL_WinRTApp::SDL_WinRTApp() :
   272     m_windowClosed(false),
   273     m_windowVisible(true)
   274 {
   275 }
   276 
   277 void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
   278 {
   279     applicationView->Activated +=
   280         ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnActivated);
   281 
   282     CoreApplication::Suspending +=
   283         ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
   284 
   285     CoreApplication::Resuming +=
   286         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
   287 
   288     CoreApplication::Exiting +=
   289         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
   290 }
   291 
   292 #if NTDDI_VERSION > NTDDI_WIN8
   293 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
   294 #else
   295 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
   296 #endif
   297 {
   298 #if LOG_ORIENTATION_EVENTS==1
   299     CoreWindow^ window = CoreWindow::GetForCurrentThread();
   300     if (window) {
   301         SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Size={%f,%f}\n",
   302             __FUNCTION__,
   303             (int)DisplayProperties::CurrentOrientation,
   304             (int)DisplayProperties::NativeOrientation,
   305             (int)DisplayProperties::AutoRotationPreferences,
   306             window->Bounds.Width,
   307             window->Bounds.Height);
   308     } else {
   309         SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
   310             __FUNCTION__,
   311             (int)DisplayProperties::CurrentOrientation,
   312             (int)DisplayProperties::NativeOrientation,
   313             (int)DisplayProperties::AutoRotationPreferences);
   314     }
   315 #endif
   316 
   317 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   318     // On Windows Phone, treat an orientation change as a change in window size.
   319     // The native window's size doesn't seem to change, however SDL will simulate
   320     // a window size change.
   321     WINRT_ProcessWindowSizeChange();
   322 #endif
   323 }
   324 
   325 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
   326 {
   327 #if LOG_WINDOW_EVENTS==1
   328     SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window Size={%f,%f}\n",
   329         __FUNCTION__,
   330         (int)DisplayProperties::CurrentOrientation,
   331         (int)DisplayProperties::NativeOrientation,
   332         (int)DisplayProperties::AutoRotationPreferences,
   333         window->Bounds.Width,
   334         window->Bounds.Height);
   335 #endif
   336 
   337     window->SizeChanged += 
   338         ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
   339 
   340     window->VisibilityChanged +=
   341         ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
   342 
   343     window->Closed += 
   344         ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
   345 
   346 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
   347     window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
   348 #endif
   349 
   350     window->PointerPressed +=
   351         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
   352 
   353     window->PointerMoved +=
   354         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
   355 
   356     window->PointerReleased +=
   357         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
   358 
   359     window->PointerWheelChanged +=
   360         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
   361 
   362 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
   363     // Retrieves relative-only mouse movements:
   364     Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
   365         ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
   366 #endif
   367 
   368     window->KeyDown +=
   369         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
   370 
   371     window->KeyUp +=
   372         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
   373 
   374 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   375     HardwareButtons::BackPressed +=
   376         ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
   377 #endif
   378 
   379 #if NTDDI_VERSION > NTDDI_WIN8
   380     DisplayInformation::GetForCurrentView()->OrientationChanged +=
   381         ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
   382 #else
   383     DisplayProperties::OrientationChanged +=
   384         ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
   385 #endif
   386 
   387     // Register the hint, SDL_HINT_ORIENTATIONS, with SDL.
   388     // 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.
   389     SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
   390 
   391 #if WINAPI_FAMILY == WINAPI_FAMILY_APP  // for Windows 8/8.1/RT apps... (and not Phone apps)
   392     // Make sure we know when a user has opened the app's settings pane.
   393     // This is needed in order to display a privacy policy, which needs
   394     // to be done for network-enabled apps, as per Windows Store requirements.
   395     using namespace Windows::UI::ApplicationSettings;
   396     SettingsPane::GetForCurrentView()->CommandsRequested +=
   397         ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
   398             (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
   399 #endif
   400 }
   401 
   402 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
   403 {
   404 }
   405 
   406 void SDL_WinRTApp::Run()
   407 {
   408     SDL_SetMainReady();
   409     if (WINRT_SDLAppEntryPoint)
   410     {
   411         // TODO, WinRT: pass the C-style main() a reasonably realistic
   412         // representation of command line arguments.
   413         int argc = 0;
   414         char **argv = NULL;
   415         WINRT_SDLAppEntryPoint(argc, argv);
   416     }
   417 }
   418 
   419 void SDL_WinRTApp::PumpEvents()
   420 {
   421     if (!m_windowClosed)
   422     {
   423         if (m_windowVisible)
   424         {
   425             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
   426         }
   427         else
   428         {
   429             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
   430         }
   431     }
   432 }
   433 
   434 void SDL_WinRTApp::Uninitialize()
   435 {
   436 }
   437 
   438 #if WINAPI_FAMILY == WINAPI_FAMILY_APP
   439 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
   440     Windows::UI::ApplicationSettings::SettingsPane ^p,
   441     Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
   442 {
   443     using namespace Platform;
   444     using namespace Windows::UI::ApplicationSettings;
   445     using namespace Windows::UI::Popups;
   446 
   447     String ^privacyPolicyURL = nullptr;     // a URL to an app's Privacy Policy
   448     String ^privacyPolicyLabel = nullptr;   // label/link text
   449     const char *tmpHintValue = NULL;        // SDL_GetHint-retrieved value, used immediately
   450     wchar_t *tmpStr = NULL;                 // used for UTF8 to UCS2 conversion
   451 
   452     // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
   453     tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
   454     if (tmpHintValue && tmpHintValue[0] != '\0') {
   455         // Convert the privacy policy's URL to UCS2:
   456         tmpStr = WIN_UTF8ToString(tmpHintValue);
   457         privacyPolicyURL = ref new String(tmpStr);
   458         SDL_free(tmpStr);
   459 
   460         // Optionally retrieve custom label-text for the link.  If this isn't
   461         // available, a default value will be used instead.
   462         tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
   463         if (tmpHintValue && tmpHintValue[0] != '\0') {
   464             tmpStr = WIN_UTF8ToString(tmpHintValue);
   465             privacyPolicyLabel = ref new String(tmpStr);
   466             SDL_free(tmpStr);
   467         } else {
   468             privacyPolicyLabel = ref new String(L"Privacy Policy");
   469         }
   470 
   471         // Register the link, along with a handler to be called if and when it is
   472         // clicked:
   473         auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
   474             ref new UICommandInvokedHandler([=](IUICommand ^) {
   475                 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
   476         }));
   477         args->Request->ApplicationCommands->Append(cmd);
   478     }
   479 }
   480 #endif // if WINAPI_FAMILY == WINAPI_FAMILY_APP
   481 
   482 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
   483 {
   484 #if LOG_WINDOW_EVENTS==1
   485     SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n",
   486         __FUNCTION__,
   487         args->Size.Width, args->Size.Height,
   488         (int)DisplayProperties::CurrentOrientation,
   489         (int)DisplayProperties::NativeOrientation,
   490         (int)DisplayProperties::AutoRotationPreferences,
   491         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   492 #endif
   493 
   494     WINRT_ProcessWindowSizeChange();
   495 }
   496 
   497 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
   498 {
   499 #if LOG_WINDOW_EVENTS==1
   500     SDL_Log("%s, visible?=%s, WINRT_GlobalSDLWindow?=%s\n",
   501         __FUNCTION__,
   502         (args->Visible ? "yes" : "no"),
   503         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   504 #endif
   505 
   506     m_windowVisible = args->Visible;
   507     if (WINRT_GlobalSDLWindow) {
   508         SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
   509 
   510         if (args->Visible) {
   511             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
   512         } else {
   513             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   514         }
   515 
   516         // HACK: Prevent SDL's window-hide handling code, which currently
   517         // triggers a fake window resize (possibly erronously), from
   518         // marking the SDL window's surface as invalid.
   519         //
   520         // A better solution to this probably involves figuring out if the
   521         // fake window resize can be prevented.
   522         WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
   523     }
   524 }
   525 
   526 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
   527 {
   528 #if LOG_WINDOW_EVENTS==1
   529     SDL_Log("%s\n", __FUNCTION__);
   530 #endif
   531     m_windowClosed = true;
   532 }
   533 
   534 void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
   535 {
   536     CoreWindow::GetForCurrentThread()->Activate();
   537 }
   538 
   539 static int SDLCALL RemoveAppSuspendAndResumeEvents(void * userdata, SDL_Event * event)
   540 {
   541     if (event->type == SDL_WINDOWEVENT)
   542     {
   543         switch (event->window.event)
   544         {
   545             case SDL_WINDOWEVENT_MINIMIZED:
   546             case SDL_WINDOWEVENT_RESTORED:
   547                 // Return 0 to indicate that the event should be removed from the
   548                 // event queue:
   549                 return 0;
   550             default:
   551                 break;
   552         }
   553     }
   554 
   555     // Return 1 to indicate that the event should stay in the event queue:
   556     return 1;
   557 }
   558 
   559 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
   560 {
   561     // Save app state asynchronously after requesting a deferral. Holding a deferral
   562     // indicates that the application is busy performing suspending operations. Be
   563     // aware that a deferral may not be held indefinitely. After about five seconds,
   564     // the app will be forced to exit.
   565     SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
   566     create_task([this, deferral]()
   567     {
   568         // Send a window-minimized event immediately to observers.
   569         // CoreDispatcher::ProcessEvents, which is the backbone on which
   570         // SDL_WinRTApp::PumpEvents is built, will not return to its caller
   571         // once it sends out a suspend event.  Any events posted to SDL's
   572         // event queue won't get received until the WinRT app is resumed.
   573         // SDL_AddEventWatch() may be used to receive app-suspend events on
   574         // WinRT.
   575         //
   576         // In order to prevent app-suspend events from being received twice:
   577         // first via a callback passed to SDL_AddEventWatch, and second via
   578         // SDL's event queue, the event will be sent to SDL, then immediately
   579         // removed from the queue.
   580         if (WINRT_GlobalSDLWindow)
   581         {
   582             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);   // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
   583             SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
   584         }
   585 
   586         SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
   587         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
   588 
   589         deferral->Complete();
   590     });
   591 }
   592 
   593 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
   594 {
   595     SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
   596     SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
   597 
   598     // Restore any data or state that was unloaded on suspend. By default, data
   599     // and state are persisted when resuming from suspend. Note that this event
   600     // does not occur if the app was previously terminated.
   601     if (WINRT_GlobalSDLWindow)
   602     {
   603         SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);    // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
   604 
   605         // Remove the app-resume event from the queue, as is done with the
   606         // app-suspend event.
   607         //
   608         // TODO, WinRT: consider posting this event to the queue even though
   609         // its counterpart, the app-suspend event, effectively has to be
   610         // processed immediately.
   611         SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
   612     }
   613 }
   614 
   615 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
   616 {
   617     SDL_SendAppEvent(SDL_APP_TERMINATING);
   618 }
   619 
   620 static void
   621 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
   622 {
   623     Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
   624     SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d\n",
   625         header,
   626         pt->Position.X, pt->Position.Y,
   627         transformedPoint.X, transformedPoint.Y,
   628         pt->Properties->MouseWheelDelta,
   629         pt->FrameId,
   630         pt->PointerId,
   631         WINRT_GetSDLButtonForPointerPoint(pt));
   632 }
   633 
   634 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
   635 {
   636 #if LOG_POINTER_EVENTS
   637     WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   638 #endif
   639 
   640     WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   641 }
   642 
   643 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
   644 {
   645 #if LOG_POINTER_EVENTS
   646     WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   647 #endif
   648 
   649     WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   650 }
   651 
   652 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
   653 {
   654 #if LOG_POINTER_EVENTS
   655     WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   656 #endif
   657 
   658     WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   659 }
   660 
   661 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
   662 {
   663 #if LOG_POINTER_EVENTS
   664     WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   665 #endif
   666 
   667     WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   668 }
   669 
   670 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
   671 {
   672     WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
   673 }
   674 
   675 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   676 {
   677     WINRT_ProcessKeyDownEvent(args);
   678 }
   679 
   680 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   681 {
   682     WINRT_ProcessKeyUpEvent(args);
   683 }
   684 
   685 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   686 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
   687 {
   688     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
   689     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
   690 
   691     const char *hint = SDL_GetHint(SDL_HINT_WINRT_HANDLE_BACK_BUTTON);
   692     if (hint) {
   693         if (*hint == '1') {
   694             args->Handled = true;
   695         }
   696     }
   697 }
   698 #endif
   699