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