| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | |
| | | /* SDL includes */ |
| | | extern "C" { |
| | | #include "../../SDL_internal.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_events.h" |
| | | #include "SDL_hints.h" |
| | |
| | | #include "../../video/winrt/SDL_winrtvideo_cpp.h" |
| | | #include "SDL_winrtapp_common.h" |
| | | #include "SDL_winrtapp_direct3d.h" |
| | | |
| | | #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED |
| | | /* Calling IDXGIDevice3::Trim on the active Direct3D 11.x device is necessary |
| | | * when Windows 8.1 apps are about to get suspended. |
| | | */ |
| | | extern "C" void D3D11_Trim(SDL_Renderer *); |
| | | #endif |
| | | |
| | | |
| | | // Compile-time debugging options: |
| | |
| | | return 0; |
| | | } |
| | | |
| | | static void WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue) |
| | | static void SDLCALL |
| | | WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue) |
| | | { |
| | | SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0); |
| | | |
| | | /* HACK: prevent SDL from altering an app's .appxmanifest-set orientation |
| | | * from being changed on startup, by detecting when SDL_HINT_ORIENTATIONS |
| | | * is getting registered. |
| | | * |
| | | * TODO, WinRT: consider reading in an app's .appxmanifest file, and apply its orientation when 'newValue == NULL'. |
| | | */ |
| | | if ((oldValue == NULL) && (newValue == NULL)) { |
| | | return; |
| | | } |
| | | |
| | | // Start with no orientation flags, then add each in as they're parsed |
| | | // from newValue. |
| | |
| | | // for details. Microsoft's "Display orientation sample" also gives an |
| | | // outline of how Windows treats device rotation |
| | | // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93). |
| | | #if NTDDI_VERSION > NTDDI_WIN8 |
| | | DisplayInformation::AutoRotationPreferences = (DisplayOrientations) orientationFlags; |
| | | #else |
| | | DisplayProperties::AutoRotationPreferences = (DisplayOrientations) orientationFlags; |
| | | #endif |
| | | WINRT_DISPLAY_PROPERTY(AutoRotationPreferences) = (DisplayOrientations) orientationFlags; |
| | | } |
| | | |
| | | static void |
| | | WINRT_ProcessWindowSizeChange() |
| | | WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange() |
| | | { |
| | | // Make the new window size be the one true fullscreen mode. |
| | | // This change was initially done, in part, to allow the Direct3D 11.1 |
| | | // renderer to receive window-resize events as a device rotates. |
| | | // Before, rotating a device from landscape, to portrait, and then |
| | | // back to landscape would cause the Direct3D 11.1 swap buffer to |
| | | // not get resized appropriately. SDL would, on the rotation from |
| | | // landscape to portrait, re-resize the SDL window to it's initial |
| | | // size (landscape). On the subsequent rotation, SDL would drop the |
| | | // window-resize event as it appeared the SDL window didn't change |
| | | // size, and the Direct3D 11.1 renderer wouldn't resize its swap |
| | | // chain. |
| | | SDL_DisplayMode newDisplayMode; |
| | | if (WINRT_CalcDisplayModeUsingNativeWindow(&newDisplayMode) != 0) { |
| | | return; |
| | | } |
| | | CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread(); |
| | | if (coreWindow) { |
| | | if (WINRT_GlobalSDLWindow) { |
| | | SDL_Window * window = WINRT_GlobalSDLWindow; |
| | | SDL_WindowData * data = (SDL_WindowData *) window->driverdata; |
| | | |
| | | // Make note of the old display mode, and it's old driverdata. |
| | | SDL_DisplayMode oldDisplayMode; |
| | | SDL_zero(oldDisplayMode); |
| | | if (WINRT_GlobalSDLVideoDevice) { |
| | | oldDisplayMode = WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode; |
| | | } |
| | | int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left); |
| | | int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top); |
| | | int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); |
| | | int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); |
| | | |
| | | // Setup the new display mode in the appropriate spots. |
| | | if (WINRT_GlobalSDLVideoDevice) { |
| | | // Make a full copy of the display mode for display_modes[0], |
| | | // one with with a separately malloced 'driverdata' field. |
| | | // SDL_VideoQuit(), if called, will attempt to free the driverdata |
| | | // fields in 'desktop_mode' and each entry in the 'display_modes' |
| | | // array. |
| | | if (WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata) { |
| | | // Free the previous mode's memory |
| | | SDL_free(WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata); |
| | | WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0].driverdata = NULL; |
| | | } |
| | | if (WINRT_DuplicateDisplayMode(&(WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0]), &newDisplayMode) != 0) { |
| | | // Uh oh, something went wrong. A malloc call probably failed. |
| | | SDL_free(newDisplayMode.driverdata); |
| | | return; |
| | | } |
| | | #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8) |
| | | /* WinPhone 8.0 always keeps its native window size in portrait, |
| | | regardless of orientation. This changes in WinPhone 8.1, |
| | | in which the native window's size changes along with |
| | | orientation. |
| | | |
| | | // Install 'newDisplayMode' into 'current_mode' and 'desktop_mode'. |
| | | WINRT_GlobalSDLVideoDevice->displays[0].current_mode = newDisplayMode; |
| | | WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode = newDisplayMode; |
| | | } |
| | | |
| | | if (WINRT_GlobalSDLWindow) { |
| | | // Send a window-resize event to the rest of SDL, and to apps: |
| | | SDL_SendWindowEvent( |
| | | WINRT_GlobalSDLWindow, |
| | | SDL_WINDOWEVENT_RESIZED, |
| | | newDisplayMode.w, |
| | | newDisplayMode.h); |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | // HACK: On Windows Phone, make sure that orientation changes from |
| | | // Landscape to LandscapeFlipped, Portrait to PortraitFlipped, |
| | | // or vice-versa on either of those two, lead to the Direct3D renderer |
| | | // getting updated. |
| | | const DisplayOrientations oldOrientation = ((SDL_DisplayModeData *)oldDisplayMode.driverdata)->currentOrientation; |
| | | const DisplayOrientations newOrientation = ((SDL_DisplayModeData *)newDisplayMode.driverdata)->currentOrientation; |
| | | |
| | | if ((oldOrientation == DisplayOrientations::Landscape && newOrientation == DisplayOrientations::LandscapeFlipped) || |
| | | (oldOrientation == DisplayOrientations::LandscapeFlipped && newOrientation == DisplayOrientations::Landscape) || |
| | | (oldOrientation == DisplayOrientations::Portrait && newOrientation == DisplayOrientations::PortraitFlipped) || |
| | | (oldOrientation == DisplayOrientations::PortraitFlipped && newOrientation == DisplayOrientations::Portrait)) |
| | | { |
| | | // One of the reasons this event is getting sent out is because SDL |
| | | // will ignore requests to send out SDL_WINDOWEVENT_RESIZED events |
| | | // if and when the event size doesn't change (and the Direct3D 11.1 |
| | | // renderer doesn't get the memo). |
| | | // |
| | | // Make sure that the display/window size really didn't change. If |
| | | // it did, then a SDL_WINDOWEVENT_SIZE_CHANGED event got sent, and |
| | | // the Direct3D 11.1 renderer picked it up, presumably. |
| | | if (oldDisplayMode.w == newDisplayMode.w && |
| | | oldDisplayMode.h == newDisplayMode.h) |
| | | { |
| | | SDL_SendWindowEvent( |
| | | WINRT_GlobalSDLWindow, |
| | | SDL_WINDOWEVENT_SIZE_CHANGED, |
| | | newDisplayMode.w, |
| | | newDisplayMode.h); |
| | | Attempt to emulate WinPhone 8.1's behavior on WinPhone 8.0, with |
| | | regards to window size. This fixes a rendering bug that occurs |
| | | when a WinPhone 8.0 app is rotated to either 90 or 270 degrees. |
| | | */ |
| | | const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation); |
| | | switch (currentOrientation) { |
| | | case DisplayOrientations::Landscape: |
| | | case DisplayOrientations::LandscapeFlipped: { |
| | | int tmp = w; |
| | | w = h; |
| | | h = tmp; |
| | | } break; |
| | | } |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | // Finally, free the 'driverdata' field of the old 'desktop_mode'. |
| | | if (oldDisplayMode.driverdata) { |
| | | SDL_free(oldDisplayMode.driverdata); |
| | | oldDisplayMode.driverdata = NULL; |
| | | |
| | | const Uint32 latestFlags = WINRT_DetectWindowFlags(window); |
| | | if (latestFlags & SDL_WINDOW_MAXIMIZED) { |
| | | SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); |
| | | } else { |
| | | SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); |
| | | } |
| | | |
| | | WINRT_UpdateWindowFlags(window, SDL_WINDOW_FULLSCREEN_DESKTOP); |
| | | |
| | | /* The window can move during a resize event, such as when maximizing |
| | | or resizing from a corner */ |
| | | SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); |
| | | SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView) |
| | | { |
| | | applicationView->Activated += |
| | | ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnActivated); |
| | | ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &SDL_WinRTApp::OnAppActivated); |
| | | |
| | | CoreApplication::Suspending += |
| | | ref new EventHandler<SuspendingEventArgs^>(this, &SDL_WinRTApp::OnSuspending); |
| | |
| | | |
| | | CoreApplication::Exiting += |
| | | ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting); |
| | | |
| | | #if NTDDI_VERSION >= NTDDI_WIN10 |
| | | /* HACK ALERT! Xbox One doesn't seem to detect gamepads unless something |
| | | gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded |
| | | events. We'll register an event handler for these events here, to make |
| | | sure that gamepad detection works later on, if requested. |
| | | */ |
| | | Windows::Gaming::Input::Gamepad::GamepadAdded += |
| | | ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>( |
| | | this, &SDL_WinRTApp::OnGamepadAdded |
| | | ); |
| | | #endif |
| | | } |
| | | |
| | | #if NTDDI_VERSION > NTDDI_WIN8 |
| | |
| | | #endif |
| | | { |
| | | #if LOG_ORIENTATION_EVENTS==1 |
| | | CoreWindow^ window = CoreWindow::GetForCurrentThread(); |
| | | if (window) { |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Size={%f,%f}\n", |
| | | __FUNCTION__, |
| | | (int)DisplayProperties::CurrentOrientation, |
| | | (int)DisplayProperties::NativeOrientation, |
| | | (int)DisplayProperties::AutoRotationPreferences, |
| | | window->Bounds.Width, |
| | | window->Bounds.Height); |
| | | } else { |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n", |
| | | __FUNCTION__, |
| | | (int)DisplayProperties::CurrentOrientation, |
| | | (int)DisplayProperties::NativeOrientation, |
| | | (int)DisplayProperties::AutoRotationPreferences); |
| | | { |
| | | CoreWindow^ window = CoreWindow::GetForCurrentThread(); |
| | | if (window) { |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Bounds={%f,%f,%f,%f}\n", |
| | | __FUNCTION__, |
| | | WINRT_DISPLAY_PROPERTY(CurrentOrientation), |
| | | WINRT_DISPLAY_PROPERTY(NativeOrientation), |
| | | WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), |
| | | window->Bounds.X, |
| | | window->Bounds.Y, |
| | | window->Bounds.Width, |
| | | window->Bounds.Height); |
| | | } else { |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n", |
| | | __FUNCTION__, |
| | | WINRT_DISPLAY_PROPERTY(CurrentOrientation), |
| | | WINRT_DISPLAY_PROPERTY(NativeOrientation), |
| | | WINRT_DISPLAY_PROPERTY(AutoRotationPreferences)); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | // On Windows Phone, treat an orientation change as a change in window size. |
| | | // The native window's size doesn't seem to change, however SDL will simulate |
| | | // a window size change. |
| | | WINRT_ProcessWindowSizeChange(); |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | // HACK: Make sure that orientation changes |
| | | // lead to the Direct3D renderer's viewport getting updated: |
| | | // |
| | | // For some reason, this doesn't seem to need to be done on Windows 8.x, |
| | | // even when going from Landscape to LandscapeFlipped. It only seems to |
| | | // be needed on Windows Phone, at least when I tested on my devices. |
| | | // I'm not currently sure why this is, but it seems to work fine. -- David L. |
| | | // |
| | | // 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 |
| | | SDL_Window * window = WINRT_GlobalSDLWindow; |
| | | if (window) { |
| | | SDL_WindowData * data = (SDL_WindowData *)window->driverdata; |
| | | int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); |
| | | int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h); |
| | | } |
| | | #endif |
| | | |
| | | } |
| | | |
| | | void SDL_WinRTApp::SetWindow(CoreWindow^ window) |
| | | { |
| | | #if LOG_WINDOW_EVENTS==1 |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window Size={%f,%f}\n", |
| | | SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window bounds={%f, %f, %f,%f}\n", |
| | | __FUNCTION__, |
| | | (int)DisplayProperties::CurrentOrientation, |
| | | (int)DisplayProperties::NativeOrientation, |
| | | (int)DisplayProperties::AutoRotationPreferences, |
| | | WINRT_DISPLAY_PROPERTY(CurrentOrientation), |
| | | WINRT_DISPLAY_PROPERTY(NativeOrientation), |
| | | WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), |
| | | window->Bounds.X, |
| | | window->Bounds.Y, |
| | | window->Bounds.Width, |
| | | window->Bounds.Height); |
| | | #endif |
| | |
| | | |
| | | window->VisibilityChanged += |
| | | ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &SDL_WinRTApp::OnVisibilityChanged); |
| | | |
| | | window->Activated += |
| | | ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>(this, &SDL_WinRTApp::OnWindowActivated); |
| | | |
| | | window->Closed += |
| | | ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &SDL_WinRTApp::OnWindowClosed); |
| | |
| | | window->PointerReleased += |
| | | ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerReleased); |
| | | |
| | | window->PointerEntered += |
| | | ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerEntered); |
| | | |
| | | window->PointerExited += |
| | | ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerExited); |
| | | |
| | | window->PointerWheelChanged += |
| | | ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &SDL_WinRTApp::OnPointerWheelChanged); |
| | | |
| | |
| | | window->KeyUp += |
| | | ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &SDL_WinRTApp::OnKeyUp); |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | window->CharacterReceived += |
| | | ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &SDL_WinRTApp::OnCharacterReceived); |
| | | |
| | | #if NTDDI_VERSION >= NTDDI_WIN10 |
| | | Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested += |
| | | ref new EventHandler<BackRequestedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed); |
| | | #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | HardwareButtons::BackPressed += |
| | | ref new EventHandler<BackPressedEventArgs^>(this, &SDL_WinRTApp::OnBackButtonPressed); |
| | | #endif |
| | |
| | | // 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. |
| | | SDL_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL); |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_APP // for Windows 8/8.1/RT apps... (and not Phone apps) |
| | | #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) // for Windows 8/8.1/RT apps... (and not Phone apps) |
| | | // Make sure we know when a user has opened the app's settings pane. |
| | | // This is needed in order to display a privacy policy, which needs |
| | | // to be done for network-enabled apps, as per Windows Store requirements. |
| | |
| | | } |
| | | } |
| | | |
| | | static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID) |
| | | { |
| | | SDL_Event events[128]; |
| | | const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT); |
| | | for (int i = 0; i < count; ++i) { |
| | | if (events[i].window.event == windowEventID) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | bool SDL_WinRTApp::ShouldWaitForAppResumeEvents() |
| | | { |
| | | /* Don't wait if the app is visible: */ |
| | | if (m_windowVisible) { |
| | | return false; |
| | | } |
| | | |
| | | /* Don't wait until the window-hide events finish processing. |
| | | * Do note that if an app-suspend event is sent (as indicated |
| | | * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND |
| | | * events), then this code may be a moot point, as WinRT's |
| | | * own event pump (aka ProcessEvents()) will pause regardless |
| | | * of what we do here. This happens on Windows Phone 8, to note. |
| | | * Windows 8.x apps, on the other hand, may get a chance to run |
| | | * these. |
| | | */ |
| | | if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) { |
| | | return false; |
| | | } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) { |
| | | return false; |
| | | } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void SDL_WinRTApp::PumpEvents() |
| | | { |
| | | if (!m_windowClosed) |
| | | { |
| | | if (m_windowVisible) |
| | | { |
| | | if (!m_windowClosed) { |
| | | if (!ShouldWaitForAppResumeEvents()) { |
| | | /* This is the normal way in which events should be pumped. |
| | | * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere |
| | | * from zero to N events, and will then return. |
| | | */ |
| | | CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); |
| | | } |
| | | else |
| | | { |
| | | } else { |
| | | /* This style of event-pumping, with 'ProcessOneAndAllPending', |
| | | * will cause anywhere from one to N events to be processed. If |
| | | * at least one event is processed, the call will return. If |
| | | * no events are pending, then the call will wait until one is |
| | | * available, and will not return (to the caller) until this |
| | | * happens! This should only occur when the app is hidden. |
| | | */ |
| | | CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); |
| | | } |
| | | } |
| | |
| | | { |
| | | } |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_APP |
| | | #if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) |
| | | void SDL_WinRTApp::OnSettingsPaneCommandsRequested( |
| | | Windows::UI::ApplicationSettings::SettingsPane ^p, |
| | | Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args) |
| | |
| | | args->Request->ApplicationCommands->Append(cmd); |
| | | } |
| | | } |
| | | #endif // if WINAPI_FAMILY == WINAPI_FAMILY_APP |
| | | #endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) |
| | | |
| | | void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args) |
| | | { |
| | | #if LOG_WINDOW_EVENTS==1 |
| | | SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n", |
| | | 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", |
| | | __FUNCTION__, |
| | | args->Size.Width, args->Size.Height, |
| | | (int)DisplayProperties::CurrentOrientation, |
| | | (int)DisplayProperties::NativeOrientation, |
| | | (int)DisplayProperties::AutoRotationPreferences, |
| | | sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height, |
| | | WINRT_DISPLAY_PROPERTY(CurrentOrientation), |
| | | WINRT_DISPLAY_PROPERTY(NativeOrientation), |
| | | WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), |
| | | (WINRT_GlobalSDLWindow ? "yes" : "no")); |
| | | #endif |
| | | |
| | |
| | | void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) |
| | | { |
| | | #if LOG_WINDOW_EVENTS==1 |
| | | SDL_Log("%s, visible?=%s, WINRT_GlobalSDLWindow?=%s\n", |
| | | SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n", |
| | | __FUNCTION__, |
| | | (args->Visible ? "yes" : "no"), |
| | | sender->Bounds.X, sender->Bounds.Y, |
| | | sender->Bounds.Width, sender->Bounds.Height, |
| | | (WINRT_GlobalSDLWindow ? "yes" : "no")); |
| | | #endif |
| | | |
| | | m_windowVisible = args->Visible; |
| | | if (WINRT_GlobalSDLWindow) { |
| | | SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid; |
| | | |
| | | Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow); |
| | | if (args->Visible) { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0); |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0); |
| | | if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); |
| | | } else { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0); |
| | | } |
| | | } else { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0); |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0); |
| | | } |
| | | |
| | | // HACK: Prevent SDL's window-hide handling code, which currently |
| | |
| | | } |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args) |
| | | { |
| | | #if LOG_WINDOW_EVENTS==1 |
| | | SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n", |
| | | __FUNCTION__, |
| | | (WINRT_GlobalSDLWindow ? "yes" : "no")); |
| | | #endif |
| | | |
| | | /* There's no property in Win 8.x to tell whether a window is active or |
| | | not. [De]activation events are, however, sent to the app. We'll just |
| | | record those, in case the CoreWindow gets wrapped by an SDL_Window at |
| | | some future time. |
| | | */ |
| | | sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState); |
| | | |
| | | SDL_Window * window = WINRT_GlobalSDLWindow; |
| | | if (window) { |
| | | if (args->WindowActivationState != CoreWindowActivationState::Deactivated) { |
| | | SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0); |
| | | if (SDL_GetKeyboardFocus() != window) { |
| | | SDL_SetKeyboardFocus(window); |
| | | } |
| | | |
| | | /* Send a mouse-motion event as appropriate. |
| | | This doesn't work when called from OnPointerEntered, at least |
| | | not in WinRT CoreWindow apps (as OnPointerEntered doesn't |
| | | appear to be called after window-reactivation, at least not |
| | | in Windows 10, Build 10586.3 (November 2015 update, non-beta). |
| | | |
| | | Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition' |
| | | property isn't available. |
| | | */ |
| | | #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE) |
| | | Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize); |
| | | SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y); |
| | | #endif |
| | | |
| | | /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */ |
| | | //WIN_CheckAsyncMouseRelease(data); |
| | | |
| | | /* TODO, WinRT: implement clipboard support, if possible */ |
| | | ///* |
| | | // * FIXME: Update keyboard state |
| | | // */ |
| | | //WIN_CheckClipboardUpdate(data->videodata); |
| | | |
| | | // HACK: Resetting the mouse-cursor here seems to fix |
| | | // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a |
| | | // WinRT app's mouse cursor may switch to Windows' 'wait' cursor, |
| | | // after a user alt-tabs back into a full-screened SDL app. |
| | | // This bug does not appear to reproduce 100% of the time. |
| | | // It may be a bug in Windows itself (v.10.0.586.36, as tested, |
| | | // and the most-recent as of this writing). |
| | | SDL_SetCursor(NULL); |
| | | } else { |
| | | if (SDL_GetKeyboardFocus() == window) { |
| | | SDL_SetKeyboardFocus(NULL); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) |
| | | { |
| | | #if LOG_WINDOW_EVENTS==1 |
| | |
| | | m_windowClosed = true; |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) |
| | | void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) |
| | | { |
| | | CoreWindow::GetForCurrentThread()->Activate(); |
| | | } |
| | | |
| | | static int SDLCALL RemoveAppSuspendAndResumeEvents(void * userdata, SDL_Event * event) |
| | | { |
| | | if (event->type == SDL_WINDOWEVENT) |
| | | { |
| | | switch (event->window.event) |
| | | { |
| | | case SDL_WINDOWEVENT_MINIMIZED: |
| | | case SDL_WINDOWEVENT_RESTORED: |
| | | // Return 0 to indicate that the event should be removed from the |
| | | // event queue: |
| | | return 0; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // Return 1 to indicate that the event should stay in the event queue: |
| | | return 1; |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) |
| | |
| | | // indicates that the application is busy performing suspending operations. Be |
| | | // aware that a deferral may not be held indefinitely. After about five seconds, |
| | | // the app will be forced to exit. |
| | | |
| | | // ... but first, let the app know it's about to go to the background. |
| | | // The separation of events may be important, given that the deferral |
| | | // runs in a separate thread. This'll make SDL_APP_WILLENTERBACKGROUND |
| | | // the only event among the two that runs in the main thread. Given |
| | | // that a few WinRT operations can only be done from the main thread |
| | | // (things that access the WinRT CoreWindow are one example of this), |
| | | // this could be important. |
| | | SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); |
| | | |
| | | SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); |
| | | create_task([this, deferral]() |
| | | { |
| | | // Send a window-minimized event immediately to observers. |
| | | // Send an app did-enter-background event immediately to observers. |
| | | // CoreDispatcher::ProcessEvents, which is the backbone on which |
| | | // SDL_WinRTApp::PumpEvents is built, will not return to its caller |
| | | // once it sends out a suspend event. Any events posted to SDL's |
| | | // event queue won't get received until the WinRT app is resumed. |
| | | // SDL_AddEventWatch() may be used to receive app-suspend events on |
| | | // WinRT. |
| | | // |
| | | // In order to prevent app-suspend events from being received twice: |
| | | // first via a callback passed to SDL_AddEventWatch, and second via |
| | | // SDL's event queue, the event will be sent to SDL, then immediately |
| | | // removed from the queue. |
| | | if (WINRT_GlobalSDLWindow) |
| | | { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently) |
| | | SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0); |
| | | } |
| | | |
| | | SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); |
| | | SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); |
| | | |
| | | // Let the Direct3D 11 renderer prepare for the app to be backgrounded. |
| | | // This is necessary for Windows 8.1, possibly elsewhere in the future. |
| | | // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx |
| | | #if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED |
| | | if (WINRT_GlobalSDLWindow) { |
| | | SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow); |
| | | if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) { |
| | | D3D11_Trim(renderer); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | deferral->Complete(); |
| | | }); |
| | |
| | | |
| | | void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args) |
| | | { |
| | | // Restore any data or state that was unloaded on suspend. By default, data |
| | | // and state are persisted when resuming from suspend. Note that these events |
| | | // do not occur if the app was previously terminated. |
| | | SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); |
| | | SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); |
| | | |
| | | // Restore any data or state that was unloaded on suspend. By default, data |
| | | // and state are persisted when resuming from suspend. Note that this event |
| | | // does not occur if the app was previously terminated. |
| | | if (WINRT_GlobalSDLWindow) |
| | | { |
| | | SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently) |
| | | |
| | | // Remove the app-resume event from the queue, as is done with the |
| | | // app-suspend event. |
| | | // |
| | | // TODO, WinRT: consider posting this event to the queue even though |
| | | // its counterpart, the app-suspend event, effectively has to be |
| | | // processed immediately. |
| | | SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0); |
| | | } |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args) |
| | |
| | | #if LOG_POINTER_EVENTS |
| | | WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); |
| | | #endif |
| | | |
| | | |
| | | WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args) |
| | | { |
| | | #if LOG_POINTER_EVENTS |
| | | WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); |
| | | #endif |
| | | |
| | | WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args) |
| | | { |
| | | #if LOG_POINTER_EVENTS |
| | | WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); |
| | | #endif |
| | | |
| | | WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); |
| | | } |
| | | |
| | | void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args) |
| | |
| | | WINRT_ProcessKeyUpEvent(args); |
| | | } |
| | | |
| | | #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args) |
| | | void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args) |
| | | { |
| | | WINRT_ProcessCharacterReceivedEvent(args); |
| | | } |
| | | |
| | | template <typename BackButtonEventArgs> |
| | | static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args) |
| | | { |
| | | SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK); |
| | | SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK); |
| | | |
| | | const char *hint = SDL_GetHint(SDL_HINT_WINRT_HANDLE_BACK_BUTTON); |
| | | if (hint) { |
| | | if (*hint == '1') { |
| | | args->Handled = true; |
| | | } |
| | | if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) { |
| | | args->Handled = true; |
| | | } |
| | | } |
| | | |
| | | #if NTDDI_VERSION >= NTDDI_WIN10 |
| | | void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args) |
| | | |
| | | { |
| | | WINRT_OnBackButtonPressed(args); |
| | | } |
| | | #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| | | void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args) |
| | | |
| | | { |
| | | WINRT_OnBackButtonPressed(args); |
| | | } |
| | | #endif |
| | | |
| | | #if NTDDI_VERSION >= NTDDI_WIN10 |
| | | void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad) |
| | | { |
| | | /* HACK ALERT: Nothing needs to be done here, as this method currently |
| | | only exists to allow something to be registered with Win10's |
| | | GamepadAdded event, an operation that seems to be necessary to get |
| | | Xinput-based detection to work on Xbox One. |
| | | */ |
| | | } |
| | | #endif |
| | | |
| | | /* vi: set ts=4 sw=4 expandtab: */ |