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