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