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