2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "../../SDL_internal.h"
23 /* Standard C++11 includes */
30 /* Windows includes */
32 using namespace concurrency;
33 using namespace Windows::ApplicationModel;
34 using namespace Windows::ApplicationModel::Core;
35 using namespace Windows::ApplicationModel::Activation;
36 using namespace Windows::Devices::Input;
37 using namespace Windows::Graphics::Display;
38 using namespace Windows::Foundation;
39 using namespace Windows::System;
40 using namespace Windows::UI::Core;
41 using namespace Windows::UI::Input;
43 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
44 using namespace Windows::Phone::UI::Input;
50 #include "SDL_events.h"
51 #include "SDL_hints.h"
53 #include "SDL_stdinc.h"
54 #include "SDL_render.h"
55 #include "../../video/SDL_sysvideo.h"
56 //#include "../../SDL_hints_c.h"
57 #include "../../events/SDL_events_c.h"
58 #include "../../events/SDL_keyboard_c.h"
59 #include "../../events/SDL_mouse_c.h"
60 #include "../../events/SDL_windowevents_c.h"
61 #include "../../render/SDL_sysrender.h"
62 #include "../windows/SDL_windows.h"
65 #include "../../video/winrt/SDL_winrtevents_c.h"
66 #include "../../video/winrt/SDL_winrtvideo_cpp.h"
67 #include "SDL_winrtapp_common.h"
68 #include "SDL_winrtapp_direct3d.h"
70 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
71 /* Calling IDXGIDevice3::Trim on the active Direct3D 11.x device is necessary
72 * when Windows 8.1 apps are about to get suspended.
74 extern "C" void D3D11_Trim(SDL_Renderer *);
78 // Compile-time debugging options:
79 // To enable, uncomment; to disable, comment them out.
80 //#define LOG_POINTER_EVENTS 1
81 //#define LOG_WINDOW_EVENTS 1
82 //#define LOG_ORIENTATION_EVENTS 1
85 // HACK, DLudwig: record a reference to the global, WinRT 'app'/view.
86 // SDL/WinRT will use this throughout its code.
88 // TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something
89 // non-global, such as something created inside
90 // SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside
91 // SDL_CreateWindow().
92 SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr;
94 ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
97 virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
100 IFrameworkView^ SDLApplicationSource::CreateView()
102 // TODO, WinRT: see if this function (CreateView) can ever get called
103 // more than once. For now, just prevent it from ever assigning
104 // SDL_WinRTGlobalApp more than once.
105 SDL_assert(!SDL_WinRTGlobalApp);
106 SDL_WinRTApp ^ app = ref new SDL_WinRTApp();
107 if (!SDL_WinRTGlobalApp)
109 SDL_WinRTGlobalApp = app;
114 int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **))
116 WINRT_SDLAppEntryPoint = mainFunction;
117 auto direct3DApplicationSource = ref new SDLApplicationSource();
118 CoreApplication::Run(direct3DApplicationSource);
123 WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue)
125 SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0);
127 /* HACK: prevent SDL from altering an app's .appxmanifest-set orientation
128 * from being changed on startup, by detecting when SDL_HINT_ORIENTATIONS
129 * is getting registered.
131 * TODO, WinRT: consider reading in an app's .appxmanifest file, and apply its orientation when 'newValue == NULL'.
133 if ((oldValue == NULL) && (newValue == NULL)) {
137 // Start with no orientation flags, then add each in as they're parsed
139 unsigned int orientationFlags = 0;
141 std::istringstream tokenizer(newValue);
142 while (!tokenizer.eof()) {
143 std::string orientationName;
144 std::getline(tokenizer, orientationName, ' ');
145 if (orientationName == "LandscapeLeft") {
146 orientationFlags |= (unsigned int) DisplayOrientations::LandscapeFlipped;
147 } else if (orientationName == "LandscapeRight") {
148 orientationFlags |= (unsigned int) DisplayOrientations::Landscape;
149 } else if (orientationName == "Portrait") {
150 orientationFlags |= (unsigned int) DisplayOrientations::Portrait;
151 } else if (orientationName == "PortraitUpsideDown") {
152 orientationFlags |= (unsigned int) DisplayOrientations::PortraitFlipped;
157 // If no valid orientation flags were specified, use a reasonable set of defaults:
158 if (!orientationFlags) {
159 // TODO, WinRT: consider seeing if an app's default orientation flags can be found out via some API call(s).
160 orientationFlags = (unsigned int) ( \
161 DisplayOrientations::Landscape |
162 DisplayOrientations::LandscapeFlipped |
163 DisplayOrientations::Portrait |
164 DisplayOrientations::PortraitFlipped);
167 // Set the orientation/rotation preferences. Please note that this does
168 // not constitute a 100%-certain lock of a given set of possible
169 // orientations. According to Microsoft's documentation on WinRT [1]
170 // when a device is not capable of being rotated, Windows may ignore
171 // the orientation preferences, and stick to what the device is capable of
174 // [1] Documentation on the 'InitialRotationPreference' setting for a
175 // Windows app's manifest file describes how some orientation/rotation
176 // preferences may be ignored. See
177 // http://msdn.microsoft.com/en-us/library/windows/apps/hh700343.aspx
178 // for details. Microsoft's "Display orientation sample" also gives an
179 // outline of how Windows treats device rotation
180 // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93).
181 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences) = (DisplayOrientations) orientationFlags;
185 WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange()
187 CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread();
189 if (WINRT_GlobalSDLWindow) {
190 SDL_Window * window = WINRT_GlobalSDLWindow;
191 SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
193 int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left);
194 int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top);
195 int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
196 int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
198 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8)
199 /* WinPhone 8.0 always keeps its native window size in portrait,
200 regardless of orientation. This changes in WinPhone 8.1,
201 in which the native window's size changes along with
204 Attempt to emulate WinPhone 8.1's behavior on WinPhone 8.0, with
205 regards to window size. This fixes a rendering bug that occurs
206 when a WinPhone 8.0 app is rotated to either 90 or 270 degrees.
208 const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation);
209 switch (currentOrientation) {
210 case DisplayOrientations::Landscape:
211 case DisplayOrientations::LandscapeFlipped: {
219 const Uint32 latestFlags = WINRT_DetectWindowFlags(window);
220 if (latestFlags & SDL_WINDOW_MAXIMIZED) {
221 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
223 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
226 WINRT_UpdateWindowFlags(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
228 /* The window can move during a resize event, such as when maximizing
229 or resizing from a corner */
230 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
231 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
236 SDL_WinRTApp::SDL_WinRTApp() :
237 m_windowClosed(false),
238 m_windowVisible(true)
242 void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
244 applicationView->Activated +=
245 ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnAppActivated);
247 CoreApplication::Suspending +=
248 ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
250 CoreApplication::Resuming +=
251 ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
253 CoreApplication::Exiting +=
254 ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
256 #if NTDDI_VERSION >= NTDDI_WIN10
257 /* HACK ALERT! Xbox One doesn't seem to detect gamepads unless something
258 gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded
259 events. We'll register an event handler for these events here, to make
260 sure that gamepad detection works later on, if requested.
262 Windows::Gaming::Input::Gamepad::GamepadAdded +=
263 ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>(
264 this, &SDL_WinRTApp::OnGamepadAdded
269 #if NTDDI_VERSION > NTDDI_WIN8
270 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
272 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
275 #if LOG_ORIENTATION_EVENTS==1
277 CoreWindow^ window = CoreWindow::GetForCurrentThread();
279 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Bounds={%f,%f,%f,%f}\n",
281 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
282 WINRT_DISPLAY_PROPERTY(NativeOrientation),
283 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
286 window->Bounds.Width,
287 window->Bounds.Height);
289 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
291 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
292 WINRT_DISPLAY_PROPERTY(NativeOrientation),
293 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences));
298 WINRT_ProcessWindowSizeChange();
300 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
301 // HACK: Make sure that orientation changes
302 // lead to the Direct3D renderer's viewport getting updated:
304 // For some reason, this doesn't seem to need to be done on Windows 8.x,
305 // even when going from Landscape to LandscapeFlipped. It only seems to
306 // be needed on Windows Phone, at least when I tested on my devices.
307 // I'm not currently sure why this is, but it seems to work fine. -- David L.
309 // TODO, WinRT: do more extensive research into why orientation changes on Win 8.x don't need D3D changes, or if they might, in some cases
310 SDL_Window * window = WINRT_GlobalSDLWindow;
312 SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
313 int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
314 int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
315 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h);
321 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
323 #if LOG_WINDOW_EVENTS==1
324 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window bounds={%f, %f, %f,%f}\n",
326 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
327 WINRT_DISPLAY_PROPERTY(NativeOrientation),
328 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
331 window->Bounds.Width,
332 window->Bounds.Height);
335 window->SizeChanged +=
336 ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
338 window->VisibilityChanged +=
339 ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
342 ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>(this, &SDL_WinRTApp::OnWindowActivated);
345 ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
347 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
348 window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
351 window->PointerPressed +=
352 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
354 window->PointerMoved +=
355 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
357 window->PointerReleased +=
358 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
360 window->PointerEntered +=
361 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerEntered);
363 window->PointerExited +=
364 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerExited);
366 window->PointerWheelChanged +=
367 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
369 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
370 // Retrieves relative-only mouse movements:
371 Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
372 ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
376 ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
379 ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
381 window->CharacterReceived +=
382 ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &SDL_WinRTApp::OnCharacterReceived);
384 #if NTDDI_VERSION >= NTDDI_WIN10
385 Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested +=
386 ref new EventHandler<BackRequestedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
387 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
388 HardwareButtons::BackPressed +=
389 ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
392 #if NTDDI_VERSION > NTDDI_WIN8
393 DisplayInformation::GetForCurrentView()->OrientationChanged +=
394 ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
396 DisplayProperties::OrientationChanged +=
397 ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
400 // Register the hint, SDL_HINT_ORIENTATIONS, with SDL.
401 // 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.
402 SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
404 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) // for Windows 8/8.1/RT apps... (and not Phone apps)
405 // Make sure we know when a user has opened the app's settings pane.
406 // This is needed in order to display a privacy policy, which needs
407 // to be done for network-enabled apps, as per Windows Store requirements.
408 using namespace Windows::UI::ApplicationSettings;
409 SettingsPane::GetForCurrentView()->CommandsRequested +=
410 ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
411 (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
415 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
419 void SDL_WinRTApp::Run()
422 if (WINRT_SDLAppEntryPoint)
424 // TODO, WinRT: pass the C-style main() a reasonably realistic
425 // representation of command line arguments.
428 WINRT_SDLAppEntryPoint(argc, argv);
432 static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
434 SDL_Event events[128];
435 const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
436 for (int i = 0; i < count; ++i) {
437 if (events[i].window.event == windowEventID) {
444 bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
446 /* Don't wait if the app is visible: */
447 if (m_windowVisible) {
451 /* Don't wait until the window-hide events finish processing.
452 * Do note that if an app-suspend event is sent (as indicated
453 * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
454 * events), then this code may be a moot point, as WinRT's
455 * own event pump (aka ProcessEvents()) will pause regardless
456 * of what we do here. This happens on Windows Phone 8, to note.
457 * Windows 8.x apps, on the other hand, may get a chance to run
460 if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
462 } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
464 } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
471 void SDL_WinRTApp::PumpEvents()
473 if (!m_windowClosed) {
474 if (!ShouldWaitForAppResumeEvents()) {
475 /* This is the normal way in which events should be pumped.
476 * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
477 * from zero to N events, and will then return.
479 CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
481 /* This style of event-pumping, with 'ProcessOneAndAllPending',
482 * will cause anywhere from one to N events to be processed. If
483 * at least one event is processed, the call will return. If
484 * no events are pending, then the call will wait until one is
485 * available, and will not return (to the caller) until this
486 * happens! This should only occur when the app is hidden.
488 CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
493 void SDL_WinRTApp::Uninitialize()
497 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
498 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
499 Windows::UI::ApplicationSettings::SettingsPane ^p,
500 Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
502 using namespace Platform;
503 using namespace Windows::UI::ApplicationSettings;
504 using namespace Windows::UI::Popups;
506 String ^privacyPolicyURL = nullptr; // a URL to an app's Privacy Policy
507 String ^privacyPolicyLabel = nullptr; // label/link text
508 const char *tmpHintValue = NULL; // SDL_GetHint-retrieved value, used immediately
509 wchar_t *tmpStr = NULL; // used for UTF8 to UCS2 conversion
511 // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
512 tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
513 if (tmpHintValue && tmpHintValue[0] != '\0') {
514 // Convert the privacy policy's URL to UCS2:
515 tmpStr = WIN_UTF8ToString(tmpHintValue);
516 privacyPolicyURL = ref new String(tmpStr);
519 // Optionally retrieve custom label-text for the link. If this isn't
520 // available, a default value will be used instead.
521 tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
522 if (tmpHintValue && tmpHintValue[0] != '\0') {
523 tmpStr = WIN_UTF8ToString(tmpHintValue);
524 privacyPolicyLabel = ref new String(tmpStr);
527 privacyPolicyLabel = ref new String(L"Privacy Policy");
530 // Register the link, along with a handler to be called if and when it is
532 auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
533 ref new UICommandInvokedHandler([=](IUICommand ^) {
534 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
536 args->Request->ApplicationCommands->Append(cmd);
539 #endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
541 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
543 #if LOG_WINDOW_EVENTS==1
544 SDL_Log("%s, size={%f,%f}, bounds={%f,%f,%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n",
546 args->Size.Width, args->Size.Height,
547 sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height,
548 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
549 WINRT_DISPLAY_PROPERTY(NativeOrientation),
550 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
551 (WINRT_GlobalSDLWindow ? "yes" : "no"));
554 WINRT_ProcessWindowSizeChange();
557 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
559 #if LOG_WINDOW_EVENTS==1
560 SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n",
562 (args->Visible ? "yes" : "no"),
563 sender->Bounds.X, sender->Bounds.Y,
564 sender->Bounds.Width, sender->Bounds.Height,
565 (WINRT_GlobalSDLWindow ? "yes" : "no"));
568 m_windowVisible = args->Visible;
569 if (WINRT_GlobalSDLWindow) {
570 SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
571 Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow);
573 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
574 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
575 if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) {
576 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
578 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
581 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
582 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
583 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
586 // HACK: Prevent SDL's window-hide handling code, which currently
587 // triggers a fake window resize (possibly erronously), from
588 // marking the SDL window's surface as invalid.
590 // A better solution to this probably involves figuring out if the
591 // fake window resize can be prevented.
592 WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
596 void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args)
598 #if LOG_WINDOW_EVENTS==1
599 SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n",
601 (WINRT_GlobalSDLWindow ? "yes" : "no"));
604 /* There's no property in Win 8.x to tell whether a window is active or
605 not. [De]activation events are, however, sent to the app. We'll just
606 record those, in case the CoreWindow gets wrapped by an SDL_Window at
609 sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState);
611 SDL_Window * window = WINRT_GlobalSDLWindow;
613 if (args->WindowActivationState != CoreWindowActivationState::Deactivated) {
614 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
615 if (SDL_GetKeyboardFocus() != window) {
616 SDL_SetKeyboardFocus(window);
619 /* Send a mouse-motion event as appropriate.
620 This doesn't work when called from OnPointerEntered, at least
621 not in WinRT CoreWindow apps (as OnPointerEntered doesn't
622 appear to be called after window-reactivation, at least not
623 in Windows 10, Build 10586.3 (November 2015 update, non-beta).
625 Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition'
626 property isn't available.
628 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE)
629 Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize);
630 SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
633 /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */
634 //WIN_CheckAsyncMouseRelease(data);
636 /* TODO, WinRT: implement clipboard support, if possible */
638 // * FIXME: Update keyboard state
640 //WIN_CheckClipboardUpdate(data->videodata);
642 // HACK: Resetting the mouse-cursor here seems to fix
643 // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a
644 // WinRT app's mouse cursor may switch to Windows' 'wait' cursor,
645 // after a user alt-tabs back into a full-screened SDL app.
646 // This bug does not appear to reproduce 100% of the time.
647 // It may be a bug in Windows itself (v.10.0.586.36, as tested,
648 // and the most-recent as of this writing).
651 if (SDL_GetKeyboardFocus() == window) {
652 SDL_SetKeyboardFocus(NULL);
658 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
660 #if LOG_WINDOW_EVENTS==1
661 SDL_Log("%s\n", __FUNCTION__);
663 m_windowClosed = true;
666 void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
668 CoreWindow::GetForCurrentThread()->Activate();
671 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
673 // Save app state asynchronously after requesting a deferral. Holding a deferral
674 // indicates that the application is busy performing suspending operations. Be
675 // aware that a deferral may not be held indefinitely. After about five seconds,
676 // the app will be forced to exit.
678 // ... but first, let the app know it's about to go to the background.
679 // The separation of events may be important, given that the deferral
680 // runs in a separate thread. This'll make SDL_APP_WILLENTERBACKGROUND
681 // the only event among the two that runs in the main thread. Given
682 // that a few WinRT operations can only be done from the main thread
683 // (things that access the WinRT CoreWindow are one example of this),
684 // this could be important.
685 SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
687 SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
688 create_task([this, deferral]()
690 // Send an app did-enter-background event immediately to observers.
691 // CoreDispatcher::ProcessEvents, which is the backbone on which
692 // SDL_WinRTApp::PumpEvents is built, will not return to its caller
693 // once it sends out a suspend event. Any events posted to SDL's
694 // event queue won't get received until the WinRT app is resumed.
695 // SDL_AddEventWatch() may be used to receive app-suspend events on
697 SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
699 // Let the Direct3D 11 renderer prepare for the app to be backgrounded.
700 // This is necessary for Windows 8.1, possibly elsewhere in the future.
701 // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx
702 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
703 if (WINRT_GlobalSDLWindow) {
704 SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow);
705 if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) {
706 D3D11_Trim(renderer);
711 deferral->Complete();
715 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
717 // Restore any data or state that was unloaded on suspend. By default, data
718 // and state are persisted when resuming from suspend. Note that these events
719 // do not occur if the app was previously terminated.
720 SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
721 SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
724 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
726 SDL_SendAppEvent(SDL_APP_TERMINATING);
730 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
732 Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
733 SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d\n",
735 pt->Position.X, pt->Position.Y,
736 transformedPoint.X, transformedPoint.Y,
737 pt->Properties->MouseWheelDelta,
740 WINRT_GetSDLButtonForPointerPoint(pt));
743 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
745 #if LOG_POINTER_EVENTS
746 WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
749 WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
752 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
754 #if LOG_POINTER_EVENTS
755 WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
758 WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
761 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
763 #if LOG_POINTER_EVENTS
764 WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
767 WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
770 void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args)
772 #if LOG_POINTER_EVENTS
773 WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
776 WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
779 void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args)
781 #if LOG_POINTER_EVENTS
782 WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
785 WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
788 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
790 #if LOG_POINTER_EVENTS
791 WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
794 WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
797 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
799 WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
802 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
804 WINRT_ProcessKeyDownEvent(args);
807 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
809 WINRT_ProcessKeyUpEvent(args);
812 void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args)
814 WINRT_ProcessCharacterReceivedEvent(args);
817 template <typename BackButtonEventArgs>
818 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
820 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
821 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
823 if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
824 args->Handled = true;
828 #if NTDDI_VERSION >= NTDDI_WIN10
829 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args)
832 WINRT_OnBackButtonPressed(args);
834 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
835 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
838 WINRT_OnBackButtonPressed(args);
842 #if NTDDI_VERSION >= NTDDI_WIN10
843 void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad)
845 /* HACK ALERT: Nothing needs to be done here, as this method currently
846 only exists to allow something to be registered with Win10's
847 GamepadAdded event, an operation that seems to be necessary to get
848 Xinput-based detection to work on Xbox One.
853 /* vi: set ts=4 sw=4 expandtab: */