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