src/core/linux/SDL_evdev.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jul 2019 15:57:55 -0700
changeset 12927 abb47c384db3
parent 12845 5b0bc0ed588a
child 12979 bbbb30026158
permissions -rw-r--r--
Fixed bug 4707 - SDL_SetRelativeMouseMode fails on Vivante

Cameron Gutman

The bugfix in https://hg.libsdl.org/SDL/rev/ba1a66b57385 caused SDL_SetRelativeMouseMode() to begin failing on Vivante (Steam Link). Even though Vivante doesn't have a SetRelativeMouseMode() or WarpMouse() function, it's in relative mode already (because it uses evdev) so the function was actually working as intended.

I think providing a no-op SetRelativeMouseMode() function for Vivante is a reasonable fix. Since it's already getting relative events through evdev, it really is a no-op to "enter relative mode".

In fact, this is probably the right thing to do for all backends that use evdev (vivante, raspberry, and kmsdrm). Raspberry and kmsdrm both have WarpMouse() implementations so SDL_SetRelativeMouseMode() isn't failing there, but it still seems to make sense not to have to do the fake warping if they're evdev-based anyway.
     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                             switch(item->touchscreen_data->slots[j].delta) {
   405                             case EVDEV_TOUCH_SLOTDELTA_DOWN:
   406                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, norm_pressure);
   407                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   408                                 break;
   409                             case EVDEV_TOUCH_SLOTDELTA_UP:
   410                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, norm_pressure);
   411                                 item->touchscreen_data->slots[j].tracking_id = -1;
   412                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   413                                 break;
   414                             case EVDEV_TOUCH_SLOTDELTA_MOVE:
   415                                 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, norm_pressure);
   416                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   417                                 break;
   418                             default:
   419                                 break;
   420                             }
   421                         }
   422 
   423                         if (item->out_of_sync)
   424                             item->out_of_sync = 0;
   425                         break;
   426                     case SYN_DROPPED:
   427                         if (item->is_touchscreen)
   428                             item->out_of_sync = 1;
   429                         SDL_EVDEV_sync_device(item);
   430                         break;
   431                     default:
   432                         break;
   433                     }
   434                     break;
   435                 }
   436             }
   437         }    
   438     }
   439 }
   440 
   441 static SDL_Scancode
   442 SDL_EVDEV_translate_keycode(int keycode)
   443 {
   444     SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
   445 
   446     if (keycode < SDL_arraysize(linux_scancode_table))
   447         scancode = linux_scancode_table[keycode];
   448 
   449     if (scancode == SDL_SCANCODE_UNKNOWN) {
   450         /* BTN_TOUCH is handled elsewhere, but we might still end up here if
   451            you get an unexpected BTN_TOUCH from something SDL believes is not
   452            a touch device. In this case, we'd rather not get a misleading
   453            SDL_Log message about an unknown key. */
   454         if (keycode != BTN_TOUCH) {
   455             SDL_Log("The key you just pressed is not recognized by SDL. To help "
   456                 "get this fixed, please report this to the SDL forums/mailing list "
   457                 "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
   458         }
   459     }
   460 
   461     return scancode;
   462 }
   463 
   464 #ifdef SDL_USE_LIBUDEV
   465 static int
   466 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
   467 {
   468     int ret, i;
   469     unsigned long xreq, yreq;
   470     char name[64];
   471     struct input_absinfo abs_info;
   472 
   473     if (!item->is_touchscreen)
   474         return 0;
   475 
   476     item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
   477     if (item->touchscreen_data == NULL)
   478         return SDL_OutOfMemory();
   479 
   480     ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
   481     if (ret < 0) {
   482         SDL_free(item->touchscreen_data);
   483         return SDL_SetError("Failed to get evdev touchscreen name");
   484     }
   485 
   486     item->touchscreen_data->name = SDL_strdup(name);
   487     if (item->touchscreen_data->name == NULL) {
   488         SDL_free(item->touchscreen_data);
   489         return SDL_OutOfMemory();
   490     }
   491 
   492     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   493     if (ret < 0) {
   494         SDL_free(item->touchscreen_data->name);
   495         SDL_free(item->touchscreen_data);
   496         return SDL_SetError("Failed to get evdev touchscreen limits");
   497     }
   498 
   499     if (abs_info.maximum == 0) {
   500         item->touchscreen_data->max_slots = 1;
   501         xreq = EVIOCGABS(ABS_X);
   502         yreq = EVIOCGABS(ABS_Y);
   503     } else {
   504         item->touchscreen_data->max_slots = abs_info.maximum + 1;
   505         xreq = EVIOCGABS(ABS_MT_POSITION_X);
   506         yreq = EVIOCGABS(ABS_MT_POSITION_Y);
   507     }
   508 
   509     ret = ioctl(item->fd, xreq, &abs_info);
   510     if (ret < 0) {
   511         SDL_free(item->touchscreen_data->name);
   512         SDL_free(item->touchscreen_data);
   513         return SDL_SetError("Failed to get evdev touchscreen limits");
   514     }
   515     item->touchscreen_data->min_x = abs_info.minimum;
   516     item->touchscreen_data->max_x = abs_info.maximum;
   517     item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
   518 
   519     ret = ioctl(item->fd, yreq, &abs_info);
   520     if (ret < 0) {
   521         SDL_free(item->touchscreen_data->name);
   522         SDL_free(item->touchscreen_data);
   523         return SDL_SetError("Failed to get evdev touchscreen limits");
   524     }
   525     item->touchscreen_data->min_y = abs_info.minimum;
   526     item->touchscreen_data->max_y = abs_info.maximum;
   527     item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
   528 
   529     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
   530     if (ret < 0) {
   531         SDL_free(item->touchscreen_data->name);
   532         SDL_free(item->touchscreen_data);
   533         return SDL_SetError("Failed to get evdev touchscreen limits");
   534     }
   535     item->touchscreen_data->min_pressure = abs_info.minimum;
   536     item->touchscreen_data->max_pressure = abs_info.maximum;
   537     item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
   538 
   539     item->touchscreen_data->slots = SDL_calloc(
   540         item->touchscreen_data->max_slots,
   541         sizeof(*item->touchscreen_data->slots));
   542     if (item->touchscreen_data->slots == NULL) {
   543         SDL_free(item->touchscreen_data->name);
   544         SDL_free(item->touchscreen_data);
   545         return SDL_OutOfMemory();
   546     }
   547 
   548     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   549         item->touchscreen_data->slots[i].tracking_id = -1;
   550     }
   551 
   552     ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
   553         SDL_TOUCH_DEVICE_DIRECT,
   554         item->touchscreen_data->name);
   555     if (ret < 0) {
   556         SDL_free(item->touchscreen_data->slots);
   557         SDL_free(item->touchscreen_data->name);
   558         SDL_free(item->touchscreen_data);
   559         return ret;
   560     }
   561 
   562     return 0;
   563 }
   564 #endif /* SDL_USE_LIBUDEV */
   565 
   566 static void
   567 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
   568     if (!item->is_touchscreen)
   569         return;
   570 
   571     SDL_DelTouch(item->fd);
   572     SDL_free(item->touchscreen_data->slots);
   573     SDL_free(item->touchscreen_data->name);
   574     SDL_free(item->touchscreen_data);
   575 }
   576 
   577 static void
   578 SDL_EVDEV_sync_device(SDL_evdevlist_item *item) 
   579 {
   580 #ifdef EVIOCGMTSLOTS
   581     int i, ret;
   582     struct input_absinfo abs_info;
   583     /*
   584      * struct input_mt_request_layout {
   585      *     __u32 code;
   586      *     __s32 values[num_slots];
   587      * };
   588      *
   589      * this is the structure we're trying to emulate
   590      */
   591     Uint32* mt_req_code;
   592     Sint32* mt_req_values;
   593     size_t mt_req_size;
   594 
   595     /* TODO: sync devices other than touchscreen */
   596     if (!item->is_touchscreen)
   597         return;
   598 
   599     mt_req_size = sizeof(*mt_req_code) +
   600         sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
   601 
   602     mt_req_code = SDL_calloc(1, mt_req_size);
   603     if (mt_req_code == NULL) {
   604         return;
   605     }
   606 
   607     mt_req_values = (Sint32*)mt_req_code + 1;
   608 
   609     *mt_req_code = ABS_MT_TRACKING_ID;
   610     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   611     if (ret < 0) {
   612         SDL_free(mt_req_code);
   613         return;
   614     }
   615     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   616         /*
   617          * This doesn't account for the very edge case of the user removing their
   618          * finger and replacing it on the screen during the time we're out of sync,
   619          * which'll mean that we're not going from down -> up or up -> down, we're
   620          * going from down -> down but with a different tracking id, meaning we'd
   621          * have to tell SDL of the two events, but since we wait till SYN_REPORT in
   622          * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
   623          * allow it. Lets just pray to God it doesn't happen.
   624          */
   625         if (item->touchscreen_data->slots[i].tracking_id < 0 &&
   626             mt_req_values[i] >= 0) {
   627             item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
   628             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
   629         } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   630             mt_req_values[i] < 0) {
   631             item->touchscreen_data->slots[i].tracking_id = -1;
   632             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
   633         }
   634     }
   635 
   636     *mt_req_code = ABS_MT_POSITION_X;
   637     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   638     if (ret < 0) {
   639         SDL_free(mt_req_code);
   640         return;
   641     }
   642     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   643         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   644             item->touchscreen_data->slots[i].x != mt_req_values[i]) {
   645             item->touchscreen_data->slots[i].x = mt_req_values[i];
   646             if (item->touchscreen_data->slots[i].delta ==
   647                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   648                 item->touchscreen_data->slots[i].delta =
   649                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   650             }
   651         }
   652     }
   653 
   654     *mt_req_code = ABS_MT_POSITION_Y;
   655     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   656     if (ret < 0) {
   657         SDL_free(mt_req_code);
   658         return;
   659     }
   660     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   661         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   662             item->touchscreen_data->slots[i].y != mt_req_values[i]) {
   663             item->touchscreen_data->slots[i].y = mt_req_values[i];
   664             if (item->touchscreen_data->slots[i].delta ==
   665                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   666                 item->touchscreen_data->slots[i].delta =
   667                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   668             }
   669         }
   670     }
   671 
   672     *mt_req_code = ABS_MT_PRESSURE;
   673     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   674     if (ret < 0) {
   675         SDL_free(mt_req_code);
   676         return;
   677     }
   678     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   679         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   680             item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
   681             item->touchscreen_data->slots[i].pressure = mt_req_values[i];
   682             if (item->touchscreen_data->slots[i].delta ==
   683                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   684                 item->touchscreen_data->slots[i].delta =
   685                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   686             }
   687         }
   688     }
   689 
   690     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   691     if (ret < 0) {
   692         SDL_free(mt_req_code);
   693         return;
   694     }
   695     item->touchscreen_data->current_slot = abs_info.value;
   696 
   697     SDL_free(mt_req_code);
   698 
   699 #endif /* EVIOCGMTSLOTS */
   700 }
   701 
   702 #if SDL_USE_LIBUDEV
   703 static int
   704 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
   705 {
   706     int ret;
   707     SDL_evdevlist_item *item;
   708 
   709     /* Check to make sure it's not already in list. */
   710     for (item = _this->first; item != NULL; item = item->next) {
   711         if (SDL_strcmp(dev_path, item->path) == 0) {
   712             return -1;  /* already have this one */
   713         }
   714     }
   715 
   716     item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
   717     if (item == NULL) {
   718         return SDL_OutOfMemory();
   719     }
   720 
   721     item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
   722     if (item->fd < 0) {
   723         SDL_free(item);
   724         return SDL_SetError("Unable to open %s", dev_path);
   725     }
   726 
   727     item->path = SDL_strdup(dev_path);
   728     if (item->path == NULL) {
   729         close(item->fd);
   730         SDL_free(item);
   731         return SDL_OutOfMemory();
   732     }
   733 
   734     if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
   735         item->is_touchscreen = 1;
   736 
   737         if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
   738             close(item->fd);
   739             SDL_free(item);
   740             return ret;
   741         }
   742     }
   743 
   744     if (_this->last == NULL) {
   745         _this->first = _this->last = item;
   746     } else {
   747         _this->last->next = item;
   748         _this->last = item;
   749     }
   750 
   751     SDL_EVDEV_sync_device(item);
   752 
   753     return _this->num_devices++;
   754 }
   755 #endif /* SDL_USE_LIBUDEV */
   756 
   757 static int
   758 SDL_EVDEV_device_removed(const char *dev_path)
   759 {
   760     SDL_evdevlist_item *item;
   761     SDL_evdevlist_item *prev = NULL;
   762 
   763     for (item = _this->first; item != NULL; item = item->next) {
   764         /* found it, remove it. */
   765         if (SDL_strcmp(dev_path, item->path) == 0) {
   766             if (prev != NULL) {
   767                 prev->next = item->next;
   768             } else {
   769                 SDL_assert(_this->first == item);
   770                 _this->first = item->next;
   771             }
   772             if (item == _this->last) {
   773                 _this->last = prev;
   774             }
   775             if (item->is_touchscreen) {
   776                 SDL_EVDEV_destroy_touchscreen(item);
   777             }
   778             close(item->fd);
   779             SDL_free(item->path);
   780             SDL_free(item);
   781             _this->num_devices--;
   782             return 0;
   783         }
   784         prev = item;
   785     }
   786 
   787     return -1;
   788 }
   789 
   790 
   791 #endif /* SDL_INPUT_LINUXEV */
   792 
   793 /* vi: set ts=4 sw=4 expandtab: */