Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / core / linux / SDL_evdev.c
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_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         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 
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
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
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
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
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
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
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: */