| /* |
| Simple DirectMedia Layer |
| 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 |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "../../SDL_internal.h" |
| |
| #if SDL_VIDEO_DRIVER_WINRT |
| |
| /* SDL includes */ |
| #include "SDL_winrtevents_c.h" |
| #include "SDL_winrtmouse_c.h" |
| #include "SDL_winrtvideo_cpp.h" |
| #include "SDL_assert.h" |
| #include "SDL_system.h" |
| |
| extern "C" { |
| #include "../SDL_sysvideo.h" |
| #include "../../events/SDL_events_c.h" |
| #include "../../events/SDL_mouse_c.h" |
| #include "../../events/SDL_touch_c.h" |
| } |
| |
| /* File-specific globals: */ |
| static SDL_TouchID WINRT_TouchID = 1; |
| |
| |
| void |
| WINRT_InitTouch(_THIS) |
| { |
| SDL_AddTouch(WINRT_TouchID, SDL_TOUCH_DEVICE_DIRECT, ""); |
| } |
| |
| |
| // |
| // Applies necessary geometric transformations to raw cursor positions: |
| // |
| Windows::Foundation::Point |
| WINRT_TransformCursorPosition(SDL_Window * window, |
| Windows::Foundation::Point rawPosition, |
| WINRT_CursorNormalizationType normalization) |
| { |
| using namespace Windows::UI::Core; |
| using namespace Windows::Graphics::Display; |
| |
| if (!window) { |
| return rawPosition; |
| } |
| |
| SDL_WindowData * windowData = (SDL_WindowData *) window->driverdata; |
| if (windowData->coreWindow == nullptr) { |
| // For some reason, the window isn't associated with a CoreWindow. |
| // This might end up being the case as XAML support is extended. |
| // For now, if there's no CoreWindow attached to the SDL_Window, |
| // don't do any transforms. |
| |
| // TODO, WinRT: make sure touch input coordinate ranges are correct when using XAML support |
| return rawPosition; |
| } |
| |
| // The CoreWindow can only be accessed on certain thread(s). |
| SDL_assert(CoreWindow::GetForCurrentThread() != nullptr); |
| |
| CoreWindow ^ nativeWindow = windowData->coreWindow.Get(); |
| Windows::Foundation::Point outputPosition; |
| |
| // Compute coordinates normalized from 0..1. |
| // If the coordinates need to be sized to the SDL window, |
| // we'll do that after. |
| #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION > NTDDI_WIN8) |
| outputPosition.X = rawPosition.X / nativeWindow->Bounds.Width; |
| outputPosition.Y = rawPosition.Y / nativeWindow->Bounds.Height; |
| #else |
| switch (WINRT_DISPLAY_PROPERTY(CurrentOrientation)) |
| { |
| case DisplayOrientations::Portrait: |
| outputPosition.X = rawPosition.X / nativeWindow->Bounds.Width; |
| outputPosition.Y = rawPosition.Y / nativeWindow->Bounds.Height; |
| break; |
| case DisplayOrientations::PortraitFlipped: |
| outputPosition.X = 1.0f - (rawPosition.X / nativeWindow->Bounds.Width); |
| outputPosition.Y = 1.0f - (rawPosition.Y / nativeWindow->Bounds.Height); |
| break; |
| case DisplayOrientations::Landscape: |
| outputPosition.X = rawPosition.Y / nativeWindow->Bounds.Height; |
| outputPosition.Y = 1.0f - (rawPosition.X / nativeWindow->Bounds.Width); |
| break; |
| case DisplayOrientations::LandscapeFlipped: |
| outputPosition.X = 1.0f - (rawPosition.Y / nativeWindow->Bounds.Height); |
| outputPosition.Y = rawPosition.X / nativeWindow->Bounds.Width; |
| break; |
| default: |
| break; |
| } |
| #endif |
| |
| if (normalization == TransformToSDLWindowSize) { |
| outputPosition.X *= ((float32) window->w); |
| outputPosition.Y *= ((float32) window->h); |
| } |
| |
| return outputPosition; |
| } |
| |
| static inline int |
| _lround(float arg) |
| { |
| if (arg >= 0.0f) { |
| return (int)floor(arg + 0.5f); |
| } else { |
| return (int)ceil(arg - 0.5f); |
| } |
| } |
| |
| Uint8 |
| WINRT_GetSDLButtonForPointerPoint(Windows::UI::Input::PointerPoint ^pt) |
| { |
| using namespace Windows::UI::Input; |
| |
| #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| return SDL_BUTTON_LEFT; |
| #else |
| switch (pt->Properties->PointerUpdateKind) |
| { |
| case PointerUpdateKind::LeftButtonPressed: |
| case PointerUpdateKind::LeftButtonReleased: |
| return SDL_BUTTON_LEFT; |
| |
| case PointerUpdateKind::RightButtonPressed: |
| case PointerUpdateKind::RightButtonReleased: |
| return SDL_BUTTON_RIGHT; |
| |
| case PointerUpdateKind::MiddleButtonPressed: |
| case PointerUpdateKind::MiddleButtonReleased: |
| return SDL_BUTTON_MIDDLE; |
| |
| case PointerUpdateKind::XButton1Pressed: |
| case PointerUpdateKind::XButton1Released: |
| return SDL_BUTTON_X1; |
| |
| case PointerUpdateKind::XButton2Pressed: |
| case PointerUpdateKind::XButton2Released: |
| return SDL_BUTTON_X2; |
| |
| default: |
| break; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| //const char * |
| //WINRT_ConvertPointerUpdateKindToString(Windows::UI::Input::PointerUpdateKind kind) |
| //{ |
| // using namespace Windows::UI::Input; |
| // |
| // switch (kind) |
| // { |
| // case PointerUpdateKind::Other: |
| // return "Other"; |
| // case PointerUpdateKind::LeftButtonPressed: |
| // return "LeftButtonPressed"; |
| // case PointerUpdateKind::LeftButtonReleased: |
| // return "LeftButtonReleased"; |
| // case PointerUpdateKind::RightButtonPressed: |
| // return "RightButtonPressed"; |
| // case PointerUpdateKind::RightButtonReleased: |
| // return "RightButtonReleased"; |
| // case PointerUpdateKind::MiddleButtonPressed: |
| // return "MiddleButtonPressed"; |
| // case PointerUpdateKind::MiddleButtonReleased: |
| // return "MiddleButtonReleased"; |
| // case PointerUpdateKind::XButton1Pressed: |
| // return "XButton1Pressed"; |
| // case PointerUpdateKind::XButton1Released: |
| // return "XButton1Released"; |
| // case PointerUpdateKind::XButton2Pressed: |
| // return "XButton2Pressed"; |
| // case PointerUpdateKind::XButton2Released: |
| // return "XButton2Released"; |
| // } |
| // |
| // return ""; |
| //} |
| |
| static bool |
| WINRT_IsTouchEvent(Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP |
| return true; |
| #else |
| using namespace Windows::Devices::Input; |
| switch (pointerPoint->PointerDevice->PointerDeviceType) { |
| case PointerDeviceType::Touch: |
| case PointerDeviceType::Pen: |
| return true; |
| default: |
| return false; |
| } |
| #endif |
| } |
| |
| void WINRT_ProcessPointerPressedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window) { |
| return; |
| } |
| |
| Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint); |
| |
| if ( ! WINRT_IsTouchEvent(pointerPoint)) { |
| SDL_SendMouseButton(window, 0, SDL_PRESSED, button); |
| } else { |
| Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); |
| Windows::Foundation::Point windowPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, TransformToSDLWindowSize); |
| |
| SDL_SendTouch( |
| WINRT_TouchID, |
| (SDL_FingerID) pointerPoint->PointerId, |
| window, |
| SDL_TRUE, |
| normalizedPoint.X, |
| normalizedPoint.Y, |
| pointerPoint->Properties->Pressure); |
| } |
| } |
| |
| void |
| WINRT_ProcessPointerMovedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window || WINRT_UsingRelativeMouseMode) { |
| return; |
| } |
| |
| Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); |
| Windows::Foundation::Point windowPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, TransformToSDLWindowSize); |
| |
| if ( ! WINRT_IsTouchEvent(pointerPoint)) { |
| SDL_SendMouseMotion(window, 0, 0, (int)windowPoint.X, (int)windowPoint.Y); |
| } else { |
| SDL_SendTouchMotion( |
| WINRT_TouchID, |
| (SDL_FingerID) pointerPoint->PointerId, |
| window, |
| normalizedPoint.X, |
| normalizedPoint.Y, |
| pointerPoint->Properties->Pressure); |
| } |
| } |
| |
| void WINRT_ProcessPointerReleasedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window) { |
| return; |
| } |
| |
| Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint); |
| |
| if (!WINRT_IsTouchEvent(pointerPoint)) { |
| SDL_SendMouseButton(window, 0, SDL_RELEASED, button); |
| } else { |
| Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); |
| |
| SDL_SendTouch( |
| WINRT_TouchID, |
| (SDL_FingerID) pointerPoint->PointerId, |
| window, |
| SDL_FALSE, |
| normalizedPoint.X, |
| normalizedPoint.Y, |
| pointerPoint->Properties->Pressure); |
| } |
| } |
| |
| void WINRT_ProcessPointerEnteredEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window) { |
| return; |
| } |
| |
| if (!WINRT_IsTouchEvent(pointerPoint)) { |
| SDL_SetMouseFocus(window); |
| } |
| } |
| |
| void WINRT_ProcessPointerExitedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window) { |
| return; |
| } |
| |
| if (!WINRT_IsTouchEvent(pointerPoint)) { |
| SDL_SetMouseFocus(NULL); |
| } |
| } |
| |
| void |
| WINRT_ProcessPointerWheelChangedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) |
| { |
| if (!window) { |
| return; |
| } |
| |
| float motion = (float) pointerPoint->Properties->MouseWheelDelta / WHEEL_DELTA; |
| SDL_SendMouseWheel(window, 0, 0, (float) motion, SDL_MOUSEWHEEL_NORMAL); |
| } |
| |
| void |
| WINRT_ProcessMouseMovedEvent(SDL_Window * window, Windows::Devices::Input::MouseEventArgs ^args) |
| { |
| if (!window || !WINRT_UsingRelativeMouseMode) { |
| return; |
| } |
| |
| // DLudwig, 2012-12-28: On some systems, namely Visual Studio's Windows |
| // Simulator, as well as Windows 8 in a Parallels 8 VM, MouseEventArgs' |
| // MouseDelta field often reports very large values. More information |
| // on this can be found at the following pages on MSDN: |
| // - http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/a3c789fa-f1c5-49c4-9c0a-7db88d0f90f8 |
| // - https://connect.microsoft.com/VisualStudio/Feedback/details/756515 |
| // |
| // The values do not appear to be as large when running on some systems, |
| // most notably a Surface RT. Furthermore, the values returned by |
| // CoreWindow's PointerMoved event, and sent to this class' OnPointerMoved |
| // method, do not ever appear to be large, even when MouseEventArgs' |
| // MouseDelta is reporting to the contrary. |
| // |
| // On systems with the large-values behavior, it appears that the values |
| // get reported as if the screen's size is 65536 units in both the X and Y |
| // dimensions. This can be viewed by using Windows' now-private, "Raw Input" |
| // APIs. (GetRawInputData, RegisterRawInputDevices, WM_INPUT, etc.) |
| // |
| // MSDN's documentation on MouseEventArgs' MouseDelta field (at |
| // http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.input.mouseeventargs.mousedelta ), |
| // does not seem to indicate (to me) that its values should be so large. It |
| // says that its values should be a "change in screen location". I could |
| // be misinterpreting this, however a post on MSDN from a Microsoft engineer (see: |
| // http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/09a9868e-95bb-4858-ba1a-cb4d2c298d62 ), |
| // indicates that these values are in DIPs, which is the same unit used |
| // by CoreWindow's PointerMoved events (via the Position field in its CurrentPoint |
| // property. See http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.input.pointerpoint.position.aspx |
| // for details.) |
| // |
| // To note, PointerMoved events are sent a 'RawPosition' value (via the |
| // CurrentPoint property in MouseEventArgs), however these do not seem |
| // to exhibit the same large-value behavior. |
| // |
| // The values passed via PointerMoved events can't always be used for relative |
| // mouse motion, unfortunately. Its values are bound to the cursor's position, |
| // which stops when it hits one of the screen's edges. This can be a problem in |
| // first person shooters, whereby it is normal for mouse motion to travel far |
| // along any one axis for a period of time. MouseMoved events do not have the |
| // screen-bounding limitation, and can be used regardless of where the system's |
| // cursor is. |
| // |
| // One possible workaround would be to programmatically set the cursor's |
| // position to the screen's center (when SDL's relative mouse mode is enabled), |
| // however WinRT does not yet seem to have the ability to set the cursor's |
| // position via a public API. Win32 did this via an API call, SetCursorPos, |
| // however WinRT makes this function be private. Apps that use it won't get |
| // approved for distribution in the Windows Store. I've yet to be able to find |
| // a suitable, store-friendly counterpart for WinRT. |
| // |
| // There may be some room for a workaround whereby OnPointerMoved's values |
| // are compared to the values from OnMouseMoved in order to detect |
| // when this bug is active. A suitable transformation could then be made to |
| // OnMouseMoved's values. For now, however, the system-reported values are sent |
| // to SDL with minimal transformation: from native screen coordinates (in DIPs) |
| // to SDL window coordinates. |
| // |
| const Windows::Foundation::Point mouseDeltaInDIPs((float)args->MouseDelta.X, (float)args->MouseDelta.Y); |
| const Windows::Foundation::Point mouseDeltaInSDLWindowCoords = WINRT_TransformCursorPosition(window, mouseDeltaInDIPs, TransformToSDLWindowSize); |
| SDL_SendMouseMotion( |
| window, |
| 0, |
| 1, |
| _lround(mouseDeltaInSDLWindowCoords.X), |
| _lround(mouseDeltaInSDLWindowCoords.Y)); |
| } |
| |
| #endif // SDL_VIDEO_DRIVER_WINRT |