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