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