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