src/core/linux/SDL_evdev.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 12 Jun 2019 07:55:48 -0700
changeset 12845 5b0bc0ed588a
parent 12761 f5c0b39addb1
child 12927 abb47c384db3
permissions -rw-r--r--
Fixed bug 4665 - Add support for single touch evdev devices

Jan Martin Mikkelsen

The attached patch adds support for single-touch evdev devices.

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