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 | #ifdef SDL_INPUT_LINUXEV |
24 | |
25 | /* This is based on the linux joystick driver */ |
26 | /* References: https://www.kernel.org/doc/Documentation/input/input.txt |
27 | * https://www.kernel.org/doc/Documentation/input/event-codes.txt |
28 | * /usr/include/linux/input.h |
29 | * The evtest application is also useful to debug the protocol |
30 | */ |
31 | |
32 | #include "SDL_evdev.h" |
33 | #include "SDL_evdev_kbd.h" |
34 | |
35 | #include <sys/stat.h> |
36 | #include <unistd.h> |
37 | #include <fcntl.h> |
38 | #include <sys/ioctl.h> |
39 | #include <linux/input.h> |
40 | |
41 | #include "SDL.h" |
42 | #include "SDL_endian.h" |
43 | #include "SDL_scancode.h" |
44 | #include "../../events/SDL_events_c.h" |
45 | #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */ |
46 | #include "../../core/linux/SDL_evdev_capabilities.h" |
47 | #include "../../core/linux/SDL_udev.h" |
48 | |
49 | /* These are not defined in older Linux kernel headers */ |
50 | #ifndef SYN_DROPPED |
51 | #define SYN_DROPPED 3 |
52 | #endif |
53 | #ifndef ABS_MT_SLOT |
54 | #define ABS_MT_SLOT 0x2f |
55 | #define ABS_MT_POSITION_X 0x35 |
56 | #define ABS_MT_POSITION_Y 0x36 |
57 | #define ABS_MT_TRACKING_ID 0x39 |
58 | #define ABS_MT_PRESSURE 0x3a |
59 | #endif |
60 | |
61 | typedef struct SDL_evdevlist_item |
62 | { |
63 | char *path; |
64 | int fd; |
65 | |
66 | /* TODO: use this for every device, not just touchscreen */ |
67 | int out_of_sync; |
68 | |
69 | /* TODO: expand on this to have data for every possible class (mouse, |
70 | keyboard, touchpad, etc.). Also there's probably some things in here we |
71 | can pull out to the SDL_evdevlist_item i.e. name */ |
72 | int is_touchscreen; |
73 | struct { |
74 | char* name; |
75 | |
76 | int min_x, max_x, range_x; |
77 | int min_y, max_y, range_y; |
78 | int min_pressure, max_pressure, range_pressure; |
79 | |
80 | int max_slots; |
81 | int current_slot; |
82 | struct { |
83 | enum { |
84 | EVDEV_TOUCH_SLOTDELTA_NONE = 0, |
85 | EVDEV_TOUCH_SLOTDELTA_DOWN, |
86 | EVDEV_TOUCH_SLOTDELTA_UP, |
87 | EVDEV_TOUCH_SLOTDELTA_MOVE |
88 | } delta; |
89 | int tracking_id; |
90 | int x, y, pressure; |
91 | } * slots; |
92 | |
93 | } * touchscreen_data; |
94 | |
95 | struct SDL_evdevlist_item *next; |
96 | } SDL_evdevlist_item; |
97 | |
98 | typedef struct SDL_EVDEV_PrivateData |
99 | { |
100 | int ref_count; |
101 | int num_devices; |
102 | SDL_evdevlist_item *first; |
103 | SDL_evdevlist_item *last; |
104 | SDL_EVDEV_keyboard_state *kbd; |
105 | } SDL_EVDEV_PrivateData; |
106 | |
107 | #undef _THIS |
108 | #define _THIS SDL_EVDEV_PrivateData *_this |
109 | static _THIS = NULL; |
110 | |
111 | static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode); |
112 | static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item); |
113 | static int SDL_EVDEV_device_removed(const char *dev_path); |
114 | |
115 | #if SDL_USE_LIBUDEV |
116 | static int SDL_EVDEV_device_added(const char *dev_path, int udev_class); |
117 | static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, |
118 | const char *dev_path); |
119 | #endif /* SDL_USE_LIBUDEV */ |
120 | |
121 | static Uint8 EVDEV_MouseButtons[] = { |
122 | SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */ |
123 | SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */ |
124 | SDL_BUTTON_MIDDLE, /* BTN_MIDDLE 0x112 */ |
125 | SDL_BUTTON_X1, /* BTN_SIDE 0x113 */ |
126 | SDL_BUTTON_X2, /* BTN_EXTRA 0x114 */ |
127 | SDL_BUTTON_X2 + 1, /* BTN_FORWARD 0x115 */ |
128 | SDL_BUTTON_X2 + 2, /* BTN_BACK 0x116 */ |
129 | SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */ |
130 | }; |
131 | |
132 | static int |
133 | SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled) |
134 | { |
135 | /* Mice already send relative events through this interface */ |
136 | return 0; |
137 | } |
138 | |
139 | |
140 | int |
141 | SDL_EVDEV_Init(void) |
142 | { |
143 | if (_this == NULL) { |
144 | _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this)); |
145 | if (_this == NULL) { |
146 | return SDL_OutOfMemory(); |
147 | } |
148 | |
149 | #if SDL_USE_LIBUDEV |
150 | if (SDL_UDEV_Init() < 0) { |
151 | SDL_free(_this); |
152 | _this = NULL; |
153 | return -1; |
154 | } |
155 | |
156 | /* Set up the udev callback */ |
157 | if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) { |
158 | SDL_UDEV_Quit(); |
159 | SDL_free(_this); |
160 | _this = NULL; |
161 | return -1; |
162 | } |
163 | |
164 | /* Force a scan to build the initial device list */ |
165 | SDL_UDEV_Scan(); |
166 | #else |
167 | /* TODO: Scan the devices manually, like a caveman */ |
168 | #endif /* SDL_USE_LIBUDEV */ |
169 | |
170 | _this->kbd = SDL_EVDEV_kbd_init(); |
171 | } |
172 | |
173 | SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode; |
174 | |
175 | _this->ref_count += 1; |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | void |
181 | SDL_EVDEV_Quit(void) |
182 | { |
183 | if (_this == NULL) { |
184 | return; |
185 | } |
186 | |
187 | _this->ref_count -= 1; |
188 | |
189 | if (_this->ref_count < 1) { |
190 | #if SDL_USE_LIBUDEV |
191 | SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback); |
192 | SDL_UDEV_Quit(); |
193 | #endif /* SDL_USE_LIBUDEV */ |
194 | |
195 | SDL_EVDEV_kbd_quit(_this->kbd); |
196 | |
197 | /* Remove existing devices */ |
198 | while(_this->first != NULL) { |
199 | SDL_EVDEV_device_removed(_this->first->path); |
200 | } |
201 | |
202 | SDL_assert(_this->first == NULL); |
203 | SDL_assert(_this->last == NULL); |
204 | SDL_assert(_this->num_devices == 0); |
205 | |
206 | SDL_free(_this); |
207 | _this = NULL; |
208 | } |
209 | } |
210 | |
211 | #if SDL_USE_LIBUDEV |
212 | static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class, |
213 | const char* dev_path) |
214 | { |
215 | if (dev_path == NULL) { |
216 | return; |
217 | } |
218 | |
219 | switch(udev_event) { |
220 | case SDL_UDEV_DEVICEADDED: |
221 | if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD | |
222 | SDL_UDEV_DEVICE_TOUCHSCREEN))) |
223 | return; |
224 | |
225 | if ((udev_class & SDL_UDEV_DEVICE_JOYSTICK)) |
226 | return; |
227 | |
228 | SDL_EVDEV_device_added(dev_path, udev_class); |
229 | break; |
230 | case SDL_UDEV_DEVICEREMOVED: |
231 | SDL_EVDEV_device_removed(dev_path); |
232 | break; |
233 | default: |
234 | break; |
235 | } |
236 | } |
237 | #endif /* SDL_USE_LIBUDEV */ |
238 | |
239 | void |
240 | SDL_EVDEV_Poll(void) |
241 | { |
242 | struct input_event events[32]; |
243 | int i, j, len; |
244 | SDL_evdevlist_item *item; |
245 | SDL_Scancode scan_code; |
246 | int mouse_button; |
247 | SDL_Mouse *mouse; |
248 | float norm_x, norm_y, norm_pressure; |
249 | |
250 | if (!_this) { |
251 | return; |
252 | } |
253 | |
254 | #if SDL_USE_LIBUDEV |
255 | SDL_UDEV_Poll(); |
256 | #endif |
257 | |
258 | mouse = SDL_GetMouse(); |
259 | |
260 | for (item = _this->first; item != NULL; item = item->next) { |
261 | while ((len = read(item->fd, events, (sizeof events))) > 0) { |
262 | len /= sizeof(events[0]); |
263 | for (i = 0; i < len; ++i) { |
264 | /* special handling for touchscreen, that should eventually be |
265 | used for all devices */ |
266 | if (item->out_of_sync && item->is_touchscreen && |
267 | events[i].type == EV_SYN && events[i].code != SYN_REPORT) { |
268 | break; |
269 | } |
270 | |
271 | switch (events[i].type) { |
272 | case EV_KEY: |
273 | if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) { |
274 | mouse_button = events[i].code - BTN_MOUSE; |
275 | if (events[i].value == 0) { |
276 | SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]); |
277 | } else if (events[i].value == 1) { |
278 | SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]); |
279 | } |
280 | break; |
281 | } |
282 | |
283 | /* BTH_TOUCH event value 1 indicates there is contact with |
284 | a touchscreen or trackpad (earlist finger's current |
285 | position is sent in EV_ABS ABS_X/ABS_Y, switching to |
286 | next finger after earlist is released) */ |
287 | if (item->is_touchscreen && events[i].code == BTN_TOUCH) { |
288 | if (item->touchscreen_data->max_slots == 1) { |
289 | if (events[i].value) |
290 | item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; |
291 | else |
292 | item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP; |
293 | } |
294 | break; |
295 | } |
296 | |
297 | /* Probably keyboard */ |
298 | scan_code = SDL_EVDEV_translate_keycode(events[i].code); |
299 | if (scan_code != SDL_SCANCODE_UNKNOWN) { |
300 | if (events[i].value == 0) { |
301 | SDL_SendKeyboardKey(SDL_RELEASED, scan_code); |
302 | } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) { |
303 | SDL_SendKeyboardKey(SDL_PRESSED, scan_code); |
304 | } |
305 | } |
306 | SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value); |
307 | break; |
308 | case EV_ABS: |
309 | switch(events[i].code) { |
310 | case ABS_MT_SLOT: |
311 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
312 | break; |
313 | item->touchscreen_data->current_slot = events[i].value; |
314 | break; |
315 | case ABS_MT_TRACKING_ID: |
316 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
317 | break; |
318 | if (events[i].value >= 0) { |
319 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value; |
320 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; |
321 | } else { |
322 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP; |
323 | } |
324 | break; |
325 | case ABS_MT_POSITION_X: |
326 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
327 | break; |
328 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value; |
329 | if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) { |
330 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; |
331 | } |
332 | break; |
333 | case ABS_MT_POSITION_Y: |
334 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
335 | break; |
336 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value; |
337 | if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) { |
338 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; |
339 | } |
340 | break; |
341 | case ABS_MT_PRESSURE: |
342 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
343 | break; |
344 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value; |
345 | if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) { |
346 | item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; |
347 | } |
348 | break; |
349 | case ABS_X: |
350 | if (item->is_touchscreen) { |
351 | if (item->touchscreen_data->max_slots != 1) |
352 | break; |
353 | item->touchscreen_data->slots[0].x = events[i].value; |
354 | } else |
355 | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y); |
356 | break; |
357 | case ABS_Y: |
358 | if (item->is_touchscreen) { |
359 | if (item->touchscreen_data->max_slots != 1) |
360 | break; |
361 | item->touchscreen_data->slots[0].y = events[i].value; |
362 | } else |
363 | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value); |
364 | break; |
365 | default: |
366 | break; |
367 | } |
368 | break; |
369 | case EV_REL: |
370 | switch(events[i].code) { |
371 | case REL_X: |
372 | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0); |
373 | break; |
374 | case REL_Y: |
375 | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value); |
376 | break; |
377 | case REL_WHEEL: |
378 | SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL); |
379 | break; |
380 | case REL_HWHEEL: |
381 | SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL); |
382 | break; |
383 | default: |
384 | break; |
385 | } |
386 | break; |
387 | case EV_SYN: |
388 | switch (events[i].code) { |
389 | case SYN_REPORT: |
390 | if (!item->is_touchscreen) /* FIXME: temp hack */ |
391 | break; |
392 | |
393 | for(j = 0; j < item->touchscreen_data->max_slots; j++) { |
394 | norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) / |
395 | (float)item->touchscreen_data->range_x; |
396 | norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) / |
397 | (float)item->touchscreen_data->range_y; |
398 | |
399 | if (item->touchscreen_data->range_pressure > 0) { |
400 | norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) / |
401 | (float)item->touchscreen_data->range_pressure; |
402 | } else { |
403 | /* This touchscreen does not support pressure */ |
404 | norm_pressure = 1.0f; |
405 | } |
406 | |
407 | /* FIXME: the touch's window shouldn't be null, but |
408 | * the coordinate space of touch positions needs to |
409 | * be window-relative in that case. */ |
410 | switch(item->touchscreen_data->slots[j].delta) { |
411 | case EVDEV_TOUCH_SLOTDELTA_DOWN: |
412 | SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure); |
413 | item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; |
414 | break; |
415 | case EVDEV_TOUCH_SLOTDELTA_UP: |
416 | SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure); |
417 | item->touchscreen_data->slots[j].tracking_id = -1; |
418 | item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; |
419 | break; |
420 | case EVDEV_TOUCH_SLOTDELTA_MOVE: |
421 | SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure); |
422 | item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; |
423 | break; |
424 | default: |
425 | break; |
426 | } |
427 | } |
428 | |
429 | if (item->out_of_sync) |
430 | item->out_of_sync = 0; |
431 | break; |
432 | case SYN_DROPPED: |
433 | if (item->is_touchscreen) |
434 | item->out_of_sync = 1; |
435 | SDL_EVDEV_sync_device(item); |
436 | break; |
437 | default: |
438 | break; |
439 | } |
440 | break; |
441 | } |
442 | } |
443 | } |
444 | } |
445 | } |
446 | |
447 | static SDL_Scancode |
448 | SDL_EVDEV_translate_keycode(int keycode) |
449 | { |
450 | SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; |
451 | |
452 | if (keycode < SDL_arraysize(linux_scancode_table)) { |
453 | scancode = linux_scancode_table[keycode]; |
454 | |
455 | if (scancode == SDL_SCANCODE_UNKNOWN) { |
456 | /* BTN_TOUCH is handled elsewhere, but we might still end up here if |
457 | you get an unexpected BTN_TOUCH from something SDL believes is not |
458 | a touch device. In this case, we'd rather not get a misleading |
459 | SDL_Log message about an unknown key. */ |
460 | if (keycode != BTN_TOUCH) { |
461 | SDL_Log("The key you just pressed is not recognized by SDL. To help " |
462 | "get this fixed, please report this to the SDL forums/mailing list " |
463 | "<https://discourse.libsdl.org/> EVDEV KeyCode %d" , keycode); |
464 | } |
465 | } |
466 | } |
467 | |
468 | return scancode; |
469 | } |
470 | |
471 | #ifdef SDL_USE_LIBUDEV |
472 | static int |
473 | SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item) |
474 | { |
475 | int ret, i; |
476 | unsigned long xreq, yreq; |
477 | char name[64]; |
478 | struct input_absinfo abs_info; |
479 | |
480 | if (!item->is_touchscreen) |
481 | return 0; |
482 | |
483 | item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data)); |
484 | if (item->touchscreen_data == NULL) |
485 | return SDL_OutOfMemory(); |
486 | |
487 | ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name); |
488 | if (ret < 0) { |
489 | SDL_free(item->touchscreen_data); |
490 | return SDL_SetError("Failed to get evdev touchscreen name" ); |
491 | } |
492 | |
493 | item->touchscreen_data->name = SDL_strdup(name); |
494 | if (item->touchscreen_data->name == NULL) { |
495 | SDL_free(item->touchscreen_data); |
496 | return SDL_OutOfMemory(); |
497 | } |
498 | |
499 | ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); |
500 | if (ret < 0) { |
501 | SDL_free(item->touchscreen_data->name); |
502 | SDL_free(item->touchscreen_data); |
503 | return SDL_SetError("Failed to get evdev touchscreen limits" ); |
504 | } |
505 | |
506 | if (abs_info.maximum == 0) { |
507 | item->touchscreen_data->max_slots = 1; |
508 | xreq = EVIOCGABS(ABS_X); |
509 | yreq = EVIOCGABS(ABS_Y); |
510 | } else { |
511 | item->touchscreen_data->max_slots = abs_info.maximum + 1; |
512 | xreq = EVIOCGABS(ABS_MT_POSITION_X); |
513 | yreq = EVIOCGABS(ABS_MT_POSITION_Y); |
514 | } |
515 | |
516 | ret = ioctl(item->fd, xreq, &abs_info); |
517 | if (ret < 0) { |
518 | SDL_free(item->touchscreen_data->name); |
519 | SDL_free(item->touchscreen_data); |
520 | return SDL_SetError("Failed to get evdev touchscreen limits" ); |
521 | } |
522 | item->touchscreen_data->min_x = abs_info.minimum; |
523 | item->touchscreen_data->max_x = abs_info.maximum; |
524 | item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum; |
525 | |
526 | ret = ioctl(item->fd, yreq, &abs_info); |
527 | if (ret < 0) { |
528 | SDL_free(item->touchscreen_data->name); |
529 | SDL_free(item->touchscreen_data); |
530 | return SDL_SetError("Failed to get evdev touchscreen limits" ); |
531 | } |
532 | item->touchscreen_data->min_y = abs_info.minimum; |
533 | item->touchscreen_data->max_y = abs_info.maximum; |
534 | item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum; |
535 | |
536 | ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info); |
537 | if (ret < 0) { |
538 | SDL_free(item->touchscreen_data->name); |
539 | SDL_free(item->touchscreen_data); |
540 | return SDL_SetError("Failed to get evdev touchscreen limits" ); |
541 | } |
542 | item->touchscreen_data->min_pressure = abs_info.minimum; |
543 | item->touchscreen_data->max_pressure = abs_info.maximum; |
544 | item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum; |
545 | |
546 | item->touchscreen_data->slots = SDL_calloc( |
547 | item->touchscreen_data->max_slots, |
548 | sizeof(*item->touchscreen_data->slots)); |
549 | if (item->touchscreen_data->slots == NULL) { |
550 | SDL_free(item->touchscreen_data->name); |
551 | SDL_free(item->touchscreen_data); |
552 | return SDL_OutOfMemory(); |
553 | } |
554 | |
555 | for(i = 0; i < item->touchscreen_data->max_slots; i++) { |
556 | item->touchscreen_data->slots[i].tracking_id = -1; |
557 | } |
558 | |
559 | ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */ |
560 | SDL_TOUCH_DEVICE_DIRECT, |
561 | item->touchscreen_data->name); |
562 | if (ret < 0) { |
563 | SDL_free(item->touchscreen_data->slots); |
564 | SDL_free(item->touchscreen_data->name); |
565 | SDL_free(item->touchscreen_data); |
566 | return ret; |
567 | } |
568 | |
569 | return 0; |
570 | } |
571 | #endif /* SDL_USE_LIBUDEV */ |
572 | |
573 | static void |
574 | SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) { |
575 | if (!item->is_touchscreen) |
576 | return; |
577 | |
578 | SDL_DelTouch(item->fd); |
579 | SDL_free(item->touchscreen_data->slots); |
580 | SDL_free(item->touchscreen_data->name); |
581 | SDL_free(item->touchscreen_data); |
582 | } |
583 | |
584 | static void |
585 | SDL_EVDEV_sync_device(SDL_evdevlist_item *item) |
586 | { |
587 | #ifdef EVIOCGMTSLOTS |
588 | int i, ret; |
589 | struct input_absinfo abs_info; |
590 | /* |
591 | * struct input_mt_request_layout { |
592 | * __u32 code; |
593 | * __s32 values[num_slots]; |
594 | * }; |
595 | * |
596 | * this is the structure we're trying to emulate |
597 | */ |
598 | Uint32* mt_req_code; |
599 | Sint32* mt_req_values; |
600 | size_t mt_req_size; |
601 | |
602 | /* TODO: sync devices other than touchscreen */ |
603 | if (!item->is_touchscreen) |
604 | return; |
605 | |
606 | mt_req_size = sizeof(*mt_req_code) + |
607 | sizeof(*mt_req_values) * item->touchscreen_data->max_slots; |
608 | |
609 | mt_req_code = SDL_calloc(1, mt_req_size); |
610 | if (mt_req_code == NULL) { |
611 | return; |
612 | } |
613 | |
614 | mt_req_values = (Sint32*)mt_req_code + 1; |
615 | |
616 | *mt_req_code = ABS_MT_TRACKING_ID; |
617 | ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); |
618 | if (ret < 0) { |
619 | SDL_free(mt_req_code); |
620 | return; |
621 | } |
622 | for(i = 0; i < item->touchscreen_data->max_slots; i++) { |
623 | /* |
624 | * This doesn't account for the very edge case of the user removing their |
625 | * finger and replacing it on the screen during the time we're out of sync, |
626 | * which'll mean that we're not going from down -> up or up -> down, we're |
627 | * going from down -> down but with a different tracking id, meaning we'd |
628 | * have to tell SDL of the two events, but since we wait till SYN_REPORT in |
629 | * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't |
630 | * allow it. Lets just pray to God it doesn't happen. |
631 | */ |
632 | if (item->touchscreen_data->slots[i].tracking_id < 0 && |
633 | mt_req_values[i] >= 0) { |
634 | item->touchscreen_data->slots[i].tracking_id = mt_req_values[i]; |
635 | item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; |
636 | } else if (item->touchscreen_data->slots[i].tracking_id >= 0 && |
637 | mt_req_values[i] < 0) { |
638 | item->touchscreen_data->slots[i].tracking_id = -1; |
639 | item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP; |
640 | } |
641 | } |
642 | |
643 | *mt_req_code = ABS_MT_POSITION_X; |
644 | ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); |
645 | if (ret < 0) { |
646 | SDL_free(mt_req_code); |
647 | return; |
648 | } |
649 | for(i = 0; i < item->touchscreen_data->max_slots; i++) { |
650 | if (item->touchscreen_data->slots[i].tracking_id >= 0 && |
651 | item->touchscreen_data->slots[i].x != mt_req_values[i]) { |
652 | item->touchscreen_data->slots[i].x = mt_req_values[i]; |
653 | if (item->touchscreen_data->slots[i].delta == |
654 | EVDEV_TOUCH_SLOTDELTA_NONE) { |
655 | item->touchscreen_data->slots[i].delta = |
656 | EVDEV_TOUCH_SLOTDELTA_MOVE; |
657 | } |
658 | } |
659 | } |
660 | |
661 | *mt_req_code = ABS_MT_POSITION_Y; |
662 | ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); |
663 | if (ret < 0) { |
664 | SDL_free(mt_req_code); |
665 | return; |
666 | } |
667 | for(i = 0; i < item->touchscreen_data->max_slots; i++) { |
668 | if (item->touchscreen_data->slots[i].tracking_id >= 0 && |
669 | item->touchscreen_data->slots[i].y != mt_req_values[i]) { |
670 | item->touchscreen_data->slots[i].y = mt_req_values[i]; |
671 | if (item->touchscreen_data->slots[i].delta == |
672 | EVDEV_TOUCH_SLOTDELTA_NONE) { |
673 | item->touchscreen_data->slots[i].delta = |
674 | EVDEV_TOUCH_SLOTDELTA_MOVE; |
675 | } |
676 | } |
677 | } |
678 | |
679 | *mt_req_code = ABS_MT_PRESSURE; |
680 | ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); |
681 | if (ret < 0) { |
682 | SDL_free(mt_req_code); |
683 | return; |
684 | } |
685 | for(i = 0; i < item->touchscreen_data->max_slots; i++) { |
686 | if (item->touchscreen_data->slots[i].tracking_id >= 0 && |
687 | item->touchscreen_data->slots[i].pressure != mt_req_values[i]) { |
688 | item->touchscreen_data->slots[i].pressure = mt_req_values[i]; |
689 | if (item->touchscreen_data->slots[i].delta == |
690 | EVDEV_TOUCH_SLOTDELTA_NONE) { |
691 | item->touchscreen_data->slots[i].delta = |
692 | EVDEV_TOUCH_SLOTDELTA_MOVE; |
693 | } |
694 | } |
695 | } |
696 | |
697 | ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); |
698 | if (ret < 0) { |
699 | SDL_free(mt_req_code); |
700 | return; |
701 | } |
702 | item->touchscreen_data->current_slot = abs_info.value; |
703 | |
704 | SDL_free(mt_req_code); |
705 | |
706 | #endif /* EVIOCGMTSLOTS */ |
707 | } |
708 | |
709 | #if SDL_USE_LIBUDEV |
710 | static int |
711 | SDL_EVDEV_device_added(const char *dev_path, int udev_class) |
712 | { |
713 | int ret; |
714 | SDL_evdevlist_item *item; |
715 | |
716 | /* Check to make sure it's not already in list. */ |
717 | for (item = _this->first; item != NULL; item = item->next) { |
718 | if (SDL_strcmp(dev_path, item->path) == 0) { |
719 | return -1; /* already have this one */ |
720 | } |
721 | } |
722 | |
723 | item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item)); |
724 | if (item == NULL) { |
725 | return SDL_OutOfMemory(); |
726 | } |
727 | |
728 | item->fd = open(dev_path, O_RDONLY | O_NONBLOCK); |
729 | if (item->fd < 0) { |
730 | SDL_free(item); |
731 | return SDL_SetError("Unable to open %s" , dev_path); |
732 | } |
733 | |
734 | item->path = SDL_strdup(dev_path); |
735 | if (item->path == NULL) { |
736 | close(item->fd); |
737 | SDL_free(item); |
738 | return SDL_OutOfMemory(); |
739 | } |
740 | |
741 | if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) { |
742 | item->is_touchscreen = 1; |
743 | |
744 | if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) { |
745 | close(item->fd); |
746 | SDL_free(item); |
747 | return ret; |
748 | } |
749 | } |
750 | |
751 | if (_this->last == NULL) { |
752 | _this->first = _this->last = item; |
753 | } else { |
754 | _this->last->next = item; |
755 | _this->last = item; |
756 | } |
757 | |
758 | SDL_EVDEV_sync_device(item); |
759 | |
760 | return _this->num_devices++; |
761 | } |
762 | #endif /* SDL_USE_LIBUDEV */ |
763 | |
764 | static int |
765 | SDL_EVDEV_device_removed(const char *dev_path) |
766 | { |
767 | SDL_evdevlist_item *item; |
768 | SDL_evdevlist_item *prev = NULL; |
769 | |
770 | for (item = _this->first; item != NULL; item = item->next) { |
771 | /* found it, remove it. */ |
772 | if (SDL_strcmp(dev_path, item->path) == 0) { |
773 | if (prev != NULL) { |
774 | prev->next = item->next; |
775 | } else { |
776 | SDL_assert(_this->first == item); |
777 | _this->first = item->next; |
778 | } |
779 | if (item == _this->last) { |
780 | _this->last = prev; |
781 | } |
782 | if (item->is_touchscreen) { |
783 | SDL_EVDEV_destroy_touchscreen(item); |
784 | } |
785 | close(item->fd); |
786 | SDL_free(item->path); |
787 | SDL_free(item); |
788 | _this->num_devices--; |
789 | return 0; |
790 | } |
791 | prev = item; |
792 | } |
793 | |
794 | return -1; |
795 | } |
796 | |
797 | |
798 | #endif /* SDL_INPUT_LINUXEV */ |
799 | |
800 | /* vi: set ts=4 sw=4 expandtab: */ |
801 | |