SDL_winrtvideo.cpp 32 KB

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