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