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