1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4 
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.
8 
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:
12 
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.
20 */
21 #include "../../SDL_internal.h"
22 
23 /* Standard C++11 includes */
24 #include <functional>
25 #include <string>
26 #include <sstream>
27 using namespace std;
28 
29 
30 /* Windows includes */
31 #include "ppltasks.h"
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;
42 
43 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
44 using namespace Windows::Phone::UI::Input;
45 #endif
46 
47 
48 /* SDL includes */
49 extern "C" {
50 #include "SDL_events.h"
51 #include "SDL_hints.h"
52 #include "SDL_main.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"
63 }
64 
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"
69 
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.
73  */
74 extern "C" void D3D11_Trim(SDL_Renderer *);
75 #endif
76 
77 
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
83 
84 
85 // HACK, DLudwig: record a reference to the global, WinRT 'app'/view.
86 // SDL/WinRT will use this throughout its code.
87 //
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;
93 
94 ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
95 {
96 public:
97     virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
98 };
99 
100 IFrameworkView^ SDLApplicationSource::CreateView()
101 {
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)
108     {
109         SDL_WinRTGlobalApp = app;
110     }
111     return app;
112 }
113 
SDL_WinRTInitNonXAMLApp(int (* mainFunction)(int,char **))114 int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **))
115 {
116     WINRT_SDLAppEntryPoint = mainFunction;
117     auto direct3DApplicationSource = ref new SDLApplicationSource();
118     CoreApplication::Run(direct3DApplicationSource);
119     return 0;
120 }
121 
122 static void
WINRT_ProcessWindowSizeChange()123 WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange()
124 {
125     CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread();
126     if (coreWindow) {
127         if (WINRT_GlobalSDLWindow) {
128             SDL_Window * window = WINRT_GlobalSDLWindow;
129             SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
130 
131             int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left);
132             int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top);
133             int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
134             int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
135 
136 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8)
137             /* WinPhone 8.0 always keeps its native window size in portrait,
138                regardless of orientation.  This changes in WinPhone 8.1,
139                in which the native window's size changes along with
140                orientation.
141 
142                Attempt to emulate WinPhone 8.1's behavior on WinPhone 8.0, with
143                regards to window size.  This fixes a rendering bug that occurs
144                when a WinPhone 8.0 app is rotated to either 90 or 270 degrees.
145             */
146             const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation);
147             switch (currentOrientation) {
148                 case DisplayOrientations::Landscape:
149                 case DisplayOrientations::LandscapeFlipped: {
150                     int tmp = w;
151                     w = h;
152                     h = tmp;
153                 } break;
154             }
155 #endif
156 
157             const Uint32 latestFlags = WINRT_DetectWindowFlags(window);
158             if (latestFlags & SDL_WINDOW_MAXIMIZED) {
159                 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
160             } else {
161                 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
162             }
163 
164             WINRT_UpdateWindowFlags(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
165 
166             /* The window can move during a resize event, such as when maximizing
167                or resizing from a corner */
168             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
169             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
170         }
171     }
172 }
173 
SDL_WinRTApp()174 SDL_WinRTApp::SDL_WinRTApp() :
175     m_windowClosed(false),
176     m_windowVisible(true)
177 {
178 }
179 
180 void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
181 {
182     applicationView->Activated +=
183         ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnAppActivated);
184 
185     CoreApplication::Suspending +=
186         ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending);
187 
188     CoreApplication::Resuming +=
189         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnResuming);
190 
191     CoreApplication::Exiting +=
192         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
193 
194 #if NTDDI_VERSION >= NTDDI_WIN10
195     /* HACK ALERT!  Xbox One doesn't seem to detect gamepads unless something
196        gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded
197        events.  We'll register an event handler for these events here, to make
198        sure that gamepad detection works later on, if requested.
199     */
200     Windows::Gaming::Input::Gamepad::GamepadAdded +=
201         ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>(
202             this, &SDL_WinRTApp::OnGamepadAdded
203         );
204 #endif
205 }
206 
207 #if NTDDI_VERSION > NTDDI_WIN8
208 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
209 #else
210 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
211 #endif
212 {
213 #if LOG_ORIENTATION_EVENTS==1
214     {
215         CoreWindow^ window = CoreWindow::GetForCurrentThread();
216         if (window) {
217             SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Bounds={%f,%f,%f,%f}\n",
218                 __FUNCTION__,
219                 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
220                 WINRT_DISPLAY_PROPERTY(NativeOrientation),
221                 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
222                 window->Bounds.X,
223                 window->Bounds.Y,
224                 window->Bounds.Width,
225                 window->Bounds.Height);
226         } else {
227             SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
228                 __FUNCTION__,
229                 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
230                 WINRT_DISPLAY_PROPERTY(NativeOrientation),
231                 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences));
232         }
233     }
234 #endif
235 
236     WINRT_ProcessWindowSizeChange();
237 
238 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
239     // HACK: Make sure that orientation changes
240     // lead to the Direct3D renderer's viewport getting updated:
241     //
242     // For some reason, this doesn't seem to need to be done on Windows 8.x,
243     // even when going from Landscape to LandscapeFlipped.  It only seems to
244     // be needed on Windows Phone, at least when I tested on my devices.
245     // I'm not currently sure why this is, but it seems to work fine. -- David L.
246     //
247     // 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
248     SDL_Window * window = WINRT_GlobalSDLWindow;
249     if (window) {
250         SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
251         int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
252         int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
253         SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h);
254     }
255 #endif
256 
257 }
258 
259 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
260 {
261 #if LOG_WINDOW_EVENTS==1
262     SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window bounds={%f, %f, %f,%f}\n",
263         __FUNCTION__,
264         WINRT_DISPLAY_PROPERTY(CurrentOrientation),
265         WINRT_DISPLAY_PROPERTY(NativeOrientation),
266         WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
267         window->Bounds.X,
268         window->Bounds.Y,
269         window->Bounds.Width,
270         window->Bounds.Height);
271 #endif
272 
273     window->SizeChanged +=
274         ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
275 
276     window->VisibilityChanged +=
277         ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
278 
279     window->Activated +=
280         ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>(this, &SDL_WinRTApp::OnWindowActivated);
281 
282     window->Closed +=
283         ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
284 
285 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
286     window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
287 #endif
288 
289     window->PointerPressed +=
290         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
291 
292     window->PointerMoved +=
293         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
294 
295     window->PointerReleased +=
296         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
297 
298     window->PointerEntered +=
299         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerEntered);
300 
301     window->PointerExited +=
302         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerExited);
303 
304     window->PointerWheelChanged +=
305         ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
306 
307 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
308     // Retrieves relative-only mouse movements:
309     Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
310         ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
311 #endif
312 
313     window->KeyDown +=
314         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
315 
316     window->KeyUp +=
317         ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
318 
319     window->CharacterReceived +=
320         ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &SDL_WinRTApp::OnCharacterReceived);
321 
322 #if NTDDI_VERSION >= NTDDI_WIN10
323     Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested +=
324         ref new EventHandler<BackRequestedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
325 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
326     HardwareButtons::BackPressed +=
327         ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
328 #endif
329 
330 #if NTDDI_VERSION > NTDDI_WIN8
331     DisplayInformation::GetForCurrentView()->OrientationChanged +=
332         ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
333 #else
334     DisplayProperties::OrientationChanged +=
335         ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
336 #endif
337 
338 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)  // for Windows 8/8.1/RT apps... (and not Phone apps)
339     // Make sure we know when a user has opened the app's settings pane.
340     // This is needed in order to display a privacy policy, which needs
341     // to be done for network-enabled apps, as per Windows Store requirements.
342     using namespace Windows::UI::ApplicationSettings;
343     SettingsPane::GetForCurrentView()->CommandsRequested +=
344         ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
345             (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
346 #endif
347 }
348 
349 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
350 {
351 }
352 
Run()353 void SDL_WinRTApp::Run()
354 {
355     SDL_SetMainReady();
356     if (WINRT_SDLAppEntryPoint)
357     {
358         // TODO, WinRT: pass the C-style main() a reasonably realistic
359         // representation of command line arguments.
360         int argc = 1;
361         char **argv = (char **)SDL_malloc(2 * sizeof(*argv));
362         if (!argv) {
363             return;
364         }
365         argv[0] = SDL_strdup("WinRTApp");
366         argv[1] = NULL;
367         WINRT_SDLAppEntryPoint(argc, argv);
368         SDL_free(argv[0]);
369         SDL_free(argv);
370     }
371 }
372 
IsSDLWindowEventPending(SDL_WindowEventID windowEventID)373 static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
374 {
375     SDL_Event events[128];
376     const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
377     for (int i = 0; i < count; ++i) {
378         if (events[i].window.event == windowEventID) {
379             return true;
380         }
381     }
382     return false;
383 }
384 
ShouldWaitForAppResumeEvents()385 bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
386 {
387     /* Don't wait if the app is visible: */
388     if (m_windowVisible) {
389         return false;
390     }
391 
392     /* Don't wait until the window-hide events finish processing.
393      * Do note that if an app-suspend event is sent (as indicated
394      * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
395      * events), then this code may be a moot point, as WinRT's
396      * own event pump (aka ProcessEvents()) will pause regardless
397      * of what we do here.  This happens on Windows Phone 8, to note.
398      * Windows 8.x apps, on the other hand, may get a chance to run
399      * these.
400      */
401     if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
402         return false;
403     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
404         return false;
405     } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
406         return false;
407     }
408 
409     return true;
410 }
411 
PumpEvents()412 void SDL_WinRTApp::PumpEvents()
413 {
414     if (!m_windowClosed) {
415         if (!ShouldWaitForAppResumeEvents()) {
416             /* This is the normal way in which events should be pumped.
417              * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
418              * from zero to N events, and will then return.
419              */
420             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
421         } else {
422             /* This style of event-pumping, with 'ProcessOneAndAllPending',
423              * will cause anywhere from one to N events to be processed.  If
424              * at least one event is processed, the call will return.  If
425              * no events are pending, then the call will wait until one is
426              * available, and will not return (to the caller) until this
427              * happens!  This should only occur when the app is hidden.
428              */
429             CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
430         }
431     }
432 }
433 
Uninitialize()434 void SDL_WinRTApp::Uninitialize()
435 {
436 }
437 
438 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
439 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
440     Windows::UI::ApplicationSettings::SettingsPane ^p,
441     Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
442 {
443     using namespace Platform;
444     using namespace Windows::UI::ApplicationSettings;
445     using namespace Windows::UI::Popups;
446 
447     String ^privacyPolicyURL = nullptr;     // a URL to an app's Privacy Policy
448     String ^privacyPolicyLabel = nullptr;   // label/link text
449     const char *tmpHintValue = NULL;        // SDL_GetHint-retrieved value, used immediately
450     wchar_t *tmpStr = NULL;                 // used for UTF8 to UCS2 conversion
451 
452     // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
453     tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
454     if (tmpHintValue && tmpHintValue[0] != '\0') {
455         // Convert the privacy policy's URL to UCS2:
456         tmpStr = WIN_UTF8ToString(tmpHintValue);
457         privacyPolicyURL = ref new String(tmpStr);
458         SDL_free(tmpStr);
459 
460         // Optionally retrieve custom label-text for the link.  If this isn't
461         // available, a default value will be used instead.
462         tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
463         if (tmpHintValue && tmpHintValue[0] != '\0') {
464             tmpStr = WIN_UTF8ToString(tmpHintValue);
465             privacyPolicyLabel = ref new String(tmpStr);
466             SDL_free(tmpStr);
467         } else {
468             privacyPolicyLabel = ref new String(L"Privacy Policy");
469         }
470 
471         // Register the link, along with a handler to be called if and when it is
472         // clicked:
473         auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
__anon4ae695c00102(IUICommand ^) 474             ref new UICommandInvokedHandler([=](IUICommand ^) {
475                 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
476         }));
477         args->Request->ApplicationCommands->Append(cmd);
478     }
479 }
480 #endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
481 
482 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
483 {
484 #if LOG_WINDOW_EVENTS==1
485     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",
486         __FUNCTION__,
487         args->Size.Width, args->Size.Height,
488         sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height,
489         WINRT_DISPLAY_PROPERTY(CurrentOrientation),
490         WINRT_DISPLAY_PROPERTY(NativeOrientation),
491         WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
492         (WINRT_GlobalSDLWindow ? "yes" : "no"));
493 #endif
494 
495     WINRT_ProcessWindowSizeChange();
496 }
497 
498 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
499 {
500 #if LOG_WINDOW_EVENTS==1
501     SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n",
502         __FUNCTION__,
503         (args->Visible ? "yes" : "no"),
504         sender->Bounds.X, sender->Bounds.Y,
505         sender->Bounds.Width, sender->Bounds.Height,
506         (WINRT_GlobalSDLWindow ? "yes" : "no"));
507 #endif
508 
509     m_windowVisible = args->Visible;
510     if (WINRT_GlobalSDLWindow) {
511         SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
512         Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow);
513         if (args->Visible) {
514             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
515             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
516             if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) {
517                 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
518             } else {
519                 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
520             }
521         } else {
522             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
523             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
524             SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
525         }
526 
527         // HACK: Prevent SDL's window-hide handling code, which currently
528         // triggers a fake window resize (possibly erronously), from
529         // marking the SDL window's surface as invalid.
530         //
531         // A better solution to this probably involves figuring out if the
532         // fake window resize can be prevented.
533         WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
534     }
535 }
536 
537 void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args)
538 {
539 #if LOG_WINDOW_EVENTS==1
540     SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n",
541         __FUNCTION__,
542         (WINRT_GlobalSDLWindow ? "yes" : "no"));
543 #endif
544 
545     /* There's no property in Win 8.x to tell whether a window is active or
546        not.  [De]activation events are, however, sent to the app.  We'll just
547        record those, in case the CoreWindow gets wrapped by an SDL_Window at
548        some future time.
549     */
550     sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState);
551 
552     SDL_Window * window = WINRT_GlobalSDLWindow;
553     if (window) {
554         if (args->WindowActivationState != CoreWindowActivationState::Deactivated) {
555             SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
556             if (SDL_GetKeyboardFocus() != window) {
557                 SDL_SetKeyboardFocus(window);
558             }
559 
560             /* Send a mouse-motion event as appropriate.
561                This doesn't work when called from OnPointerEntered, at least
562                not in WinRT CoreWindow apps (as OnPointerEntered doesn't
563                appear to be called after window-reactivation, at least not
564                in Windows 10, Build 10586.3 (November 2015 update, non-beta).
565 
566                Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition'
567                property isn't available.
568              */
569 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE)
570             Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize);
571             SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
572 #endif
573 
574             /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */
575             //WIN_CheckAsyncMouseRelease(data);
576 
577             /* TODO, WinRT: implement clipboard support, if possible */
578             ///*
579             // * FIXME: Update keyboard state
580             // */
581             //WIN_CheckClipboardUpdate(data->videodata);
582 
583             // HACK: Resetting the mouse-cursor here seems to fix
584             // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a
585             // WinRT app's mouse cursor may switch to Windows' 'wait' cursor,
586             // after a user alt-tabs back into a full-screened SDL app.
587             // This bug does not appear to reproduce 100% of the time.
588             // It may be a bug in Windows itself (v.10.0.586.36, as tested,
589             // and the most-recent as of this writing).
590             SDL_SetCursor(NULL);
591         } else {
592             if (SDL_GetKeyboardFocus() == window) {
593                 SDL_SetKeyboardFocus(NULL);
594             }
595         }
596     }
597 }
598 
599 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
600 {
601 #if LOG_WINDOW_EVENTS==1
602     SDL_Log("%s\n", __FUNCTION__);
603 #endif
604     m_windowClosed = true;
605 }
606 
607 void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
608 {
609     CoreWindow::GetForCurrentThread()->Activate();
610 }
611 
612 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
613 {
614     // Save app state asynchronously after requesting a deferral. Holding a deferral
615     // indicates that the application is busy performing suspending operations. Be
616     // aware that a deferral may not be held indefinitely. After about five seconds,
617     // the app will be forced to exit.
618 
619     // ... but first, let the app know it's about to go to the background.
620     // The separation of events may be important, given that the deferral
621     // runs in a separate thread.  This'll make SDL_APP_WILLENTERBACKGROUND
622     // the only event among the two that runs in the main thread.  Given
623     // that a few WinRT operations can only be done from the main thread
624     // (things that access the WinRT CoreWindow are one example of this),
625     // this could be important.
626     SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
627 
628     SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
629     create_task([this, deferral]()
__anon4ae695c00202() 630     {
631         // Send an app did-enter-background event immediately to observers.
632         // CoreDispatcher::ProcessEvents, which is the backbone on which
633         // SDL_WinRTApp::PumpEvents is built, will not return to its caller
634         // once it sends out a suspend event.  Any events posted to SDL's
635         // event queue won't get received until the WinRT app is resumed.
636         // SDL_AddEventWatch() may be used to receive app-suspend events on
637         // WinRT.
638         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
639 
640         // Let the Direct3D 11 renderer prepare for the app to be backgrounded.
641         // This is necessary for Windows 8.1, possibly elsewhere in the future.
642         // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx
643 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
644         if (WINRT_GlobalSDLWindow) {
645             SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow);
646             if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) {
647                 D3D11_Trim(renderer);
648             }
649         }
650 #endif
651 
652         deferral->Complete();
653     });
654 }
655 
656 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
657 {
658     // Restore any data or state that was unloaded on suspend. By default, data
659     // and state are persisted when resuming from suspend. Note that these events
660     // do not occur if the app was previously terminated.
661     SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
662     SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
663 }
664 
665 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
666 {
667     SDL_SendAppEvent(SDL_APP_TERMINATING);
668 }
669 
670 static void
671 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
672 {
673     Uint8 button, pressed;
674     Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
675     WINRT_GetSDLButtonForPointerPoint(pt, &button, &pressed);
676     SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d pressed=%d\n",
677         header,
678         pt->Position.X, pt->Position.Y,
679         transformedPoint.X, transformedPoint.Y,
680         pt->Properties->MouseWheelDelta,
681         pt->FrameId,
682         pt->PointerId,
683         button,
684         pressed);
685 }
686 
687 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
688 {
689 #if LOG_POINTER_EVENTS
690     WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
691 #endif
692 
693     WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
694 }
695 
696 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
697 {
698 #if LOG_POINTER_EVENTS
699     WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
700 #endif
701 
702     WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
703 }
704 
705 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
706 {
707 #if LOG_POINTER_EVENTS
708     WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
709 #endif
710 
711     WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
712 }
713 
714 void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args)
715 {
716 #if LOG_POINTER_EVENTS
717     WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
718 #endif
719 
720     WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
721 }
722 
723 void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args)
724 {
725 #if LOG_POINTER_EVENTS
726     WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
727 #endif
728 
729     WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
730 }
731 
732 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
733 {
734 #if LOG_POINTER_EVENTS
735     WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
736 #endif
737 
738     WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
739 }
740 
741 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
742 {
743     WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
744 }
745 
746 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
747 {
748     WINRT_ProcessKeyDownEvent(args);
749 }
750 
751 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
752 {
753     WINRT_ProcessKeyUpEvent(args);
754 }
755 
756 void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args)
757 {
758     WINRT_ProcessCharacterReceivedEvent(args);
759 }
760 
761 template <typename BackButtonEventArgs>
762 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
763 {
764     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
765     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
766 
767     if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
768         args->Handled = true;
769     }
770 }
771 
772 #if NTDDI_VERSION >= NTDDI_WIN10
773 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args)
774 
775 {
776     WINRT_OnBackButtonPressed(args);
777 }
778 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
779 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
780 
781 {
782     WINRT_OnBackButtonPressed(args);
783 }
784 #endif
785 
786 #if NTDDI_VERSION >= NTDDI_WIN10
787 void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad)
788 {
789     /* HACK ALERT: Nothing needs to be done here, as this method currently
790        only exists to allow something to be registered with Win10's
791        GamepadAdded event, an operation that seems to be necessary to get
792        Xinput-based detection to work on Xbox One.
793     */
794 }
795 #endif
796 
797 /* vi: set ts=4 sw=4 expandtab: */
798