src/core/linux/SDL_evdev.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 21 May 2019 17:33:31 -0700
changeset 12747 cdf53e16feb7
parent 12687 712d84dd14c9
permissions -rw-r--r--
Fixed bug 4639 - CMake build does not generate libhidapi.so for Android

Manuel Sabogal

I noticed that the current Android.mk builds a libhidapi.so library for Android but the CMake build hasn't been updated to do so. I'll attach a patch that fixes this issue.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #ifdef SDL_INPUT_LINUXEV
    24 
    25 /* This is based on the linux joystick driver */
    26 /* References: https://www.kernel.org/doc/Documentation/input/input.txt 
    27  *             https://www.kernel.org/doc/Documentation/input/event-codes.txt
    28  *             /usr/include/linux/input.h
    29  *             The evtest application is also useful to debug the protocol
    30  */
    31 
    32 #include "SDL_evdev.h"
    33 #include "SDL_evdev_kbd.h"
    34 
    35 #include <sys/stat.h>
    36 #include <unistd.h>
    37 #include <fcntl.h>
    38 #include <sys/ioctl.h>
    39 #include <linux/input.h>
    40 
    41 #include "SDL.h"
    42 #include "SDL_assert.h"
    43 #include "SDL_endian.h"
    44 #include "SDL_scancode.h"
    45 #include "../../events/SDL_events_c.h"
    46 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
    47 #include "../../core/linux/SDL_udev.h"
    48 
    49 /* These are not defined in older Linux kernel headers */
    50 #ifndef SYN_DROPPED
    51 #define SYN_DROPPED 3
    52 #endif
    53 #ifndef ABS_MT_SLOT
    54 #define ABS_MT_SLOT         0x2f
    55 #define ABS_MT_POSITION_X   0x35
    56 #define ABS_MT_POSITION_Y   0x36
    57 #define ABS_MT_TRACKING_ID  0x39
    58 #define ABS_MT_PRESSURE     0x3a
    59 #endif
    60 
    61 typedef struct SDL_evdevlist_item
    62 {
    63     char *path;
    64     int fd;
    65 
    66     /* TODO: use this for every device, not just touchscreen */
    67     int out_of_sync;
    68 
    69     /* TODO: expand on this to have data for every possible class (mouse,
    70        keyboard, touchpad, etc.). Also there's probably some things in here we
    71        can pull out to the SDL_evdevlist_item i.e. name */
    72     int is_touchscreen;
    73     struct {
    74         char* name;
    75 
    76         int min_x, max_x, range_x;
    77         int min_y, max_y, range_y;
    78         int min_pressure, max_pressure, range_pressure;
    79 
    80         int max_slots;
    81         int current_slot;
    82         struct {
    83             enum {
    84                 EVDEV_TOUCH_SLOTDELTA_NONE = 0,
    85                 EVDEV_TOUCH_SLOTDELTA_DOWN,
    86                 EVDEV_TOUCH_SLOTDELTA_UP,
    87                 EVDEV_TOUCH_SLOTDELTA_MOVE
    88             } delta;
    89             int tracking_id;
    90             int x, y, pressure;
    91         } * slots;
    92 
    93     } * touchscreen_data;
    94 
    95     struct SDL_evdevlist_item *next;
    96 } SDL_evdevlist_item;
    97 
    98 typedef struct SDL_EVDEV_PrivateData
    99 {
   100     int ref_count;
   101     int num_devices;
   102     SDL_evdevlist_item *first;
   103     SDL_evdevlist_item *last;
   104     SDL_EVDEV_keyboard_state *kbd;
   105 } SDL_EVDEV_PrivateData;
   106 
   107 #undef _THIS
   108 #define _THIS SDL_EVDEV_PrivateData *_this
   109 static _THIS = NULL;
   110 
   111 static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
   112 static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
   113 static int SDL_EVDEV_device_removed(const char *dev_path);
   114 
   115 #if SDL_USE_LIBUDEV
   116 static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
   117 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
   118     const char *dev_path);
   119 #endif /* SDL_USE_LIBUDEV */
   120 
   121 static Uint8 EVDEV_MouseButtons[] = {
   122     SDL_BUTTON_LEFT,            /*  BTN_LEFT        0x110 */
   123     SDL_BUTTON_RIGHT,           /*  BTN_RIGHT       0x111 */
   124     SDL_BUTTON_MIDDLE,          /*  BTN_MIDDLE      0x112 */
   125     SDL_BUTTON_X1,              /*  BTN_SIDE        0x113 */
   126     SDL_BUTTON_X2,              /*  BTN_EXTRA       0x114 */
   127     SDL_BUTTON_X2 + 1,          /*  BTN_FORWARD     0x115 */
   128     SDL_BUTTON_X2 + 2,          /*  BTN_BACK        0x116 */
   129     SDL_BUTTON_X2 + 3           /*  BTN_TASK        0x117 */
   130 };
   131 
   132 int
   133 SDL_EVDEV_Init(void)
   134 {
   135     if (_this == NULL) {
   136         _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this));
   137         if (_this == NULL) {
   138             return SDL_OutOfMemory();
   139         }
   140 
   141 #if SDL_USE_LIBUDEV
   142         if (SDL_UDEV_Init() < 0) {
   143             SDL_free(_this);
   144             _this = NULL;
   145             return -1;
   146         }
   147 
   148         /* Set up the udev callback */
   149         if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
   150             SDL_UDEV_Quit();
   151             SDL_free(_this);
   152             _this = NULL;
   153             return -1;
   154         }
   155 
   156         /* Force a scan to build the initial device list */
   157         SDL_UDEV_Scan();
   158 #else
   159         /* TODO: Scan the devices manually, like a caveman */
   160 #endif /* SDL_USE_LIBUDEV */
   161 
   162         _this->kbd = SDL_EVDEV_kbd_init();
   163     }
   164 
   165     _this->ref_count += 1;
   166 
   167     return 0;
   168 }
   169 
   170 void
   171 SDL_EVDEV_Quit(void)
   172 {
   173     if (_this == NULL) {
   174         return;
   175     }
   176 
   177     _this->ref_count -= 1;
   178 
   179     if (_this->ref_count < 1) {
   180 #if SDL_USE_LIBUDEV
   181         SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
   182         SDL_UDEV_Quit();
   183 #endif /* SDL_USE_LIBUDEV */
   184 
   185         SDL_EVDEV_kbd_quit(_this->kbd);
   186 
   187         /* Remove existing devices */
   188         while(_this->first != NULL) {
   189             SDL_EVDEV_device_removed(_this->first->path);
   190         }
   191 
   192         SDL_assert(_this->first == NULL);
   193         SDL_assert(_this->last == NULL);
   194         SDL_assert(_this->num_devices == 0);
   195 
   196         SDL_free(_this);
   197         _this = NULL;
   198     }
   199 }
   200 
   201 #if SDL_USE_LIBUDEV
   202 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
   203     const char* dev_path)
   204 {
   205     if (dev_path == NULL) {
   206         return;
   207     }
   208 
   209     switch(udev_event) {
   210     case SDL_UDEV_DEVICEADDED:
   211         if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD |
   212             SDL_UDEV_DEVICE_TOUCHSCREEN)))
   213             return;
   214 
   215         SDL_EVDEV_device_added(dev_path, udev_class);
   216         break;  
   217     case SDL_UDEV_DEVICEREMOVED:
   218         SDL_EVDEV_device_removed(dev_path);
   219         break;
   220     default:
   221         break;
   222     }
   223 }
   224 #endif /* SDL_USE_LIBUDEV */
   225 
   226 void 
   227 SDL_EVDEV_Poll(void)
   228 {
   229     struct input_event events[32];
   230     int i, j, len;
   231     SDL_evdevlist_item *item;
   232     SDL_Scancode scan_code;
   233     int mouse_button;
   234     SDL_Mouse *mouse;
   235     float norm_x, norm_y, norm_pressure;
   236 
   237     if (!_this) {
   238         return;
   239     }
   240 
   241 #if SDL_USE_LIBUDEV
   242     SDL_UDEV_Poll();
   243 #endif
   244 
   245     mouse = SDL_GetMouse();
   246 
   247     for (item = _this->first; item != NULL; item = item->next) {
   248         while ((len = read(item->fd, events, (sizeof events))) > 0) {
   249             len /= sizeof(events[0]);
   250             for (i = 0; i < len; ++i) {
   251                 /* special handling for touchscreen, that should eventually be
   252                    used for all devices */
   253                 if (item->out_of_sync && item->is_touchscreen &&
   254                     events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
   255                     break;
   256                 }
   257 
   258                 switch (events[i].type) {
   259                 case EV_KEY:
   260                     if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
   261                         mouse_button = events[i].code - BTN_MOUSE;
   262                         if (events[i].value == 0) {
   263                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
   264                         } else if (events[i].value == 1) {
   265                             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
   266                         }
   267                         break;
   268                     }
   269 
   270                     /* BTH_TOUCH event value 1 indicates there is contact with
   271                        a touchscreen or trackpad (earlist finger's current
   272                        position is sent in EV_ABS ABS_X/ABS_Y, switching to
   273                        next finger after earlist is released) */
   274                     if (item->is_touchscreen && events[i].code == BTN_TOUCH) {
   275                         break;
   276                     }
   277 
   278                     /* Probably keyboard */
   279                     scan_code = SDL_EVDEV_translate_keycode(events[i].code);
   280                     if (scan_code != SDL_SCANCODE_UNKNOWN) {
   281                         if (events[i].value == 0) {
   282                             SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
   283                         } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
   284                             SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
   285                         }
   286                     }
   287                     SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
   288                     break;
   289                 case EV_ABS:
   290                     switch(events[i].code) {
   291                     case ABS_MT_SLOT:
   292                         if (!item->is_touchscreen) /* FIXME: temp hack */
   293                             break;
   294                         item->touchscreen_data->current_slot = events[i].value;
   295                         break;
   296                     case ABS_MT_TRACKING_ID:
   297                         if (!item->is_touchscreen) /* FIXME: temp hack */
   298                             break;
   299                         if (events[i].value >= 0) {
   300                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
   301                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
   302                         } else {
   303                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
   304                         }
   305                         break;
   306                     case ABS_MT_POSITION_X:
   307                         if (!item->is_touchscreen) /* FIXME: temp hack */
   308                             break;
   309                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
   310                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
   311                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
   312                         }
   313                         break;
   314                     case ABS_MT_POSITION_Y:
   315                         if (!item->is_touchscreen) /* FIXME: temp hack */
   316                             break;
   317                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
   318                         if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
   319                             item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
   320                         }
   321                         break;
   322                     case ABS_MT_PRESSURE:
   323                         if (!item->is_touchscreen) /* FIXME: temp hack */
   324                             break;
   325                         item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = 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_X:
   331                         if (item->is_touchscreen) /* FIXME: temp hack */
   332                             break;
   333                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
   334                         break;
   335                     case ABS_Y:
   336                         if (item->is_touchscreen) /* FIXME: temp hack */
   337                             break;
   338                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
   339                         break;
   340                     default:
   341                         break;
   342                     }
   343                     break;
   344                 case EV_REL:
   345                     switch(events[i].code) {
   346                     case REL_X:
   347                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
   348                         break;
   349                     case REL_Y:
   350                         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
   351                         break;
   352                     case REL_WHEEL:
   353                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
   354                         break;
   355                     case REL_HWHEEL:
   356                         SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
   357                         break;
   358                     default:
   359                         break;
   360                     }
   361                     break;
   362                 case EV_SYN:
   363                     switch (events[i].code) {
   364                     case SYN_REPORT:
   365                         if (!item->is_touchscreen) /* FIXME: temp hack */
   366                             break;
   367 
   368                         for(j = 0; j < item->touchscreen_data->max_slots; j++) {
   369                             norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
   370                                 (float)item->touchscreen_data->range_x;
   371                             norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
   372                                 (float)item->touchscreen_data->range_y;
   373 
   374                             if (item->touchscreen_data->range_pressure > 0) {
   375                                 norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
   376                                     (float)item->touchscreen_data->range_pressure;
   377                             } else {
   378                                 /* This touchscreen does not support pressure */
   379                                 norm_pressure = 1.0f;
   380                             }
   381 
   382                             switch(item->touchscreen_data->slots[j].delta) {
   383                             case EVDEV_TOUCH_SLOTDELTA_DOWN:
   384                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, norm_pressure);
   385                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   386                                 break;
   387                             case EVDEV_TOUCH_SLOTDELTA_UP:
   388                                 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, norm_pressure);
   389                                 item->touchscreen_data->slots[j].tracking_id = -1;
   390                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   391                                 break;
   392                             case EVDEV_TOUCH_SLOTDELTA_MOVE:
   393                                 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, norm_pressure);
   394                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   395                                 break;
   396                             default:
   397                                 break;
   398                             }
   399                         }
   400 
   401                         if (item->out_of_sync)
   402                             item->out_of_sync = 0;
   403                         break;
   404                     case SYN_DROPPED:
   405                         if (item->is_touchscreen)
   406                             item->out_of_sync = 1;
   407                         SDL_EVDEV_sync_device(item);
   408                         break;
   409                     default:
   410                         break;
   411                     }
   412                     break;
   413                 }
   414             }
   415         }    
   416     }
   417 }
   418 
   419 static SDL_Scancode
   420 SDL_EVDEV_translate_keycode(int keycode)
   421 {
   422     SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
   423 
   424     if (keycode < SDL_arraysize(linux_scancode_table))
   425         scancode = linux_scancode_table[keycode];
   426 
   427     if (scancode == SDL_SCANCODE_UNKNOWN) {
   428         /* BTN_TOUCH is handled elsewhere, but we might still end up here if
   429            you get an unexpected BTN_TOUCH from something SDL believes is not
   430            a touch device. In this case, we'd rather not get a misleading
   431            SDL_Log message about an unknown key. */
   432         if (keycode != BTN_TOUCH) {
   433             SDL_Log("The key you just pressed is not recognized by SDL. To help "
   434                 "get this fixed, please report this to the SDL forums/mailing list "
   435                 "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
   436         }
   437     }
   438 
   439     return scancode;
   440 }
   441 
   442 #ifdef SDL_USE_LIBUDEV
   443 static int
   444 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
   445 {
   446     int ret, i;
   447     char name[64];
   448     struct input_absinfo abs_info;
   449 
   450     if (!item->is_touchscreen)
   451         return 0;
   452 
   453     item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
   454     if (item->touchscreen_data == NULL)
   455         return SDL_OutOfMemory();
   456 
   457     ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
   458     if (ret < 0) {
   459         SDL_free(item->touchscreen_data);
   460         return SDL_SetError("Failed to get evdev touchscreen name");
   461     }
   462 
   463     item->touchscreen_data->name = SDL_strdup(name);
   464     if (item->touchscreen_data->name == NULL) {
   465         SDL_free(item->touchscreen_data);
   466         return SDL_OutOfMemory();
   467     }
   468 
   469     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_X), &abs_info);
   470     if (ret < 0) {
   471         SDL_free(item->touchscreen_data->name);
   472         SDL_free(item->touchscreen_data);
   473         return SDL_SetError("Failed to get evdev touchscreen limits");
   474     }
   475     item->touchscreen_data->min_x = abs_info.minimum;
   476     item->touchscreen_data->max_x = abs_info.maximum;
   477     item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
   478 
   479     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_Y), &abs_info);
   480     if (ret < 0) {
   481         SDL_free(item->touchscreen_data->name);
   482         SDL_free(item->touchscreen_data);
   483         return SDL_SetError("Failed to get evdev touchscreen limits");
   484     }
   485     item->touchscreen_data->min_y = abs_info.minimum;
   486     item->touchscreen_data->max_y = abs_info.maximum;
   487     item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
   488 
   489     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
   490     if (ret < 0) {
   491         SDL_free(item->touchscreen_data->name);
   492         SDL_free(item->touchscreen_data);
   493         return SDL_SetError("Failed to get evdev touchscreen limits");
   494     }
   495     item->touchscreen_data->min_pressure = abs_info.minimum;
   496     item->touchscreen_data->max_pressure = abs_info.maximum;
   497     item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
   498 
   499     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   500     if (ret < 0) {
   501         SDL_free(item->touchscreen_data->name);
   502         SDL_free(item->touchscreen_data);
   503         return SDL_SetError("Failed to get evdev touchscreen limits");
   504     }
   505     item->touchscreen_data->max_slots = abs_info.maximum + 1;
   506 
   507     item->touchscreen_data->slots = SDL_calloc(
   508         item->touchscreen_data->max_slots,
   509         sizeof(*item->touchscreen_data->slots));
   510     if (item->touchscreen_data->slots == NULL) {
   511         SDL_free(item->touchscreen_data->name);
   512         SDL_free(item->touchscreen_data);
   513         return SDL_OutOfMemory();
   514     }
   515 
   516     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   517         item->touchscreen_data->slots[i].tracking_id = -1;
   518     }
   519 
   520     ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
   521         SDL_TOUCH_DEVICE_DIRECT,
   522         item->touchscreen_data->name);
   523     if (ret < 0) {
   524         SDL_free(item->touchscreen_data->slots);
   525         SDL_free(item->touchscreen_data->name);
   526         SDL_free(item->touchscreen_data);
   527         return ret;
   528     }
   529 
   530     return 0;
   531 }
   532 #endif /* SDL_USE_LIBUDEV */
   533 
   534 static void
   535 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
   536     if (!item->is_touchscreen)
   537         return;
   538 
   539     SDL_DelTouch(item->fd);
   540     SDL_free(item->touchscreen_data->slots);
   541     SDL_free(item->touchscreen_data->name);
   542     SDL_free(item->touchscreen_data);
   543 }
   544 
   545 static void
   546 SDL_EVDEV_sync_device(SDL_evdevlist_item *item) 
   547 {
   548 #ifdef EVIOCGMTSLOTS
   549     int i, ret;
   550     struct input_absinfo abs_info;
   551     /*
   552      * struct input_mt_request_layout {
   553      *     __u32 code;
   554      *     __s32 values[num_slots];
   555      * };
   556      *
   557      * this is the structure we're trying to emulate
   558      */
   559     __u32* mt_req_code;
   560     __s32* mt_req_values;
   561     size_t mt_req_size;
   562 
   563     /* TODO: sync devices other than touchscreen */
   564     if (!item->is_touchscreen)
   565         return;
   566 
   567     mt_req_size = sizeof(*mt_req_code) +
   568         sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
   569 
   570     mt_req_code = SDL_calloc(1, mt_req_size);
   571     if (mt_req_code == NULL) {
   572         return;
   573     }
   574 
   575     mt_req_values = (__s32*)mt_req_code + 1;
   576 
   577     *mt_req_code = ABS_MT_TRACKING_ID;
   578     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   579     if (ret < 0) {
   580         SDL_free(mt_req_code);
   581         return;
   582     }
   583     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   584         /*
   585          * This doesn't account for the very edge case of the user removing their
   586          * finger and replacing it on the screen during the time we're out of sync,
   587          * which'll mean that we're not going from down -> up or up -> down, we're
   588          * going from down -> down but with a different tracking id, meaning we'd
   589          * have to tell SDL of the two events, but since we wait till SYN_REPORT in
   590          * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
   591          * allow it. Lets just pray to God it doesn't happen.
   592          */
   593         if (item->touchscreen_data->slots[i].tracking_id < 0 &&
   594             mt_req_values[i] >= 0) {
   595             item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
   596             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
   597         } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   598             mt_req_values[i] < 0) {
   599             item->touchscreen_data->slots[i].tracking_id = -1;
   600             item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
   601         }
   602     }
   603 
   604     *mt_req_code = ABS_MT_POSITION_X;
   605     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   606     if (ret < 0) {
   607         SDL_free(mt_req_code);
   608         return;
   609     }
   610     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   611         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   612             item->touchscreen_data->slots[i].x != mt_req_values[i]) {
   613             item->touchscreen_data->slots[i].x = mt_req_values[i];
   614             if (item->touchscreen_data->slots[i].delta ==
   615                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   616                 item->touchscreen_data->slots[i].delta =
   617                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   618             }
   619         }
   620     }
   621 
   622     *mt_req_code = ABS_MT_POSITION_Y;
   623     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   624     if (ret < 0) {
   625         SDL_free(mt_req_code);
   626         return;
   627     }
   628     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   629         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   630             item->touchscreen_data->slots[i].y != mt_req_values[i]) {
   631             item->touchscreen_data->slots[i].y = mt_req_values[i];
   632             if (item->touchscreen_data->slots[i].delta ==
   633                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   634                 item->touchscreen_data->slots[i].delta =
   635                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   636             }
   637         }
   638     }
   639 
   640     *mt_req_code = ABS_MT_PRESSURE;
   641     ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   642     if (ret < 0) {
   643         SDL_free(mt_req_code);
   644         return;
   645     }
   646     for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   647         if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   648             item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
   649             item->touchscreen_data->slots[i].pressure = mt_req_values[i];
   650             if (item->touchscreen_data->slots[i].delta ==
   651                 EVDEV_TOUCH_SLOTDELTA_NONE) {
   652                 item->touchscreen_data->slots[i].delta =
   653                     EVDEV_TOUCH_SLOTDELTA_MOVE;
   654             }
   655         }
   656     }
   657 
   658     ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   659     if (ret < 0) {
   660         SDL_free(mt_req_code);
   661         return;
   662     }
   663     item->touchscreen_data->current_slot = abs_info.value;
   664 
   665     SDL_free(mt_req_code);
   666 
   667 #endif /* EVIOCGMTSLOTS */
   668 }
   669 
   670 #if SDL_USE_LIBUDEV
   671 static int
   672 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
   673 {
   674     int ret;
   675     SDL_evdevlist_item *item;
   676 
   677     /* Check to make sure it's not already in list. */
   678     for (item = _this->first; item != NULL; item = item->next) {
   679         if (SDL_strcmp(dev_path, item->path) == 0) {
   680             return -1;  /* already have this one */
   681         }
   682     }
   683 
   684     item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
   685     if (item == NULL) {
   686         return SDL_OutOfMemory();
   687     }
   688 
   689     item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
   690     if (item->fd < 0) {
   691         SDL_free(item);
   692         return SDL_SetError("Unable to open %s", dev_path);
   693     }
   694 
   695     item->path = SDL_strdup(dev_path);
   696     if (item->path == NULL) {
   697         close(item->fd);
   698         SDL_free(item);
   699         return SDL_OutOfMemory();
   700     }
   701 
   702     if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
   703         item->is_touchscreen = 1;
   704 
   705         if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
   706             close(item->fd);
   707             SDL_free(item);
   708             return ret;
   709         }
   710     }
   711 
   712     if (_this->last == NULL) {
   713         _this->first = _this->last = item;
   714     } else {
   715         _this->last->next = item;
   716         _this->last = item;
   717     }
   718 
   719     SDL_EVDEV_sync_device(item);
   720 
   721     return _this->num_devices++;
   722 }
   723 #endif /* SDL_USE_LIBUDEV */
   724 
   725 static int
   726 SDL_EVDEV_device_removed(const char *dev_path)
   727 {
   728     SDL_evdevlist_item *item;
   729     SDL_evdevlist_item *prev = NULL;
   730 
   731     for (item = _this->first; item != NULL; item = item->next) {
   732         /* found it, remove it. */
   733         if (SDL_strcmp(dev_path, item->path) == 0) {
   734             if (prev != NULL) {
   735                 prev->next = item->next;
   736             } else {
   737                 SDL_assert(_this->first == item);
   738                 _this->first = item->next;
   739             }
   740             if (item == _this->last) {
   741                 _this->last = prev;
   742             }
   743             if (item->is_touchscreen) {
   744                 SDL_EVDEV_destroy_touchscreen(item);
   745             }
   746             close(item->fd);
   747             SDL_free(item->path);
   748             SDL_free(item);
   749             _this->num_devices--;
   750             return 0;
   751         }
   752         prev = item;
   753     }
   754 
   755     return -1;
   756 }
   757 
   758 
   759 #endif /* SDL_INPUT_LINUXEV */
   760 
   761 /* vi: set ts=4 sw=4 expandtab: */