SDL_winrtapp.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. 
  2. /* Standard C++11 includes */
  3. #include <functional>
  4. #include <string>
  5. #include <sstream>
  6. using namespace std;
  7. /* Windows includes */
  8. #include "ppltasks.h"
  9. using namespace concurrency;
  10. using namespace Windows::ApplicationModel;
  11. using namespace Windows::ApplicationModel::Core;
  12. using namespace Windows::ApplicationModel::Activation;
  13. using namespace Windows::Devices::Input;
  14. using namespace Windows::Graphics::Display;
  15. using namespace Windows::Foundation;
  16. using namespace Windows::System;
  17. using namespace Windows::UI::Core;
  18. using namespace Windows::UI::Input;
  19. /* SDL includes */
  20. extern "C" {
  21. #include "SDL_assert.h"
  22. #include "SDL_events.h"
  23. #include "SDL_hints.h"
  24. #include "SDL_log.h"
  25. #include "SDL_main.h"
  26. #include "SDL_stdinc.h"
  27. #include "SDL_render.h"
  28. #include "../../video/SDL_sysvideo.h"
  29. //#include "../../SDL_hints_c.h"
  30. #include "../../events/SDL_mouse_c.h"
  31. #include "../../events/SDL_windowevents_c.h"
  32. #include "../../render/SDL_sysrender.h"
  33. }
  34. #include "../../video/winrt/SDL_winrtevents_c.h"
  35. #include "SDL_winrtapp.h"
  36. extern SDL_Window * WINRT_GlobalSDLWindow;
  37. extern SDL_VideoDevice * WINRT_GlobalSDLVideoDevice;
  38. // Compile-time debugging options:
  39. // To enable, uncomment; to disable, comment them out.
  40. //#define LOG_POINTER_EVENTS 1
  41. //#define LOG_WINDOW_EVENTS 1
  42. //#define LOG_ORIENTATION_EVENTS 1
  43. // HACK, DLudwig: The C-style main() will get loaded via the app's
  44. // WinRT-styled main(), which is part of SDLmain_for_WinRT.cpp.
  45. // This seems wrong on some level, but does seem to work.
  46. typedef int (*SDL_WinRT_MainFunction)(int, char **);
  47. static SDL_WinRT_MainFunction SDL_WinRT_main = nullptr;
  48. // HACK, DLudwig: record a reference to the global, WinRT 'app'/view.
  49. // SDL/WinRT will use this throughout its code.
  50. //
  51. // TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something
  52. // non-global, such as something created inside
  53. // SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside
  54. // SDL_CreateWindow().
  55. SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr;
  56. ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
  57. {
  58. public:
  59. virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
  60. };
  61. IFrameworkView^ SDLApplicationSource::CreateView()
  62. {
  63. // TODO, WinRT: see if this function (CreateView) can ever get called
  64. // more than once. For now, just prevent it from ever assigning
  65. // SDL_WinRTGlobalApp more than once.
  66. SDL_assert(!SDL_WinRTGlobalApp);
  67. SDL_WinRTApp ^ app = ref new SDL_WinRTApp();
  68. if (!SDL_WinRTGlobalApp)
  69. {
  70. SDL_WinRTGlobalApp = app;
  71. }
  72. return app;
  73. }
  74. __declspec(dllexport) int SDL_WinRT_RunApplication(SDL_WinRT_MainFunction mainFunction)
  75. {
  76. SDL_WinRT_main = mainFunction;
  77. auto direct3DApplicationSource = ref new SDLApplicationSource();
  78. CoreApplication::Run(direct3DApplicationSource);
  79. return 0;
  80. }
  81. static void WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue)
  82. {
  83. SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0);
  84. // Start with no orientation flags, then add each in as they're parsed
  85. // from newValue.
  86. unsigned int orientationFlags = 0;
  87. if (newValue) {
  88. std::istringstream tokenizer(newValue);
  89. while (!tokenizer.eof()) {
  90. std::string orientationName;
  91. std::getline(tokenizer, orientationName, ' ');
  92. if (orientationName == "LandscapeLeft") {
  93. orientationFlags |= (unsigned int) DisplayOrientations::LandscapeFlipped;
  94. } else if (orientationName == "LandscapeRight") {
  95. orientationFlags |= (unsigned int) DisplayOrientations::Landscape;
  96. } else if (orientationName == "Portrait") {
  97. orientationFlags |= (unsigned int) DisplayOrientations::Portrait;
  98. } else if (orientationName == "PortraitUpsideDown") {
  99. orientationFlags |= (unsigned int) DisplayOrientations::PortraitFlipped;
  100. }
  101. }
  102. }
  103. // If no valid orientation flags were specified, use a reasonable set of defaults:
  104. if (!orientationFlags) {
  105. // TODO, WinRT: consider seeing if an app's default orientation flags can be found out via some API call(s).
  106. orientationFlags = (unsigned int) ( \
  107. DisplayOrientations::Landscape |
  108. DisplayOrientations::LandscapeFlipped |
  109. DisplayOrientations::Portrait |
  110. DisplayOrientations::PortraitFlipped);
  111. }
  112. // Set the orientation/rotation preferences. Please note that this does
  113. // not constitute a 100%-certain lock of a given set of possible
  114. // orientations. According to Microsoft's documentation on WinRT [1]
  115. // when a device is not capable of being rotated, Windows may ignore
  116. // the orientation preferences, and stick to what the device is capable of
  117. // displaying.
  118. //
  119. // [1] Documentation on the 'InitialRotationPreference' setting for a
  120. // Windows app's manifest file describes how some orientation/rotation
  121. // preferences may be ignored. See
  122. // http://msdn.microsoft.com/en-us/library/windows/apps/hh700343.aspx
  123. // for details. Microsoft's "Display orientation sample" also gives an
  124. // outline of how Windows treats device rotation
  125. // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93).
  126. DisplayProperties::AutoRotationPreferences = (DisplayOrientations) orientationFlags;
  127. }
  128. SDL_WinRTApp::SDL_WinRTApp() :
  129. m_windowClosed(false),
  130. m_windowVisible(true)
  131. {
  132. }
  133. void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
  134. {
  135. applicationView->Activated +=
  136. ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnActivated);
  137. CoreApplication::Suspending +=
  138. ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
  139. CoreApplication::Resuming +=
  140. ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
  141. DisplayProperties::OrientationChanged +=
  142. ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
  143. // Register the hint, SDL_HINT_ORIENTATIONS, with SDL. This needs to be
  144. // done before the hint's callback is registered (as of Feb 22, 2013),
  145. // otherwise the hint callback won't get registered.
  146. //
  147. // WinRT, TODO: 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.
  148. //SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight Portrait PortraitUpsideDown"); // DavidL: this is no longer needed (for SDL_AddHintCallback)
  149. SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
  150. }
  151. void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
  152. {
  153. #if LOG_ORIENTATION_EVENTS==1
  154. CoreWindow^ window = CoreWindow::GetForCurrentThread();
  155. if (window) {
  156. SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Size={%f,%f}\n",
  157. __FUNCTION__,
  158. (int)DisplayProperties::CurrentOrientation,
  159. (int)DisplayProperties::NativeOrientation,
  160. (int)DisplayProperties::AutoRotationPreferences,
  161. window->Bounds.Width,
  162. window->Bounds.Height);
  163. } else {
  164. SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
  165. __FUNCTION__,
  166. (int)DisplayProperties::CurrentOrientation,
  167. (int)DisplayProperties::NativeOrientation,
  168. (int)DisplayProperties::AutoRotationPreferences);
  169. }
  170. #endif
  171. }
  172. void SDL_WinRTApp::SetWindow(CoreWindow^ window)
  173. {
  174. #if LOG_WINDOW_EVENTS==1
  175. SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window Size={%f,%f}\n",
  176. __FUNCTION__,
  177. (int)DisplayProperties::CurrentOrientation,
  178. (int)DisplayProperties::NativeOrientation,
  179. (int)DisplayProperties::AutoRotationPreferences,
  180. window->Bounds.Width,
  181. window->Bounds.Height);
  182. #endif
  183. window->SizeChanged +=
  184. ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
  185. window->VisibilityChanged +=
  186. ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
  187. window->Closed +=
  188. ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
  189. #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
  190. window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
  191. #endif
  192. window->PointerPressed +=
  193. ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
  194. window->PointerReleased +=
  195. ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
  196. window->PointerWheelChanged +=
  197. ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
  198. window->PointerMoved +=
  199. ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
  200. #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
  201. // Retrieves relative-only mouse movements:
  202. Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
  203. ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
  204. #endif
  205. window->KeyDown +=
  206. ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
  207. window->KeyUp +=
  208. ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
  209. }
  210. void SDL_WinRTApp::Load(Platform::String^ entryPoint)
  211. {
  212. }
  213. void SDL_WinRTApp::Run()
  214. {
  215. SDL_SetMainReady();
  216. if (SDL_WinRT_main)
  217. {
  218. // TODO, WinRT: pass the C-style main() a reasonably realistic
  219. // representation of command line arguments.
  220. int argc = 0;
  221. char **argv = NULL;
  222. SDL_WinRT_main(argc, argv);
  223. }
  224. }
  225. void SDL_WinRTApp::PumpEvents()
  226. {
  227. if (!m_windowClosed)
  228. {
  229. if (m_windowVisible)
  230. {
  231. CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
  232. }
  233. else
  234. {
  235. CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
  236. }
  237. }
  238. }
  239. void SDL_WinRTApp::Uninitialize()
  240. {
  241. }
  242. void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
  243. {
  244. #if LOG_WINDOW_EVENTS==1
  245. SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n",
  246. __FUNCTION__,
  247. args->Size.Width, args->Size.Height,
  248. (int)DisplayProperties::CurrentOrientation,
  249. (int)DisplayProperties::NativeOrientation,
  250. (int)DisplayProperties::AutoRotationPreferences,
  251. (WINRT_GlobalSDLWindow ? "yes" : "no"));
  252. #endif
  253. if (WINRT_GlobalSDLWindow) {
  254. // Make the new window size be the one true fullscreen mode.
  255. // This change was initially done, in part, to allow the Direct3D 11.1
  256. // renderer to receive window-resize events as a device rotates.
  257. // Before, rotating a device from landscape, to portrait, and then
  258. // back to landscape would cause the Direct3D 11.1 swap buffer to
  259. // not get resized appropriately. SDL would, on the rotation from
  260. // landscape to portrait, re-resize the SDL window to it's initial
  261. // size (landscape). On the subsequent rotation, SDL would drop the
  262. // window-resize event as it appeared the SDL window didn't change
  263. // size, and the Direct3D 11.1 renderer wouldn't resize its swap
  264. // chain.
  265. SDL_DisplayMode resizedDisplayMode = CalcCurrentDisplayMode();
  266. WINRT_GlobalSDLVideoDevice->displays[0].current_mode = resizedDisplayMode;
  267. WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode = resizedDisplayMode;
  268. WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0] = resizedDisplayMode;
  269. // Send the window-resize event to the rest of SDL, and to apps:
  270. const int windowWidth = (int) ceil(args->Size.Width);
  271. const int windowHeight = (int) ceil(args->Size.Height);
  272. SDL_SendWindowEvent(
  273. WINRT_GlobalSDLWindow,
  274. SDL_WINDOWEVENT_RESIZED,
  275. windowWidth,
  276. windowHeight);
  277. }
  278. }
  279. void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
  280. {
  281. #if LOG_WINDOW_EVENTS==1
  282. SDL_Log("%s, visible?=%s, WINRT_GlobalSDLWindow?=%s\n",
  283. __FUNCTION__,
  284. (args->Visible ? "yes" : "no"),
  285. (WINRT_GlobalSDLWindow ? "yes" : "no"));
  286. #endif
  287. m_windowVisible = args->Visible;
  288. if (WINRT_GlobalSDLWindow) {
  289. SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
  290. if (args->Visible) {
  291. SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
  292. } else {
  293. SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
  294. }
  295. // HACK: Prevent SDL's window-hide handling code, which currently
  296. // triggers a fake window resize (possibly erronously), from
  297. // marking the SDL window's surface as invalid.
  298. //
  299. // A better solution to this probably involves figuring out if the
  300. // fake window resize can be prevented.
  301. WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
  302. }
  303. }
  304. void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
  305. {
  306. #if LOG_WINDOW_EVENTS==1
  307. SDL_Log("%s\n", __FUNCTION__);
  308. #endif
  309. m_windowClosed = true;
  310. }
  311. void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
  312. {
  313. WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args);
  314. }
  315. void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
  316. {
  317. WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args);
  318. }
  319. void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
  320. {
  321. WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args);
  322. }
  323. void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
  324. {
  325. WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
  326. }
  327. void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
  328. {
  329. WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args);
  330. }
  331. void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
  332. {
  333. WINRT_ProcessKeyDownEvent(args);
  334. }
  335. void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
  336. {
  337. WINRT_ProcessKeyUpEvent(args);
  338. }
  339. void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
  340. {
  341. CoreWindow::GetForCurrentThread()->Activate();
  342. }
  343. static int SDLCALL RemoveAppSuspendAndResumeEvents(void * userdata, SDL_Event * event)
  344. {
  345. if (event->type == SDL_WINDOWEVENT)
  346. {
  347. switch (event->window.event)
  348. {
  349. case SDL_WINDOWEVENT_MINIMIZED:
  350. case SDL_WINDOWEVENT_RESTORED:
  351. // Return 0 to indicate that the event should be removed from the
  352. // event queue:
  353. return 0;
  354. default:
  355. break;
  356. }
  357. }
  358. // Return 1 to indicate that the event should stay in the event queue:
  359. return 1;
  360. }
  361. void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
  362. {
  363. // Save app state asynchronously after requesting a deferral. Holding a deferral
  364. // indicates that the application is busy performing suspending operations. Be
  365. // aware that a deferral may not be held indefinitely. After about five seconds,
  366. // the app will be forced to exit.
  367. SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
  368. create_task([this, deferral]()
  369. {
  370. // Send a window-minimized event immediately to observers.
  371. // CoreDispatcher::ProcessEvents, which is the backbone on which
  372. // SDL_WinRTApp::PumpEvents is built, will not return to its caller
  373. // once it sends out a suspend event. Any events posted to SDL's
  374. // event queue won't get received until the WinRT app is resumed.
  375. // SDL_AddEventWatch() may be used to receive app-suspend events on
  376. // WinRT.
  377. //
  378. // In order to prevent app-suspend events from being received twice:
  379. // first via a callback passed to SDL_AddEventWatch, and second via
  380. // SDL's event queue, the event will be sent to SDL, then immediately
  381. // removed from the queue.
  382. if (WINRT_GlobalSDLWindow)
  383. {
  384. SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
  385. SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
  386. }
  387. deferral->Complete();
  388. });
  389. }
  390. void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
  391. {
  392. // Restore any data or state that was unloaded on suspend. By default, data
  393. // and state are persisted when resuming from suspend. Note that this event
  394. // does not occur if the app was previously terminated.
  395. if (WINRT_GlobalSDLWindow)
  396. {
  397. SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently)
  398. // Remove the app-resume event from the queue, as is done with the
  399. // app-suspend event.
  400. //
  401. // TODO, WinRT: consider posting this event to the queue even though
  402. // its counterpart, the app-suspend event, effectively has to be
  403. // processed immediately.
  404. SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0);
  405. }
  406. }
  407. SDL_DisplayMode SDL_WinRTApp::CalcCurrentDisplayMode()
  408. {
  409. // Create an empty, zeroed-out display mode:
  410. SDL_DisplayMode mode;
  411. SDL_zero(mode);
  412. // Fill in most fields:
  413. mode.format = SDL_PIXELFORMAT_RGB888;
  414. mode.refresh_rate = 0; // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps)
  415. mode.driverdata = NULL;
  416. // Calculate the display size given the window size, taking into account
  417. // the current display's DPI:
  418. const float currentDPI = Windows::Graphics::Display::DisplayProperties::LogicalDpi;
  419. const float dipsPerInch = 96.0f;
  420. mode.w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch);
  421. mode.h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch);
  422. return mode;
  423. }