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