1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 #include <limits.h> /* For the definition of PATH_MAX */
39 #include <linux/input.h>
40 #ifdef SDL_INPUT_LINUXKD
41 #include <linux/kd.h>
42 #include <linux/keyboard.h>
43 #include <linux/vt.h>
44 #include <linux/tiocl.h> /* for TIOCL_GETSHIFTSTATE */
45 #endif
46
47 #include "SDL.h"
48 #include "SDL_assert.h"
49 #include "SDL_endian.h"
50 #include "../../core/linux/SDL_udev.h"
51 #include "SDL_scancode.h"
52 #include "../../events/SDL_events_c.h"
53 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
54
55 /* This isn't defined in older Linux kernel headers */
56 #ifndef SYN_DROPPED
57 #define SYN_DROPPED 3
58 #endif
59
60 typedef struct SDL_evdevlist_item
61 {
62 char *path;
63 int fd;
64
65 /* TODO: use this for every device, not just touchscreen */
66 int out_of_sync;
67
68 /* TODO: expand on this to have data for every possible class (mouse,
69 keyboard, touchpad, etc.). Also there's probably some things in here we
70 can pull out to the SDL_evdevlist_item i.e. name */
71 int is_touchscreen;
72 struct {
73 char* name;
74
75 int min_x, max_x, range_x;
76 int min_y, max_y, range_y;
77
78 int max_slots;
79 int current_slot;
80 struct {
81 enum {
82 EVDEV_TOUCH_SLOTDELTA_NONE = 0,
83 EVDEV_TOUCH_SLOTDELTA_DOWN,
84 EVDEV_TOUCH_SLOTDELTA_UP,
85 EVDEV_TOUCH_SLOTDELTA_MOVE
86 } delta;
87 int tracking_id;
88 int x, y;
89 } * slots;
90 } * touchscreen_data;
91
92 struct SDL_evdevlist_item *next;
93 } SDL_evdevlist_item;
94
95 typedef struct SDL_EVDEV_PrivateData
96 {
97 SDL_evdevlist_item *first;
98 SDL_evdevlist_item *last;
99 int num_devices;
100 int ref_count;
101 int console_fd;
102 int kb_mode;
103 } SDL_EVDEV_PrivateData;
104
105 #define _THIS SDL_EVDEV_PrivateData *_this
106 static _THIS = NULL;
107
108 static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
109 static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
110 static int SDL_EVDEV_device_removed(const char *dev_path);
111
112 #if SDL_USE_LIBUDEV
113 static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
114 void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
115 const char *dev_path);
116 #endif /* SDL_USE_LIBUDEV */
117
118 static Uint8 EVDEV_MouseButtons[] = {
119 SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */
120 SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */
121 SDL_BUTTON_MIDDLE, /* BTN_MIDDLE 0x112 */
122 SDL_BUTTON_X1, /* BTN_SIDE 0x113 */
123 SDL_BUTTON_X2, /* BTN_EXTRA 0x114 */
124 SDL_BUTTON_X2 + 1, /* BTN_FORWARD 0x115 */
125 SDL_BUTTON_X2 + 2, /* BTN_BACK 0x116 */
126 SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */
127 };
128
129 static const char* EVDEV_consoles[] = {
130 /* "/proc/self/fd/0",
131 "/dev/tty",
132 "/dev/tty0", */ /* the tty ioctl's prohibit these */
133 "/dev/tty1",
134 "/dev/tty2",
135 "/dev/tty3",
136 "/dev/tty4",
137 "/dev/tty5",
138 "/dev/tty6",
139 "/dev/tty7", /* usually X is spawned in tty7 */
140 "/dev/vc/0",
141 "/dev/console"
142 };
143
SDL_EVDEV_is_console(int fd)144 static int SDL_EVDEV_is_console(int fd) {
145 int type;
146
147 return isatty(fd) && ioctl(fd, KDGKBTYPE, &type) == 0 &&
148 (type == KB_101 || type == KB_84);
149 }
150
151 /* Prevent keystrokes from reaching the tty */
SDL_EVDEV_mute_keyboard(int tty_fd,int * old_kb_mode)152 static int SDL_EVDEV_mute_keyboard(int tty_fd, int* old_kb_mode)
153 {
154 if (!SDL_EVDEV_is_console(tty_fd)) {
155 return SDL_SetError("Tried to mute an invalid tty");
156 }
157
158 if (ioctl(tty_fd, KDGKBMODE, old_kb_mode) < 0) {
159 return SDL_SetError("Failed to get keyboard mode during muting");
160 }
161
162 /* FIXME: atm this absolutely ruins the vt, and KDSKBMUTE isn't implemented
163 in the kernel */
164 /*
165 if (ioctl(tty_fd, KDSKBMODE, K_OFF) < 0) {
166 return SDL_SetError("Failed to set keyboard mode during muting");
167 }
168 */
169
170 return 0;
171 }
172
173 /* Restore the keyboard mode for given tty */
SDL_EVDEV_unmute_keyboard(int tty_fd,int kb_mode)174 static void SDL_EVDEV_unmute_keyboard(int tty_fd, int kb_mode)
175 {
176 /* read above */
177 /*
178 if (ioctl(tty_fd, KDSKBMODE, kb_mode) < 0) {
179 SDL_Log("Failed to unmute keyboard");
180 }
181 */
182 }
183
SDL_EVDEV_get_active_tty()184 static int SDL_EVDEV_get_active_tty()
185 {
186 int i, fd, ret, tty = 0;
187 char tiocl;
188 struct vt_stat vt_state;
189 char path[PATH_MAX + 1];
190
191 for(i = 0; i < SDL_arraysize(EVDEV_consoles); i++) {
192 fd = open(EVDEV_consoles[i], O_RDONLY);
193
194 if (fd < 0 && !SDL_EVDEV_is_console(fd))
195 break;
196
197 tiocl = TIOCL_GETFGCONSOLE;
198 if ((ret = ioctl(fd, TIOCLINUX, &tiocl)) >= 0)
199 tty = ret + 1;
200 else if (ioctl(fd, VT_GETSTATE, &vt_state) == 0)
201 tty = vt_state.v_active;
202
203 close(fd);
204
205 if (tty) {
206 sprintf(path, "/dev/tty%u", tty);
207 fd = open(path, O_RDONLY);
208 if (fd >= 0 && SDL_EVDEV_is_console(fd))
209 return fd;
210 }
211 }
212
213 return SDL_SetError("Failed to determine active tty");
214 }
215
216 int
SDL_EVDEV_Init(void)217 SDL_EVDEV_Init(void)
218 {
219 if (_this == NULL) {
220 _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this));
221 if (_this == NULL) {
222 return SDL_OutOfMemory();
223 }
224
225 #if SDL_USE_LIBUDEV
226 if (SDL_UDEV_Init() < 0) {
227 SDL_free(_this);
228 _this = NULL;
229 return -1;
230 }
231
232 /* Set up the udev callback */
233 if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
234 SDL_UDEV_Quit();
235 SDL_free(_this);
236 _this = NULL;
237 return -1;
238 }
239
240 /* Force a scan to build the initial device list */
241 SDL_UDEV_Scan();
242 #else
243 /* TODO: Scan the devices manually, like a caveman */
244 #endif /* SDL_USE_LIBUDEV */
245
246 /* We need a physical terminal (not PTS) to be able to translate key
247 code to symbols via the kernel tables */
248 _this->console_fd = SDL_EVDEV_get_active_tty();
249
250 /* Mute the keyboard so keystrokes only generate evdev events and do not
251 leak through to the console */
252 SDL_EVDEV_mute_keyboard(_this->console_fd, &_this->kb_mode);
253 }
254
255 _this->ref_count += 1;
256
257 return 0;
258 }
259
260 void
SDL_EVDEV_Quit(void)261 SDL_EVDEV_Quit(void)
262 {
263 if (_this == NULL) {
264 return;
265 }
266
267 _this->ref_count -= 1;
268
269 if (_this->ref_count < 1) {
270 #if SDL_USE_LIBUDEV
271 SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
272 SDL_UDEV_Quit();
273 #endif /* SDL_USE_LIBUDEV */
274
275 if (_this->console_fd >= 0) {
276 SDL_EVDEV_unmute_keyboard(_this->console_fd, _this->kb_mode);
277 close(_this->console_fd);
278 }
279
280 /* Remove existing devices */
281 while(_this->first != NULL) {
282 SDL_EVDEV_device_removed(_this->first->path);
283 }
284
285 SDL_assert(_this->first == NULL);
286 SDL_assert(_this->last == NULL);
287 SDL_assert(_this->num_devices == 0);
288
289 SDL_free(_this);
290 _this = NULL;
291 }
292 }
293
294 #if SDL_USE_LIBUDEV
SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event,int udev_class,const char * dev_path)295 void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
296 const char* dev_path)
297 {
298 if (dev_path == NULL) {
299 return;
300 }
301
302 switch(udev_event) {
303 case SDL_UDEV_DEVICEADDED:
304 if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD |
305 SDL_UDEV_DEVICE_TOUCHSCREEN)))
306 return;
307
308 SDL_EVDEV_device_added(dev_path, udev_class);
309 break;
310 case SDL_UDEV_DEVICEREMOVED:
311 SDL_EVDEV_device_removed(dev_path);
312 break;
313 default:
314 break;
315 }
316 }
317 #endif /* SDL_USE_LIBUDEV */
318
319 #ifdef SDL_INPUT_LINUXKD
320 /* this logic is pulled from kbd_keycode() in drivers/tty/vt/keyboard.c in the
321 Linux kernel source */
SDL_EVDEV_do_text_input(unsigned short keycode)322 static void SDL_EVDEV_do_text_input(unsigned short keycode) {
323 char shift_state;
324 int locks_state;
325 struct kbentry kbe;
326 unsigned char type;
327 char text[2] = { 0 };
328
329 if (_this->console_fd < 0)
330 return;
331
332 shift_state = TIOCL_GETSHIFTSTATE;
333 if (ioctl(_this->console_fd, TIOCLINUX, &shift_state) < 0) {
334 /* TODO: error */
335 return;
336 }
337
338 kbe.kb_table = shift_state;
339 kbe.kb_index = keycode;
340
341 if (ioctl(_this->console_fd, KDGKBENT, &kbe) < 0) {
342 /* TODO: error */
343 return;
344 }
345
346 type = KTYP(kbe.kb_value);
347
348 if (type < 0xf0) {
349 /*
350 * FIXME: keysyms with a type below 0xf0 represent a unicode character
351 * which requires special handling due to dead characters, diacritics,
352 * etc. For perfect input a proper way to deal with such characters
353 * should be implemented.
354 *
355 * For reference, the only place I was able to find out about this
356 * special 0xf0 value was in an unused? couple of patches listed below.
357 *
358 * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-keyboard.diff
359 * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-console.diff
360 */
361
362 return;
363 }
364
365 type -= 0xf0;
366
367 /* if type is KT_LETTER then it can be affected by Caps Lock */
368 if (type == KT_LETTER) {
369 type = KT_LATIN;
370
371 if (ioctl(_this->console_fd, KDGKBLED, &locks_state) < 0) {
372 /* TODO: error */
373 return;
374 }
375
376 if (locks_state & K_CAPSLOCK) {
377 kbe.kb_table = shift_state ^ (1 << KG_SHIFT);
378
379 if (ioctl(_this->console_fd, KDGKBENT, &kbe) < 0) {
380 /* TODO: error */
381 return;
382 }
383 }
384 }
385
386 /* TODO: convert values >= 0x80 from ISO-8859-1? to UTF-8 */
387 if (type != KT_LATIN || KVAL(kbe.kb_value) >= 0x80)
388 return;
389
390 *text = KVAL(kbe.kb_value);
391 SDL_SendKeyboardText(text);
392 }
393 #endif /* SDL_INPUT_LINUXKD */
394
395 void
SDL_EVDEV_Poll(void)396 SDL_EVDEV_Poll(void)
397 {
398 struct input_event events[32];
399 int i, j, len;
400 SDL_evdevlist_item *item;
401 SDL_Scancode scan_code;
402 int mouse_button;
403 SDL_Mouse *mouse;
404 float norm_x, norm_y;
405
406 if (!_this) {
407 return;
408 }
409
410 #if SDL_USE_LIBUDEV
411 SDL_UDEV_Poll();
412 #endif
413
414 mouse = SDL_GetMouse();
415
416 for (item = _this->first; item != NULL; item = item->next) {
417 while ((len = read(item->fd, events, (sizeof events))) > 0) {
418 len /= sizeof(events[0]);
419 for (i = 0; i < len; ++i) {
420 /* special handling for touchscreen, that should eventually be
421 used for all devices */
422 if (item->out_of_sync && item->is_touchscreen &&
423 events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
424 break;
425 }
426
427 switch (events[i].type) {
428 case EV_KEY:
429 if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
430 mouse_button = events[i].code - BTN_MOUSE;
431 if (events[i].value == 0) {
432 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
433 } else if (events[i].value == 1) {
434 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
435 }
436 break;
437 }
438
439 /* Probably keyboard */
440 scan_code = SDL_EVDEV_translate_keycode(events[i].code);
441 if (scan_code != SDL_SCANCODE_UNKNOWN) {
442 if (events[i].value == 0) {
443 SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
444 } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
445 SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
446 #ifdef SDL_INPUT_LINUXKD
447 SDL_EVDEV_do_text_input(events[i].code);
448 #endif /* SDL_INPUT_LINUXKD */
449 }
450 }
451 break;
452 case EV_ABS:
453 switch(events[i].code) {
454 case ABS_MT_SLOT:
455 if (!item->is_touchscreen) /* FIXME: temp hack */
456 break;
457 item->touchscreen_data->current_slot = events[i].value;
458 break;
459 case ABS_MT_TRACKING_ID:
460 if (!item->is_touchscreen) /* FIXME: temp hack */
461 break;
462 if (events[i].value >= 0) {
463 item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
464 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
465 } else {
466 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
467 }
468 break;
469 case ABS_MT_POSITION_X:
470 if (!item->is_touchscreen) /* FIXME: temp hack */
471 break;
472 item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
473 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
474 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
475 }
476 break;
477 case ABS_MT_POSITION_Y:
478 if (!item->is_touchscreen) /* FIXME: temp hack */
479 break;
480 item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
481 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
482 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
483 }
484 break;
485 case ABS_X:
486 if (item->is_touchscreen) /* FIXME: temp hack */
487 break;
488 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
489 break;
490 case ABS_Y:
491 if (item->is_touchscreen) /* FIXME: temp hack */
492 break;
493 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
494 break;
495 default:
496 break;
497 }
498 break;
499 case EV_REL:
500 switch(events[i].code) {
501 case REL_X:
502 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
503 break;
504 case REL_Y:
505 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
506 break;
507 case REL_WHEEL:
508 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
509 break;
510 case REL_HWHEEL:
511 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
512 break;
513 default:
514 break;
515 }
516 break;
517 case EV_SYN:
518 switch (events[i].code) {
519 case SYN_REPORT:
520 if (!item->is_touchscreen) /* FIXME: temp hack */
521 break;
522
523 for(j = 0; j < item->touchscreen_data->max_slots; j++) {
524 norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
525 (float)item->touchscreen_data->range_x;
526 norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
527 (float)item->touchscreen_data->range_y;
528
529 switch(item->touchscreen_data->slots[j].delta) {
530 case EVDEV_TOUCH_SLOTDELTA_DOWN:
531 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f);
532 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
533 break;
534 case EVDEV_TOUCH_SLOTDELTA_UP:
535 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f);
536 item->touchscreen_data->slots[j].tracking_id = -1;
537 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
538 break;
539 case EVDEV_TOUCH_SLOTDELTA_MOVE:
540 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f);
541 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
542 break;
543 default:
544 break;
545 }
546 }
547
548 if (item->out_of_sync)
549 item->out_of_sync = 0;
550 break;
551 case SYN_DROPPED:
552 if (item->is_touchscreen)
553 item->out_of_sync = 1;
554 SDL_EVDEV_sync_device(item);
555 break;
556 default:
557 break;
558 }
559 break;
560 }
561 }
562 }
563 }
564 }
565
566 static SDL_Scancode
SDL_EVDEV_translate_keycode(int keycode)567 SDL_EVDEV_translate_keycode(int keycode)
568 {
569 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
570
571 if (keycode < SDL_arraysize(linux_scancode_table))
572 scancode = linux_scancode_table[keycode];
573
574 if (scancode == SDL_SCANCODE_UNKNOWN) {
575 SDL_Log("The key you just pressed is not recognized by SDL. To help "
576 "get this fixed, please report this to the SDL mailing list "
577 "<sdl@libsdl.org> EVDEV KeyCode %d\n", keycode);
578 }
579
580 return scancode;
581 }
582
583 #ifdef SDL_USE_LIBUDEV
584 static int
SDL_EVDEV_init_touchscreen(SDL_evdevlist_item * item)585 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
586 {
587 int ret, i;
588 char name[64];
589 struct input_absinfo abs_info;
590
591 if (!item->is_touchscreen)
592 return 0;
593
594 item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
595 if (item->touchscreen_data == NULL)
596 return SDL_OutOfMemory();
597
598 ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
599 if (ret < 0) {
600 SDL_free(item->touchscreen_data);
601 return SDL_SetError("Failed to get evdev touchscreen name");
602 }
603
604 item->touchscreen_data->name = SDL_strdup(name);
605 if (item->touchscreen_data->name == NULL) {
606 SDL_free(item->touchscreen_data);
607 return SDL_OutOfMemory();
608 }
609
610 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_X), &abs_info);
611 if (ret < 0) {
612 SDL_free(item->touchscreen_data->name);
613 SDL_free(item->touchscreen_data);
614 return SDL_SetError("Failed to get evdev touchscreen limits");
615 }
616 item->touchscreen_data->min_x = abs_info.minimum;
617 item->touchscreen_data->max_x = abs_info.maximum;
618 item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
619
620 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_Y), &abs_info);
621 if (ret < 0) {
622 SDL_free(item->touchscreen_data->name);
623 SDL_free(item->touchscreen_data);
624 return SDL_SetError("Failed to get evdev touchscreen limits");
625 }
626 item->touchscreen_data->min_y = abs_info.minimum;
627 item->touchscreen_data->max_y = abs_info.maximum;
628 item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
629
630 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
631 if (ret < 0) {
632 SDL_free(item->touchscreen_data->name);
633 SDL_free(item->touchscreen_data);
634 return SDL_SetError("Failed to get evdev touchscreen limits");
635 }
636 item->touchscreen_data->max_slots = abs_info.maximum + 1;
637
638 item->touchscreen_data->slots = SDL_calloc(
639 item->touchscreen_data->max_slots,
640 sizeof(*item->touchscreen_data->slots));
641 if (item->touchscreen_data->slots == NULL) {
642 SDL_free(item->touchscreen_data->name);
643 SDL_free(item->touchscreen_data);
644 return SDL_OutOfMemory();
645 }
646
647 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
648 item->touchscreen_data->slots[i].tracking_id = -1;
649 }
650
651 ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
652 item->touchscreen_data->name);
653 if (ret < 0) {
654 SDL_free(item->touchscreen_data->slots);
655 SDL_free(item->touchscreen_data->name);
656 SDL_free(item->touchscreen_data);
657 return ret;
658 }
659
660 return 0;
661 }
662 #endif /* SDL_USE_LIBUDEV */
663
664 static void
SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item * item)665 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
666 if (!item->is_touchscreen)
667 return;
668
669 SDL_DelTouch(item->fd);
670 SDL_free(item->touchscreen_data->slots);
671 SDL_free(item->touchscreen_data->name);
672 SDL_free(item->touchscreen_data);
673 }
674
675 static void
SDL_EVDEV_sync_device(SDL_evdevlist_item * item)676 SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
677 {
678 #ifdef EVIOCGMTSLOTS
679 int i, ret;
680 struct input_absinfo abs_info;
681 /*
682 * struct input_mt_request_layout {
683 * __u32 code;
684 * __s32 values[num_slots];
685 * };
686 *
687 * this is the structure we're trying to emulate
688 */
689 __u32* mt_req_code;
690 __s32* mt_req_values;
691 size_t mt_req_size;
692
693 /* TODO: sync devices other than touchscreen */
694 if (!item->is_touchscreen)
695 return;
696
697 mt_req_size = sizeof(*mt_req_code) +
698 sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
699
700 mt_req_code = SDL_calloc(1, mt_req_size);
701 if (mt_req_code == NULL) {
702 return;
703 }
704
705 mt_req_values = (__s32*)mt_req_code + 1;
706
707 *mt_req_code = ABS_MT_TRACKING_ID;
708 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
709 if (ret < 0) {
710 SDL_free(mt_req_code);
711 return;
712 }
713 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
714 /*
715 * This doesn't account for the very edge case of the user removing their
716 * finger and replacing it on the screen during the time we're out of sync,
717 * which'll mean that we're not going from down -> up or up -> down, we're
718 * going from down -> down but with a different tracking id, meaning we'd
719 * have to tell SDL of the two events, but since we wait till SYN_REPORT in
720 * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
721 * allow it. Lets just pray to God it doesn't happen.
722 */
723 if (item->touchscreen_data->slots[i].tracking_id < 0 &&
724 mt_req_values[i] >= 0) {
725 item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
726 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
727 } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
728 mt_req_values[i] < 0) {
729 item->touchscreen_data->slots[i].tracking_id = -1;
730 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
731 }
732 }
733
734 *mt_req_code = ABS_MT_POSITION_X;
735 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
736 if (ret < 0) {
737 SDL_free(mt_req_code);
738 return;
739 }
740 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
741 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
742 item->touchscreen_data->slots[i].x != mt_req_values[i]) {
743 item->touchscreen_data->slots[i].x = mt_req_values[i];
744 if (item->touchscreen_data->slots[i].delta ==
745 EVDEV_TOUCH_SLOTDELTA_NONE) {
746 item->touchscreen_data->slots[i].delta =
747 EVDEV_TOUCH_SLOTDELTA_MOVE;
748 }
749 }
750 }
751
752 *mt_req_code = ABS_MT_POSITION_Y;
753 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
754 if (ret < 0) {
755 SDL_free(mt_req_code);
756 return;
757 }
758 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
759 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
760 item->touchscreen_data->slots[i].y != mt_req_values[i]) {
761 item->touchscreen_data->slots[i].y = mt_req_values[i];
762 if (item->touchscreen_data->slots[i].delta ==
763 EVDEV_TOUCH_SLOTDELTA_NONE) {
764 item->touchscreen_data->slots[i].delta =
765 EVDEV_TOUCH_SLOTDELTA_MOVE;
766 }
767 }
768 }
769
770 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
771 if (ret < 0) {
772 SDL_free(mt_req_code);
773 return;
774 }
775 item->touchscreen_data->current_slot = abs_info.value;
776
777 SDL_free(mt_req_code);
778
779 #endif /* EVIOCGMTSLOTS */
780 }
781
782 #if SDL_USE_LIBUDEV
783 static int
SDL_EVDEV_device_added(const char * dev_path,int udev_class)784 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
785 {
786 int ret;
787 SDL_evdevlist_item *item;
788
789 /* Check to make sure it's not already in list. */
790 for (item = _this->first; item != NULL; item = item->next) {
791 if (SDL_strcmp(dev_path, item->path) == 0) {
792 return -1; /* already have this one */
793 }
794 }
795
796 item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
797 if (item == NULL) {
798 return SDL_OutOfMemory();
799 }
800
801 item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
802 if (item->fd < 0) {
803 SDL_free(item);
804 return SDL_SetError("Unable to open %s", dev_path);
805 }
806
807 item->path = SDL_strdup(dev_path);
808 if (item->path == NULL) {
809 close(item->fd);
810 SDL_free(item);
811 return SDL_OutOfMemory();
812 }
813
814 if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
815 item->is_touchscreen = 1;
816
817 if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
818 close(item->fd);
819 SDL_free(item);
820 return ret;
821 }
822 }
823
824 if (_this->last == NULL) {
825 _this->first = _this->last = item;
826 } else {
827 _this->last->next = item;
828 _this->last = item;
829 }
830
831 SDL_EVDEV_sync_device(item);
832
833 return _this->num_devices++;
834 }
835 #endif /* SDL_USE_LIBUDEV */
836
837 static int
SDL_EVDEV_device_removed(const char * dev_path)838 SDL_EVDEV_device_removed(const char *dev_path)
839 {
840 SDL_evdevlist_item *item;
841 SDL_evdevlist_item *prev = NULL;
842
843 for (item = _this->first; item != NULL; item = item->next) {
844 /* found it, remove it. */
845 if (SDL_strcmp(dev_path, item->path) == 0) {
846 if (prev != NULL) {
847 prev->next = item->next;
848 } else {
849 SDL_assert(_this->first == item);
850 _this->first = item->next;
851 }
852 if (item == _this->last) {
853 _this->last = prev;
854 }
855 if (item->is_touchscreen) {
856 SDL_EVDEV_destroy_touchscreen(item);
857 }
858 close(item->fd);
859 SDL_free(item->path);
860 SDL_free(item);
861 _this->num_devices--;
862 return 0;
863 }
864 prev = item;
865 }
866
867 return -1;
868 }
869
870
871 #endif /* SDL_INPUT_LINUXEV */
872
873 /* vi: set ts=4 sw=4 expandtab: */
874