cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_winrtapp_direct3d.cpp (28855B)


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