sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_winrtvideo.cpp (30564B)


      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 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: */