1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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 /* General touch handling code for SDL */
24
25 #include "SDL_assert.h"
26 #include "SDL_events.h"
27 #include "SDL_events_c.h"
28 #include "../video/SDL_sysvideo.h"
29
30
31 static int SDL_num_touch = 0;
32 static SDL_Touch **SDL_touchDevices = NULL;
33
34 /* for mapping touch events to mice */
35
36 #define SYNTHESIZE_TOUCH_TO_MOUSE 1
37
38 #if SYNTHESIZE_TOUCH_TO_MOUSE
39 static SDL_bool finger_touching = SDL_FALSE;
40 static SDL_FingerID track_fingerid;
41 static SDL_TouchID track_touchid;
42 #endif
43
44 /* Public functions */
45 int
SDL_TouchInit(void)46 SDL_TouchInit(void)
47 {
48 return (0);
49 }
50
51 int
SDL_GetNumTouchDevices(void)52 SDL_GetNumTouchDevices(void)
53 {
54 return SDL_num_touch;
55 }
56
57 SDL_TouchID
SDL_GetTouchDevice(int index)58 SDL_GetTouchDevice(int index)
59 {
60 if (index < 0 || index >= SDL_num_touch) {
61 SDL_SetError("Unknown touch device index %d", index);
62 return 0;
63 }
64 return SDL_touchDevices[index]->id;
65 }
66
67 static int
SDL_GetTouchIndex(SDL_TouchID id)68 SDL_GetTouchIndex(SDL_TouchID id)
69 {
70 int index;
71 SDL_Touch *touch;
72
73 for (index = 0; index < SDL_num_touch; ++index) {
74 touch = SDL_touchDevices[index];
75 if (touch->id == id) {
76 return index;
77 }
78 }
79 return -1;
80 }
81
82 SDL_Touch *
SDL_GetTouch(SDL_TouchID id)83 SDL_GetTouch(SDL_TouchID id)
84 {
85 int index = SDL_GetTouchIndex(id);
86 if (index < 0 || index >= SDL_num_touch) {
87 if (SDL_GetVideoDevice()->ResetTouch != NULL) {
88 SDL_SetError("Unknown touch id %d, resetting", (int) id);
89 (SDL_GetVideoDevice()->ResetTouch)(SDL_GetVideoDevice());
90 } else {
91 SDL_SetError("Unknown touch device id %d, cannot reset", (int) id);
92 }
93 return NULL;
94 }
95 return SDL_touchDevices[index];
96 }
97
98 SDL_TouchDeviceType
SDL_GetTouchDeviceType(SDL_TouchID id)99 SDL_GetTouchDeviceType(SDL_TouchID id)
100 {
101 SDL_Touch *touch = SDL_GetTouch(id);
102 if (touch) {
103 return touch->type;
104 }
105 return SDL_TOUCH_DEVICE_INVALID;
106 }
107
108 static int
SDL_GetFingerIndex(const SDL_Touch * touch,SDL_FingerID fingerid)109 SDL_GetFingerIndex(const SDL_Touch * touch, SDL_FingerID fingerid)
110 {
111 int index;
112 for (index = 0; index < touch->num_fingers; ++index) {
113 if (touch->fingers[index]->id == fingerid) {
114 return index;
115 }
116 }
117 return -1;
118 }
119
120 static SDL_Finger *
SDL_GetFinger(const SDL_Touch * touch,SDL_FingerID id)121 SDL_GetFinger(const SDL_Touch * touch, SDL_FingerID id)
122 {
123 int index = SDL_GetFingerIndex(touch, id);
124 if (index < 0 || index >= touch->num_fingers) {
125 return NULL;
126 }
127 return touch->fingers[index];
128 }
129
130 int
SDL_GetNumTouchFingers(SDL_TouchID touchID)131 SDL_GetNumTouchFingers(SDL_TouchID touchID)
132 {
133 SDL_Touch *touch = SDL_GetTouch(touchID);
134 if (touch) {
135 return touch->num_fingers;
136 }
137 return 0;
138 }
139
140 SDL_Finger *
SDL_GetTouchFinger(SDL_TouchID touchID,int index)141 SDL_GetTouchFinger(SDL_TouchID touchID, int index)
142 {
143 SDL_Touch *touch = SDL_GetTouch(touchID);
144 if (!touch) {
145 return NULL;
146 }
147 if (index < 0 || index >= touch->num_fingers) {
148 SDL_SetError("Unknown touch finger");
149 return NULL;
150 }
151 return touch->fingers[index];
152 }
153
154 int
SDL_AddTouch(SDL_TouchID touchID,SDL_TouchDeviceType type,const char * name)155 SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
156 {
157 SDL_Touch **touchDevices;
158 int index;
159
160 index = SDL_GetTouchIndex(touchID);
161 if (index >= 0) {
162 return index;
163 }
164
165 /* Add the touch to the list of touch */
166 touchDevices = (SDL_Touch **) SDL_realloc(SDL_touchDevices,
167 (SDL_num_touch + 1) * sizeof(*touchDevices));
168 if (!touchDevices) {
169 return SDL_OutOfMemory();
170 }
171
172 SDL_touchDevices = touchDevices;
173 index = SDL_num_touch;
174
175 SDL_touchDevices[index] = (SDL_Touch *) SDL_malloc(sizeof(*SDL_touchDevices[index]));
176 if (!SDL_touchDevices[index]) {
177 return SDL_OutOfMemory();
178 }
179
180 /* Added touch to list */
181 ++SDL_num_touch;
182
183 /* we're setting the touch properties */
184 SDL_touchDevices[index]->id = touchID;
185 SDL_touchDevices[index]->type = type;
186 SDL_touchDevices[index]->num_fingers = 0;
187 SDL_touchDevices[index]->max_fingers = 0;
188 SDL_touchDevices[index]->fingers = NULL;
189
190 /* Record this touch device for gestures */
191 /* We could do this on the fly in the gesture code if we wanted */
192 SDL_GestureAddTouch(touchID);
193
194 return index;
195 }
196
197 static int
SDL_AddFinger(SDL_Touch * touch,SDL_FingerID fingerid,float x,float y,float pressure)198 SDL_AddFinger(SDL_Touch *touch, SDL_FingerID fingerid, float x, float y, float pressure)
199 {
200 SDL_Finger *finger;
201
202 if (touch->num_fingers == touch->max_fingers) {
203 SDL_Finger **new_fingers;
204 new_fingers = (SDL_Finger **)SDL_realloc(touch->fingers, (touch->max_fingers+1)*sizeof(*touch->fingers));
205 if (!new_fingers) {
206 return SDL_OutOfMemory();
207 }
208 touch->fingers = new_fingers;
209 touch->fingers[touch->max_fingers] = (SDL_Finger *)SDL_malloc(sizeof(*finger));
210 if (!touch->fingers[touch->max_fingers]) {
211 return SDL_OutOfMemory();
212 }
213 touch->max_fingers++;
214 }
215
216 finger = touch->fingers[touch->num_fingers++];
217 finger->id = fingerid;
218 finger->x = x;
219 finger->y = y;
220 finger->pressure = pressure;
221 return 0;
222 }
223
224 static int
SDL_DelFinger(SDL_Touch * touch,SDL_FingerID fingerid)225 SDL_DelFinger(SDL_Touch* touch, SDL_FingerID fingerid)
226 {
227 SDL_Finger *temp;
228
229 int index = SDL_GetFingerIndex(touch, fingerid);
230 if (index < 0) {
231 return -1;
232 }
233
234 touch->num_fingers--;
235 temp = touch->fingers[index];
236 touch->fingers[index] = touch->fingers[touch->num_fingers];
237 touch->fingers[touch->num_fingers] = temp;
238 return 0;
239 }
240
241 int
SDL_SendTouch(SDL_TouchID id,SDL_FingerID fingerid,SDL_Window * window,SDL_bool down,float x,float y,float pressure)242 SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_Window * window,
243 SDL_bool down, float x, float y, float pressure)
244 {
245 int posted;
246 SDL_Finger *finger;
247 SDL_Mouse *mouse;
248
249 SDL_Touch* touch = SDL_GetTouch(id);
250 if (!touch) {
251 return -1;
252 }
253
254 mouse = SDL_GetMouse();
255
256 #if SYNTHESIZE_TOUCH_TO_MOUSE
257 /* SDL_HINT_TOUCH_MOUSE_EVENTS: controlling whether touch events should generate synthetic mouse events */
258 {
259 if (mouse->touch_mouse_events) {
260 /* FIXME: maybe we should only restrict to a few SDL_TouchDeviceType */
261 if (id != SDL_MOUSE_TOUCHID) {
262 if (window) {
263 if (down) {
264 if (finger_touching == SDL_FALSE) {
265 int pos_x = (int)(x * (float)window->w);
266 int pos_y = (int)(y * (float)window->h);
267 if (pos_x < 0) pos_x = 0;
268 if (pos_x > window->w - 1) pos_x = window->w - 1;
269 if (pos_y < 0) pos_y = 0;
270 if (pos_y > window->h - 1) pos_y = window->h - 1;
271 SDL_SendMouseMotion(window, SDL_TOUCH_MOUSEID, 0, pos_x, pos_y);
272 SDL_SendMouseButton(window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
273 }
274 } else {
275 if (finger_touching == SDL_TRUE && track_touchid == id && track_fingerid == fingerid) {
276 SDL_SendMouseButton(window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
277 }
278 }
279 }
280 if (down) {
281 if (finger_touching == SDL_FALSE) {
282 finger_touching = SDL_TRUE;
283 track_touchid = id;
284 track_fingerid = fingerid;
285 }
286 } else {
287 if (finger_touching == SDL_TRUE && track_touchid == id && track_fingerid == fingerid) {
288 finger_touching = SDL_FALSE;
289 }
290 }
291 }
292 }
293 }
294 #endif
295
296 /* SDL_HINT_MOUSE_TOUCH_EVENTS: if not set, discard synthetic touch events coming from platform layer */
297 if (mouse->mouse_touch_events == 0) {
298 if (id == SDL_MOUSE_TOUCHID) {
299 return 0;
300 }
301 }
302
303 finger = SDL_GetFinger(touch, fingerid);
304 if (down) {
305 if (finger) {
306 /* This finger is already down */
307 return 0;
308 }
309
310 if (SDL_AddFinger(touch, fingerid, x, y, pressure) < 0) {
311 return 0;
312 }
313
314 posted = 0;
315 if (SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
316 SDL_Event event;
317 event.tfinger.type = SDL_FINGERDOWN;
318 event.tfinger.touchId = id;
319 event.tfinger.fingerId = fingerid;
320 event.tfinger.x = x;
321 event.tfinger.y = y;
322 event.tfinger.dx = 0;
323 event.tfinger.dy = 0;
324 event.tfinger.pressure = pressure;
325 event.tfinger.windowID = window ? SDL_GetWindowID(window) : 0;
326 posted = (SDL_PushEvent(&event) > 0);
327 }
328 } else {
329 if (!finger) {
330 /* This finger is already up */
331 return 0;
332 }
333
334 posted = 0;
335 if (SDL_GetEventState(SDL_FINGERUP) == SDL_ENABLE) {
336 SDL_Event event;
337 event.tfinger.type = SDL_FINGERUP;
338 event.tfinger.touchId = id;
339 event.tfinger.fingerId = fingerid;
340 /* I don't trust the coordinates passed on fingerUp */
341 event.tfinger.x = finger->x;
342 event.tfinger.y = finger->y;
343 event.tfinger.dx = 0;
344 event.tfinger.dy = 0;
345 event.tfinger.pressure = pressure;
346 event.tfinger.windowID = window ? SDL_GetWindowID(window) : 0;
347 posted = (SDL_PushEvent(&event) > 0);
348 }
349
350 SDL_DelFinger(touch, fingerid);
351 }
352 return posted;
353 }
354
355 int
SDL_SendTouchMotion(SDL_TouchID id,SDL_FingerID fingerid,SDL_Window * window,float x,float y,float pressure)356 SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, SDL_Window * window,
357 float x, float y, float pressure)
358 {
359 SDL_Touch *touch;
360 SDL_Finger *finger;
361 SDL_Mouse *mouse;
362 int posted;
363 float xrel, yrel, prel;
364
365 touch = SDL_GetTouch(id);
366 if (!touch) {
367 return -1;
368 }
369
370 mouse = SDL_GetMouse();
371
372 #if SYNTHESIZE_TOUCH_TO_MOUSE
373 /* SDL_HINT_TOUCH_MOUSE_EVENTS: controlling whether touch events should generate synthetic mouse events */
374 {
375 if (mouse->touch_mouse_events) {
376 if (id != SDL_MOUSE_TOUCHID) {
377 if (window) {
378 if (finger_touching == SDL_TRUE && track_touchid == id && track_fingerid == fingerid) {
379 int pos_x = (int)(x * (float)window->w);
380 int pos_y = (int)(y * (float)window->h);
381 if (pos_x < 0) pos_x = 0;
382 if (pos_x > window->w - 1) pos_x = window->w - 1;
383 if (pos_y < 0) pos_y = 0;
384 if (pos_y > window->h - 1) pos_y = window->h - 1;
385 SDL_SendMouseMotion(window, SDL_TOUCH_MOUSEID, 0, pos_x, pos_y);
386 }
387 }
388 }
389 }
390 }
391 #endif
392
393 /* SDL_HINT_MOUSE_TOUCH_EVENTS: if not set, discard synthetic touch events coming from platform layer */
394 if (mouse->mouse_touch_events == 0) {
395 if (id == SDL_MOUSE_TOUCHID) {
396 return 0;
397 }
398 }
399
400 finger = SDL_GetFinger(touch,fingerid);
401 if (!finger) {
402 return SDL_SendTouch(id, fingerid, window, SDL_TRUE, x, y, pressure);
403 }
404
405 xrel = x - finger->x;
406 yrel = y - finger->y;
407 prel = pressure - finger->pressure;
408
409 /* Drop events that don't change state */
410 if (xrel == 0.0f && yrel == 0.0f && prel == 0.0f) {
411 #if 0
412 printf("Touch event didn't change state - dropped!\n");
413 #endif
414 return 0;
415 }
416
417 /* Update internal touch coordinates */
418 finger->x = x;
419 finger->y = y;
420 finger->pressure = pressure;
421
422 /* Post the event, if desired */
423 posted = 0;
424 if (SDL_GetEventState(SDL_FINGERMOTION) == SDL_ENABLE) {
425 SDL_Event event;
426 event.tfinger.type = SDL_FINGERMOTION;
427 event.tfinger.touchId = id;
428 event.tfinger.fingerId = fingerid;
429 event.tfinger.x = x;
430 event.tfinger.y = y;
431 event.tfinger.dx = xrel;
432 event.tfinger.dy = yrel;
433 event.tfinger.pressure = pressure;
434 event.tfinger.windowID = window ? SDL_GetWindowID(window) : 0;
435 posted = (SDL_PushEvent(&event) > 0);
436 }
437 return posted;
438 }
439
440 void
SDL_DelTouch(SDL_TouchID id)441 SDL_DelTouch(SDL_TouchID id)
442 {
443 int i;
444 int index = SDL_GetTouchIndex(id);
445 SDL_Touch *touch = SDL_GetTouch(id);
446
447 if (!touch) {
448 return;
449 }
450
451 for (i = 0; i < touch->max_fingers; ++i) {
452 SDL_free(touch->fingers[i]);
453 }
454 SDL_free(touch->fingers);
455 SDL_free(touch);
456
457 SDL_num_touch--;
458 SDL_touchDevices[index] = SDL_touchDevices[SDL_num_touch];
459
460 /* Delete this touch device for gestures */
461 SDL_GestureDelTouch(id);
462 }
463
464 void
SDL_TouchQuit(void)465 SDL_TouchQuit(void)
466 {
467 int i;
468
469 for (i = SDL_num_touch; i--; ) {
470 SDL_DelTouch(SDL_touchDevices[i]->id);
471 }
472 SDL_assert(SDL_num_touch == 0);
473
474 SDL_free(SDL_touchDevices);
475 SDL_touchDevices = NULL;
476 SDL_GestureQuit();
477 }
478
479 /* vi: set ts=4 sw=4 expandtab: */
480