src/video/winrt/SDL_winrtmouse.cpp
changeset 8515 bc6cf9201dab
parent 8514 8ba600edd93f
child 8516 f3e0e381bdcd
equal deleted inserted replaced
8514:8ba600edd93f 8515:bc6cf9201dab
    45 #include "../../core/winrt/SDL_winrtapp.h"
    45 #include "../../core/winrt/SDL_winrtapp.h"
    46 #include "SDL_winrtvideo_cpp.h"
    46 #include "SDL_winrtvideo_cpp.h"
    47 #include "SDL_winrtmouse.h"
    47 #include "SDL_winrtmouse.h"
    48 
    48 
    49 
    49 
    50 static SDL_bool WINRT_UseRelativeMouseMode = SDL_FALSE;
    50 extern "C" SDL_bool WINRT_UsingRelativeMouseMode = SDL_FALSE;
    51 static SDL_TouchID WINRT_TouchID = 1;
       
    52 static unsigned int WINRT_LeftFingerDown = 0;
       
    53 
    51 
    54 
    52 
    55 static SDL_Cursor *
    53 static SDL_Cursor *
    56 WINRT_CreateSystemCursor(SDL_SystemCursor id)
    54 WINRT_CreateSystemCursor(SDL_SystemCursor id)
    57 {
    55 {
   129 }
   127 }
   130 
   128 
   131 static int
   129 static int
   132 WINRT_SetRelativeMouseMode(SDL_bool enabled)
   130 WINRT_SetRelativeMouseMode(SDL_bool enabled)
   133 {
   131 {
   134     WINRT_UseRelativeMouseMode = enabled;
   132     WINRT_UsingRelativeMouseMode = enabled;
   135     return 0;
   133     return 0;
   136 }
   134 }
   137 
   135 
   138 void
   136 void
   139 WINRT_InitMouse(_THIS)
   137 WINRT_InitMouse(_THIS)
   154     //mouse->WarpMouse = WINRT_WarpMouse;
   152     //mouse->WarpMouse = WINRT_WarpMouse;
   155     mouse->SetRelativeMouseMode = WINRT_SetRelativeMouseMode;
   153     mouse->SetRelativeMouseMode = WINRT_SetRelativeMouseMode;
   156 
   154 
   157     SDL_SetDefaultCursor(WINRT_CreateDefaultCursor());
   155     SDL_SetDefaultCursor(WINRT_CreateDefaultCursor());
   158 #endif
   156 #endif
   159 
       
   160     /* Init touch: */
       
   161     SDL_AddTouch(WINRT_TouchID, "");
       
   162 }
   157 }
   163 
   158 
   164 void
   159 void
   165 WINRT_QuitMouse(_THIS)
   160 WINRT_QuitMouse(_THIS)
   166 {
   161 {
   167 }
   162 }
   168 
   163 
   169 // Applies necessary geometric transformations to raw cursor positions:
       
   170 Windows::Foundation::Point
       
   171 WINRT_TransformCursorPosition(SDL_Window * window, Windows::Foundation::Point rawPosition)
       
   172 {
       
   173     using namespace Windows::Graphics::Display;
       
   174 
       
   175     if (!window) {
       
   176         return rawPosition;
       
   177     }
       
   178 
       
   179     SDL_WindowData * windowData = (SDL_WindowData *) window->driverdata;
       
   180     if (windowData->coreWindow == nullptr) {
       
   181         // For some reason, the window isn't associated with a CoreWindow.
       
   182         // This might end up being the case as XAML support is extended.
       
   183         // For now, if there's no CoreWindow attached to the SDL_Window,
       
   184         // don't do any transforms.
       
   185         return rawPosition;
       
   186     }
       
   187 
       
   188     // The CoreWindow can only be accessed on certain thread(s).
       
   189     SDL_assert(CoreWindow::GetForCurrentThread() != nullptr);
       
   190 
       
   191     CoreWindow ^ nativeWindow = windowData->coreWindow.Get();
       
   192     Windows::Foundation::Point outputPosition;
       
   193 
       
   194 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
       
   195     outputPosition.X = rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
   196     outputPosition.Y = rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
   197 #else
       
   198     switch (DisplayProperties::CurrentOrientation)
       
   199     {
       
   200         case DisplayOrientations::Portrait:
       
   201             outputPosition.X = rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
   202             outputPosition.Y = rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
   203             break;
       
   204         case DisplayOrientations::PortraitFlipped:
       
   205             outputPosition.X = (float32)window->w - rawPosition.X * (((float32)window->w) / nativeWindow->Bounds.Width);
       
   206             outputPosition.Y = (float32)window->h - rawPosition.Y * (((float32)window->h) / nativeWindow->Bounds.Height);
       
   207             break;
       
   208         case DisplayOrientations::Landscape:
       
   209             outputPosition.X = rawPosition.Y * (((float32)window->w) / nativeWindow->Bounds.Height);
       
   210             outputPosition.Y = (float32)window->h - rawPosition.X * (((float32)window->h) / nativeWindow->Bounds.Width);
       
   211             break;
       
   212         case DisplayOrientations::LandscapeFlipped:
       
   213             outputPosition.X = (float32)window->w - rawPosition.Y * (((float32)window->w) / nativeWindow->Bounds.Height);
       
   214             outputPosition.Y = rawPosition.X * (((float32)window->h) / nativeWindow->Bounds.Width);
       
   215             break;
       
   216         default:
       
   217             break;
       
   218     }
       
   219 #endif
       
   220 
       
   221     return outputPosition;
       
   222 }
       
   223 
       
   224 static inline int
       
   225 _lround(float arg)
       
   226 {
       
   227     if (arg >= 0.0f) {
       
   228         return (int)floor(arg + 0.5f);
       
   229     } else {
       
   230         return (int)ceil(arg - 0.5f);
       
   231     }
       
   232 }
       
   233 
       
   234 void
       
   235 WINRT_ProcessMouseMovedEvent(SDL_Window * window, Windows::Devices::Input::MouseEventArgs ^args)
       
   236 {
       
   237     if (!window || !WINRT_UseRelativeMouseMode) {
       
   238         return;
       
   239     }
       
   240 
       
   241     // DLudwig, 2012-12-28: On some systems, namely Visual Studio's Windows
       
   242     // Simulator, as well as Windows 8 in a Parallels 8 VM, MouseEventArgs'
       
   243     // MouseDelta field often reports very large values.  More information
       
   244     // on this can be found at the following pages on MSDN:
       
   245     //  - http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/a3c789fa-f1c5-49c4-9c0a-7db88d0f90f8
       
   246     //  - https://connect.microsoft.com/VisualStudio/Feedback/details/756515
       
   247     //
       
   248     // The values do not appear to be as large when running on some systems,
       
   249     // most notably a Surface RT.  Furthermore, the values returned by
       
   250     // CoreWindow's PointerMoved event, and sent to this class' OnPointerMoved
       
   251     // method, do not ever appear to be large, even when MouseEventArgs'
       
   252     // MouseDelta is reporting to the contrary.
       
   253     //
       
   254     // On systems with the large-values behavior, it appears that the values
       
   255     // get reported as if the screen's size is 65536 units in both the X and Y
       
   256     // dimensions.  This can be viewed by using Windows' now-private, "Raw Input"
       
   257     // APIs.  (GetRawInputData, RegisterRawInputDevices, WM_INPUT, etc.)
       
   258     //
       
   259     // MSDN's documentation on MouseEventArgs' MouseDelta field (at
       
   260     // http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.input.mouseeventargs.mousedelta ),
       
   261     // does not seem to indicate (to me) that its values should be so large.  It
       
   262     // says that its values should be a "change in screen location".  I could
       
   263     // be misinterpreting this, however a post on MSDN from a Microsoft engineer (see: 
       
   264     // http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/09a9868e-95bb-4858-ba1a-cb4d2c298d62 ),
       
   265     // indicates that these values are in DIPs, which is the same unit used
       
   266     // by CoreWindow's PointerMoved events (via the Position field in its CurrentPoint
       
   267     // property.  See http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.input.pointerpoint.position.aspx
       
   268     // for details.)
       
   269     //
       
   270     // To note, PointerMoved events are sent a 'RawPosition' value (via the
       
   271     // CurrentPoint property in MouseEventArgs), however these do not seem
       
   272     // to exhibit the same large-value behavior.
       
   273     //
       
   274     // The values passed via PointerMoved events can't always be used for relative
       
   275     // mouse motion, unfortunately.  Its values are bound to the cursor's position,
       
   276     // which stops when it hits one of the screen's edges.  This can be a problem in
       
   277     // first person shooters, whereby it is normal for mouse motion to travel far
       
   278     // along any one axis for a period of time.  MouseMoved events do not have the
       
   279     // screen-bounding limitation, and can be used regardless of where the system's
       
   280     // cursor is.
       
   281     //
       
   282     // One possible workaround would be to programmatically set the cursor's
       
   283     // position to the screen's center (when SDL's relative mouse mode is enabled),
       
   284     // however WinRT does not yet seem to have the ability to set the cursor's
       
   285     // position via a public API.  Win32 did this via an API call, SetCursorPos,
       
   286     // however WinRT makes this function be private.  Apps that use it won't get
       
   287     // approved for distribution in the Windows Store.  I've yet to be able to find
       
   288     // a suitable, store-friendly counterpart for WinRT.
       
   289     //
       
   290     // There may be some room for a workaround whereby OnPointerMoved's values
       
   291     // are compared to the values from OnMouseMoved in order to detect
       
   292     // when this bug is active.  A suitable transformation could then be made to
       
   293     // OnMouseMoved's values.  For now, however, the system-reported values are sent
       
   294     // to SDL with minimal transformation: from native screen coordinates (in DIPs)
       
   295     // to SDL window coordinates.
       
   296     //
       
   297     const Windows::Foundation::Point mouseDeltaInDIPs((float)args->MouseDelta.X, (float)args->MouseDelta.Y);
       
   298     const Windows::Foundation::Point mouseDeltaInSDLWindowCoords = WINRT_TransformCursorPosition(window, mouseDeltaInDIPs);
       
   299     SDL_SendMouseMotion(
       
   300         window,
       
   301         0,
       
   302         1,
       
   303         _lround(mouseDeltaInSDLWindowCoords.X),
       
   304         _lround(mouseDeltaInSDLWindowCoords.Y));
       
   305 }
       
   306 
       
   307 Uint8
       
   308 WINRT_GetSDLButtonForPointerPoint(Windows::UI::Input::PointerPoint ^pt)
       
   309 {
       
   310     using namespace Windows::UI::Input;
       
   311 
       
   312 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   313     return SDL_BUTTON_LEFT;
       
   314 #else
       
   315     switch (pt->Properties->PointerUpdateKind)
       
   316     {
       
   317         case PointerUpdateKind::LeftButtonPressed:
       
   318         case PointerUpdateKind::LeftButtonReleased:
       
   319             return SDL_BUTTON_LEFT;
       
   320 
       
   321         case PointerUpdateKind::RightButtonPressed:
       
   322         case PointerUpdateKind::RightButtonReleased:
       
   323             return SDL_BUTTON_RIGHT;
       
   324 
       
   325         case PointerUpdateKind::MiddleButtonPressed:
       
   326         case PointerUpdateKind::MiddleButtonReleased:
       
   327             return SDL_BUTTON_MIDDLE;
       
   328 
       
   329         case PointerUpdateKind::XButton1Pressed:
       
   330         case PointerUpdateKind::XButton1Released:
       
   331             return SDL_BUTTON_X1;
       
   332 
       
   333         case PointerUpdateKind::XButton2Pressed:
       
   334         case PointerUpdateKind::XButton2Released:
       
   335             return SDL_BUTTON_X2;
       
   336 
       
   337         default:
       
   338             break;
       
   339     }
       
   340 #endif
       
   341 
       
   342     return 0;
       
   343 }
       
   344 
       
   345 //const char *
       
   346 //WINRT_ConvertPointerUpdateKindToString(Windows::UI::Input::PointerUpdateKind kind)
       
   347 //{
       
   348 //    using namespace Windows::UI::Input;
       
   349 //
       
   350 //    switch (kind)
       
   351 //    {
       
   352 //        case PointerUpdateKind::Other:
       
   353 //            return "Other";
       
   354 //        case PointerUpdateKind::LeftButtonPressed:
       
   355 //            return "LeftButtonPressed";
       
   356 //        case PointerUpdateKind::LeftButtonReleased:
       
   357 //            return "LeftButtonReleased";
       
   358 //        case PointerUpdateKind::RightButtonPressed:
       
   359 //            return "RightButtonPressed";
       
   360 //        case PointerUpdateKind::RightButtonReleased:
       
   361 //            return "RightButtonReleased";
       
   362 //        case PointerUpdateKind::MiddleButtonPressed:
       
   363 //            return "MiddleButtonPressed";
       
   364 //        case PointerUpdateKind::MiddleButtonReleased:
       
   365 //            return "MiddleButtonReleased";
       
   366 //        case PointerUpdateKind::XButton1Pressed:
       
   367 //            return "XButton1Pressed";
       
   368 //        case PointerUpdateKind::XButton1Released:
       
   369 //            return "XButton1Released";
       
   370 //        case PointerUpdateKind::XButton2Pressed:
       
   371 //            return "XButton2Pressed";
       
   372 //        case PointerUpdateKind::XButton2Released:
       
   373 //            return "XButton2Released";
       
   374 //    }
       
   375 //
       
   376 //    return "";
       
   377 //}
       
   378 
       
   379 static bool
       
   380 WINRT_IsTouchEvent(Windows::UI::Input::PointerPoint ^pointerPoint)
       
   381 {
       
   382 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   383     return true;
       
   384 #else
       
   385     using namespace Windows::Devices::Input;
       
   386     switch (pointerPoint->PointerDevice->PointerDeviceType) {
       
   387         case PointerDeviceType::Touch:
       
   388         case PointerDeviceType::Pen:
       
   389             return true;
       
   390         default:
       
   391             return false;
       
   392     }
       
   393 #endif
       
   394 }
       
   395 
       
   396 void
       
   397 WINRT_ProcessPointerMovedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   398 {
       
   399     if (!window || WINRT_UseRelativeMouseMode) {
       
   400         return;
       
   401     }
       
   402 
       
   403     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   404 
       
   405     if (pointerPoint->PointerId == WINRT_LeftFingerDown) {
       
   406         SDL_SendMouseMotion(window, 0, 0, (int)transformedPoint.X, (int)transformedPoint.Y);
       
   407     }
       
   408 
       
   409     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   410         SDL_SendTouchMotion(
       
   411             WINRT_TouchID,
       
   412             (SDL_FingerID) pointerPoint->PointerId,
       
   413             transformedPoint.X,
       
   414             transformedPoint.Y,
       
   415             pointerPoint->Properties->Pressure);
       
   416     }
       
   417 }
       
   418 
       
   419 void
       
   420 WINRT_ProcessPointerWheelChangedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   421 {
       
   422     if (!window) {
       
   423         return;
       
   424     }
       
   425 
       
   426     // FIXME: This may need to accumulate deltas up to WHEEL_DELTA
       
   427     short motion = pointerPoint->Properties->MouseWheelDelta / WHEEL_DELTA;
       
   428     SDL_SendMouseWheel(window, 0, 0, motion);
       
   429 }
       
   430 
       
   431 void WINRT_ProcessPointerReleasedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   432 {
       
   433     if (!window) {
       
   434         return;
       
   435     }
       
   436 
       
   437     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   438 
       
   439     if (WINRT_LeftFingerDown == pointerPoint->PointerId) {
       
   440         Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint);
       
   441         if (button) {
       
   442             SDL_SendMouseButton(window, 0, SDL_RELEASED, button);
       
   443         }
       
   444         WINRT_LeftFingerDown = 0;
       
   445     }
       
   446 
       
   447     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   448         SDL_SendTouch(
       
   449             WINRT_TouchID,
       
   450             (SDL_FingerID) pointerPoint->PointerId,
       
   451             SDL_FALSE,
       
   452             transformedPoint.X,
       
   453             transformedPoint.Y,
       
   454             pointerPoint->Properties->Pressure);
       
   455     }
       
   456 }
       
   457 
       
   458 void WINRT_ProcessPointerPressedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint)
       
   459 {
       
   460     if (!window) {
       
   461         return;
       
   462     }
       
   463 
       
   464     Windows::Foundation::Point transformedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position);
       
   465 
       
   466     if (!WINRT_LeftFingerDown) {
       
   467         Uint8 button = WINRT_GetSDLButtonForPointerPoint(pointerPoint);
       
   468         if (button) {
       
   469 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
       
   470             SDL_SendMouseMotion(window, 0, 0, (int)transformedPoint.X, (int)transformedPoint.Y);
       
   471 #endif
       
   472             SDL_SendMouseButton(window, 0, SDL_PRESSED, button);
       
   473         }
       
   474 
       
   475         WINRT_LeftFingerDown = pointerPoint->PointerId;
       
   476     }
       
   477 
       
   478     if (WINRT_IsTouchEvent(pointerPoint)) {
       
   479         SDL_SendTouch(
       
   480             WINRT_TouchID,
       
   481             (SDL_FingerID) pointerPoint->PointerId,
       
   482             SDL_TRUE,
       
   483             transformedPoint.X,
       
   484             transformedPoint.Y,
       
   485             pointerPoint->Properties->Pressure);
       
   486     }
       
   487 }
       
   488 
       
   489 #endif /* SDL_VIDEO_DRIVER_WINRT */
   164 #endif /* SDL_VIDEO_DRIVER_WINRT */
   490 
   165 
   491 /* vi: set ts=4 sw=4 expandtab: */
   166 /* vi: set ts=4 sw=4 expandtab: */