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