SDL_winrtvideo.cpp 33 KB

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