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 | |
30 | static int SDL_num_touch = 0; |
31 | static 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 |
38 | static SDL_bool finger_touching = SDL_FALSE; |
39 | static SDL_FingerID track_fingerid; |
40 | static SDL_TouchID track_touchid; |
41 | #endif |
42 | |
43 | /* Public functions */ |
44 | int |
45 | SDL_TouchInit(void) |
46 | { |
47 | return (0); |
48 | } |
49 | |
50 | int |
51 | SDL_GetNumTouchDevices(void) |
52 | { |
53 | return SDL_num_touch; |
54 | } |
55 | |
56 | SDL_TouchID |
57 | SDL_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 | |
66 | static int |
67 | SDL_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 | |
81 | SDL_Touch * |
82 | SDL_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 | |
97 | SDL_TouchDeviceType |
98 | SDL_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 | |
107 | static int |
108 | SDL_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 | |
119 | static SDL_Finger * |
120 | SDL_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 | |
129 | int |
130 | SDL_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 | |
139 | SDL_Finger * |
140 | SDL_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 | |
153 | int |
154 | SDL_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 | |
196 | static int |
197 | SDL_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 | |
223 | static int |
224 | SDL_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 | |
240 | int |
241 | SDL_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 | |
354 | int |
355 | SDL_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 | |
439 | void |
440 | SDL_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 | |
463 | void |
464 | SDL_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 | |