src/video/winrt/SDL_winrtpointerinput.cpp
author David Ludwig <dludwig@pobox.com>
Mon, 02 Sep 2013 15:29:46 -0400
changeset 8516 f3e0e381bdcd
parent 8515 bc6cf9201dab
child 8521 cefdaf414ead
permissions -rw-r--r--
WinRT: renamed a mouse-related header file for naming-consistency's sake
     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_c.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