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