src/core/linux/SDL_evdev.c
author Alex Szpakowski <slime73@gmail.com>
Sat, 10 Nov 2018 16:15:48 -0400
changeset 12404 eb60e952b13f
parent 12160 c63903ab7150
child 12503 806492103856
permissions -rw-r--r--
Add SDL_TouchDeviceType enum and SDL_GetTouchDeviceType(SDL_TouchID id).

Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).

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