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