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