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
SDL_WinRTInitNonXAMLApp(int (* mainFunction)(int,char **))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
WINRT_SetDisplayOrientationsPreference(void * userdata,const char * name,const char * oldValue,const char * newValue)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
WINRT_ProcessWindowSizeChange()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
SDL_WinRTApp()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 #if NTDDI_VERSION >= NTDDI_WIN10
259 /* HACK ALERT! Xbox One doesn't seem to detect gamepads unless something
260 gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded
261 events. We'll register an event handler for these events here, to make
262 sure that gamepad detection works later on, if requested.
263 */
264 Windows::Gaming::Input::Gamepad::GamepadAdded +=
265 ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>(
266 this, &SDL_WinRTApp::OnGamepadAdded
267 );
268 #endif
269 }
270
271 #if NTDDI_VERSION > NTDDI_WIN8
272 void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
273 #else
274 void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
275 #endif
276 {
277 #if LOG_ORIENTATION_EVENTS==1
278 {
279 CoreWindow^ window = CoreWindow::GetForCurrentThread();
280 if (window) {
281 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Bounds={%f,%f,%f,%f}\n",
282 __FUNCTION__,
283 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
284 WINRT_DISPLAY_PROPERTY(NativeOrientation),
285 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
286 window->Bounds.X,
287 window->Bounds.Y,
288 window->Bounds.Width,
289 window->Bounds.Height);
290 } else {
291 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n",
292 __FUNCTION__,
293 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
294 WINRT_DISPLAY_PROPERTY(NativeOrientation),
295 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences));
296 }
297 }
298 #endif
299
300 WINRT_ProcessWindowSizeChange();
301
302 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
303 // HACK: Make sure that orientation changes
304 // lead to the Direct3D renderer's viewport getting updated:
305 //
306 // For some reason, this doesn't seem to need to be done on Windows 8.x,
307 // even when going from Landscape to LandscapeFlipped. It only seems to
308 // be needed on Windows Phone, at least when I tested on my devices.
309 // I'm not currently sure why this is, but it seems to work fine. -- David L.
310 //
311 // 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
312 SDL_Window * window = WINRT_GlobalSDLWindow;
313 if (window) {
314 SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
315 int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
316 int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
317 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h);
318 }
319 #endif
320
321 }
322
323 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
324 {
325 #if LOG_WINDOW_EVENTS==1
326 SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window bounds={%f, %f, %f,%f}\n",
327 __FUNCTION__,
328 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
329 WINRT_DISPLAY_PROPERTY(NativeOrientation),
330 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
331 window->Bounds.X,
332 window->Bounds.Y,
333 window->Bounds.Width,
334 window->Bounds.Height);
335 #endif
336
337 window->SizeChanged +=
338 ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &SDL_WinRTApp::OnWindowSizeChanged);
339
340 window->VisibilityChanged +=
341 ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged);
342
343 window->Activated +=
344 ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>(this, &SDL_WinRTApp::OnWindowActivated);
345
346 window->Closed +=
347 ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed);
348
349 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
350 window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
351 #endif
352
353 window->PointerPressed +=
354 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerPressed);
355
356 window->PointerMoved +=
357 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerMoved);
358
359 window->PointerReleased +=
360 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased);
361
362 window->PointerEntered +=
363 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerEntered);
364
365 window->PointerExited +=
366 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerExited);
367
368 window->PointerWheelChanged +=
369 ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged);
370
371 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
372 // Retrieves relative-only mouse movements:
373 Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved +=
374 ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &SDL_WinRTApp::OnMouseMoved);
375 #endif
376
377 window->KeyDown +=
378 ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyDown);
379
380 window->KeyUp +=
381 ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp);
382
383 window->CharacterReceived +=
384 ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &SDL_WinRTApp::OnCharacterReceived);
385
386 #if NTDDI_VERSION >= NTDDI_WIN10
387 Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested +=
388 ref new EventHandler<BackRequestedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
389 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
390 HardwareButtons::BackPressed +=
391 ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed);
392 #endif
393
394 #if NTDDI_VERSION > NTDDI_WIN8
395 DisplayInformation::GetForCurrentView()->OrientationChanged +=
396 ref new TypedEventHandler<Windows::Graphics::Display::DisplayInformation^, Object^>(this, &SDL_WinRTApp::OnOrientationChanged);
397 #else
398 DisplayProperties::OrientationChanged +=
399 ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged);
400 #endif
401
402 // Register the hint, SDL_HINT_ORIENTATIONS, with SDL.
403 // 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.
404 SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL);
405
406 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) // for Windows 8/8.1/RT apps... (and not Phone apps)
407 // Make sure we know when a user has opened the app's settings pane.
408 // This is needed in order to display a privacy policy, which needs
409 // to be done for network-enabled apps, as per Windows Store requirements.
410 using namespace Windows::UI::ApplicationSettings;
411 SettingsPane::GetForCurrentView()->CommandsRequested +=
412 ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>
413 (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested);
414 #endif
415 }
416
417 void SDL_WinRTApp::Load(Platform::String^ entryPoint)
418 {
419 }
420
Run()421 void SDL_WinRTApp::Run()
422 {
423 SDL_SetMainReady();
424 if (WINRT_SDLAppEntryPoint)
425 {
426 // TODO, WinRT: pass the C-style main() a reasonably realistic
427 // representation of command line arguments.
428 int argc = 0;
429 char **argv = NULL;
430 WINRT_SDLAppEntryPoint(argc, argv);
431 }
432 }
433
IsSDLWindowEventPending(SDL_WindowEventID windowEventID)434 static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID)
435 {
436 SDL_Event events[128];
437 const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
438 for (int i = 0; i < count; ++i) {
439 if (events[i].window.event == windowEventID) {
440 return true;
441 }
442 }
443 return false;
444 }
445
ShouldWaitForAppResumeEvents()446 bool SDL_WinRTApp::ShouldWaitForAppResumeEvents()
447 {
448 /* Don't wait if the app is visible: */
449 if (m_windowVisible) {
450 return false;
451 }
452
453 /* Don't wait until the window-hide events finish processing.
454 * Do note that if an app-suspend event is sent (as indicated
455 * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND
456 * events), then this code may be a moot point, as WinRT's
457 * own event pump (aka ProcessEvents()) will pause regardless
458 * of what we do here. This happens on Windows Phone 8, to note.
459 * Windows 8.x apps, on the other hand, may get a chance to run
460 * these.
461 */
462 if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) {
463 return false;
464 } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) {
465 return false;
466 } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) {
467 return false;
468 }
469
470 return true;
471 }
472
PumpEvents()473 void SDL_WinRTApp::PumpEvents()
474 {
475 if (!m_windowClosed) {
476 if (!ShouldWaitForAppResumeEvents()) {
477 /* This is the normal way in which events should be pumped.
478 * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere
479 * from zero to N events, and will then return.
480 */
481 CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
482 } else {
483 /* This style of event-pumping, with 'ProcessOneAndAllPending',
484 * will cause anywhere from one to N events to be processed. If
485 * at least one event is processed, the call will return. If
486 * no events are pending, then the call will wait until one is
487 * available, and will not return (to the caller) until this
488 * happens! This should only occur when the app is hidden.
489 */
490 CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
491 }
492 }
493 }
494
Uninitialize()495 void SDL_WinRTApp::Uninitialize()
496 {
497 }
498
499 #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
500 void SDL_WinRTApp::OnSettingsPaneCommandsRequested(
501 Windows::UI::ApplicationSettings::SettingsPane ^p,
502 Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args)
503 {
504 using namespace Platform;
505 using namespace Windows::UI::ApplicationSettings;
506 using namespace Windows::UI::Popups;
507
508 String ^privacyPolicyURL = nullptr; // a URL to an app's Privacy Policy
509 String ^privacyPolicyLabel = nullptr; // label/link text
510 const char *tmpHintValue = NULL; // SDL_GetHint-retrieved value, used immediately
511 wchar_t *tmpStr = NULL; // used for UTF8 to UCS2 conversion
512
513 // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint):
514 tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL);
515 if (tmpHintValue && tmpHintValue[0] != '\0') {
516 // Convert the privacy policy's URL to UCS2:
517 tmpStr = WIN_UTF8ToString(tmpHintValue);
518 privacyPolicyURL = ref new String(tmpStr);
519 SDL_free(tmpStr);
520
521 // Optionally retrieve custom label-text for the link. If this isn't
522 // available, a default value will be used instead.
523 tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL);
524 if (tmpHintValue && tmpHintValue[0] != '\0') {
525 tmpStr = WIN_UTF8ToString(tmpHintValue);
526 privacyPolicyLabel = ref new String(tmpStr);
527 SDL_free(tmpStr);
528 } else {
529 privacyPolicyLabel = ref new String(L"Privacy Policy");
530 }
531
532 // Register the link, along with a handler to be called if and when it is
533 // clicked:
534 auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel,
__anon7a6c26eb0102(IUICommand ^) 535 ref new UICommandInvokedHandler([=](IUICommand ^) {
536 Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL));
537 }));
538 args->Request->ApplicationCommands->Append(cmd);
539 }
540 }
541 #endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10)
542
543 void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
544 {
545 #if LOG_WINDOW_EVENTS==1
546 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",
547 __FUNCTION__,
548 args->Size.Width, args->Size.Height,
549 sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height,
550 WINRT_DISPLAY_PROPERTY(CurrentOrientation),
551 WINRT_DISPLAY_PROPERTY(NativeOrientation),
552 WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
553 (WINRT_GlobalSDLWindow ? "yes" : "no"));
554 #endif
555
556 WINRT_ProcessWindowSizeChange();
557 }
558
559 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
560 {
561 #if LOG_WINDOW_EVENTS==1
562 SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n",
563 __FUNCTION__,
564 (args->Visible ? "yes" : "no"),
565 sender->Bounds.X, sender->Bounds.Y,
566 sender->Bounds.Width, sender->Bounds.Height,
567 (WINRT_GlobalSDLWindow ? "yes" : "no"));
568 #endif
569
570 m_windowVisible = args->Visible;
571 if (WINRT_GlobalSDLWindow) {
572 SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid;
573 Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow);
574 if (args->Visible) {
575 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0);
576 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
577 if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) {
578 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
579 } else {
580 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0);
581 }
582 } else {
583 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0);
584 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
585 SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
586 }
587
588 // HACK: Prevent SDL's window-hide handling code, which currently
589 // triggers a fake window resize (possibly erronously), from
590 // marking the SDL window's surface as invalid.
591 //
592 // A better solution to this probably involves figuring out if the
593 // fake window resize can be prevented.
594 WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid;
595 }
596 }
597
598 void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args)
599 {
600 #if LOG_WINDOW_EVENTS==1
601 SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n",
602 __FUNCTION__,
603 (WINRT_GlobalSDLWindow ? "yes" : "no"));
604 #endif
605
606 /* There's no property in Win 8.x to tell whether a window is active or
607 not. [De]activation events are, however, sent to the app. We'll just
608 record those, in case the CoreWindow gets wrapped by an SDL_Window at
609 some future time.
610 */
611 sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState);
612
613 SDL_Window * window = WINRT_GlobalSDLWindow;
614 if (window) {
615 if (args->WindowActivationState != CoreWindowActivationState::Deactivated) {
616 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
617 if (SDL_GetKeyboardFocus() != window) {
618 SDL_SetKeyboardFocus(window);
619 }
620
621 /* Send a mouse-motion event as appropriate.
622 This doesn't work when called from OnPointerEntered, at least
623 not in WinRT CoreWindow apps (as OnPointerEntered doesn't
624 appear to be called after window-reactivation, at least not
625 in Windows 10, Build 10586.3 (November 2015 update, non-beta).
626
627 Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition'
628 property isn't available.
629 */
630 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE)
631 Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize);
632 SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
633 #endif
634
635 /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */
636 //WIN_CheckAsyncMouseRelease(data);
637
638 /* TODO, WinRT: implement clipboard support, if possible */
639 ///*
640 // * FIXME: Update keyboard state
641 // */
642 //WIN_CheckClipboardUpdate(data->videodata);
643
644 // HACK: Resetting the mouse-cursor here seems to fix
645 // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a
646 // WinRT app's mouse cursor may switch to Windows' 'wait' cursor,
647 // after a user alt-tabs back into a full-screened SDL app.
648 // This bug does not appear to reproduce 100% of the time.
649 // It may be a bug in Windows itself (v.10.0.586.36, as tested,
650 // and the most-recent as of this writing).
651 SDL_SetCursor(NULL);
652 } else {
653 if (SDL_GetKeyboardFocus() == window) {
654 SDL_SetKeyboardFocus(NULL);
655 }
656 }
657 }
658 }
659
660 void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
661 {
662 #if LOG_WINDOW_EVENTS==1
663 SDL_Log("%s\n", __FUNCTION__);
664 #endif
665 m_windowClosed = true;
666 }
667
668 void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
669 {
670 CoreWindow::GetForCurrentThread()->Activate();
671 }
672
673 void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
674 {
675 // Save app state asynchronously after requesting a deferral. Holding a deferral
676 // indicates that the application is busy performing suspending operations. Be
677 // aware that a deferral may not be held indefinitely. After about five seconds,
678 // the app will be forced to exit.
679
680 // ... but first, let the app know it's about to go to the background.
681 // The separation of events may be important, given that the deferral
682 // runs in a separate thread. This'll make SDL_APP_WILLENTERBACKGROUND
683 // the only event among the two that runs in the main thread. Given
684 // that a few WinRT operations can only be done from the main thread
685 // (things that access the WinRT CoreWindow are one example of this),
686 // this could be important.
687 SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
688
689 SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
690 create_task([this, deferral]()
__anon7a6c26eb0202() 691 {
692 // Send an app did-enter-background event immediately to observers.
693 // CoreDispatcher::ProcessEvents, which is the backbone on which
694 // SDL_WinRTApp::PumpEvents is built, will not return to its caller
695 // once it sends out a suspend event. Any events posted to SDL's
696 // event queue won't get received until the WinRT app is resumed.
697 // SDL_AddEventWatch() may be used to receive app-suspend events on
698 // WinRT.
699 SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
700
701 // Let the Direct3D 11 renderer prepare for the app to be backgrounded.
702 // This is necessary for Windows 8.1, possibly elsewhere in the future.
703 // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx
704 #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED
705 if (WINRT_GlobalSDLWindow) {
706 SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow);
707 if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) {
708 D3D11_Trim(renderer);
709 }
710 }
711 #endif
712
713 deferral->Complete();
714 });
715 }
716
717 void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args)
718 {
719 // Restore any data or state that was unloaded on suspend. By default, data
720 // and state are persisted when resuming from suspend. Note that these events
721 // do not occur if the app was previously terminated.
722 SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
723 SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
724 }
725
726 void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args)
727 {
728 SDL_SendAppEvent(SDL_APP_TERMINATING);
729 }
730
731 static void
732 WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint)
733 {
734 Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint;
735 SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d\n",
736 header,
737 pt->Position.X, pt->Position.Y,
738 transformedPoint.X, transformedPoint.Y,
739 pt->Properties->MouseWheelDelta,
740 pt->FrameId,
741 pt->PointerId,
742 WINRT_GetSDLButtonForPointerPoint(pt));
743 }
744
745 void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
746 {
747 #if LOG_POINTER_EVENTS
748 WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
749 #endif
750
751 WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
752 }
753
754 void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
755 {
756 #if LOG_POINTER_EVENTS
757 WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
758 #endif
759
760 WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
761 }
762
763 void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args)
764 {
765 #if LOG_POINTER_EVENTS
766 WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
767 #endif
768
769 WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
770 }
771
772 void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args)
773 {
774 #if LOG_POINTER_EVENTS
775 WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
776 #endif
777
778 WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
779 }
780
781 void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args)
782 {
783 #if LOG_POINTER_EVENTS
784 WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
785 #endif
786
787 WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
788 }
789
790 void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args)
791 {
792 #if LOG_POINTER_EVENTS
793 WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize));
794 #endif
795
796 WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint);
797 }
798
799 void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args)
800 {
801 WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args);
802 }
803
804 void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
805 {
806 WINRT_ProcessKeyDownEvent(args);
807 }
808
809 void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args)
810 {
811 WINRT_ProcessKeyUpEvent(args);
812 }
813
814 void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args)
815 {
816 WINRT_ProcessCharacterReceivedEvent(args);
817 }
818
819 template <typename BackButtonEventArgs>
820 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
821 {
822 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
823 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
824
825 if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
826 args->Handled = true;
827 }
828 }
829
830 #if NTDDI_VERSION == NTDDI_WIN10
831 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args)
832
833 {
834 WINRT_OnBackButtonPressed(args);
835 }
836 #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
837 void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args)
838
839 {
840 WINRT_OnBackButtonPressed(args);
841 }
842 #endif
843
844 #if NTDDI_VERSION >= NTDDI_WIN10
845 void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad)
846 {
847 /* HACK ALERT: Nothing needs to be done here, as this method currently
848 only exists to allow something to be registered with Win10's
849 GamepadAdded event, an operation that seems to be necessary to get
850 Xinput-based detection to work on Xbox One.
851 */
852 }
853 #endif
854
855 /* vi: set ts=4 sw=4 expandtab: */
856