• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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
WINRT_Available(void)100 WINRT_Available(void)
101 {
102     return (1);
103 }
104 
105 static void
WINRT_DeleteDevice(SDL_VideoDevice * device)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 *
WINRT_CreateDevice(int devindex)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->CreateWindow = 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
WINRT_VideoInit(_THIS)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
WINRT_DXGIModeToSDLDisplayMode(const DXGI_MODE_DESC * dxgiMode,SDL_DisplayMode * sdlMode)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
WINRT_AddDisplaysForOutput(_THIS,IDXGIAdapter1 * dxgiAdapter1,int outputIndex)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
WINRT_AddDisplaysForAdapter(_THIS,IDXGIFactory2 * dxgiFactory2,int adapterIndex)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
WINRT_InitModes(_THIS)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
WINRT_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)428 WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
429 {
430     return 0;
431 }
432 
433 void
WINRT_VideoQuit(_THIS)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
WINRT_DetectWindowFlags(SDL_Window * window)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
WINRT_UpdateWindowFlags(SDL_Window * window,Uint32 mask)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
WINRT_CreateWindow(_THIS,SDL_Window * window)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_SetError("eglCreateWindowSurface failed");
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_SetError("eglCreateWindowSurface failed");
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
WINRT_SetWindowSize(_THIS,SDL_Window * window)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
WINRT_SetWindowFullscreen(_THIS,SDL_Window * window,SDL_VideoDisplay * display,SDL_bool fullscreen)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
WINRT_DestroyWindow(_THIS,SDL_Window * window)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
WINRT_GetWindowWMInfo(_THIS,SDL_Window * window,SDL_SysWMinfo * info)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\n",
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 *
WINRT_CreateDisplayRequest(_THIS)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
WINRT_SuspendScreenSaver(_THIS)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: */
843