src/core/winrt/SDL_winrtapp_direct3d.cpp
author Sam Lantinga <slouken@libsdl.org>
Sun, 12 Nov 2017 10:59:05 -0800
changeset 11701 d131f3193794
parent 11494 d167d43d74f2
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed Android build error on older SDK
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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_assert.h"
    51 #include "SDL_events.h"
    52 #include "SDL_hints.h"
    53 #include "SDL_log.h"
    54 #include "SDL_main.h"
    55 #include "SDL_stdinc.h"
    56 #include "SDL_render.h"
    57 #include "../../video/SDL_sysvideo.h"
    58 //#include "../../SDL_hints_c.h"
    59 #include "../../events/SDL_events_c.h"
    60 #include "../../events/SDL_keyboard_c.h"
    61 #include "../../events/SDL_mouse_c.h"
    62 #include "../../events/SDL_windowevents_c.h"
    63 #include "../../render/SDL_sysrender.h"
    64 #include "../windows/SDL_windows.h"
    65 }
    66 
    67 #include "../../video/winrt/SDL_winrtevents_c.h"
    68 #include "../../video/winrt/SDL_winrtvideo_cpp.h"
    69 #include "SDL_winrtapp_common.h"
    70 #include "SDL_winrtapp_direct3d.h"
    71 
    72 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
    73 /* Calling IDXGIDevice3::Trim on the active Direct3D 11.x device is necessary
    74  * when Windows 8.1 apps are about to get suspended.
    75  */
    76 extern "C" void D3D11_Trim(SDL_Renderer *);
    77 #endif
    78 
    79 
    80 // Compile-time debugging options:
    81 // To enable, uncomment; to disable, comment them out.
    82 //#define LOG_POINTER_EVENTS 1
    83 //#define LOG_WINDOW_EVENTS 1
    84 //#define LOG_ORIENTATION_EVENTS 1
    85 
    86 
    87 // HACK, DLudwig: record a reference to the global, WinRT 'app'/view.
    88 // SDL/WinRT will use this throughout its code.
    89 //
    90 // TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something
    91 // non-global, such as something created inside
    92 // SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside
    93 // SDL_CreateWindow().
    94 SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr;
    95 
    96 ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
    97 {
    98 public:
    99     virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
   100 };
   101 
   102 IFrameworkView^ SDLApplicationSource::CreateView()
   103 {
   104     // TODO, WinRT: see if this function (CreateView) can ever get called
   105     // more than once.  For now, just prevent it from ever assigning
   106     // SDL_WinRTGlobalApp more than once.
   107     SDL_assert(!SDL_WinRTGlobalApp);
   108     SDL_WinRTApp ^ app = ref new SDL_WinRTApp();
   109     if (!SDL_WinRTGlobalApp)
   110     {
   111         SDL_WinRTGlobalApp = app;
   112     }
   113     return app;
   114 }
   115 
   116 int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **))
   117 {
   118     WINRT_SDLAppEntryPoint = mainFunction;
   119     auto direct3DApplicationSource = ref new SDLApplicationSource();
   120     CoreApplication::Run(direct3DApplicationSource);
   121     return 0;
   122 }
   123 
   124 static void SDLCALL
   125 WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue)
   126 {
   127     SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0);
   128 
   129     /* HACK: prevent SDL from altering an app's .appxmanifest-set orientation
   130      * from being changed on startup, by detecting when SDL_HINT_ORIENTATIONS
   131      * is getting registered.
   132      *
   133      * TODO, WinRT: consider reading in an app's .appxmanifest file, and apply its orientation when 'newValue == NULL'.
   134      */
   135     if ((oldValue == NULL) && (newValue == NULL)) {
   136         return;
   137     }
   138 
   139     // Start with no orientation flags, then add each in as they're parsed
   140     // from newValue.
   141     unsigned int orientationFlags = 0;
   142     if (newValue) {
   143         std::istringstream tokenizer(newValue);
   144         while (!tokenizer.eof()) {
   145             std::string orientationName;
   146             std::getline(tokenizer, orientationName, ' ');
   147             if (orientationName == "LandscapeLeft") {
   148                 orientationFlags |= (unsigned int) DisplayOrientations::LandscapeFlipped;
   149             } else if (orientationName == "LandscapeRight") {
   150                 orientationFlags |= (unsigned int) DisplayOrientations::Landscape;
   151             } else if (orientationName == "Portrait") {
   152                 orientationFlags |= (unsigned int) DisplayOrientations::Portrait;
   153             } else if (orientationName == "PortraitUpsideDown") {
   154                 orientationFlags |= (unsigned int) DisplayOrientations::PortraitFlipped;
   155             }
   156         }
   157     }
   158 
   159     // If no valid orientation flags were specified, use a reasonable set of defaults:
   160     if (!orientationFlags) {
   161         // TODO, WinRT: consider seeing if an app's default orientation flags can be found out via some API call(s).
   162         orientationFlags = (unsigned int) ( \
   163             DisplayOrientations::Landscape |
   164             DisplayOrientations::LandscapeFlipped |
   165             DisplayOrientations::Portrait |
   166             DisplayOrientations::PortraitFlipped);
   167     }
   168 
   169     // Set the orientation/rotation preferences.  Please note that this does
   170     // not constitute a 100%-certain lock of a given set of possible
   171     // orientations.  According to Microsoft's documentation on WinRT [1]
   172     // when a device is not capable of being rotated, Windows may ignore
   173     // the orientation preferences, and stick to what the device is capable of
   174     // displaying.
   175     //
   176     // [1] Documentation on the 'InitialRotationPreference' setting for a
   177     // Windows app's manifest file describes how some orientation/rotation
   178     // preferences may be ignored.  See
   179     // http://msdn.microsoft.com/en-us/library/windows/apps/hh700343.aspx
   180     // for details.  Microsoft's "Display orientation sample" also gives an
   181     // outline of how Windows treats device rotation
   182     // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93).
   183     WINRT_DISPLAY_PROPERTY(AutoRotationPreferences) = (DisplayOrientations) orientationFlags;
   184 }
   185 
   186 static void
   187 WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange()
   188 {
   189     CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread();
   190     if (coreWindow) {
   191         if (WINRT_GlobalSDLWindow) {
   192             SDL_Window * window = WINRT_GlobalSDLWindow;
   193             SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   194 
   195             int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left);
   196             int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top);
   197             int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
   198             int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
   199 
   200 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8)
   201             /* WinPhone 8.0 always keeps its native window size in portrait,
   202                regardless of orientation.  This changes in WinPhone 8.1,
   203                in which the native window's size changes along with
   204                orientation.
   205 
   206                Attempt to emulate WinPhone 8.1's behavior on WinPhone 8.0, with
   207                regards to window size.  This fixes a rendering bug that occurs
   208                when a WinPhone 8.0 app is rotated to either 90 or 270 degrees.
   209             */
   210             const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation);
   211             switch (currentOrientation) {
   212                 case DisplayOrientations::Landscape:
   213                 case DisplayOrientations::LandscapeFlipped: {
   214                     int tmp = w;
   215                     w = h;
   216                     h = tmp;
   217                 } break;
   218             }
   219 #endif
   220 
   221             const Uint32 latestFlags = WINRT_DetectWindowFlags(window);
   222             if (latestFlags & SDL_WINDOW_MAXIMIZED) {
   223                 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   224             } else {
   225                 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   226             }
   227 
   228             WINRT_UpdateWindowFlags(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
   229 
   230             /* The window can move during a resize event, such as when maximizing
   231                or resizing from a corner */
   232             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   233             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
   234         }
   235     }
   236 }
   237 
   238 SDL_WinRTApp::SDL_WinRTApp() :
   239     m_windowClosed(false),
   240     m_windowVisible(true)
   241 {
   242 }
   243 
   244 void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
   245 {
   246     applicationView->Activated +=
   247         ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnAppActivated);
   248 
   249     CoreApplication::Suspending +=
   250         ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
   251 
   252     CoreApplication::Resuming +=
   253         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
   254 
   255     CoreApplication::Exiting +=
   256         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
   257 
   258 #if NTDDI_VERSION >= NTDDI_WIN10
   259     /* HACK ALERT!  Xbox One doesn't seem to detect gamepads unless something
   260        gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded
   261        events.  We'll register an event handler for these events here, to make
   262        sure that gamepad detection works later on, if requested.
   263     */
   264     Windows::Gaming::Input::Gamepad::GamepadAdded +=
   265         ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>(
   266             this, &SDL_WinRTApp::OnGamepadAdded
   267         );
   268 #endif
   269 }
   270 
   271 #if NTDDI_VERSION > NTDDI_WIN8
   272 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
   273 #else
   274 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
   275 #endif
   276 {
   277 #if LOG_ORIENTATION_EVENTS==1
   278     {
   279         CoreWindow^ window = CoreWindow::GetForCurrentThread();
   280         if (window) {
   281             SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Bounds={%f,%f,%f,%f}\n",
   282                 __FUNCTION__,
   283                 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
   284                 WINRT_DISPLAY_PROPERTY(NativeOrientation),
   285                 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
   286                 window->Bounds.X,
   287                 window->Bounds.Y,
   288                 window->Bounds.Width,
   289                 window->Bounds.Height);
   290         } else {
   291             SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
   292                 __FUNCTION__,
   293                 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
   294                 WINRT_DISPLAY_PROPERTY(NativeOrientation),
   295                 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences));
   296         }
   297     }
   298 #endif
   299 
   300     WINRT_ProcessWindowSizeChange();
   301 
   302 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   303     // HACK: Make sure that orientation changes
   304     // lead to the Direct3D renderer's viewport getting updated:
   305     //
   306     // For some reason, this doesn't seem to need to be done on Windows 8.x,
   307     // even when going from Landscape to LandscapeFlipped.  It only seems to
   308     // be needed on Windows Phone, at least when I tested on my devices.
   309     // I'm not currently sure why this is, but it seems to work fine. -- David L.
   310     //
   311     // TODO, WinRT: do more extensive research into why orientation changes on Win 8.x don't need D3D changes, or if they might, in some cases
   312     SDL_Window * window = WINRT_GlobalSDLWindow;
   313     if (window) {
   314         SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
   315         int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
   316         int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
   317         SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h);
   318     }
   319 #endif
   320 
   321 }
   322 
   323 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
   324 {
   325 #if LOG_WINDOW_EVENTS==1
   326     SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window bounds={%f, %f, %f,%f}\n",
   327         __FUNCTION__,
   328         WINRT_DISPLAY_PROPERTY(CurrentOrientation),
   329         WINRT_DISPLAY_PROPERTY(NativeOrientation),
   330         WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
   331         window->Bounds.X,
   332         window->Bounds.Y,
   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->Activated +=
   344         ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>(this, &SDL_WinRTApp::OnWindowActivated);
   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->PointerEntered +=
   363         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerEntered);
   364 
   365     window->PointerExited +=
   366         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerExited);
   367 
   368     window->PointerWheelChanged +=
   369         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
   370 
   371 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
   372     // Retrieves relative-only mouse movements:
   373     Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
   374         ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
   375 #endif
   376 
   377     window->KeyDown +=
   378         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
   379 
   380     window->KeyUp +=
   381         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
   382 
   383     window->CharacterReceived +=
   384         ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &SDL_WinRTApp::OnCharacterReceived);
   385 
   386 #if NTDDI_VERSION >= NTDDI_WIN10
   387     Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested +=
   388         ref new EventHandler<BackRequestedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
   389 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   390     HardwareButtons::BackPressed +=
   391         ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
   392 #endif
   393 
   394 #if NTDDI_VERSION > NTDDI_WIN8
   395     DisplayInformation::GetForCurrentView()->OrientationChanged +=
   396         ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
   397 #else
   398     DisplayProperties::OrientationChanged +=
   399         ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
   400 #endif
   401 
   402     // Register the hint, SDL_HINT_ORIENTATIONS, with SDL.
   403     // 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.
   404     SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
   405 
   406 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)  // for Windows 8/8.1/RT apps... (and not Phone apps)
   407     // Make sure we know when a user has opened the app's settings pane.
   408     // This is needed in order to display a privacy policy, which needs
   409     // to be done for network-enabled apps, as per Windows Store requirements.
   410     using namespace Windows::UI::ApplicationSettings;
   411     SettingsPane::GetForCurrentView()->CommandsRequested +=
   412         ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
   413             (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
   414 #endif
   415 }
   416 
   417 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
   418 {
   419 }
   420 
   421 void SDL_WinRTApp::Run()
   422 {
   423     SDL_SetMainReady();
   424     if (WINRT_SDLAppEntryPoint)
   425     {
   426         // TODO, WinRT: pass the C-style main() a reasonably realistic
   427         // representation of command line arguments.
   428         int argc = 0;
   429         char **argv = NULL;
   430         WINRT_SDLAppEntryPoint(argc, argv);
   431     }
   432 }
   433 
   434 static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
   435 {
   436     SDL_Event events[128];
   437     const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
   438     for (int i = 0; i < count; ++i) {
   439         if (events[i].window.event == windowEventID) {
   440             return true;
   441         }
   442     }
   443     return false;
   444 }
   445 
   446 bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
   447 {
   448     /* Don't wait if the app is visible: */
   449     if (m_windowVisible) {
   450         return false;
   451     }
   452     
   453     /* Don't wait until the window-hide events finish processing.
   454      * Do note that if an app-suspend event is sent (as indicated
   455      * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
   456      * events), then this code may be a moot point, as WinRT's
   457      * own event pump (aka ProcessEvents()) will pause regardless
   458      * of what we do here.  This happens on Windows Phone 8, to note.
   459      * Windows 8.x apps, on the other hand, may get a chance to run
   460      * these.
   461      */
   462     if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
   463         return false;
   464     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
   465         return false;
   466     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
   467         return false;
   468     }
   469 
   470     return true;
   471 }
   472 
   473 void SDL_WinRTApp::PumpEvents()
   474 {
   475     if (!m_windowClosed) {
   476         if (!ShouldWaitForAppResumeEvents()) {
   477             /* This is the normal way in which events should be pumped.
   478              * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
   479              * from zero to N events, and will then return.
   480              */
   481             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
   482         } else {
   483             /* This style of event-pumping, with 'ProcessOneAndAllPending',
   484              * will cause anywhere from one to N events to be processed.  If
   485              * at least one event is processed, the call will return.  If
   486              * no events are pending, then the call will wait until one is
   487              * available, and will not return (to the caller) until this
   488              * happens!  This should only occur when the app is hidden.
   489              */
   490             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
   491         }
   492     }
   493 }
   494 
   495 void SDL_WinRTApp::Uninitialize()
   496 {
   497 }
   498 
   499 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
   500 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
   501     Windows::UI::ApplicationSettings::SettingsPane ^p,
   502     Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
   503 {
   504     using namespace Platform;
   505     using namespace Windows::UI::ApplicationSettings;
   506     using namespace Windows::UI::Popups;
   507 
   508     String ^privacyPolicyURL = nullptr;     // a URL to an app's Privacy Policy
   509     String ^privacyPolicyLabel = nullptr;   // label/link text
   510     const char *tmpHintValue = NULL;        // SDL_GetHint-retrieved value, used immediately
   511     wchar_t *tmpStr = NULL;                 // used for UTF8 to UCS2 conversion
   512 
   513     // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
   514     tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
   515     if (tmpHintValue && tmpHintValue[0] != '\0') {
   516         // Convert the privacy policy's URL to UCS2:
   517         tmpStr = WIN_UTF8ToString(tmpHintValue);
   518         privacyPolicyURL = ref new String(tmpStr);
   519         SDL_free(tmpStr);
   520 
   521         // Optionally retrieve custom label-text for the link.  If this isn't
   522         // available, a default value will be used instead.
   523         tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
   524         if (tmpHintValue && tmpHintValue[0] != '\0') {
   525             tmpStr = WIN_UTF8ToString(tmpHintValue);
   526             privacyPolicyLabel = ref new String(tmpStr);
   527             SDL_free(tmpStr);
   528         } else {
   529             privacyPolicyLabel = ref new String(L"Privacy Policy");
   530         }
   531 
   532         // Register the link, along with a handler to be called if and when it is
   533         // clicked:
   534         auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
   535             ref new UICommandInvokedHandler([=](IUICommand ^) {
   536                 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
   537         }));
   538         args->Request->ApplicationCommands->Append(cmd);
   539     }
   540 }
   541 #endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
   542 
   543 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
   544 {
   545 #if LOG_WINDOW_EVENTS==1
   546     SDL_Log("%s, size={%f,%f}, bounds={%f,%f,%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n",
   547         __FUNCTION__,
   548         args->Size.Width, args->Size.Height,
   549         sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height,
   550         WINRT_DISPLAY_PROPERTY(CurrentOrientation),
   551         WINRT_DISPLAY_PROPERTY(NativeOrientation),
   552         WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
   553         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   554 #endif
   555 
   556     WINRT_ProcessWindowSizeChange();
   557 }
   558 
   559 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
   560 {
   561 #if LOG_WINDOW_EVENTS==1
   562     SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n",
   563         __FUNCTION__,
   564         (args->Visible ? "yes" : "no"),
   565         sender->Bounds.X, sender->Bounds.Y,
   566         sender->Bounds.Width, sender->Bounds.Height,
   567         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   568 #endif
   569 
   570     m_windowVisible = args->Visible;
   571     if (WINRT_GlobalSDLWindow) {
   572         SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
   573         Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow);
   574         if (args->Visible) {
   575             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
   576             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
   577             if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) {
   578                 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   579             } else {
   580                 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
   581             }
   582         } else {
   583             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   584             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   585             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   586         }
   587 
   588         // HACK: Prevent SDL's window-hide handling code, which currently
   589         // triggers a fake window resize (possibly erronously), from
   590         // marking the SDL window's surface as invalid.
   591         //
   592         // A better solution to this probably involves figuring out if the
   593         // fake window resize can be prevented.
   594         WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
   595     }
   596 }
   597 
   598 void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args)
   599 {
   600 #if LOG_WINDOW_EVENTS==1
   601     SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n",
   602         __FUNCTION__,
   603         (WINRT_GlobalSDLWindow ? "yes" : "no"));
   604 #endif
   605 
   606     /* There's no property in Win 8.x to tell whether a window is active or
   607        not.  [De]activation events are, however, sent to the app.  We'll just
   608        record those, in case the CoreWindow gets wrapped by an SDL_Window at
   609        some future time.
   610     */
   611     sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState);
   612 
   613     SDL_Window * window = WINRT_GlobalSDLWindow;
   614     if (window) {
   615         if (args->WindowActivationState != CoreWindowActivationState::Deactivated) {
   616             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   617             if (SDL_GetKeyboardFocus() != window) {
   618                 SDL_SetKeyboardFocus(window);
   619             }
   620         
   621             /* Send a mouse-motion event as appropriate.
   622                This doesn't work when called from OnPointerEntered, at least
   623                not in WinRT CoreWindow apps (as OnPointerEntered doesn't
   624                appear to be called after window-reactivation, at least not
   625                in Windows 10, Build 10586.3 (November 2015 update, non-beta).
   626 
   627                Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition'
   628                property isn't available.
   629              */
   630 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE)
   631             Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize);
   632             SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
   633 #endif
   634 
   635             /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */
   636             //WIN_CheckAsyncMouseRelease(data);
   637 
   638             /* TODO, WinRT: implement clipboard support, if possible */
   639             ///*
   640             // * FIXME: Update keyboard state
   641             // */
   642             //WIN_CheckClipboardUpdate(data->videodata);
   643 
   644             // HACK: Resetting the mouse-cursor here seems to fix
   645             // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a
   646             // WinRT app's mouse cursor may switch to Windows' 'wait' cursor,
   647             // after a user alt-tabs back into a full-screened SDL app.
   648             // This bug does not appear to reproduce 100% of the time.
   649             // It may be a bug in Windows itself (v.10.0.586.36, as tested,
   650             // and the most-recent as of this writing).
   651             SDL_SetCursor(NULL);
   652         } else {
   653             if (SDL_GetKeyboardFocus() == window) {
   654                 SDL_SetKeyboardFocus(NULL);
   655             }
   656         }
   657     }
   658 }
   659 
   660 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
   661 {
   662 #if LOG_WINDOW_EVENTS==1
   663     SDL_Log("%s\n", __FUNCTION__);
   664 #endif
   665     m_windowClosed = true;
   666 }
   667 
   668 void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
   669 {
   670     CoreWindow::GetForCurrentThread()->Activate();
   671 }
   672 
   673 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
   674 {
   675     // Save app state asynchronously after requesting a deferral. Holding a deferral
   676     // indicates that the application is busy performing suspending operations. Be
   677     // aware that a deferral may not be held indefinitely. After about five seconds,
   678     // the app will be forced to exit.
   679 
   680     // ... but first, let the app know it's about to go to the background.
   681     // The separation of events may be important, given that the deferral
   682     // runs in a separate thread.  This'll make SDL_APP_WILLENTERBACKGROUND
   683     // the only event among the two that runs in the main thread.  Given
   684     // that a few WinRT operations can only be done from the main thread
   685     // (things that access the WinRT CoreWindow are one example of this),
   686     // this could be important.
   687     SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
   688 
   689     SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
   690     create_task([this, deferral]()
   691     {
   692         // Send an app did-enter-background event immediately to observers.
   693         // CoreDispatcher::ProcessEvents, which is the backbone on which
   694         // SDL_WinRTApp::PumpEvents is built, will not return to its caller
   695         // once it sends out a suspend event.  Any events posted to SDL's
   696         // event queue won't get received until the WinRT app is resumed.
   697         // SDL_AddEventWatch() may be used to receive app-suspend events on
   698         // WinRT.
   699         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
   700 
   701         // Let the Direct3D 11 renderer prepare for the app to be backgrounded.
   702         // This is necessary for Windows 8.1, possibly elsewhere in the future.
   703         // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx
   704 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
   705         if (WINRT_GlobalSDLWindow) {
   706             SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow);
   707             if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) {
   708                 D3D11_Trim(renderer);
   709             }
   710         }
   711 #endif
   712 
   713         deferral->Complete();
   714     });
   715 }
   716 
   717 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
   718 {
   719     // Restore any data or state that was unloaded on suspend. By default, data
   720     // and state are persisted when resuming from suspend. Note that these events
   721     // do not occur if the app was previously terminated.
   722     SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
   723     SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
   724 }
   725 
   726 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
   727 {
   728     SDL_SendAppEvent(SDL_APP_TERMINATING);
   729 }
   730 
   731 static void
   732 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
   733 {
   734     Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
   735     SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d\n",
   736         header,
   737         pt->Position.X, pt->Position.Y,
   738         transformedPoint.X, transformedPoint.Y,
   739         pt->Properties->MouseWheelDelta,
   740         pt->FrameId,
   741         pt->PointerId,
   742         WINRT_GetSDLButtonForPointerPoint(pt));
   743 }
   744 
   745 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
   746 {
   747 #if LOG_POINTER_EVENTS
   748     WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   749 #endif
   750 
   751     WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   752 }
   753 
   754 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
   755 {
   756 #if LOG_POINTER_EVENTS
   757     WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   758 #endif
   759 
   760     WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   761 }
   762 
   763 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
   764 {
   765 #if LOG_POINTER_EVENTS
   766     WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   767 #endif
   768     
   769     WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   770 }
   771 
   772 void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args)
   773 {
   774 #if LOG_POINTER_EVENTS
   775     WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   776 #endif
   777 
   778     WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   779 }
   780 
   781 void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args)
   782 {
   783 #if LOG_POINTER_EVENTS
   784     WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   785 #endif
   786 
   787     WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   788 }
   789 
   790 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
   791 {
   792 #if LOG_POINTER_EVENTS
   793     WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
   794 #endif
   795 
   796     WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
   797 }
   798 
   799 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
   800 {
   801     WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
   802 }
   803 
   804 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   805 {
   806     WINRT_ProcessKeyDownEvent(args);
   807 }
   808 
   809 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
   810 {
   811     WINRT_ProcessKeyUpEvent(args);
   812 }
   813 
   814 void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args)
   815 {
   816     WINRT_ProcessCharacterReceivedEvent(args);
   817 }
   818 
   819 template <typename BackButtonEventArgs>
   820 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
   821 {
   822     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
   823     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
   824 
   825     if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
   826         args->Handled = true;
   827     }
   828 }
   829 
   830 #if NTDDI_VERSION >= NTDDI_WIN10
   831 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args)
   832 
   833 {
   834     WINRT_OnBackButtonPressed(args);
   835 }
   836 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   837 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
   838 
   839 {
   840     WINRT_OnBackButtonPressed(args);
   841 }
   842 #endif
   843 
   844 #if NTDDI_VERSION >= NTDDI_WIN10
   845 void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad)
   846 {
   847     /* HACK ALERT: Nothing needs to be done here, as this method currently
   848        only exists to allow something to be registered with Win10's
   849        GamepadAdded event, an operation that seems to be necessary to get
   850        Xinput-based detection to work on Xbox One.
   851     */
   852 }
   853 #endif
   854 
   855 /* vi: set ts=4 sw=4 expandtab: */