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