src/core/linux/SDL_evdev.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 18 Aug 2017 20:00:29 -0400
changeset 11323 46861f3fc187
parent 11094 093b58f5de3c
child 11811 5d94cb6b24d3
permissions -rw-r--r--
cmake: added a FIXME for later.

Have to figure out what cmake version fixed this and bump the minimum to that.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 #endif
    59 
    60 typedef struct SDL_evdevlist_item
    61 {
    62     char *path;
    63     int fd;
    64 
    65     /* TODO: use this for every device, not just touchscreen */
    66     int out_of_sync;
    67 
    68     /* TODO: expand on this to have data for every possible class (mouse,
    69        keyboard, touchpad, etc.). Also there's probably some things in here we
    70        can pull out to the SDL_evdevlist_item i.e. name */
    71     int is_touchscreen;
    72     struct {
    73         char* name;
    74 
    75         int min_x, max_x, range_x;
    76         int min_y, max_y, range_y;
    77 
    78         int max_slots;
    79         int current_slot;
    80         struct {
    81             enum {
    82                 EVDEV_TOUCH_SLOTDELTA_NONE = 0,
    83                 EVDEV_TOUCH_SLOTDELTA_DOWN,
    84                 EVDEV_TOUCH_SLOTDELTA_UP,
    85                 EVDEV_TOUCH_SLOTDELTA_MOVE
    86             } delta;
    87             int tracking_id;
    88             int x, y;
    89         } * slots;
    90     } * touchscreen_data;
    91 
    92     struct SDL_evdevlist_item *next;
    93 } SDL_evdevlist_item;
    94 
    95 typedef struct SDL_EVDEV_PrivateData
    96 {
    97     int ref_count;
    98     int num_devices;
    99     SDL_evdevlist_item *first;
   100     SDL_evdevlist_item *last;
   101     SDL_EVDEV_keyboard_state *kbd;
   102 } SDL_EVDEV_PrivateData;
   103 
   104 #define _THIS SDL_EVDEV_PrivateData *_this
   105 static _THIS = NULL;
   106 
   107 static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
   108 static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
   109 static int SDL_EVDEV_device_removed(const char *dev_path);
   110 
   111 #if SDL_USE_LIBUDEV
   112 static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
   113 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
   114     const char *dev_path);
   115 #endif /* SDL_USE_LIBUDEV */
   116 
   117 static Uint8 EVDEV_MouseButtons[] = {
   118     SDL_BUTTON_LEFT,            /*  BTN_LEFT        0x110 */
   119     SDL_BUTTON_RIGHT,           /*  BTN_RIGHT       0x111 */
   120     SDL_BUTTON_MIDDLE,          /*  BTN_MIDDLE      0x112 */
   121     SDL_BUTTON_X1,              /*  BTN_SIDE        0x113 */
   122     SDL_BUTTON_X2,              /*  BTN_EXTRA       0x114 */
   123     SDL_BUTTON_X2 + 1,          /*  BTN_FORWARD     0x115 */
   124     SDL_BUTTON_X2 + 2,          /*  BTN_BACK        0x116 */
   125     SDL_BUTTON_X2 + 3           /*  BTN_TASK        0x117 */
   126 };
   127 
   128 int
   129 SDL_EVDEV_Init(void)
   130 {
   131     if (_this == NULL) {
   132         _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this));
   133         if (_this == NULL) {
   134             return SDL_OutOfMemory();
   135         }
   136 
   137 #if SDL_USE_LIBUDEV
   138         if (SDL_UDEV_Init() < 0) {
   139             SDL_free(_this);
   140             _this = NULL;
   141             return -1;
   142         }
   143 
   144         /* Set up the udev callback */
   145         if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
   146             SDL_UDEV_Quit();
   147             SDL_free(_this);
   148             _this = NULL;
   149             return -1;
   150         }
   151 
   152         /* Force a scan to build the initial device list */
   153         SDL_UDEV_Scan();
   154 #else
   155         /* TODO: Scan the devices manually, like a caveman */
   156 #endif /* SDL_USE_LIBUDEV */
   157 
   158         _this->kbd = SDL_EVDEV_kbd_init();
   159     }
   160 
   161     _this->ref_count += 1;
   162 
   163     return 0;
   164 }
   165 
   166 void
   167 SDL_EVDEV_Quit(void)
   168 {
   169     if (_this == NULL) {
   170         return;
   171     }
   172 
   173     _this->ref_count -= 1;
   174 
   175     if (_this->ref_count < 1) {
   176 #if SDL_USE_LIBUDEV
   177         SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
   178         SDL_UDEV_Quit();
   179 #endif /* SDL_USE_LIBUDEV */
   180 
   181         SDL_EVDEV_kbd_quit(_this->kbd);
   182 
   183         /* Remove existing devices */
   184         while(_this->first != NULL) {
   185             SDL_EVDEV_device_removed(_this->first->path);
   186         }
   187 
   188         SDL_assert(_this->first == NULL);
   189         SDL_assert(_this->last == NULL);
   190         SDL_assert(_this->num_devices == 0);
   191 
   192         SDL_free(_this);
   193         _this = NULL;
   194     }
   195 }
   196 
   197 #if SDL_USE_LIBUDEV
   198 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
   199     const char* dev_path)
   200 {
   201     if (dev_path == NULL) {
   202         return;
   203     }
   204 
   205     switch(udev_event) {
   206     case SDL_UDEV_DEVICEADDED:
   207         if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD |
   208             SDL_UDEV_DEVICE_TOUCHSCREEN)))
   209             return;
   210 
   211         SDL_EVDEV_device_added(dev_path, udev_class);
   212         break;  
   213     case SDL_UDEV_DEVICEREMOVED:
   214         SDL_EVDEV_device_removed(dev_path);
   215         break;
   216     default:
   217         break;
   218     }
   219 }
   220 #endif /* SDL_USE_LIBUDEV */
   221 
   222 void 
   223 SDL_EVDEV_Poll(void)
   224 {
   225     struct input_event events[32];
   226     int i, j, len;
   227     SDL_evdevlist_item *item;
   228     SDL_Scancode scan_code;
   229     int mouse_button;
   230     SDL_Mouse *mouse;
   231     float norm_x, norm_y;
   232 
   233     if (!_this) {
   234         return;
   235     }
   236 
   237 #if SDL_USE_LIBUDEV
   238     SDL_UDEV_Poll();
   239 #endif
   240 
   241     mouse = SDL_GetMouse();
   242 
   243     for (item = _this->first; item != NULL; item = item->next) {
   244         while ((len = read(item->fd, events, (sizeof events))) > 0) {
   245             len /= sizeof(events[0]);
   246             for (i = 0; i < len; ++i) {
   247                 /* special handling for touchscreen, that should eventually be
   248                    used for all devices */
   249                 if (item->out_of_sync && item->is_touchscreen &&
   250                     events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
   251                     break;
   252                 }
   253 
   254                 switch (events[i].type) {
   255                 case EV_KEY:
   256                     if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
   257                         mouse_button = events[i].code - BTN_MOUSE;
   258                         if (events[i].value == 0) {
   259                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
   260                         } else if (events[i].value == 1) {
   261                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
   262                         }
   263                         break;
   264                     }
   265 
   266                     /* Probably keyboard */
   267                     scan_code = SDL_EVDEV_translate_keycode(events[i].code);
   268                     if (scan_code != SDL_SCANCODE_UNKNOWN) {
   269                         if (events[i].value == 0) {
   270                             SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
   271                         } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
   272                             SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
   273                         }
   274                     }
   275                     SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
   276                     break;
   277                 case EV_ABS:
   278                     switch(events[i].code) {
   279                     case ABS_MT_SLOT:
   280                         if (!item->is_touchscreen) /* FIXME: temp hack */
   281                             break;
   282                         item->touchscreen_data->current_slot = events[i].value;
   283                         break;
   284                     case ABS_MT_TRACKING_ID:
   285                         if (!item->is_touchscreen) /* FIXME: temp hack */
   286                             break;
   287                         if (events[i].value >= 0) {
   288                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
   289                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
   290                         } else {
   291                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
   292                         }
   293                         break;
   294                     case ABS_MT_POSITION_X:
   295                         if (!item->is_touchscreen) /* FIXME: temp hack */
   296                             break;
   297                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
   298                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
   299                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
   300                         }
   301                         break;
   302                     case ABS_MT_POSITION_Y:
   303                         if (!item->is_touchscreen) /* FIXME: temp hack */
   304                             break;
   305                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
   306                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
   307                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
   308                         }
   309                         break;
   310                     case ABS_X:
   311                         if (item->is_touchscreen) /* FIXME: temp hack */
   312                             break;
   313                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
   314                         break;
   315                     case ABS_Y:
   316                         if (item->is_touchscreen) /* FIXME: temp hack */
   317                             break;
   318                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
   319                         break;
   320                     default:
   321                         break;
   322                     }
   323                     break;
   324                 case EV_REL:
   325                     switch(events[i].code) {
   326                     case REL_X:
   327                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
   328                         break;
   329                     case REL_Y:
   330                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
   331                         break;
   332                     case REL_WHEEL:
   333                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
   334                         break;
   335                     case REL_HWHEEL:
   336                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
   337                         break;
   338                     default:
   339                         break;
   340                     }
   341                     break;
   342                 case EV_SYN:
   343                     switch (events[i].code) {
   344                     case SYN_REPORT:
   345                         if (!item->is_touchscreen) /* FIXME: temp hack */
   346                             break;
   347 
   348                         for(j = 0; j < item->touchscreen_data->max_slots; j++) {
   349                             norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
   350                                 (float)item->touchscreen_data->range_x;
   351                             norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
   352                                 (float)item->touchscreen_data->range_y;
   353 
   354                             switch(item->touchscreen_data->slots[j].delta) {
   355                             case EVDEV_TOUCH_SLOTDELTA_DOWN:
   356                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f);
   357                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   358                                 break;
   359                             case EVDEV_TOUCH_SLOTDELTA_UP:
   360                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f);
   361                                 item->touchscreen_data->slots[j].tracking_id = -1;
   362                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   363                                 break;
   364                             case EVDEV_TOUCH_SLOTDELTA_MOVE:
   365                                 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f);
   366                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   367                                 break;
   368                             default:
   369                                 break;
   370                             }
   371                         }
   372 
   373                         if (item->out_of_sync)
   374                             item->out_of_sync = 0;
   375                         break;
   376                     case SYN_DROPPED:
   377                         if (item->is_touchscreen)
   378                             item->out_of_sync = 1;
   379                         SDL_EVDEV_sync_device(item);
   380                         break;
   381                     default:
   382                         break;
   383                     }
   384                     break;
   385                 }
   386             }
   387         }    
   388     }
   389 }
   390 
   391 static SDL_Scancode
   392 SDL_EVDEV_translate_keycode(int keycode)
   393 {
   394     SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
   395 
   396     if (keycode < SDL_arraysize(linux_scancode_table))
   397         scancode = linux_scancode_table[keycode];
   398 
   399     if (scancode == SDL_SCANCODE_UNKNOWN) {
   400         SDL_Log("The key you just pressed is not recognized by SDL. To help "
   401             "get this fixed, please report this to the SDL forums/mailing list "
   402             "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
   403     }
   404 
   405     return scancode;
   406 }
   407 
   408 #ifdef SDL_USE_LIBUDEV
   409 static int
   410 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
   411 {
   412     int ret, i;
   413     char name[64];
   414     struct input_absinfo abs_info;
   415 
   416     if (!item->is_touchscreen)
   417         return 0;
   418 
   419     item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
   420     if (item->touchscreen_data == NULL)
   421         return SDL_OutOfMemory();
   422 
   423     ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
   424     if (ret < 0) {
   425         SDL_free(item->touchscreen_data);
   426         return SDL_SetError("Failed to get evdev touchscreen name");
   427     }
   428 
   429     item->touchscreen_data->name = SDL_strdup(name);
   430     if (item->touchscreen_data->name == NULL) {
   431         SDL_free(item->touchscreen_data);
   432         return SDL_OutOfMemory();
   433     }
   434 
   435     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_X), &abs_info);
   436     if (ret < 0) {
   437         SDL_free(item->touchscreen_data->name);
   438         SDL_free(item->touchscreen_data);
   439         return SDL_SetError("Failed to get evdev touchscreen limits");
   440     }
   441     item->touchscreen_data->min_x = abs_info.minimum;
   442     item->touchscreen_data->max_x = abs_info.maximum;
   443     item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
   444 
   445     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_Y), &abs_info);
   446     if (ret < 0) {
   447         SDL_free(item->touchscreen_data->name);
   448         SDL_free(item->touchscreen_data);
   449         return SDL_SetError("Failed to get evdev touchscreen limits");
   450     }
   451     item->touchscreen_data->min_y = abs_info.minimum;
   452     item->touchscreen_data->max_y = abs_info.maximum;
   453     item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
   454 
   455     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   456     if (ret < 0) {
   457         SDL_free(item->touchscreen_data->name);
   458         SDL_free(item->touchscreen_data);
   459         return SDL_SetError("Failed to get evdev touchscreen limits");
   460     }
   461     item->touchscreen_data->max_slots = abs_info.maximum + 1;
   462 
   463     item->touchscreen_data->slots = SDL_calloc(
   464         item->touchscreen_data->max_slots,
   465         sizeof(*item->touchscreen_data->slots));
   466     if (item->touchscreen_data->slots == NULL) {
   467         SDL_free(item->touchscreen_data->name);
   468         SDL_free(item->touchscreen_data);
   469         return SDL_OutOfMemory();
   470     }
   471 
   472     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   473         item->touchscreen_data->slots[i].tracking_id = -1;
   474     }
   475 
   476     ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
   477         item->touchscreen_data->name);
   478     if (ret < 0) {
   479         SDL_free(item->touchscreen_data->slots);
   480         SDL_free(item->touchscreen_data->name);
   481         SDL_free(item->touchscreen_data);
   482         return ret;
   483     }
   484 
   485     return 0;
   486 }
   487 #endif /* SDL_USE_LIBUDEV */
   488 
   489 static void
   490 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
   491     if (!item->is_touchscreen)
   492         return;
   493 
   494     SDL_DelTouch(item->fd);
   495     SDL_free(item->touchscreen_data->slots);
   496     SDL_free(item->touchscreen_data->name);
   497     SDL_free(item->touchscreen_data);
   498 }
   499 
   500 static void
   501 SDL_EVDEV_sync_device(SDL_evdevlist_item *item) 
   502 {
   503 #ifdef EVIOCGMTSLOTS
   504     int i, ret;
   505     struct input_absinfo abs_info;
   506     /*
   507      * struct input_mt_request_layout {
   508      *     __u32 code;
   509      *     __s32 values[num_slots];
   510      * };
   511      *
   512      * this is the structure we're trying to emulate
   513      */
   514     __u32* mt_req_code;
   515     __s32* mt_req_values;
   516     size_t mt_req_size;
   517 
   518     /* TODO: sync devices other than touchscreen */
   519     if (!item->is_touchscreen)
   520         return;
   521 
   522     mt_req_size = sizeof(*mt_req_code) +
   523         sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
   524 
   525     mt_req_code = SDL_calloc(1, mt_req_size);
   526     if (mt_req_code == NULL) {
   527         return;
   528     }
   529 
   530     mt_req_values = (__s32*)mt_req_code + 1;
   531 
   532     *mt_req_code = ABS_MT_TRACKING_ID;
   533     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   534     if (ret < 0) {
   535         SDL_free(mt_req_code);
   536         return;
   537     }
   538     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   539         /*
   540          * This doesn't account for the very edge case of the user removing their
   541          * finger and replacing it on the screen during the time we're out of sync,
   542          * which'll mean that we're not going from down -> up or up -> down, we're
   543          * going from down -> down but with a different tracking id, meaning we'd
   544          * have to tell SDL of the two events, but since we wait till SYN_REPORT in
   545          * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
   546          * allow it. Lets just pray to God it doesn't happen.
   547          */
   548         if (item->touchscreen_data->slots[i].tracking_id < 0 &&
   549             mt_req_values[i] >= 0) {
   550             item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
   551             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
   552         } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   553             mt_req_values[i] < 0) {
   554             item->touchscreen_data->slots[i].tracking_id = -1;
   555             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
   556         }
   557     }
   558 
   559     *mt_req_code = ABS_MT_POSITION_X;
   560     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   561     if (ret < 0) {
   562         SDL_free(mt_req_code);
   563         return;
   564     }
   565     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   566         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   567             item->touchscreen_data->slots[i].x != mt_req_values[i]) {
   568             item->touchscreen_data->slots[i].x = mt_req_values[i];
   569             if (item->touchscreen_data->slots[i].delta ==
   570                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   571                 item->touchscreen_data->slots[i].delta =
   572                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   573             }
   574         }
   575     }
   576 
   577     *mt_req_code = ABS_MT_POSITION_Y;
   578     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   579     if (ret < 0) {
   580         SDL_free(mt_req_code);
   581         return;
   582     }
   583     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   584         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   585             item->touchscreen_data->slots[i].y != mt_req_values[i]) {
   586             item->touchscreen_data->slots[i].y = mt_req_values[i];
   587             if (item->touchscreen_data->slots[i].delta ==
   588                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   589                 item->touchscreen_data->slots[i].delta =
   590                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   591             }
   592         }
   593     }
   594 
   595     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   596     if (ret < 0) {
   597         SDL_free(mt_req_code);
   598         return;
   599     }
   600     item->touchscreen_data->current_slot = abs_info.value;
   601 
   602     SDL_free(mt_req_code);
   603 
   604 #endif /* EVIOCGMTSLOTS */
   605 }
   606 
   607 #if SDL_USE_LIBUDEV
   608 static int
   609 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
   610 {
   611     int ret;
   612     SDL_evdevlist_item *item;
   613 
   614     /* Check to make sure it's not already in list. */
   615     for (item = _this->first; item != NULL; item = item->next) {
   616         if (SDL_strcmp(dev_path, item->path) == 0) {
   617             return -1;  /* already have this one */
   618         }
   619     }
   620 
   621     item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
   622     if (item == NULL) {
   623         return SDL_OutOfMemory();
   624     }
   625 
   626     item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
   627     if (item->fd < 0) {
   628         SDL_free(item);
   629         return SDL_SetError("Unable to open %s", dev_path);
   630     }
   631 
   632     item->path = SDL_strdup(dev_path);
   633     if (item->path == NULL) {
   634         close(item->fd);
   635         SDL_free(item);
   636         return SDL_OutOfMemory();
   637     }
   638 
   639     if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
   640         item->is_touchscreen = 1;
   641 
   642         if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
   643             close(item->fd);
   644             SDL_free(item);
   645             return ret;
   646         }
   647     }
   648 
   649     if (_this->last == NULL) {
   650         _this->first = _this->last = item;
   651     } else {
   652         _this->last->next = item;
   653         _this->last = item;
   654     }
   655 
   656     SDL_EVDEV_sync_device(item);
   657 
   658     return _this->num_devices++;
   659 }
   660 #endif /* SDL_USE_LIBUDEV */
   661 
   662 static int
   663 SDL_EVDEV_device_removed(const char *dev_path)
   664 {
   665     SDL_evdevlist_item *item;
   666     SDL_evdevlist_item *prev = NULL;
   667 
   668     for (item = _this->first; item != NULL; item = item->next) {
   669         /* found it, remove it. */
   670         if (SDL_strcmp(dev_path, item->path) == 0) {
   671             if (prev != NULL) {
   672                 prev->next = item->next;
   673             } else {
   674                 SDL_assert(_this->first == item);
   675                 _this->first = item->next;
   676             }
   677             if (item == _this->last) {
   678                 _this->last = prev;
   679             }
   680             if (item->is_touchscreen) {
   681                 SDL_EVDEV_destroy_touchscreen(item);
   682             }
   683             close(item->fd);
   684             SDL_free(item->path);
   685             SDL_free(item);
   686             _this->num_devices--;
   687             return 0;
   688         }
   689         prev = item;
   690     }
   691 
   692     return -1;
   693 }
   694 
   695 
   696 #endif /* SDL_INPUT_LINUXEV */
   697 
   698 /* vi: set ts=4 sw=4 expandtab: */