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