src/video/winrt/SDL_winrtvideo.cpp
author Ozkan Sezer
Mon, 25 Jan 2021 04:11:40 +0300
changeset 14792 d01593bd58d9
parent 14640 b2b3343a310d
permissions -rw-r--r--
better check for clock_gettime_nsec_np() -- cf. bug #5467.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2021 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 #if SDL_VIDEO_DRIVER_WINRT
    24 
    25 /* WinRT SDL video driver implementation
    26 
    27    Initial work on this was done by David Ludwig (dludwig@pobox.com), and
    28    was based off of SDL's "dummy" video driver.
    29  */
    30 
    31 /* Windows includes */
    32 #include <agile.h>
    33 #include <windows.graphics.display.h>
    34 #include <windows.system.display.h>
    35 #include <dxgi.h>
    36 #include <dxgi1_2.h>
    37 using namespace Windows::ApplicationModel::Core;
    38 using namespace Windows::Foundation;
    39 using namespace Windows::Graphics::Display;
    40 using namespace Windows::UI::Core;
    41 using namespace Windows::UI::ViewManagement;
    42 
    43 
    44 /* [re]declare Windows GUIDs locally, to limit the amount of external lib(s) SDL has to link to */
    45 static const GUID IID_IDisplayRequest   = { 0xe5732044, 0xf49f, 0x4b60, { 0x8d, 0xd4, 0x5e, 0x7e, 0x3a, 0x63, 0x2a, 0xc0 } };
    46 static const GUID IID_IDXGIFactory2     = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } };
    47 
    48 
    49 /* SDL includes */
    50 extern "C" {
    51 #include "SDL_video.h"
    52 #include "SDL_mouse.h"
    53 #include "../SDL_sysvideo.h"
    54 #include "../SDL_pixels_c.h"
    55 #include "../../events/SDL_events_c.h"
    56 #include "../../render/SDL_sysrender.h"
    57 #include "SDL_syswm.h"
    58 #include "SDL_winrtopengles.h"
    59 #include "../../core/windows/SDL_windows.h"
    60 }
    61 
    62 #include "../../core/winrt/SDL_winrtapp_direct3d.h"
    63 #include "../../core/winrt/SDL_winrtapp_xaml.h"
    64 #include "SDL_winrtvideo_cpp.h"
    65 #include "SDL_winrtevents_c.h"
    66 #include "SDL_winrtgamebar_cpp.h"
    67 #include "SDL_winrtmouse_c.h"
    68 #include "SDL_main.h"
    69 #include "SDL_system.h"
    70 
    71 
    72 /* Initialization/Query functions */
    73 static int WINRT_VideoInit(_THIS);
    74 static int WINRT_InitModes(_THIS);
    75 static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
    76 static void WINRT_VideoQuit(_THIS);
    77 
    78 
    79 /* Window functions */
    80 static int WINRT_CreateWindow(_THIS, SDL_Window * window);
    81 static void WINRT_SetWindowSize(_THIS, SDL_Window * window);
    82 static void WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
    83 static void WINRT_DestroyWindow(_THIS, SDL_Window * window);
    84 static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info);
    85 
    86 
    87 /* Misc functions */
    88 static ABI::Windows::System::Display::IDisplayRequest * WINRT_CreateDisplayRequest(_THIS);
    89 extern void WINRT_SuspendScreenSaver(_THIS);
    90 
    91 
    92 /* SDL-internal globals: */
    93 SDL_Window * WINRT_GlobalSDLWindow = NULL;
    94 
    95 
    96 /* WinRT driver bootstrap functions */
    97 
    98 static void
    99 WINRT_DeleteDevice(SDL_VideoDevice * device)
   100 {
   101     if (device->driverdata) {
   102         SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata;
   103         if (video_data->winrtEglWindow) {
   104             video_data->winrtEglWindow->Release();
   105         }
   106         SDL_free(video_data);
   107     }
   108 
   109     SDL_free(device);
   110 }
   111 
   112 static SDL_VideoDevice *
   113 WINRT_CreateDevice(int devindex)
   114 {
   115     SDL_VideoDevice *device;
   116     SDL_VideoData *data;
   117 
   118     /* Initialize all variables that we clean on shutdown */
   119     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   120     if (!device) {
   121         SDL_OutOfMemory();
   122         return (0);
   123     }
   124 
   125     data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   126     if (!data) {
   127         SDL_OutOfMemory();
   128         SDL_free(device);
   129         return (0);
   130     }
   131     device->driverdata = data;
   132 
   133     /* Set the function pointers */
   134     device->VideoInit = WINRT_VideoInit;
   135     device->VideoQuit = WINRT_VideoQuit;
   136     device->CreateSDLWindow = WINRT_CreateWindow;
   137     device->SetWindowSize = WINRT_SetWindowSize;
   138     device->SetWindowFullscreen = WINRT_SetWindowFullscreen;
   139     device->DestroyWindow = WINRT_DestroyWindow;
   140     device->SetDisplayMode = WINRT_SetDisplayMode;
   141     device->PumpEvents = WINRT_PumpEvents;
   142     device->GetWindowWMInfo = WINRT_GetWindowWMInfo;
   143     device->SuspendScreenSaver = WINRT_SuspendScreenSaver;
   144 
   145 #if NTDDI_VERSION >= NTDDI_WIN10
   146     device->HasScreenKeyboardSupport = WINRT_HasScreenKeyboardSupport;
   147     device->ShowScreenKeyboard = WINRT_ShowScreenKeyboard;
   148     device->HideScreenKeyboard = WINRT_HideScreenKeyboard;
   149     device->IsScreenKeyboardShown = WINRT_IsScreenKeyboardShown;
   150 #endif
   151 
   152 #ifdef SDL_VIDEO_OPENGL_EGL
   153     device->GL_LoadLibrary = WINRT_GLES_LoadLibrary;
   154     device->GL_GetProcAddress = WINRT_GLES_GetProcAddress;
   155     device->GL_UnloadLibrary = WINRT_GLES_UnloadLibrary;
   156     device->GL_CreateContext = WINRT_GLES_CreateContext;
   157     device->GL_MakeCurrent = WINRT_GLES_MakeCurrent;
   158     device->GL_SetSwapInterval = WINRT_GLES_SetSwapInterval;
   159     device->GL_GetSwapInterval = WINRT_GLES_GetSwapInterval;
   160     device->GL_SwapWindow = WINRT_GLES_SwapWindow;
   161     device->GL_DeleteContext = WINRT_GLES_DeleteContext;
   162 #endif
   163     device->free = WINRT_DeleteDevice;
   164 
   165     return device;
   166 }
   167 
   168 #define WINRTVID_DRIVER_NAME "winrt"
   169 VideoBootStrap WINRT_bootstrap = {
   170     WINRTVID_DRIVER_NAME, "SDL WinRT video driver",
   171     WINRT_CreateDevice
   172 };
   173 
   174 int
   175 WINRT_VideoInit(_THIS)
   176 {
   177     SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata;
   178     if (WINRT_InitModes(_this) < 0) {
   179         return -1;
   180     }
   181     WINRT_InitMouse(_this);
   182     WINRT_InitTouch(_this);
   183     WINRT_InitGameBar(_this);
   184     if (driverdata) {
   185         /* Initialize screensaver-disabling support */
   186         driverdata->displayRequest = WINRT_CreateDisplayRequest(_this);
   187     }
   188     return 0;
   189 }
   190 
   191 extern "C"
   192 Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat);
   193 
   194 static void
   195 WINRT_DXGIModeToSDLDisplayMode(const DXGI_MODE_DESC * dxgiMode, SDL_DisplayMode * sdlMode)
   196 {
   197     SDL_zerop(sdlMode);
   198     sdlMode->w = dxgiMode->Width;
   199     sdlMode->h = dxgiMode->Height;
   200     sdlMode->refresh_rate = dxgiMode->RefreshRate.Numerator / dxgiMode->RefreshRate.Denominator;
   201     sdlMode->format = D3D11_DXGIFormatToSDLPixelFormat(dxgiMode->Format);
   202 }
   203 
   204 static int
   205 WINRT_AddDisplaysForOutput (_THIS, IDXGIAdapter1 * dxgiAdapter1, int outputIndex)
   206 {
   207     HRESULT hr;
   208     IDXGIOutput * dxgiOutput = NULL;
   209     DXGI_OUTPUT_DESC dxgiOutputDesc;
   210     SDL_VideoDisplay display;
   211     char * displayName = NULL;
   212     UINT numModes;
   213     DXGI_MODE_DESC * dxgiModes = NULL;
   214     int functionResult = -1;        /* -1 for failure, 0 for success */
   215     DXGI_MODE_DESC modeToMatch, closestMatch;
   216 
   217     SDL_zero(display);
   218 
   219     hr = dxgiAdapter1->EnumOutputs(outputIndex, &dxgiOutput);
   220     if (FAILED(hr)) {
   221         if (hr != DXGI_ERROR_NOT_FOUND) {
   222             WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIAdapter1::EnumOutputs failed", hr);
   223         }
   224         goto done;
   225     }
   226 
   227     hr = dxgiOutput->GetDesc(&dxgiOutputDesc);
   228     if (FAILED(hr)) {
   229         WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDesc failed", hr);
   230         goto done;
   231     }
   232 
   233     SDL_zero(modeToMatch);
   234     modeToMatch.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
   235     modeToMatch.Width = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left);
   236     modeToMatch.Height = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top);
   237     hr = dxgiOutput->FindClosestMatchingMode(&modeToMatch, &closestMatch, NULL);
   238     if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
   239         /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE gets returned by IDXGIOutput::FindClosestMatchingMode
   240            when running under the Windows Simulator, which uses Remote Desktop (formerly known as Terminal
   241            Services) under the hood.  According to the MSDN docs for the similar function,
   242            IDXGIOutput::GetDisplayModeList, DXGI_ERROR_NOT_CURRENTLY_AVAILABLE is returned if and
   243            when an app is run under a Terminal Services session, hence the assumption.
   244 
   245            In this case, just add an SDL display mode, with approximated values.
   246         */
   247         SDL_DisplayMode mode;
   248         SDL_zero(mode);
   249         display.name = "Windows Simulator / Terminal Services Display";
   250         mode.w = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left);
   251         mode.h = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top);
   252         mode.format = DXGI_FORMAT_B8G8R8A8_UNORM;
   253         mode.refresh_rate = 0;  /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
   254         display.desktop_mode = mode;
   255         display.current_mode = mode;
   256         if ( ! SDL_AddDisplayMode(&display, &mode)) {
   257             goto done;
   258         }
   259     } else if (FAILED(hr)) {
   260         WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::FindClosestMatchingMode failed", hr);
   261         goto done;
   262     } else {
   263         displayName = WIN_StringToUTF8(dxgiOutputDesc.DeviceName);
   264         display.name = displayName;
   265         WINRT_DXGIModeToSDLDisplayMode(&closestMatch, &display.desktop_mode);
   266         display.current_mode = display.desktop_mode;
   267 
   268         hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, NULL);
   269         if (FAILED(hr)) {
   270             if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
   271                 // TODO, WinRT: make sure display mode(s) are added when using Terminal Services / Windows Simulator
   272             }
   273             WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode list size] failed", hr);
   274             goto done;
   275         }
   276 
   277         dxgiModes = (DXGI_MODE_DESC *)SDL_calloc(numModes, sizeof(DXGI_MODE_DESC));
   278         if ( ! dxgiModes) {
   279             SDL_OutOfMemory();
   280             goto done;
   281         }
   282 
   283         hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, dxgiModes);
   284         if (FAILED(hr)) {
   285             WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode contents] failed", hr);
   286             goto done;
   287         }
   288 
   289         for (UINT i = 0; i < numModes; ++i) {
   290             SDL_DisplayMode sdlMode;
   291             WINRT_DXGIModeToSDLDisplayMode(&dxgiModes[i], &sdlMode);
   292             SDL_AddDisplayMode(&display, &sdlMode);
   293         }
   294     }
   295 
   296     if (SDL_AddVideoDisplay(&display, SDL_FALSE) < 0) {
   297         goto done;
   298     }
   299 
   300     functionResult = 0;     /* 0 for Success! */
   301 done:
   302     if (dxgiModes) {
   303         SDL_free(dxgiModes);
   304     }
   305     if (dxgiOutput) {
   306         dxgiOutput->Release();
   307     }
   308     if (displayName) {
   309         SDL_free(displayName);
   310     }
   311     return functionResult;
   312 }
   313 
   314 static int
   315 WINRT_AddDisplaysForAdapter (_THIS, IDXGIFactory2 * dxgiFactory2, int adapterIndex)
   316 {
   317     HRESULT hr;
   318     IDXGIAdapter1 * dxgiAdapter1;
   319 
   320     hr = dxgiFactory2->EnumAdapters1(adapterIndex, &dxgiAdapter1);
   321     if (FAILED(hr)) {
   322         if (hr != DXGI_ERROR_NOT_FOUND) {
   323             WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIFactory1::EnumAdapters1() failed", hr);
   324         }
   325         return -1;
   326     }
   327 
   328     for (int outputIndex = 0; ; ++outputIndex) {
   329         if (WINRT_AddDisplaysForOutput(_this, dxgiAdapter1, outputIndex) < 0) {
   330             /* HACK: The Windows App Certification Kit 10.0 can fail, when
   331                running the Store Apps' test, "Direct3D Feature Test".  The
   332                certification kit's error is:
   333 
   334                "Application App was not running at the end of the test. It likely crashed or was terminated for having become unresponsive."
   335 
   336                This was caused by SDL/WinRT's DXGI failing to report any
   337                outputs.  Attempts to get the 1st display-output from the
   338                1st display-adapter can fail, with IDXGIAdapter::EnumOutputs
   339                returning DXGI_ERROR_NOT_FOUND.  This could be a bug in Windows,
   340                the Windows App Certification Kit, or possibly in SDL/WinRT's
   341                display detection code.  Either way, try to detect when this
   342                happens, and use a hackish means to create a reasonable-as-possible
   343                'display mode'.  -- DavidL
   344             */
   345             if (adapterIndex == 0 && outputIndex == 0) {
   346                 SDL_VideoDisplay display;
   347                 SDL_DisplayMode mode;
   348 #if SDL_WINRT_USE_APPLICATIONVIEW
   349                 ApplicationView ^ appView = ApplicationView::GetForCurrentView();
   350 #endif
   351                 CoreWindow ^ coreWin = CoreWindow::GetForCurrentThread();
   352                 SDL_zero(display);
   353                 SDL_zero(mode);
   354                 display.name = "DXGI Display-detection Workaround";
   355 
   356                 /* HACK: ApplicationView's VisibleBounds property, appeared, via testing, to
   357                    give a better approximation of display-size, than did CoreWindow's
   358                    Bounds property, insofar that ApplicationView::VisibleBounds seems like
   359                    it will, at least some of the time, give the full display size (during the
   360                    failing test), whereas CoreWindow might not.  -- DavidL
   361                 */
   362 
   363 #if (NTDDI_VERSION >= NTDDI_WIN10) || (SDL_WINRT_USE_APPLICATIONVIEW && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
   364                 mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Width);
   365                 mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Height);
   366 #else
   367                 /* On platform(s) that do not support VisibleBounds, such as Windows 8.1,
   368                    fall back to CoreWindow's Bounds property.
   369                 */
   370                 mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Width);
   371                 mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Height);
   372 #endif
   373 
   374                 mode.format = DXGI_FORMAT_B8G8R8A8_UNORM;
   375                 mode.refresh_rate = 0;  /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
   376                 display.desktop_mode = mode;
   377                 display.current_mode = mode;
   378                 if ((SDL_AddDisplayMode(&display, &mode) < 0) ||
   379                     (SDL_AddVideoDisplay(&display, SDL_FALSE) < 0))
   380                 {
   381                     return SDL_SetError("Failed to apply DXGI Display-detection workaround");
   382                 }
   383             }
   384 
   385             break;
   386         }
   387     }
   388 
   389     dxgiAdapter1->Release();
   390     return 0;
   391 }
   392 
   393 int
   394 WINRT_InitModes(_THIS)
   395 {
   396     /* HACK: Initialize a single display, for whatever screen the app's
   397          CoreApplicationView is on.
   398        TODO, WinRT: Try initializing multiple displays, one for each monitor.
   399          Appropriate WinRT APIs for this seem elusive, though.  -- DavidL
   400     */
   401 
   402     HRESULT hr;
   403     IDXGIFactory2 * dxgiFactory2 = NULL;
   404 
   405     hr = CreateDXGIFactory1(IID_IDXGIFactory2, (void **)&dxgiFactory2);
   406     if (FAILED(hr)) {
   407         WIN_SetErrorFromHRESULT(__FUNCTION__ ", CreateDXGIFactory1() failed", hr);
   408         return -1;
   409     }
   410 
   411     for (int adapterIndex = 0; ; ++adapterIndex) {
   412         if (WINRT_AddDisplaysForAdapter(_this, dxgiFactory2, adapterIndex) < 0) {
   413             break;
   414         }
   415     }
   416 
   417     return 0;
   418 }
   419 
   420 static int
   421 WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   422 {
   423     return 0;
   424 }
   425 
   426 void
   427 WINRT_VideoQuit(_THIS)
   428 {
   429     SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata;
   430     if (driverdata && driverdata->displayRequest) {
   431         driverdata->displayRequest->Release();
   432         driverdata->displayRequest = NULL;
   433     }
   434     WINRT_QuitGameBar(_this);
   435     WINRT_QuitMouse(_this);
   436 }
   437 
   438 static const Uint32 WINRT_DetectableFlags =
   439     SDL_WINDOW_MAXIMIZED |
   440     SDL_WINDOW_FULLSCREEN_DESKTOP |
   441     SDL_WINDOW_SHOWN |
   442     SDL_WINDOW_HIDDEN |
   443     SDL_WINDOW_MOUSE_FOCUS;
   444 
   445 extern "C" Uint32
   446 WINRT_DetectWindowFlags(SDL_Window * window)
   447 {
   448     Uint32 latestFlags = 0;
   449     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   450     bool is_fullscreen = false;
   451 
   452 #if SDL_WINRT_USE_APPLICATIONVIEW
   453     if (data->appView) {
   454         is_fullscreen = data->appView->IsFullScreen;
   455     }
   456 #elif (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION == NTDDI_WIN8)
   457     is_fullscreen = true;
   458 #endif
   459 
   460     if (data->coreWindow.Get()) {
   461         if (is_fullscreen) {
   462             SDL_VideoDisplay * display = SDL_GetDisplayForWindow(window);
   463             int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
   464             int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
   465 
   466 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION > NTDDI_WIN8)
   467             // On all WinRT platforms, except for WinPhone 8.0, rotate the
   468             // window size.  This is needed to properly calculate
   469             // fullscreen vs. maximized.
   470             const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation);
   471             switch (currentOrientation) {
   472 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
   473                 case DisplayOrientations::Landscape:
   474                 case DisplayOrientations::LandscapeFlipped:
   475 #else
   476                 case DisplayOrientations::Portrait:
   477                 case DisplayOrientations::PortraitFlipped:
   478 #endif
   479                 {
   480                     int tmp = w;
   481                     w = h;
   482                     h = tmp;
   483                 } break;
   484             }
   485 #endif
   486 
   487             if (display->desktop_mode.w != w || display->desktop_mode.h != h) {
   488                 latestFlags |= SDL_WINDOW_MAXIMIZED;
   489             } else {
   490                 latestFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
   491             }
   492         }
   493 
   494         if (data->coreWindow->Visible) {
   495             latestFlags |= SDL_WINDOW_SHOWN;
   496         } else {
   497             latestFlags |= SDL_WINDOW_HIDDEN;
   498         }
   499 
   500 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION < NTDDI_WINBLUE)
   501         // data->coreWindow->PointerPosition is not supported on WinPhone 8.0
   502         latestFlags |= SDL_WINDOW_MOUSE_FOCUS;
   503 #else
   504         if (data->coreWindow->Visible && data->coreWindow->Bounds.Contains(data->coreWindow->PointerPosition)) {
   505             latestFlags |= SDL_WINDOW_MOUSE_FOCUS;
   506         }
   507 #endif
   508     }
   509 
   510     return latestFlags;
   511 }
   512 
   513 // TODO, WinRT: consider removing WINRT_UpdateWindowFlags, and just calling WINRT_DetectWindowFlags as-appropriate (with appropriate calls to SDL_SendWindowEvent)
   514 void
   515 WINRT_UpdateWindowFlags(SDL_Window * window, Uint32 mask)
   516 {
   517     mask &= WINRT_DetectableFlags;
   518     if (window) {
   519         Uint32 apply = WINRT_DetectWindowFlags(window);
   520         if ((apply & mask) & SDL_WINDOW_FULLSCREEN) {
   521             window->last_fullscreen_flags = window->flags;  // seems necessary to programmatically un-fullscreen, via SDL APIs
   522         }
   523         window->flags = (window->flags & ~mask) | (apply & mask);
   524     }
   525 }
   526 
   527 static bool
   528 WINRT_IsCoreWindowActive(CoreWindow ^ coreWindow)
   529 {
   530     /* WinRT does not appear to offer API(s) to determine window-activation state,
   531        at least not that I am aware of in Win8 - Win10.  As such, SDL tracks this
   532        itself, via window-activation events.
   533        
   534        If there *is* an API to track this, it should probably get used instead
   535        of the following hack (that uses "SDLHelperWindowActivationState").
   536          -- DavidL.
   537     */
   538     if (coreWindow->CustomProperties->HasKey("SDLHelperWindowActivationState")) {
   539         CoreWindowActivationState activationState = \
   540             safe_cast<CoreWindowActivationState>(coreWindow->CustomProperties->Lookup("SDLHelperWindowActivationState"));
   541         return (activationState != CoreWindowActivationState::Deactivated);
   542     }
   543 
   544     /* Assume that non-SDL tracked windows are active, although this should
   545        probably be avoided, if possible.
   546        
   547        This might not even be possible, in normal SDL use, at least as of
   548        this writing (Dec 22, 2015; via latest hg.libsdl.org/SDL clone)  -- DavidL
   549     */
   550     return true;
   551 }
   552 
   553 int
   554 WINRT_CreateWindow(_THIS, SDL_Window * window)
   555 {
   556     // Make sure that only one window gets created, at least until multimonitor
   557     // support is added.
   558     if (WINRT_GlobalSDLWindow != NULL) {
   559         SDL_SetError("WinRT only supports one window");
   560         return -1;
   561     }
   562 
   563     SDL_WindowData *data = new SDL_WindowData;  /* use 'new' here as SDL_WindowData may use WinRT/C++ types */
   564     if (!data) {
   565         SDL_OutOfMemory();
   566         return -1;
   567     }
   568     window->driverdata = data;
   569     data->sdlWindow = window;
   570 
   571     /* To note, when XAML support is enabled, access to the CoreWindow will not
   572        be possible, at least not via the SDL/XAML thread.  Attempts to access it
   573        from there will throw exceptions.  As such, the SDL_WindowData's
   574        'coreWindow' field will only be set (to a non-null value) if XAML isn't
   575        enabled.
   576     */
   577     if (!WINRT_XAMLWasEnabled) {
   578         data->coreWindow = CoreWindow::GetForCurrentThread();
   579 #if SDL_WINRT_USE_APPLICATIONVIEW
   580         data->appView = ApplicationView::GetForCurrentView();
   581 #endif
   582     }
   583 
   584     /* Make note of the requested window flags, before they start getting changed. */
   585     const Uint32 requestedFlags = window->flags;
   586 
   587 #if SDL_VIDEO_OPENGL_EGL
   588     /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */
   589     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   590         /* OpenGL ES 2 wasn't requested.  Don't set up an EGL surface. */
   591         data->egl_surface = EGL_NO_SURFACE;
   592     } else {
   593         /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
   594         SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata;
   595 
   596         /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly,
   597          * rather than via SDL_EGL_CreateSurface, as older versions of
   598          * ANGLE/WinRT may require that a C++ object, ComPtr<IUnknown>,
   599          * be passed into eglCreateWindowSurface.
   600          */
   601         if (SDL_EGL_ChooseConfig(_this) != 0) {
   602             char buf[512];
   603             SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError());
   604             return SDL_SetError("%s", buf);
   605         }
   606 
   607         if (video_data->winrtEglWindow) {   /* ... is the 'old' version of ANGLE/WinRT being used? */
   608             /* Attempt to create a window surface using older versions of
   609              * ANGLE/WinRT:
   610              */
   611             Microsoft::WRL::ComPtr<IUnknown> cpp_winrtEglWindow = video_data->winrtEglWindow;
   612             data->egl_surface = ((eglCreateWindowSurface_Old_Function)_this->egl_data->eglCreateWindowSurface)(
   613                 _this->egl_data->egl_display,
   614                 _this->egl_data->egl_config,
   615                 cpp_winrtEglWindow, NULL);
   616             if (data->egl_surface == NULL) {
   617                 return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface");
   618             }
   619         } else if (data->coreWindow.Get() != nullptr) {
   620             /* Attempt to create a window surface using newer versions of
   621              * ANGLE/WinRT:
   622              */
   623             IInspectable * coreWindowAsIInspectable = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   624             data->egl_surface = _this->egl_data->eglCreateWindowSurface(
   625                 _this->egl_data->egl_display,
   626                 _this->egl_data->egl_config,
   627                 coreWindowAsIInspectable,
   628                 NULL);
   629             if (data->egl_surface == NULL) {
   630                 return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface");
   631             }
   632         } else {
   633             return SDL_SetError("No supported means to create an EGL window surface are available");
   634         }
   635     }
   636 #endif
   637 
   638     /* Determine as many flags dynamically, as possible. */
   639     window->flags =
   640         SDL_WINDOW_BORDERLESS |
   641         SDL_WINDOW_RESIZABLE;
   642 
   643 #if SDL_VIDEO_OPENGL_EGL
   644     if (data->egl_surface) {
   645         window->flags |= SDL_WINDOW_OPENGL;
   646     }
   647 #endif
   648 
   649     if (WINRT_XAMLWasEnabled) {
   650         /* TODO, WinRT: set SDL_Window size, maybe position too, from XAML control */
   651         window->x = 0;
   652         window->y = 0;
   653         window->flags |= SDL_WINDOW_SHOWN;
   654         SDL_SetMouseFocus(NULL);        // TODO: detect this
   655         SDL_SetKeyboardFocus(NULL);     // TODO: detect this
   656     } else {
   657         /* WinRT 8.x apps seem to live in an environment where the OS controls the
   658            app's window size, with some apps being fullscreen, depending on
   659            user choice of various things.  For now, just adapt the SDL_Window to
   660            whatever Windows set-up as the native-window's geometry.
   661         */
   662         window->x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left);
   663         window->y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top);
   664 #if NTDDI_VERSION < NTDDI_WIN10
   665         /* On WinRT 8.x / pre-Win10, just use the size we were given. */
   666         window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
   667         window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
   668 #else
   669         /* On Windows 10, we occasionally get control over window size.  For windowed
   670            mode apps, try this.
   671         */
   672         bool didSetSize = false;
   673         if (!(requestedFlags & SDL_WINDOW_FULLSCREEN)) {
   674             const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w),
   675                                                  WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h));
   676             didSetSize = data->appView->TryResizeView(size);
   677         }
   678         if (!didSetSize) {
   679             /* We either weren't able to set the window size, or a request for
   680                fullscreen was made.  Get window-size info from the OS.
   681             */
   682             window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
   683             window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
   684         }
   685 #endif
   686 
   687         WINRT_UpdateWindowFlags(
   688             window,
   689             0xffffffff      /* Update any window flag(s) that WINRT_UpdateWindow can handle */
   690         );
   691 
   692         /* Try detecting if the window is active */
   693         bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get());
   694         if (isWindowActive) {
   695             SDL_SetKeyboardFocus(window);
   696         }
   697     }
   698  
   699     /* Make sure the WinRT app's IFramworkView can post events on
   700        behalf of SDL:
   701     */
   702     WINRT_GlobalSDLWindow = window;
   703 
   704     /* All done! */
   705     return 0;
   706 }
   707 
   708 void
   709 WINRT_SetWindowSize(_THIS, SDL_Window * window)
   710 {
   711 #if NTDDI_VERSION >= NTDDI_WIN10
   712     SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
   713     const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w),
   714                                          WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h));
   715     data->appView->TryResizeView(size); // TODO, WinRT: return failure (to caller?) from TryResizeView()
   716 #endif
   717 }
   718 
   719 void
   720 WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   721 {
   722 #if NTDDI_VERSION >= NTDDI_WIN10
   723     SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
   724     bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get());
   725     if (isWindowActive) {
   726         if (fullscreen) {
   727             if (!data->appView->IsFullScreenMode) {
   728                 data->appView->TryEnterFullScreenMode();    // TODO, WinRT: return failure (to caller?) from TryEnterFullScreenMode()
   729             }
   730         } else {
   731             if (data->appView->IsFullScreenMode) {
   732                 data->appView->ExitFullScreenMode();
   733             }
   734         }
   735     }
   736 #endif
   737 }
   738 
   739 
   740 void
   741 WINRT_DestroyWindow(_THIS, SDL_Window * window)
   742 {
   743     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   744 
   745     if (WINRT_GlobalSDLWindow == window) {
   746         WINRT_GlobalSDLWindow = NULL;
   747     }
   748 
   749     if (data) {
   750         // Delete the internal window data:
   751         delete data;
   752         data = NULL;
   753         window->driverdata = NULL;
   754     }
   755 }
   756 
   757 SDL_bool
   758 WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   759 {
   760     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   761 
   762     if (info->version.major <= SDL_MAJOR_VERSION) {
   763         info->subsystem = SDL_SYSWM_WINRT;
   764         info->info.winrt.window = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   765         return SDL_TRUE;
   766     } else {
   767         SDL_SetError("Application not compiled with SDL %d.%d",
   768                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   769         return SDL_FALSE;
   770     }
   771     return SDL_FALSE;
   772 }
   773 
   774 static ABI::Windows::System::Display::IDisplayRequest *
   775 WINRT_CreateDisplayRequest(_THIS)
   776 {
   777     /* Setup a WinRT DisplayRequest object, usable for enabling/disabling screensaver requests */
   778     wchar_t *wClassName = L"Windows.System.Display.DisplayRequest";
   779     HSTRING hClassName;
   780     IActivationFactory *pActivationFactory = NULL;
   781     IInspectable * pDisplayRequestRaw = nullptr;
   782     ABI::Windows::System::Display::IDisplayRequest * pDisplayRequest = nullptr;
   783     HRESULT hr;
   784 
   785     hr = ::WindowsCreateString(wClassName, (UINT32)wcslen(wClassName), &hClassName);
   786     if (FAILED(hr)) {
   787         goto done;
   788     }
   789 
   790     hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory);
   791     if (FAILED(hr)) {
   792         goto done;
   793     }
   794 
   795     hr = pActivationFactory->ActivateInstance(&pDisplayRequestRaw);
   796     if (FAILED(hr)) {
   797         goto done;
   798     }
   799 
   800     hr = pDisplayRequestRaw->QueryInterface(IID_IDisplayRequest, (void **) &pDisplayRequest);
   801     if (FAILED(hr)) {
   802         goto done;
   803     }
   804 
   805 done:
   806     if (pDisplayRequestRaw) {
   807         pDisplayRequestRaw->Release();
   808     }
   809     if (pActivationFactory) {
   810         pActivationFactory->Release();
   811     }
   812     if (hClassName) {
   813         ::WindowsDeleteString(hClassName);
   814     }
   815 
   816     return pDisplayRequest;
   817 }
   818 
   819 void
   820 WINRT_SuspendScreenSaver(_THIS)
   821 {
   822     SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata;
   823     if (driverdata && driverdata->displayRequest) {
   824         ABI::Windows::System::Display::IDisplayRequest * displayRequest = (ABI::Windows::System::Display::IDisplayRequest *) driverdata->displayRequest;
   825         if (_this->suspend_screensaver) {
   826             displayRequest->RequestActive();
   827         } else {
   828             displayRequest->RequestRelease();
   829         }
   830     }
   831 }
   832 
   833 #endif /* SDL_VIDEO_DRIVER_WINRT */
   834 
   835 /* vi: set ts=4 sw=4 expandtab: */