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