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