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