1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #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_assert.h"
43 #include "SDL_endian.h"
44 #include "SDL_scancode.h"
45 #include "../../events/SDL_events_c.h"
46 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
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
SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)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
SDL_EVDEV_Init(void)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
SDL_EVDEV_Quit(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
SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event,int udev_class,const char * dev_path)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         SDL_EVDEV_device_added(dev_path, udev_class);
226         break;
227     case SDL_UDEV_DEVICEREMOVED:
228         SDL_EVDEV_device_removed(dev_path);
229         break;
230     default:
231         break;
232     }
233 }
234 #endif /* SDL_USE_LIBUDEV */
235 
236 void
SDL_EVDEV_Poll(void)237 SDL_EVDEV_Poll(void)
238 {
239     struct input_event events[32];
240     int i, j, len;
241     SDL_evdevlist_item *item;
242     SDL_Scancode scan_code;
243     int mouse_button;
244     SDL_Mouse *mouse;
245     float norm_x, norm_y, norm_pressure;
246 
247     if (!_this) {
248         return;
249     }
250 
251 #if SDL_USE_LIBUDEV
252     SDL_UDEV_Poll();
253 #endif
254 
255     mouse = SDL_GetMouse();
256 
257     for (item = _this->first; item != NULL; item = item->next) {
258         while ((len = read(item->fd, events, (sizeof events))) > 0) {
259             len /= sizeof(events[0]);
260             for (i = 0; i < len; ++i) {
261                 /* special handling for touchscreen, that should eventually be
262                    used for all devices */
263                 if (item->out_of_sync && item->is_touchscreen &&
264                     events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
265                     break;
266                 }
267 
268                 switch (events[i].type) {
269                 case EV_KEY:
270                     if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
271                         mouse_button = events[i].code - BTN_MOUSE;
272                         if (events[i].value == 0) {
273                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
274                         } else if (events[i].value == 1) {
275                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
276                         }
277                         break;
278                     }
279 
280                     /* BTH_TOUCH event value 1 indicates there is contact with
281                        a touchscreen or trackpad (earlist finger's current
282                        position is sent in EV_ABS ABS_X/ABS_Y, switching to
283                        next finger after earlist is released) */
284                     if (item->is_touchscreen && events[i].code == BTN_TOUCH) {
285                         if (item->touchscreen_data->max_slots == 1) {
286                             if (events[i].value)
287                                 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
288                             else
289                                 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
290                         }
291                         break;
292                     }
293 
294                     /* Probably keyboard */
295                     scan_code = SDL_EVDEV_translate_keycode(events[i].code);
296                     if (scan_code != SDL_SCANCODE_UNKNOWN) {
297                         if (events[i].value == 0) {
298                             SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
299                         } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
300                             SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
301                         }
302                     }
303                     SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
304                     break;
305                 case EV_ABS:
306                     switch(events[i].code) {
307                     case ABS_MT_SLOT:
308                         if (!item->is_touchscreen) /* FIXME: temp hack */
309                             break;
310                         item->touchscreen_data->current_slot = events[i].value;
311                         break;
312                     case ABS_MT_TRACKING_ID:
313                         if (!item->is_touchscreen) /* FIXME: temp hack */
314                             break;
315                         if (events[i].value >= 0) {
316                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
317                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
318                         } else {
319                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
320                         }
321                         break;
322                     case ABS_MT_POSITION_X:
323                         if (!item->is_touchscreen) /* FIXME: temp hack */
324                             break;
325                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
326                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
327                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
328                         }
329                         break;
330                     case ABS_MT_POSITION_Y:
331                         if (!item->is_touchscreen) /* FIXME: temp hack */
332                             break;
333                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
334                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
335                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
336                         }
337                         break;
338                     case ABS_MT_PRESSURE:
339                         if (!item->is_touchscreen) /* FIXME: temp hack */
340                             break;
341                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value;
342                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
343                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
344                         }
345                         break;
346                     case ABS_X:
347                         if (item->is_touchscreen) {
348                             if (item->touchscreen_data->max_slots != 1)
349                                 break;
350                             item->touchscreen_data->slots[0].x = events[i].value;
351                         } else
352                             SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
353                         break;
354                     case ABS_Y:
355                         if (item->is_touchscreen) {
356                             if (item->touchscreen_data->max_slots != 1)
357                                 break;
358                             item->touchscreen_data->slots[0].y = events[i].value;
359                         } else
360                             SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
361                         break;
362                     default:
363                         break;
364                     }
365                     break;
366                 case EV_REL:
367                     switch(events[i].code) {
368                     case REL_X:
369                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
370                         break;
371                     case REL_Y:
372                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
373                         break;
374                     case REL_WHEEL:
375                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
376                         break;
377                     case REL_HWHEEL:
378                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
379                         break;
380                     default:
381                         break;
382                     }
383                     break;
384                 case EV_SYN:
385                     switch (events[i].code) {
386                     case SYN_REPORT:
387                         if (!item->is_touchscreen) /* FIXME: temp hack */
388                             break;
389 
390                         for(j = 0; j < item->touchscreen_data->max_slots; j++) {
391                             norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
392                                 (float)item->touchscreen_data->range_x;
393                             norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
394                                 (float)item->touchscreen_data->range_y;
395 
396                             if (item->touchscreen_data->range_pressure > 0) {
397                                 norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
398                                     (float)item->touchscreen_data->range_pressure;
399                             } else {
400                                 /* This touchscreen does not support pressure */
401                                 norm_pressure = 1.0f;
402                             }
403 
404                             /* FIXME: the touch's window shouldn't be null, but
405                              * the coordinate space of touch positions needs to
406                              * be window-relative in that case. */
407                             switch(item->touchscreen_data->slots[j].delta) {
408                             case EVDEV_TOUCH_SLOTDELTA_DOWN:
409                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
410                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
411                                 break;
412                             case EVDEV_TOUCH_SLOTDELTA_UP:
413                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
414                                 item->touchscreen_data->slots[j].tracking_id = -1;
415                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
416                                 break;
417                             case EVDEV_TOUCH_SLOTDELTA_MOVE:
418                                 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
419                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
420                                 break;
421                             default:
422                                 break;
423                             }
424                         }
425 
426                         if (item->out_of_sync)
427                             item->out_of_sync = 0;
428                         break;
429                     case SYN_DROPPED:
430                         if (item->is_touchscreen)
431                             item->out_of_sync = 1;
432                         SDL_EVDEV_sync_device(item);
433                         break;
434                     default:
435                         break;
436                     }
437                     break;
438                 }
439             }
440         }
441     }
442 }
443 
444 static SDL_Scancode
SDL_EVDEV_translate_keycode(int keycode)445 SDL_EVDEV_translate_keycode(int keycode)
446 {
447     SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
448 
449     if (keycode < SDL_arraysize(linux_scancode_table)) {
450         scancode = linux_scancode_table[keycode];
451 
452         if (scancode == SDL_SCANCODE_UNKNOWN) {
453             /* BTN_TOUCH is handled elsewhere, but we might still end up here if
454                you get an unexpected BTN_TOUCH from something SDL believes is not
455                a touch device. In this case, we'd rather not get a misleading
456                SDL_Log message about an unknown key. */
457             if (keycode != BTN_TOUCH) {
458                 SDL_Log("The key you just pressed is not recognized by SDL. To help "
459                     "get this fixed, please report this to the SDL forums/mailing list "
460                     "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
461             }
462         }
463     }
464 
465     return scancode;
466 }
467 
468 #ifdef SDL_USE_LIBUDEV
469 static int
SDL_EVDEV_init_touchscreen(SDL_evdevlist_item * item)470 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
471 {
472     int ret, i;
473     unsigned long xreq, yreq;
474     char name[64];
475     struct input_absinfo abs_info;
476 
477     if (!item->is_touchscreen)
478         return 0;
479 
480     item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
481     if (item->touchscreen_data == NULL)
482         return SDL_OutOfMemory();
483 
484     ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
485     if (ret < 0) {
486         SDL_free(item->touchscreen_data);
487         return SDL_SetError("Failed to get evdev touchscreen name");
488     }
489 
490     item->touchscreen_data->name = SDL_strdup(name);
491     if (item->touchscreen_data->name == NULL) {
492         SDL_free(item->touchscreen_data);
493         return SDL_OutOfMemory();
494     }
495 
496     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
497     if (ret < 0) {
498         SDL_free(item->touchscreen_data->name);
499         SDL_free(item->touchscreen_data);
500         return SDL_SetError("Failed to get evdev touchscreen limits");
501     }
502 
503     if (abs_info.maximum == 0) {
504         item->touchscreen_data->max_slots = 1;
505         xreq = EVIOCGABS(ABS_X);
506         yreq = EVIOCGABS(ABS_Y);
507     } else {
508         item->touchscreen_data->max_slots = abs_info.maximum + 1;
509         xreq = EVIOCGABS(ABS_MT_POSITION_X);
510         yreq = EVIOCGABS(ABS_MT_POSITION_Y);
511     }
512 
513     ret = ioctl(item->fd, xreq, &abs_info);
514     if (ret < 0) {
515         SDL_free(item->touchscreen_data->name);
516         SDL_free(item->touchscreen_data);
517         return SDL_SetError("Failed to get evdev touchscreen limits");
518     }
519     item->touchscreen_data->min_x = abs_info.minimum;
520     item->touchscreen_data->max_x = abs_info.maximum;
521     item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
522 
523     ret = ioctl(item->fd, yreq, &abs_info);
524     if (ret < 0) {
525         SDL_free(item->touchscreen_data->name);
526         SDL_free(item->touchscreen_data);
527         return SDL_SetError("Failed to get evdev touchscreen limits");
528     }
529     item->touchscreen_data->min_y = abs_info.minimum;
530     item->touchscreen_data->max_y = abs_info.maximum;
531     item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
532 
533     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
534     if (ret < 0) {
535         SDL_free(item->touchscreen_data->name);
536         SDL_free(item->touchscreen_data);
537         return SDL_SetError("Failed to get evdev touchscreen limits");
538     }
539     item->touchscreen_data->min_pressure = abs_info.minimum;
540     item->touchscreen_data->max_pressure = abs_info.maximum;
541     item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
542 
543     item->touchscreen_data->slots = SDL_calloc(
544         item->touchscreen_data->max_slots,
545         sizeof(*item->touchscreen_data->slots));
546     if (item->touchscreen_data->slots == NULL) {
547         SDL_free(item->touchscreen_data->name);
548         SDL_free(item->touchscreen_data);
549         return SDL_OutOfMemory();
550     }
551 
552     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
553         item->touchscreen_data->slots[i].tracking_id = -1;
554     }
555 
556     ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
557         SDL_TOUCH_DEVICE_DIRECT,
558         item->touchscreen_data->name);
559     if (ret < 0) {
560         SDL_free(item->touchscreen_data->slots);
561         SDL_free(item->touchscreen_data->name);
562         SDL_free(item->touchscreen_data);
563         return ret;
564     }
565 
566     return 0;
567 }
568 #endif /* SDL_USE_LIBUDEV */
569 
570 static void
SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item * item)571 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
572     if (!item->is_touchscreen)
573         return;
574 
575     SDL_DelTouch(item->fd);
576     SDL_free(item->touchscreen_data->slots);
577     SDL_free(item->touchscreen_data->name);
578     SDL_free(item->touchscreen_data);
579 }
580 
581 static void
SDL_EVDEV_sync_device(SDL_evdevlist_item * item)582 SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
583 {
584 #ifdef EVIOCGMTSLOTS
585     int i, ret;
586     struct input_absinfo abs_info;
587     /*
588      * struct input_mt_request_layout {
589      *     __u32 code;
590      *     __s32 values[num_slots];
591      * };
592      *
593      * this is the structure we're trying to emulate
594      */
595     Uint32* mt_req_code;
596     Sint32* mt_req_values;
597     size_t mt_req_size;
598 
599     /* TODO: sync devices other than touchscreen */
600     if (!item->is_touchscreen)
601         return;
602 
603     mt_req_size = sizeof(*mt_req_code) +
604         sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
605 
606     mt_req_code = SDL_calloc(1, mt_req_size);
607     if (mt_req_code == NULL) {
608         return;
609     }
610 
611     mt_req_values = (Sint32*)mt_req_code + 1;
612 
613     *mt_req_code = ABS_MT_TRACKING_ID;
614     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
615     if (ret < 0) {
616         SDL_free(mt_req_code);
617         return;
618     }
619     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
620         /*
621          * This doesn't account for the very edge case of the user removing their
622          * finger and replacing it on the screen during the time we're out of sync,
623          * which'll mean that we're not going from down -> up or up -> down, we're
624          * going from down -> down but with a different tracking id, meaning we'd
625          * have to tell SDL of the two events, but since we wait till SYN_REPORT in
626          * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
627          * allow it. Lets just pray to God it doesn't happen.
628          */
629         if (item->touchscreen_data->slots[i].tracking_id < 0 &&
630             mt_req_values[i] >= 0) {
631             item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
632             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
633         } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
634             mt_req_values[i] < 0) {
635             item->touchscreen_data->slots[i].tracking_id = -1;
636             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
637         }
638     }
639 
640     *mt_req_code = ABS_MT_POSITION_X;
641     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
642     if (ret < 0) {
643         SDL_free(mt_req_code);
644         return;
645     }
646     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
647         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
648             item->touchscreen_data->slots[i].x != mt_req_values[i]) {
649             item->touchscreen_data->slots[i].x = mt_req_values[i];
650             if (item->touchscreen_data->slots[i].delta ==
651                 EVDEV_TOUCH_SLOTDELTA_NONE) {
652                 item->touchscreen_data->slots[i].delta =
653                     EVDEV_TOUCH_SLOTDELTA_MOVE;
654             }
655         }
656     }
657 
658     *mt_req_code = ABS_MT_POSITION_Y;
659     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
660     if (ret < 0) {
661         SDL_free(mt_req_code);
662         return;
663     }
664     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
665         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
666             item->touchscreen_data->slots[i].y != mt_req_values[i]) {
667             item->touchscreen_data->slots[i].y = mt_req_values[i];
668             if (item->touchscreen_data->slots[i].delta ==
669                 EVDEV_TOUCH_SLOTDELTA_NONE) {
670                 item->touchscreen_data->slots[i].delta =
671                     EVDEV_TOUCH_SLOTDELTA_MOVE;
672             }
673         }
674     }
675 
676     *mt_req_code = ABS_MT_PRESSURE;
677     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
678     if (ret < 0) {
679         SDL_free(mt_req_code);
680         return;
681     }
682     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
683         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
684             item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
685             item->touchscreen_data->slots[i].pressure = mt_req_values[i];
686             if (item->touchscreen_data->slots[i].delta ==
687                 EVDEV_TOUCH_SLOTDELTA_NONE) {
688                 item->touchscreen_data->slots[i].delta =
689                     EVDEV_TOUCH_SLOTDELTA_MOVE;
690             }
691         }
692     }
693 
694     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
695     if (ret < 0) {
696         SDL_free(mt_req_code);
697         return;
698     }
699     item->touchscreen_data->current_slot = abs_info.value;
700 
701     SDL_free(mt_req_code);
702 
703 #endif /* EVIOCGMTSLOTS */
704 }
705 
706 #if SDL_USE_LIBUDEV
707 static int
SDL_EVDEV_device_added(const char * dev_path,int udev_class)708 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
709 {
710     int ret;
711     SDL_evdevlist_item *item;
712 
713     /* Check to make sure it's not already in list. */
714     for (item = _this->first; item != NULL; item = item->next) {
715         if (SDL_strcmp(dev_path, item->path) == 0) {
716             return -1;  /* already have this one */
717         }
718     }
719 
720     item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
721     if (item == NULL) {
722         return SDL_OutOfMemory();
723     }
724 
725     item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
726     if (item->fd < 0) {
727         SDL_free(item);
728         return SDL_SetError("Unable to open %s", dev_path);
729     }
730 
731     item->path = SDL_strdup(dev_path);
732     if (item->path == NULL) {
733         close(item->fd);
734         SDL_free(item);
735         return SDL_OutOfMemory();
736     }
737 
738     if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
739         item->is_touchscreen = 1;
740 
741         if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
742             close(item->fd);
743             SDL_free(item);
744             return ret;
745         }
746     }
747 
748     if (_this->last == NULL) {
749         _this->first = _this->last = item;
750     } else {
751         _this->last->next = item;
752         _this->last = item;
753     }
754 
755     SDL_EVDEV_sync_device(item);
756 
757     return _this->num_devices++;
758 }
759 #endif /* SDL_USE_LIBUDEV */
760 
761 static int
SDL_EVDEV_device_removed(const char * dev_path)762 SDL_EVDEV_device_removed(const char *dev_path)
763 {
764     SDL_evdevlist_item *item;
765     SDL_evdevlist_item *prev = NULL;
766 
767     for (item = _this->first; item != NULL; item = item->next) {
768         /* found it, remove it. */
769         if (SDL_strcmp(dev_path, item->path) == 0) {
770             if (prev != NULL) {
771                 prev->next = item->next;
772             } else {
773                 SDL_assert(_this->first == item);
774                 _this->first = item->next;
775             }
776             if (item == _this->last) {
777                 _this->last = prev;
778             }
779             if (item->is_touchscreen) {
780                 SDL_EVDEV_destroy_touchscreen(item);
781             }
782             close(item->fd);
783             SDL_free(item->path);
784             SDL_free(item);
785             _this->num_devices--;
786             return 0;
787         }
788         prev = item;
789     }
790 
791     return -1;
792 }
793 
794 
795 #endif /* SDL_INPUT_LINUXEV */
796 
797 /* vi: set ts=4 sw=4 expandtab: */
798