src/video/winrt/SDL_winrtpointerinput.cpp
changeset 8515 bc6cf9201dab
child 8516 f3e0e381bdcd
equal deleted inserted replaced
8514:8ba600edd93f 8515:bc6cf9201dab
       
     1 /*
       
     2   Simple DirectMedia Layer
       
     3   Copyright (C) 1997-2012 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_config.h"
       
    22 
       
    23 #if SDL_VIDEO_DRIVER_WINRT
       
    24 
       
    25 /* SDL includes */
       
    26 #include "SDL_winrtevents_c.h"
       
    27 #include "SDL_winrtmouse.h"
       
    28 #include "SDL_winrtvideo_cpp.h"
       
    29 #include "SDL_assert.h"
       
    30 #include "SDL_system.h"
       
    31 
       
    32 extern "C" {
       
    33 #include "../SDL_sysvideo.h"
       
    34 #include "../../events/SDL_events_c.h"
       
    35 #include "../../events/SDL_mouse_c.h"
       
    36 #include "../../events/SDL_touch_c.h"
       
    37 }
       
    38 
       
    39 /* File-specific globals: */
       
    40 static SDL_TouchID WINRT_TouchID = 1;
       
    41 static unsigned int WINRT_LeftFingerDown = 0;
       
    42 
       
    43 
       
    44 void
       
    45 WINRT_InitTouch(_THIS)
       
    46 {
       
    47     SDL_AddTouch(WINRT_TouchID, "");
       
    48 }
       
    49 
       
    50 
       
    51 // Applies necessary geometric transformations to raw cursor positions:
       
    52 Windows::Foundation::Point
       
    53 WINRT_TransformCursorPosition(SDL_Window * window, Windows::Foundation::Point rawPosition)
       
    54 {
       
    55     using namespace Windows::UI::Core;
       
    56     using namespace Windows::Graphics::Display;
       
    57 
       
    58     if (!window) {
       
    59         return rawPosition;
       
    60     }
       
    61 
       
    62     SDL_WindowData * windowData = (SDL_WindowData *) window->driverdata;
       
    63     if (windowData->coreWindow == nullptr) {
       
    64         // For some reason, the window isn't associated with a CoreWindow.
       
    65         // This might end up being the case as XAML support is extended.
       
    66         // For now, if there's no CoreWindow attached to the SDL_Window,
       
    67         // don't do any transforms.
       
    68         return rawPosition;
       
    69     }
       
    70 
       
    71     // The CoreWindow can only be accessed on certain thread(s).
       
    72     SDL_assert(CoreWindow::GetForCurrentThread() != nullptr);
       
    73 
       
    74     CoreWindow ^ nativeWindow = windowData->coreWindow.Get();
       
    75     Windows::Foundation::Point outputPosition;
       
    76 
       
    77 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
       
    78     outputPosition.X = rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
    79     outputPosition.Y = rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
    80 #else
       
    81     switch (DisplayProperties::CurrentOrientation)
       
    82     {
       
    83         case DisplayOrientations::Portrait:
       
    84             outputPosition.X = rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
    85             outputPosition.Y = rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
    86             break;
       
    87         case DisplayOrientations::PortraitFlipped:
       
    88             outputPosition.X = (float32)window->w - rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
    89             outputPosition.Y = (float32)window->h - rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
    90             break;
       
    91         case DisplayOrientations::Landscape:
       
    92             outputPosition.X = rawPosition.Y * (((float32)window->w) / nativeWindow->Bounds.Height);
       
    93             outputPosition.Y = (float32)window->h - rawPosition.X * (((float32)window->h) / nativeWindow->Bounds.Width);
       
    94             break;
       
    95         case DisplayOrientations::LandscapeFlipped:
       
    96             outputPosition.X = (float32)window->w - rawPosition.Y * (((float32)window->w) / nativeWindow->Bounds.Height);
       
    97             outputPosition.Y = rawPosition.X * (((float32)window->h) / nativeWindow->Bounds.Width);
       
    98             break;
       
    99         default:
       
   100             break;
       
   101     }
       
   102 #endif
       
   103 
       
   104     return outputPosition;
       
   105 }
       
   106 
       
   107 static inline int
       
   108 _lround(float arg)
       
   109 {
       
   110     if (arg >= 0.0f) {
       
   111         return (int)floor(arg + 0.5f);
       
   112     } else {
       
   113         return (int)ceil(arg - 0.5f);
       
   114     }
       
   115 }
       
   116 
       
   117 void
       
   118 WINRT_ProcessMouseMovedEvent(SDL_Window * window, Windows::Devices::Input::MouseEventArgs ^args)
       
   119 {
       
   120     if (!window || !WINRT_UsingRelativeMouseMode) {
       
   121         return;
       
   122     }
       
   123 
       
   124     // DLudwig, 2012-12-28: On some systems, namely Visual Studio's Windows
       
   125     // Simulator, as well as Windows 8 in a Parallels 8 VM, MouseEventArgs'
       
   126     // MouseDelta field often reports very large values.  More information
       
   127     // on this can be found at the following pages on MSDN:
       
   128     //  - http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/a3c789fa-f1c5-49c4-9c0a-7db88d0f90f8
       
   129     //  - https://connect.microsoft.com/VisualStudio/Feedback/details/756515
       
   130     //
       
   131     // The values do not appear to be as large when running on some systems,
       
   132     // most notably a Surface RT.  Furthermore, the values returned by
       
   133     // CoreWindow's PointerMoved event, and sent to this class' OnPointerMoved
       
   134     // method, do not ever appear to be large, even when MouseEventArgs'
       
   135     // MouseDelta is reporting to the contrary.
       
   136     //
       
   137     // On systems with the large-values behavior, it appears that the values
       
   138     // get reported as if the screen's size is 65536 units in both the X and Y
       
   139     // dimensions.  This can be viewed by using Windows' now-private, "Raw Input"
       
   140     // APIs.  (GetRawInputData, RegisterRawInputDevices, WM_INPUT, etc.)
       
   141     //
       
   142     // MSDN's documentation on MouseEventArgs' MouseDelta field (at
       
   143     // http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.input.mouseeventargs.mousedelta ),
       
   144     // does not seem to indicate (to me) that its values should be so large.  It
       
   145     // says that its values should be a "change in screen location".  I could
       
   146     // be misinterpreting this, however a post on MSDN from a Microsoft engineer (see: 
       
   147     // http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/09a9868e-95bb-4858-ba1a-cb4d2c298d62 ),
       
   148     // indicates that these values are in DIPs, which is the same unit used
       
   149     // by CoreWindow's PointerMoved events (via the Position field in its CurrentPoint
       
   150     // property.  See http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.input.pointerpoint.position.aspx
       
   151     // for details.)
       
   152     //
       
   153     // To note, PointerMoved events are sent a 'RawPosition' value (via the
       
   154     // CurrentPoint property in MouseEventArgs), however these do not seem
       
   155     // to exhibit the same large-value behavior.
       
   156     //
       
   157     // The values passed via PointerMoved events can't always be used for relative
       
   158     // mouse motion, unfortunately.  Its values are bound to the cursor's position,
       
   159     // which stops when it hits one of the screen's edges.  This can be a problem in
       
   160     // first person shooters, whereby it is normal for mouse motion to travel far
       
   161     // along any one axis for a period of time.  MouseMoved events do not have the
       
   162     // screen-bounding limitation, and can be used regardless of where the system's
       
   163     // cursor is.
       
   164     //
       
   165     // One possible workaround would be to programmatically set the cursor's
       
   166     // position to the screen's center (when SDL's relative mouse mode is enabled),
       
   167     // however WinRT does not yet seem to have the ability to set the cursor's
       
   168     // position via a public API.  Win32 did this via an API call, SetCursorPos,
       
   169     // however WinRT makes this function be private.  Apps that use it won't get
       
   170     // approved for distribution in the Windows Store.  I've yet to be able to find
       
   171     // a suitable, store-friendly counterpart for WinRT.
       
   172     //
       
   173     // There may be some room for a workaround whereby OnPointerMoved's values
       
   174     // are compared to the values from OnMouseMoved in order to detect
       
   175     // when this bug is active.  A suitable transformation could then be made to
       
   176     // OnMouseMoved's values.  For now, however, the system-reported values are sent
       
   177     // to SDL with minimal transformation: from native screen coordinates (in DIPs)
       
   178     // to SDL window coordinates.
       
   179     //
       
   180     const Windows::Foundation::Point mouseDeltaInDIPs((float)args->MouseDelta.X, (float)args->MouseDelta.Y);
       
   181     const Windows::Foundation::Point mouseDeltaInSDLWindowCoords = WINRT_TransformCursorPosition(window, mouseDeltaInDIPs);
       
   182     SDL_SendMouseMotion(
       
   183         window,
       
   184         0,
       
   185         1,
       
   186         _lround(mouseDeltaInSDLWindowCoords.X),
       
   187         _lround(mouseDeltaInSDLWindowCoords.Y));
       
   188 }
       
   189 
       
   190 Uint8
       
   191 WINRT_GetSDLButtonForPointerPoint(Windows::UI::Input::PointerPoint ^pt)
       
   192 {
       
   193     using namespace Windows::UI::Input;
       
   194 
       
   195 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   196     return SDL_BUTTON_LEFT;
       
   197 #else
       
   198     switch (pt->Properties->PointerUpdateKind)
       
   199     {
       
   200         case PointerUpdateKind::LeftButtonPressed:
       
   201         case PointerUpdateKind::LeftButtonReleased:
       
   202             return SDL_BUTTON_LEFT;
       
   203 
       
   204         case PointerUpdateKind::RightButtonPressed:
       
   205         case PointerUpdateKind::RightButtonReleased:
       
   206             return SDL_BUTTON_RIGHT;
       
   207 
       
   208         case PointerUpdateKind::MiddleButtonPressed:
       
   209         case PointerUpdateKind::MiddleButtonReleased:
       
   210             return SDL_BUTTON_MIDDLE;
       
   211 
       
   212         case PointerUpdateKind::XButton1Pressed:
       
   213         case PointerUpdateKind::XButton1Released:
       
   214             return SDL_BUTTON_X1;
       
   215 
       
   216         case PointerUpdateKind::XButton2Pressed:
       
   217         case PointerUpdateKind::XButton2Released:
       
   218             return SDL_BUTTON_X2;
       
   219 
       
   220         default:
       
   221             break;
       
   222     }
       
   223 #endif
       
   224 
       
   225     return 0;
       
   226 }
       
   227 
       
   228 //const char *
       
   229 //WINRT_ConvertPointerUpdateKindToString(Windows::UI::Input::PointerUpdateKind kind)
       
   230 //{
       
   231 //    using namespace Windows::UI::Input;
       
   232 //
       
   233 //    switch (kind)
       
   234 //    {
       
   235 //        case PointerUpdateKind::Other:
       
   236 //            return "Other";
       
   237 //        case PointerUpdateKind::LeftButtonPressed:
       
   238 //            return "LeftButtonPressed";
       
   239 //        case PointerUpdateKind::LeftButtonReleased:
       
   240 //            return "LeftButtonReleased";
       
   241 //        case PointerUpdateKind::RightButtonPressed:
       
   242 //            return "RightButtonPressed";
       
   243 //        case PointerUpdateKind::RightButtonReleased:
       
   244 //            return "RightButtonReleased";
       
   245 //        case PointerUpdateKind::MiddleButtonPressed:
       
   246 //            return "MiddleButtonPressed";
       
   247 //        case PointerUpdateKind::MiddleButtonReleased:
       
   248 //            return "MiddleButtonReleased";
       
   249 //        case PointerUpdateKind::XButton1Pressed:
       
   250 //            return "XButton1Pressed";
       
   251 //        case PointerUpdateKind::XButton1Released:
       
   252 //            return "XButton1Released";
       
   253 //        case PointerUpdateKind::XButton2Pressed:
       
   254 //            return "XButton2Pressed";
       
   255 //        case PointerUpdateKind::XButton2Released:
       
   256 //            return "XButton2Released";
       
   257 //    }
       
   258 //
       
   259 //    return "";
       
   260 //}
       
   261 
       
   262 static bool
       
   263 WINRT_IsTouchEvent(Windows::UI::Input::PointerPoint ^pointerPoint)
       
   264 {
       
   265 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   266     return true;
       
   267 #else
       
   268     using namespace Windows::Devices::Input;
       
   269     switch (pointerPoint->PointerDevice->PointerDeviceType) {
       
   270         case PointerDeviceType::Touch:
       
   271         case PointerDeviceType::Pen:
       
   272             return true;
       
   273         default:
       
   274             return false;
       
   275     }
       
   276 #endif
       
   277 }
       
   278 
       
   279 void
       
   280 WINRT_ProcessPointerMovedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   281 {
       
   282     if (!window || WINRT_UsingRelativeMouseMode) {
       
   283         return;
       
   284     }
       
   285 
       
   286     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   287 
       
   288     if (pointerPoint->PointerId == WINRT_LeftFingerDown) {
       
   289         SDL_SendMouseMotion(window, 0, 0, (int)transformedPoint.X, (int)transformedPoint.Y);
       
   290     }
       
   291 
       
   292     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   293         SDL_SendTouchMotion(
       
   294             WINRT_TouchID,
       
   295             (SDL_FingerID) pointerPoint->PointerId,
       
   296             transformedPoint.X,
       
   297             transformedPoint.Y,
       
   298             pointerPoint->Properties->Pressure);
       
   299     }
       
   300 }
       
   301 
       
   302 void
       
   303 WINRT_ProcessPointerWheelChangedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   304 {
       
   305     if (!window) {
       
   306         return;
       
   307     }
       
   308 
       
   309     // FIXME: This may need to accumulate deltas up to WHEEL_DELTA
       
   310     short motion = pointerPoint->Properties->MouseWheelDelta / WHEEL_DELTA;
       
   311     SDL_SendMouseWheel(window, 0, 0, motion);
       
   312 }
       
   313 
       
   314 void WINRT_ProcessPointerReleasedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   315 {
       
   316     if (!window) {
       
   317         return;
       
   318     }
       
   319 
       
   320     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   321 
       
   322     if (WINRT_LeftFingerDown == pointerPoint->PointerId) {
       
   323         Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint);
       
   324         if (button) {
       
   325             SDL_SendMouseButton(window, 0, SDL_RELEASED, button);
       
   326         }
       
   327         WINRT_LeftFingerDown = 0;
       
   328     }
       
   329 
       
   330     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   331         SDL_SendTouch(
       
   332             WINRT_TouchID,
       
   333             (SDL_FingerID) pointerPoint->PointerId,
       
   334             SDL_FALSE,
       
   335             transformedPoint.X,
       
   336             transformedPoint.Y,
       
   337             pointerPoint->Properties->Pressure);
       
   338     }
       
   339 }
       
   340 
       
   341 void WINRT_ProcessPointerPressedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   342 {
       
   343     if (!window) {
       
   344         return;
       
   345     }
       
   346 
       
   347     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   348 
       
   349     if (!WINRT_LeftFingerDown) {
       
   350         Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint);
       
   351         if (button) {
       
   352 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   353             SDL_SendMouseMotion(window, 0, 0, (int)transformedPoint.X, (int)transformedPoint.Y);
       
   354 #endif
       
   355             SDL_SendMouseButton(window, 0, SDL_PRESSED, button);
       
   356         }
       
   357 
       
   358         WINRT_LeftFingerDown = pointerPoint->PointerId;
       
   359     }
       
   360 
       
   361     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   362         SDL_SendTouch(
       
   363             WINRT_TouchID,
       
   364             (SDL_FingerID) pointerPoint->PointerId,
       
   365             SDL_TRUE,
       
   366             transformedPoint.X,
       
   367             transformedPoint.Y,
       
   368             pointerPoint->Properties->Pressure);
       
   369     }
       
   370 }
       
   371 
       
   372 #endif // SDL_VIDEO_DRIVER_WINRT