/* Simple DirectMedia Layer Copyright (C) 1997-2017 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "../../SDL_internal.h" #if SDL_VIDEO_DRIVER_WINRT /* WinRT SDL video driver implementation Initial work on this was done by David Ludwig (dludwig@pobox.com), and was based off of SDL's "dummy" video driver. */ /* Windows includes */ #include #include #include #include #include using namespace Windows::ApplicationModel::Core; using namespace Windows::Foundation; using namespace Windows::Graphics::Display; using namespace Windows::UI::Core; using namespace Windows::UI::ViewManagement; /* [re]declare Windows GUIDs locally, to limit the amount of external lib(s) SDL has to link to */ static const GUID IID_IDisplayRequest = { 0xe5732044, 0xf49f, 0x4b60, { 0x8d, 0xd4, 0x5e, 0x7e, 0x3a, 0x63, 0x2a, 0xc0 } }; static const GUID IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } }; /* SDL includes */ extern "C" { #include "SDL_video.h" #include "SDL_mouse.h" #include "../SDL_sysvideo.h" #include "../SDL_pixels_c.h" #include "../../events/SDL_events_c.h" #include "../../render/SDL_sysrender.h" #include "SDL_syswm.h" #include "SDL_winrtopengles.h" #include "../../core/windows/SDL_windows.h" } #include "../../core/winrt/SDL_winrtapp_direct3d.h" #include "../../core/winrt/SDL_winrtapp_xaml.h" #include "SDL_winrtvideo_cpp.h" #include "SDL_winrtevents_c.h" #include "SDL_winrtgamebar_cpp.h" #include "SDL_winrtmouse_c.h" #include "SDL_main.h" #include "SDL_system.h" //#include "SDL_log.h" /* Initialization/Query functions */ static int WINRT_VideoInit(_THIS); static int WINRT_InitModes(_THIS); static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); static void WINRT_VideoQuit(_THIS); /* Window functions */ static int WINRT_CreateWindow(_THIS, SDL_Window * window); static void WINRT_SetWindowSize(_THIS, SDL_Window * window); static void WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen); static void WINRT_DestroyWindow(_THIS, SDL_Window * window); static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info); /* Misc functions */ static ABI::Windows::System::Display::IDisplayRequest * WINRT_CreateDisplayRequest(_THIS); extern void WINRT_SuspendScreenSaver(_THIS); /* SDL-internal globals: */ SDL_Window * WINRT_GlobalSDLWindow = NULL; /* WinRT driver bootstrap functions */ static int WINRT_Available(void) { return (1); } static void WINRT_DeleteDevice(SDL_VideoDevice * device) { if (device->driverdata) { SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata; if (video_data->winrtEglWindow) { video_data->winrtEglWindow->Release(); } SDL_free(video_data); } SDL_free(device); } static SDL_VideoDevice * WINRT_CreateDevice(int devindex) { SDL_VideoDevice *device; SDL_VideoData *data; /* Initialize all variables that we clean on shutdown */ device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); if (!device) { SDL_OutOfMemory(); return (0); } data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); if (!data) { SDL_OutOfMemory(); SDL_free(device); return (0); } device->driverdata = data; /* Set the function pointers */ device->VideoInit = WINRT_VideoInit; device->VideoQuit = WINRT_VideoQuit; device->CreateWindow = WINRT_CreateWindow; device->SetWindowSize = WINRT_SetWindowSize; device->SetWindowFullscreen = WINRT_SetWindowFullscreen; device->DestroyWindow = WINRT_DestroyWindow; device->SetDisplayMode = WINRT_SetDisplayMode; device->PumpEvents = WINRT_PumpEvents; device->GetWindowWMInfo = WINRT_GetWindowWMInfo; device->SuspendScreenSaver = WINRT_SuspendScreenSaver; #if NTDDI_VERSION >= NTDDI_WIN10 device->HasScreenKeyboardSupport = WINRT_HasScreenKeyboardSupport; device->ShowScreenKeyboard = WINRT_ShowScreenKeyboard; device->HideScreenKeyboard = WINRT_HideScreenKeyboard; device->IsScreenKeyboardShown = WINRT_IsScreenKeyboardShown; #endif #ifdef SDL_VIDEO_OPENGL_EGL device->GL_LoadLibrary = WINRT_GLES_LoadLibrary; device->GL_GetProcAddress = WINRT_GLES_GetProcAddress; device->GL_UnloadLibrary = WINRT_GLES_UnloadLibrary; device->GL_CreateContext = WINRT_GLES_CreateContext; device->GL_MakeCurrent = WINRT_GLES_MakeCurrent; device->GL_SetSwapInterval = WINRT_GLES_SetSwapInterval; device->GL_GetSwapInterval = WINRT_GLES_GetSwapInterval; device->GL_SwapWindow = WINRT_GLES_SwapWindow; device->GL_DeleteContext = WINRT_GLES_DeleteContext; #endif device->free = WINRT_DeleteDevice; return device; } #define WINRTVID_DRIVER_NAME "winrt" VideoBootStrap WINRT_bootstrap = { WINRTVID_DRIVER_NAME, "SDL WinRT video driver", WINRT_Available, WINRT_CreateDevice }; int WINRT_VideoInit(_THIS) { SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata; if (WINRT_InitModes(_this) < 0) { return -1; } WINRT_InitMouse(_this); WINRT_InitTouch(_this); WINRT_InitGameBar(_this); if (driverdata) { /* Initialize screensaver-disabling support */ driverdata->displayRequest = WINRT_CreateDisplayRequest(_this); } return 0; } extern "C" Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat); static void WINRT_DXGIModeToSDLDisplayMode(const DXGI_MODE_DESC * dxgiMode, SDL_DisplayMode * sdlMode) { SDL_zerop(sdlMode); sdlMode->w = dxgiMode->Width; sdlMode->h = dxgiMode->Height; sdlMode->refresh_rate = dxgiMode->RefreshRate.Numerator / dxgiMode->RefreshRate.Denominator; sdlMode->format = D3D11_DXGIFormatToSDLPixelFormat(dxgiMode->Format); } static int WINRT_AddDisplaysForOutput (_THIS, IDXGIAdapter1 * dxgiAdapter1, int outputIndex) { HRESULT hr; IDXGIOutput * dxgiOutput = NULL; DXGI_OUTPUT_DESC dxgiOutputDesc; SDL_VideoDisplay display; char * displayName = NULL; UINT numModes; DXGI_MODE_DESC * dxgiModes = NULL; int functionResult = -1; /* -1 for failure, 0 for success */ DXGI_MODE_DESC modeToMatch, closestMatch; SDL_zero(display); hr = dxgiAdapter1->EnumOutputs(outputIndex, &dxgiOutput); if (FAILED(hr)) { if (hr != DXGI_ERROR_NOT_FOUND) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIAdapter1::EnumOutputs failed", hr); } goto done; } hr = dxgiOutput->GetDesc(&dxgiOutputDesc); if (FAILED(hr)) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDesc failed", hr); goto done; } SDL_zero(modeToMatch); modeToMatch.Format = DXGI_FORMAT_B8G8R8A8_UNORM; modeToMatch.Width = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left); modeToMatch.Height = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top); hr = dxgiOutput->FindClosestMatchingMode(&modeToMatch, &closestMatch, NULL); if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE gets returned by IDXGIOutput::FindClosestMatchingMode when running under the Windows Simulator, which uses Remote Desktop (formerly known as Terminal Services) under the hood. According to the MSDN docs for the similar function, IDXGIOutput::GetDisplayModeList, DXGI_ERROR_NOT_CURRENTLY_AVAILABLE is returned if and when an app is run under a Terminal Services session, hence the assumption. In this case, just add an SDL display mode, with approximated values. */ SDL_DisplayMode mode; SDL_zero(mode); display.name = "Windows Simulator / Terminal Services Display"; mode.w = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left); mode.h = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top); mode.format = DXGI_FORMAT_B8G8R8A8_UNORM; mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */ display.desktop_mode = mode; display.current_mode = mode; if ( ! SDL_AddDisplayMode(&display, &mode)) { goto done; } } else if (FAILED(hr)) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::FindClosestMatchingMode failed", hr); goto done; } else { displayName = WIN_StringToUTF8(dxgiOutputDesc.DeviceName); display.name = displayName; WINRT_DXGIModeToSDLDisplayMode(&closestMatch, &display.desktop_mode); display.current_mode = display.desktop_mode; hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, NULL); if (FAILED(hr)) { if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { // TODO, WinRT: make sure display mode(s) are added when using Terminal Services / Windows Simulator } WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode list size] failed", hr); goto done; } dxgiModes = (DXGI_MODE_DESC *)SDL_calloc(numModes, sizeof(DXGI_MODE_DESC)); if ( ! dxgiModes) { SDL_OutOfMemory(); goto done; } hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, dxgiModes); if (FAILED(hr)) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode contents] failed", hr); goto done; } for (UINT i = 0; i < numModes; ++i) { SDL_DisplayMode sdlMode; WINRT_DXGIModeToSDLDisplayMode(&dxgiModes[i], &sdlMode); SDL_AddDisplayMode(&display, &sdlMode); } } if (SDL_AddVideoDisplay(&display) < 0) { goto done; } functionResult = 0; /* 0 for Success! */ done: if (dxgiModes) { SDL_free(dxgiModes); } if (dxgiOutput) { dxgiOutput->Release(); } if (displayName) { SDL_free(displayName); } return functionResult; } static int WINRT_AddDisplaysForAdapter (_THIS, IDXGIFactory2 * dxgiFactory2, int adapterIndex) { HRESULT hr; IDXGIAdapter1 * dxgiAdapter1; hr = dxgiFactory2->EnumAdapters1(adapterIndex, &dxgiAdapter1); if (FAILED(hr)) { if (hr != DXGI_ERROR_NOT_FOUND) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIFactory1::EnumAdapters1() failed", hr); } return -1; } for (int outputIndex = 0; ; ++outputIndex) { if (WINRT_AddDisplaysForOutput(_this, dxgiAdapter1, outputIndex) < 0) { /* HACK: The Windows App Certification Kit 10.0 can fail, when running the Store Apps' test, "Direct3D Feature Test". The certification kit's error is: "Application App was not running at the end of the test. It likely crashed or was terminated for having become unresponsive." This was caused by SDL/WinRT's DXGI failing to report any outputs. Attempts to get the 1st display-output from the 1st display-adapter can fail, with IDXGIAdapter::EnumOutputs returning DXGI_ERROR_NOT_FOUND. This could be a bug in Windows, the Windows App Certification Kit, or possibly in SDL/WinRT's display detection code. Either way, try to detect when this happens, and use a hackish means to create a reasonable-as-possible 'display mode'. -- DavidL */ if (adapterIndex == 0 && outputIndex == 0) { SDL_VideoDisplay display; SDL_DisplayMode mode; #if SDL_WINRT_USE_APPLICATIONVIEW ApplicationView ^ appView = ApplicationView::GetForCurrentView(); #endif CoreWindow ^ coreWin = CoreWindow::GetForCurrentThread(); SDL_zero(display); SDL_zero(mode); display.name = "DXGI Display-detection Workaround"; /* HACK: ApplicationView's VisibleBounds property, appeared, via testing, to give a better approximation of display-size, than did CoreWindow's Bounds property, insofar that ApplicationView::VisibleBounds seems like it will, at least some of the time, give the full display size (during the failing test), whereas CoreWindow might not. -- DavidL */ #if (NTDDI_VERSION >= NTDDI_WIN10) || (SDL_WINRT_USE_APPLICATIONVIEW && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Width); mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Height); #else /* On platform(s) that do not support VisibleBounds, such as Windows 8.1, fall back to CoreWindow's Bounds property. */ mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Width); mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Height); #endif mode.format = DXGI_FORMAT_B8G8R8A8_UNORM; mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */ display.desktop_mode = mode; display.current_mode = mode; if ((SDL_AddDisplayMode(&display, &mode) < 0) || (SDL_AddVideoDisplay(&display) < 0)) { return SDL_SetError("Failed to apply DXGI Display-detection workaround"); } } break; } } dxgiAdapter1->Release(); return 0; } int WINRT_InitModes(_THIS) { /* HACK: Initialize a single display, for whatever screen the app's CoreApplicationView is on. TODO, WinRT: Try initializing multiple displays, one for each monitor. Appropriate WinRT APIs for this seem elusive, though. -- DavidL */ HRESULT hr; IDXGIFactory2 * dxgiFactory2 = NULL; hr = CreateDXGIFactory1(IID_IDXGIFactory2, (void **)&dxgiFactory2); if (FAILED(hr)) { WIN_SetErrorFromHRESULT(__FUNCTION__ ", CreateDXGIFactory1() failed", hr); return -1; } for (int adapterIndex = 0; ; ++adapterIndex) { if (WINRT_AddDisplaysForAdapter(_this, dxgiFactory2, adapterIndex) < 0) { break; } } return 0; } static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { return 0; } void WINRT_VideoQuit(_THIS) { SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata; if (driverdata && driverdata->displayRequest) { driverdata->displayRequest->Release(); driverdata->displayRequest = NULL; } WINRT_QuitGameBar(_this); WINRT_QuitMouse(_this); } static const Uint32 WINRT_DetectableFlags = SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_SHOWN | SDL_WINDOW_HIDDEN | SDL_WINDOW_MOUSE_FOCUS; extern "C" Uint32 WINRT_DetectWindowFlags(SDL_Window * window) { Uint32 latestFlags = 0; SDL_WindowData * data = (SDL_WindowData *) window->driverdata; bool is_fullscreen = false; #if SDL_WINRT_USE_APPLICATIONVIEW if (data->appView) { is_fullscreen = data->appView->IsFullScreen; } #elif (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION == NTDDI_WIN8) is_fullscreen = true; #endif if (data->coreWindow.Get()) { if (is_fullscreen) { SDL_VideoDisplay * display = SDL_GetDisplayForWindow(window); int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION > NTDDI_WIN8) // On all WinRT platforms, except for WinPhone 8.0, rotate the // window size. This is needed to properly calculate // fullscreen vs. maximized. const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation); switch (currentOrientation) { #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) case DisplayOrientations::Landscape: case DisplayOrientations::LandscapeFlipped: #else case DisplayOrientations::Portrait: case DisplayOrientations::PortraitFlipped: #endif { int tmp = w; w = h; h = tmp; } break; } #endif if (display->desktop_mode.w != w || display->desktop_mode.h != h) { latestFlags |= SDL_WINDOW_MAXIMIZED; } else { latestFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } } if (data->coreWindow->Visible) { latestFlags |= SDL_WINDOW_SHOWN; } else { latestFlags |= SDL_WINDOW_HIDDEN; } #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION < NTDDI_WINBLUE) // data->coreWindow->PointerPosition is not supported on WinPhone 8.0 latestFlags |= SDL_WINDOW_MOUSE_FOCUS; #else if (data->coreWindow->Visible && data->coreWindow->Bounds.Contains(data->coreWindow->PointerPosition)) { latestFlags |= SDL_WINDOW_MOUSE_FOCUS; } #endif } return latestFlags; } // TODO, WinRT: consider removing WINRT_UpdateWindowFlags, and just calling WINRT_DetectWindowFlags as-appropriate (with appropriate calls to SDL_SendWindowEvent) void WINRT_UpdateWindowFlags(SDL_Window * window, Uint32 mask) { mask &= WINRT_DetectableFlags; if (window) { Uint32 apply = WINRT_DetectWindowFlags(window); if ((apply & mask) & SDL_WINDOW_FULLSCREEN) { window->last_fullscreen_flags = window->flags; // seems necessary to programmatically un-fullscreen, via SDL APIs } window->flags = (window->flags & ~mask) | (apply & mask); } } static bool WINRT_IsCoreWindowActive(CoreWindow ^ coreWindow) { /* WinRT does not appear to offer API(s) to determine window-activation state, at least not that I am aware of in Win8 - Win10. As such, SDL tracks this itself, via window-activation events. If there *is* an API to track this, it should probably get used instead of the following hack (that uses "SDLHelperWindowActivationState"). -- DavidL. */ if (coreWindow->CustomProperties->HasKey("SDLHelperWindowActivationState")) { CoreWindowActivationState activationState = \ safe_cast(coreWindow->CustomProperties->Lookup("SDLHelperWindowActivationState")); return (activationState != CoreWindowActivationState::Deactivated); } /* Assume that non-SDL tracked windows are active, although this should probably be avoided, if possible. This might not even be possible, in normal SDL use, at least as of this writing (Dec 22, 2015; via latest hg.libsdl.org/SDL clone) -- DavidL */ return true; } int WINRT_CreateWindow(_THIS, SDL_Window * window) { // Make sure that only one window gets created, at least until multimonitor // support is added. if (WINRT_GlobalSDLWindow != NULL) { SDL_SetError("WinRT only supports one window"); return -1; } SDL_WindowData *data = new SDL_WindowData; /* use 'new' here as SDL_WindowData may use WinRT/C++ types */ if (!data) { SDL_OutOfMemory(); return -1; } window->driverdata = data; data->sdlWindow = window; /* To note, when XAML support is enabled, access to the CoreWindow will not be possible, at least not via the SDL/XAML thread. Attempts to access it from there will throw exceptions. As such, the SDL_WindowData's 'coreWindow' field will only be set (to a non-null value) if XAML isn't enabled. */ if (!WINRT_XAMLWasEnabled) { data->coreWindow = CoreWindow::GetForCurrentThread(); #if SDL_WINRT_USE_APPLICATIONVIEW data->appView = ApplicationView::GetForCurrentView(); #endif } /* Make note of the requested window flags, before they start getting changed. */ const Uint32 requestedFlags = window->flags; #if SDL_VIDEO_OPENGL_EGL /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */ if (!(window->flags & SDL_WINDOW_OPENGL)) { /* OpenGL ES 2 wasn't requested. Don't set up an EGL surface. */ data->egl_surface = EGL_NO_SURFACE; } else { /* OpenGL ES 2 was reuqested. Set up an EGL surface. */ SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata; /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly, * rather than via SDL_EGL_CreateSurface, as older versions of * ANGLE/WinRT may require that a C++ object, ComPtr, * be passed into eglCreateWindowSurface. */ if (SDL_EGL_ChooseConfig(_this) != 0) { char buf[512]; SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError()); return SDL_SetError("%s", buf); } if (video_data->winrtEglWindow) { /* ... is the 'old' version of ANGLE/WinRT being used? */ /* Attempt to create a window surface using older versions of * ANGLE/WinRT: */ Microsoft::WRL::ComPtr cpp_winrtEglWindow = video_data->winrtEglWindow; data->egl_surface = ((eglCreateWindowSurface_Old_Function)_this->egl_data->eglCreateWindowSurface)( _this->egl_data->egl_display, _this->egl_data->egl_config, cpp_winrtEglWindow, NULL); if (data->egl_surface == NULL) { return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface"); } } else if (data->coreWindow.Get() != nullptr) { /* Attempt to create a window surface using newer versions of * ANGLE/WinRT: */ IInspectable * coreWindowAsIInspectable = reinterpret_cast(data->coreWindow.Get()); data->egl_surface = _this->egl_data->eglCreateWindowSurface( _this->egl_data->egl_display, _this->egl_data->egl_config, coreWindowAsIInspectable, NULL); if (data->egl_surface == NULL) { return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface"); } } else { return SDL_SetError("No supported means to create an EGL window surface are available"); } } #endif /* Determine as many flags dynamically, as possible. */ window->flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE; #if SDL_VIDEO_OPENGL_EGL if (data->egl_surface) { window->flags |= SDL_WINDOW_OPENGL; } #endif if (WINRT_XAMLWasEnabled) { /* TODO, WinRT: set SDL_Window size, maybe position too, from XAML control */ window->x = 0; window->y = 0; window->flags |= SDL_WINDOW_SHOWN; SDL_SetMouseFocus(NULL); // TODO: detect this SDL_SetKeyboardFocus(NULL); // TODO: detect this } else { /* WinRT 8.x apps seem to live in an environment where the OS controls the app's window size, with some apps being fullscreen, depending on user choice of various things. For now, just adapt the SDL_Window to whatever Windows set-up as the native-window's geometry. */ window->x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left); window->y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top); #if NTDDI_VERSION < NTDDI_WIN10 /* On WinRT 8.x / pre-Win10, just use the size we were given. */ window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); #else /* On Windows 10, we occasionally get control over window size. For windowed mode apps, try this. */ bool didSetSize = false; if (!(requestedFlags & SDL_WINDOW_FULLSCREEN)) { const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w), WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h)); didSetSize = data->appView->TryResizeView(size); } if (!didSetSize) { /* We either weren't able to set the window size, or a request for fullscreen was made. Get window-size info from the OS. */ window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); } #endif WINRT_UpdateWindowFlags( window, 0xffffffff /* Update any window flag(s) that WINRT_UpdateWindow can handle */ ); /* Try detecting if the window is active */ bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get()); if (isWindowActive) { SDL_SetKeyboardFocus(window); } } /* Make sure the WinRT app's IFramworkView can post events on behalf of SDL: */ WINRT_GlobalSDLWindow = window; /* All done! */ return 0; } void WINRT_SetWindowSize(_THIS, SDL_Window * window) { #if NTDDI_VERSION >= NTDDI_WIN10 SDL_WindowData * data = (SDL_WindowData *)window->driverdata; const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w), WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h)); data->appView->TryResizeView(size); // TODO, WinRT: return failure (to caller?) from TryResizeView() #endif } void WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { #if NTDDI_VERSION >= NTDDI_WIN10 SDL_WindowData * data = (SDL_WindowData *)window->driverdata; bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get()); if (isWindowActive) { if (fullscreen) { if (!data->appView->IsFullScreenMode) { data->appView->TryEnterFullScreenMode(); // TODO, WinRT: return failure (to caller?) from TryEnterFullScreenMode() } } else { if (data->appView->IsFullScreenMode) { data->appView->ExitFullScreenMode(); } } } #endif } void WINRT_DestroyWindow(_THIS, SDL_Window * window) { SDL_WindowData * data = (SDL_WindowData *) window->driverdata; if (WINRT_GlobalSDLWindow == window) { WINRT_GlobalSDLWindow = NULL; } if (data) { // Delete the internal window data: delete data; data = NULL; window->driverdata = NULL; } } SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) { SDL_WindowData * data = (SDL_WindowData *) window->driverdata; if (info->version.major <= SDL_MAJOR_VERSION) { info->subsystem = SDL_SYSWM_WINRT; info->info.winrt.window = reinterpret_cast(data->coreWindow.Get()); return SDL_TRUE; } else { SDL_SetError("Application not compiled with SDL %d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION); return SDL_FALSE; } return SDL_FALSE; } static ABI::Windows::System::Display::IDisplayRequest * WINRT_CreateDisplayRequest(_THIS) { /* Setup a WinRT DisplayRequest object, usable for enabling/disabling screensaver requests */ wchar_t *wClassName = L"Windows.System.Display.DisplayRequest"; HSTRING hClassName; IActivationFactory *pActivationFactory = NULL; IInspectable * pDisplayRequestRaw = nullptr; ABI::Windows::System::Display::IDisplayRequest * pDisplayRequest = nullptr; HRESULT hr; hr = ::WindowsCreateString(wClassName, (UINT32)wcslen(wClassName), &hClassName); if (FAILED(hr)) { goto done; } hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory); if (FAILED(hr)) { goto done; } hr = pActivationFactory->ActivateInstance(&pDisplayRequestRaw); if (FAILED(hr)) { goto done; } hr = pDisplayRequestRaw->QueryInterface(IID_IDisplayRequest, (void **) &pDisplayRequest); if (FAILED(hr)) { goto done; } done: if (pDisplayRequestRaw) { pDisplayRequestRaw->Release(); } if (pActivationFactory) { pActivationFactory->Release(); } if (hClassName) { ::WindowsDeleteString(hClassName); } return pDisplayRequest; } void WINRT_SuspendScreenSaver(_THIS) { SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata; if (driverdata && driverdata->displayRequest) { ABI::Windows::System::Display::IDisplayRequest * displayRequest = (ABI::Windows::System::Display::IDisplayRequest *) driverdata->displayRequest; if (_this->suspend_screensaver) { displayRequest->RequestActive(); } else { displayRequest->RequestRelease(); } } } #endif /* SDL_VIDEO_DRIVER_WINRT */ /* vi: set ts=4 sw=4 expandtab: */