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