src/core/linux/SDL_udev.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jan 2018 10:03:25 -0800
changeset 11811 5d94cb6b24d3
parent 11599 0f2de8f6c959
child 12203 b527a6a49bfd
permissions -rw-r--r--
Updated copyright for 2018
gabomdq@7753
     1
/*
gabomdq@7753
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 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
*/
gabomdq@7753
    21
gabomdq@7753
    22
/* 
gabomdq@7753
    23
 * To list the properties of a device, try something like:
gabomdq@7753
    24
 * udevadm info -a -n snd/hwC0D0 (for a sound card)
gabomdq@7753
    25
 * udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
gabomdq@7753
    26
 * udevadm info --query=property -n input/event2
gabomdq@7753
    27
 */
gabomdq@7753
    28
#include "SDL_udev.h"
gabomdq@7753
    29
gabomdq@7753
    30
#ifdef SDL_USE_LIBUDEV
gabomdq@7753
    31
slouken@9160
    32
#include <linux/input.h>
slouken@9160
    33
slouken@11296
    34
#include "SDL_assert.h"
slouken@11296
    35
#include "SDL_loadso.h"
slouken@11296
    36
#include "SDL_timer.h"
slouken@11296
    37
#include "../unix/SDL_poll.h"
slouken@9160
    38
flibitijibibo@11599
    39
static const char *SDL_UDEV_LIBS[] = { "libudev.so.1", "libudev.so.0" };
gabomdq@7753
    40
gabomdq@7753
    41
#define _THIS SDL_UDEV_PrivateData *_this
gabomdq@7753
    42
static _THIS = NULL;
gabomdq@7753
    43
gabomdq@7753
    44
static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr);
gabomdq@7753
    45
static int SDL_UDEV_load_syms(void);
gabomdq@7753
    46
static SDL_bool SDL_UDEV_hotplug_update_available(void);
gabomdq@7753
    47
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
gabomdq@7753
    48
gabomdq@7753
    49
static SDL_bool
gabomdq@7753
    50
SDL_UDEV_load_sym(const char *fn, void **addr)
gabomdq@7753
    51
{
gabomdq@7753
    52
    *addr = SDL_LoadFunction(_this->udev_handle, fn);
gabomdq@7753
    53
    if (*addr == NULL) {
gabomdq@7753
    54
        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
gabomdq@7753
    55
        return SDL_FALSE;
gabomdq@7753
    56
    }
gabomdq@7753
    57
gabomdq@7753
    58
    return SDL_TRUE;
gabomdq@7753
    59
}
gabomdq@7753
    60
gabomdq@7753
    61
static int
gabomdq@7753
    62
SDL_UDEV_load_syms(void)
gabomdq@7753
    63
{
gabomdq@7753
    64
    /* cast funcs to char* first, to please GCC's strict aliasing rules. */
gabomdq@7753
    65
    #define SDL_UDEV_SYM(x) \
gabomdq@7753
    66
        if (!SDL_UDEV_load_sym(#x, (void **) (char *) & _this->x)) return -1
gabomdq@7753
    67
gabomdq@7753
    68
    SDL_UDEV_SYM(udev_device_get_action);
gabomdq@7753
    69
    SDL_UDEV_SYM(udev_device_get_devnode);
gabomdq@7753
    70
    SDL_UDEV_SYM(udev_device_get_subsystem);
slouken@9160
    71
    SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype);
gabomdq@7753
    72
    SDL_UDEV_SYM(udev_device_get_property_value);
slouken@9160
    73
    SDL_UDEV_SYM(udev_device_get_sysattr_value);
gabomdq@7753
    74
    SDL_UDEV_SYM(udev_device_new_from_syspath);
gabomdq@7753
    75
    SDL_UDEV_SYM(udev_device_unref);
gabomdq@7753
    76
    SDL_UDEV_SYM(udev_enumerate_add_match_property);
gabomdq@7753
    77
    SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
gabomdq@7753
    78
    SDL_UDEV_SYM(udev_enumerate_get_list_entry);
gabomdq@7753
    79
    SDL_UDEV_SYM(udev_enumerate_new);
gabomdq@7753
    80
    SDL_UDEV_SYM(udev_enumerate_scan_devices);
gabomdq@7753
    81
    SDL_UDEV_SYM(udev_enumerate_unref);
gabomdq@7753
    82
    SDL_UDEV_SYM(udev_list_entry_get_name);
gabomdq@7753
    83
    SDL_UDEV_SYM(udev_list_entry_get_next);
gabomdq@7753
    84
    SDL_UDEV_SYM(udev_monitor_enable_receiving);
gabomdq@7753
    85
    SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
gabomdq@7753
    86
    SDL_UDEV_SYM(udev_monitor_get_fd);
gabomdq@7753
    87
    SDL_UDEV_SYM(udev_monitor_new_from_netlink);
gabomdq@7753
    88
    SDL_UDEV_SYM(udev_monitor_receive_device);
gabomdq@7753
    89
    SDL_UDEV_SYM(udev_monitor_unref);
gabomdq@7753
    90
    SDL_UDEV_SYM(udev_new);
gabomdq@7753
    91
    SDL_UDEV_SYM(udev_unref);
gabomdq@7753
    92
    SDL_UDEV_SYM(udev_device_new_from_devnum);
gabomdq@7753
    93
    SDL_UDEV_SYM(udev_device_get_devnum);
gabomdq@7753
    94
    #undef SDL_UDEV_SYM
gabomdq@7753
    95
gabomdq@7753
    96
    return 0;
gabomdq@7753
    97
}
gabomdq@7753
    98
gabomdq@7753
    99
static SDL_bool
gabomdq@7753
   100
SDL_UDEV_hotplug_update_available(void)
gabomdq@7753
   101
{
gabomdq@7753
   102
    if (_this->udev_mon != NULL) {
gabomdq@7753
   103
        const int fd = _this->udev_monitor_get_fd(_this->udev_mon);
slouken@11296
   104
        if (SDL_IOReady(fd, SDL_FALSE, 0)) {
gabomdq@7753
   105
            return SDL_TRUE;
gabomdq@7753
   106
        }
gabomdq@7753
   107
    }
gabomdq@7753
   108
    return SDL_FALSE;
gabomdq@7753
   109
}
gabomdq@7753
   110
gabomdq@7753
   111
gabomdq@7753
   112
int
gabomdq@7753
   113
SDL_UDEV_Init(void)
gabomdq@7753
   114
{
gabomdq@7753
   115
    int retval = 0;
gabomdq@7753
   116
    
gabomdq@7753
   117
    if (_this == NULL) {
gabomdq@7753
   118
        _this = (SDL_UDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
gabomdq@7753
   119
        if(_this == NULL) {
gabomdq@7753
   120
            return SDL_OutOfMemory();
gabomdq@7753
   121
        }
gabomdq@7753
   122
        
gabomdq@7753
   123
        retval = SDL_UDEV_LoadLibrary();
gabomdq@7753
   124
        if (retval < 0) {
gabomdq@7753
   125
            SDL_UDEV_Quit();
gabomdq@7753
   126
            return retval;
gabomdq@7753
   127
        }
gabomdq@7753
   128
        
gabomdq@7753
   129
        /* Set up udev monitoring 
gabomdq@7753
   130
         * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
gabomdq@7753
   131
         */
gabomdq@7753
   132
        
gabomdq@7753
   133
        _this->udev = _this->udev_new();
gabomdq@7753
   134
        if (_this->udev == NULL) {
gabomdq@7753
   135
            SDL_UDEV_Quit();
gabomdq@7753
   136
            return SDL_SetError("udev_new() failed");
gabomdq@7753
   137
        }
gabomdq@7753
   138
gabomdq@7753
   139
        _this->udev_mon = _this->udev_monitor_new_from_netlink(_this->udev, "udev");
gabomdq@7753
   140
        if (_this->udev_mon == NULL) {
gabomdq@7753
   141
            SDL_UDEV_Quit();
gabomdq@7753
   142
            return SDL_SetError("udev_monitor_new_from_netlink() failed");
gabomdq@7753
   143
        }
gabomdq@7753
   144
        
gabomdq@7753
   145
        _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
gabomdq@7753
   146
        _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
gabomdq@7753
   147
        _this->udev_monitor_enable_receiving(_this->udev_mon);
gabomdq@7753
   148
        
gabomdq@7753
   149
        /* Do an initial scan of existing devices */
gabomdq@7753
   150
        SDL_UDEV_Scan();
gabomdq@7753
   151
gabomdq@7753
   152
    }
gabomdq@7753
   153
    
gabomdq@7753
   154
    _this->ref_count += 1;
gabomdq@7753
   155
    
gabomdq@7753
   156
    return retval;
gabomdq@7753
   157
}
gabomdq@7753
   158
gabomdq@7753
   159
void
gabomdq@7753
   160
SDL_UDEV_Quit(void)
gabomdq@7753
   161
{
gabomdq@7753
   162
    SDL_UDEV_CallbackList *item;
gabomdq@7753
   163
    
gabomdq@7753
   164
    if (_this == NULL) {
gabomdq@7753
   165
        return;
gabomdq@7753
   166
    }
gabomdq@7753
   167
    
gabomdq@7753
   168
    _this->ref_count -= 1;
gabomdq@7753
   169
    
gabomdq@7753
   170
    if (_this->ref_count < 1) {
gabomdq@7753
   171
        
gabomdq@7753
   172
        if (_this->udev_mon != NULL) {
gabomdq@7753
   173
            _this->udev_monitor_unref(_this->udev_mon);
gabomdq@7753
   174
            _this->udev_mon = NULL;
gabomdq@7753
   175
        }
gabomdq@7753
   176
        if (_this->udev != NULL) {
gabomdq@7753
   177
            _this->udev_unref(_this->udev);
gabomdq@7753
   178
            _this->udev = NULL;
gabomdq@7753
   179
        }
gabomdq@7753
   180
        
gabomdq@7753
   181
        /* Remove existing devices */
gabomdq@7753
   182
        while (_this->first != NULL) {
gabomdq@7753
   183
            item = _this->first;
gabomdq@7753
   184
            _this->first = _this->first->next;
gabomdq@7753
   185
            SDL_free(item);
gabomdq@7753
   186
        }
gabomdq@7753
   187
        
gabomdq@7753
   188
        SDL_UDEV_UnloadLibrary();
gabomdq@7753
   189
        SDL_free(_this);
gabomdq@7753
   190
        _this = NULL;
gabomdq@7753
   191
    }
gabomdq@7753
   192
}
gabomdq@7753
   193
gabomdq@7753
   194
void
gabomdq@7753
   195
SDL_UDEV_Scan(void)
gabomdq@7753
   196
{
gabomdq@7753
   197
    struct udev_enumerate *enumerate = NULL;
gabomdq@7753
   198
    struct udev_list_entry *devs = NULL;
gabomdq@7753
   199
    struct udev_list_entry *item = NULL;  
gabomdq@7753
   200
    
gabomdq@7753
   201
    if (_this == NULL) {
gabomdq@7753
   202
        return;
gabomdq@7753
   203
    }
gabomdq@7753
   204
   
gabomdq@7753
   205
    enumerate = _this->udev_enumerate_new(_this->udev);
gabomdq@7753
   206
    if (enumerate == NULL) {
gabomdq@7753
   207
        SDL_UDEV_Quit();
philipp@10928
   208
        SDL_SetError("udev_enumerate_new() failed");
gabomdq@7753
   209
        return;
gabomdq@7753
   210
    }
gabomdq@7753
   211
    
gabomdq@7753
   212
    _this->udev_enumerate_add_match_subsystem(enumerate, "input");
gabomdq@7753
   213
    _this->udev_enumerate_add_match_subsystem(enumerate, "sound");
gabomdq@7753
   214
    
gabomdq@7753
   215
    _this->udev_enumerate_scan_devices(enumerate);
gabomdq@7753
   216
    devs = _this->udev_enumerate_get_list_entry(enumerate);
gabomdq@7753
   217
    for (item = devs; item; item = _this->udev_list_entry_get_next(item)) {
gabomdq@7753
   218
        const char *path = _this->udev_list_entry_get_name(item);
gabomdq@7753
   219
        struct udev_device *dev = _this->udev_device_new_from_syspath(_this->udev, path);
gabomdq@7753
   220
        if (dev != NULL) {
gabomdq@7753
   221
            device_event(SDL_UDEV_DEVICEADDED, dev);
gabomdq@7753
   222
            _this->udev_device_unref(dev);
gabomdq@7753
   223
        }
gabomdq@7753
   224
    }
gabomdq@7753
   225
gabomdq@7753
   226
    _this->udev_enumerate_unref(enumerate);
gabomdq@7753
   227
}
gabomdq@7753
   228
gabomdq@7753
   229
gabomdq@7753
   230
void
gabomdq@7753
   231
SDL_UDEV_UnloadLibrary(void)
gabomdq@7753
   232
{
gabomdq@7753
   233
    if (_this == NULL) {
gabomdq@7753
   234
        return;
gabomdq@7753
   235
    }
gabomdq@7753
   236
    
gabomdq@7753
   237
    if (_this->udev_handle != NULL) {
gabomdq@7753
   238
        SDL_UnloadObject(_this->udev_handle);
gabomdq@7753
   239
        _this->udev_handle = NULL;
gabomdq@7753
   240
    }
gabomdq@7753
   241
}
gabomdq@7753
   242
gabomdq@7753
   243
int
gabomdq@7753
   244
SDL_UDEV_LoadLibrary(void)
gabomdq@7753
   245
{
gabomdq@7753
   246
    int retval = 0, i;
gabomdq@7753
   247
    
gabomdq@7753
   248
    if (_this == NULL) {
gabomdq@7753
   249
        return SDL_SetError("UDEV not initialized");
gabomdq@7753
   250
    }
slouken@10660
   251
 
slouken@10660
   252
    /* See if there is a udev library already loaded */
slouken@10660
   253
    if (SDL_UDEV_load_syms() == 0) {
slouken@10660
   254
        return 0;
slouken@10660
   255
    }
slouken@10660
   256
flibitijibibo@11599
   257
#ifdef SDL_UDEV_DYNAMIC
flibitijibibo@11599
   258
    /* Check for the build environment's libudev first */
flibitijibibo@11599
   259
    if (_this->udev_handle == NULL) {
flibitijibibo@11599
   260
        _this->udev_handle = SDL_LoadObject(SDL_UDEV_DYNAMIC);
flibitijibibo@11599
   261
        if (_this->udev_handle != NULL) {
flibitijibibo@11599
   262
            retval = SDL_UDEV_load_syms();
flibitijibibo@11599
   263
            if (retval < 0) {
flibitijibibo@11599
   264
                SDL_UDEV_UnloadLibrary();
flibitijibibo@11599
   265
            }
flibitijibibo@11599
   266
        }
flibitijibibo@11599
   267
    }
flibitijibibo@11599
   268
#endif
flibitijibibo@11599
   269
gabomdq@7753
   270
    if (_this->udev_handle == NULL) {
gabomdq@7753
   271
        for( i = 0 ; i < SDL_arraysize(SDL_UDEV_LIBS); i++) {
gabomdq@7753
   272
            _this->udev_handle = SDL_LoadObject(SDL_UDEV_LIBS[i]);
gabomdq@7753
   273
            if (_this->udev_handle != NULL) {
gabomdq@7753
   274
                retval = SDL_UDEV_load_syms();
gabomdq@7753
   275
                if (retval < 0) {
gabomdq@7753
   276
                    SDL_UDEV_UnloadLibrary();
gabomdq@7753
   277
                }
gabomdq@7753
   278
                else {
gabomdq@7753
   279
                    break;
gabomdq@7753
   280
                }
gabomdq@7753
   281
            }
gabomdq@7753
   282
        }
gabomdq@7753
   283
        
gabomdq@7753
   284
        if (_this->udev_handle == NULL) {
gabomdq@7753
   285
            retval = -1;
gabomdq@7753
   286
            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
gabomdq@7753
   287
        }
gabomdq@7753
   288
    }
gabomdq@7753
   289
gabomdq@7753
   290
    return retval;
gabomdq@7753
   291
}
gabomdq@7753
   292
slouken@9160
   293
#define BITS_PER_LONG           (sizeof(unsigned long) * 8)
slouken@9160
   294
#define NBITS(x)                ((((x)-1)/BITS_PER_LONG)+1)
slouken@9160
   295
#define OFF(x)                  ((x)%BITS_PER_LONG)
slouken@9160
   296
#define LONG(x)                 ((x)/BITS_PER_LONG)
slouken@9160
   297
#define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
slouken@9160
   298
slouken@9160
   299
static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
slouken@9160
   300
{
slouken@9160
   301
    const char *value;
slouken@9160
   302
    char text[4096];
slouken@9160
   303
    char *word;
slouken@9160
   304
    int i;
slouken@9160
   305
    unsigned long v;
slouken@9160
   306
slouken@9160
   307
    SDL_memset(bitmask, 0, bitmask_len*sizeof(*bitmask));
slouken@9160
   308
    value = _this->udev_device_get_sysattr_value(pdev, attr);
slouken@9160
   309
    if (!value) {
slouken@9160
   310
        return;
slouken@9160
   311
    }
slouken@9160
   312
slouken@9160
   313
    SDL_strlcpy(text, value, sizeof(text));
slouken@9160
   314
    i = 0;
slouken@9160
   315
    while ((word = SDL_strrchr(text, ' ')) != NULL) {
slouken@9160
   316
        v = SDL_strtoul(word+1, NULL, 16);
slouken@9160
   317
        if (i < bitmask_len) {
slouken@9160
   318
            bitmask[i] = v;
slouken@9160
   319
        }
slouken@9160
   320
        ++i;
slouken@9160
   321
        *word = '\0';
slouken@9160
   322
    }
slouken@9160
   323
    v = SDL_strtoul(text, NULL, 16);
slouken@9160
   324
    if (i < bitmask_len) {
slouken@9160
   325
        bitmask[i] = v;
slouken@9160
   326
    }
slouken@9160
   327
}
slouken@9160
   328
slouken@9160
   329
static int
slouken@9160
   330
guess_device_class(struct udev_device *dev)
slouken@9160
   331
{
slouken@9160
   332
    int devclass = 0;
slouken@9160
   333
    struct udev_device *pdev;
slouken@9160
   334
    unsigned long bitmask_ev[NBITS(EV_MAX)];
slouken@9160
   335
    unsigned long bitmask_abs[NBITS(ABS_MAX)];
slouken@9160
   336
    unsigned long bitmask_key[NBITS(KEY_MAX)];
slouken@9160
   337
    unsigned long bitmask_rel[NBITS(REL_MAX)];
slouken@9160
   338
    unsigned long keyboard_mask;
slouken@9160
   339
slouken@9160
   340
    /* walk up the parental chain until we find the real input device; the
slouken@9160
   341
     * argument is very likely a subdevice of this, like eventN */
slouken@9160
   342
    pdev = dev;
slouken@9160
   343
    while (pdev && !_this->udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
slouken@9160
   344
        pdev = _this->udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
slouken@9160
   345
    }
slouken@9160
   346
    if (!pdev) {
slouken@9160
   347
        return 0;
slouken@9160
   348
    }
slouken@9160
   349
slouken@9160
   350
    get_caps(dev, pdev, "capabilities/ev", bitmask_ev, SDL_arraysize(bitmask_ev));
slouken@9160
   351
    get_caps(dev, pdev, "capabilities/abs", bitmask_abs, SDL_arraysize(bitmask_abs));
slouken@9160
   352
    get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
slouken@9160
   353
    get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
slouken@9160
   354
slouken@9160
   355
    if (test_bit(EV_ABS, bitmask_ev) &&
slouken@9160
   356
        test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
slouken@9160
   357
        if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
slouken@9160
   358
            ; /* ID_INPUT_TABLET */
slouken@9160
   359
        } else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
slouken@9160
   360
            ; /* ID_INPUT_TOUCHPAD */
slouken@9160
   361
        } else if (test_bit(BTN_MOUSE, bitmask_key)) {
slouken@9160
   362
            devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
slouken@9160
   363
        } else if (test_bit(BTN_TOUCH, bitmask_key)) {
slouken@10433
   364
            /* TODO: better determining between touchscreen and multitouch touchpad,
slouken@10433
   365
               see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
slouken@10433
   366
            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
slouken@9454
   367
        }
slouken@9454
   368
slouken@9454
   369
        if (test_bit(BTN_TRIGGER, bitmask_key) ||
slouken@9454
   370
            test_bit(BTN_A, bitmask_key) ||
slouken@9454
   371
            test_bit(BTN_1, bitmask_key) ||
slouken@9454
   372
            test_bit(ABS_RX, bitmask_abs) ||
slouken@9454
   373
            test_bit(ABS_RY, bitmask_abs) ||
slouken@9454
   374
            test_bit(ABS_RZ, bitmask_abs) ||
slouken@9454
   375
            test_bit(ABS_THROTTLE, bitmask_abs) ||
slouken@9454
   376
            test_bit(ABS_RUDDER, bitmask_abs) ||
slouken@9454
   377
            test_bit(ABS_WHEEL, bitmask_abs) ||
slouken@9454
   378
            test_bit(ABS_GAS, bitmask_abs) ||
slouken@9454
   379
            test_bit(ABS_BRAKE, bitmask_abs)) {
slouken@9160
   380
            devclass |= SDL_UDEV_DEVICE_JOYSTICK; /* ID_INPUT_JOYSTICK */
slouken@9160
   381
        }
slouken@9160
   382
    }
slouken@9160
   383
slouken@9160
   384
    if (test_bit(EV_REL, bitmask_ev) &&
slouken@9160
   385
        test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
slouken@9160
   386
        test_bit(BTN_MOUSE, bitmask_key)) {
slouken@9160
   387
        devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
slouken@9160
   388
    }
slouken@9160
   389
slouken@9276
   390
    /* the first 32 bits are ESC, numbers, and Q to D; if we have any of
slouken@9276
   391
     * those, consider it a keyboard device; do not test KEY_RESERVED, though */
slouken@9160
   392
    keyboard_mask = 0xFFFFFFFE;
slouken@9276
   393
    if ((bitmask_key[0] & keyboard_mask) != 0)
slouken@9160
   394
        devclass |= SDL_UDEV_DEVICE_KEYBOARD; /* ID_INPUT_KEYBOARD */
slouken@9160
   395
slouken@9160
   396
    return devclass;
slouken@9160
   397
}
slouken@9160
   398
gabomdq@7753
   399
static void 
gabomdq@7753
   400
device_event(SDL_UDEV_deviceevent type, struct udev_device *dev) 
gabomdq@7753
   401
{
gabomdq@7753
   402
    const char *subsystem;
gabomdq@7753
   403
    const char *val = NULL;
slouken@7788
   404
    int devclass = 0;
gabomdq@7753
   405
    const char *path;
gabomdq@7753
   406
    SDL_UDEV_CallbackList *item;
gabomdq@7753
   407
    
gabomdq@7753
   408
    path = _this->udev_device_get_devnode(dev);
gabomdq@7753
   409
    if (path == NULL) {
gabomdq@7753
   410
        return;
gabomdq@7753
   411
    }
gabomdq@7753
   412
    
gabomdq@7753
   413
    subsystem = _this->udev_device_get_subsystem(dev);
gabomdq@7753
   414
    if (SDL_strcmp(subsystem, "sound") == 0) {
gabomdq@7753
   415
        devclass = SDL_UDEV_DEVICE_SOUND;
slouken@7788
   416
    } else if (SDL_strcmp(subsystem, "input") == 0) {
gabomdq@9168
   417
        /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
gabomdq@9168
   418
        
gabomdq@7753
   419
        val = _this->udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
gabomdq@7753
   420
        if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
slouken@7788
   421
            devclass |= SDL_UDEV_DEVICE_JOYSTICK;
gabomdq@7753
   422
        }
gabomdq@7753
   423
        
slouken@7788
   424
        val = _this->udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
slouken@7788
   425
        if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
slouken@7788
   426
            devclass |= SDL_UDEV_DEVICE_MOUSE;
gabomdq@7753
   427
        }
slouken@10433
   428
        
slouken@10433
   429
        val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
slouken@10433
   430
        if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
slouken@10433
   431
            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
slouken@10433
   432
        }
slouken@7788
   433
gabomdq@9168
   434
        /* The undocumented rule is:
gabomdq@9168
   435
           - All devices with keys get ID_INPUT_KEY
gabomdq@9168
   436
           - From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
gabomdq@9168
   437
           
gabomdq@9168
   438
           Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
gabomdq@9168
   439
        */
gabomdq@9168
   440
        val = _this->udev_device_get_property_value(dev, "ID_INPUT_KEY");
slouken@7788
   441
        if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
slouken@7788
   442
            devclass |= SDL_UDEV_DEVICE_KEYBOARD;
gabomdq@7753
   443
        }
slouken@7788
   444
gabomdq@7753
   445
        if (devclass == 0) {
slouken@9160
   446
            /* Fall back to old style input classes */
slouken@8709
   447
            val = _this->udev_device_get_property_value(dev, "ID_CLASS");
slouken@8709
   448
            if (val != NULL) {
slouken@8709
   449
                if (SDL_strcmp(val, "joystick") == 0) {
slouken@8709
   450
                    devclass = SDL_UDEV_DEVICE_JOYSTICK;
slouken@8709
   451
                } else if (SDL_strcmp(val, "mouse") == 0) {
slouken@8709
   452
                    devclass = SDL_UDEV_DEVICE_MOUSE;
slouken@8709
   453
                } else if (SDL_strcmp(val, "kbd") == 0) {
slouken@8709
   454
                    devclass = SDL_UDEV_DEVICE_KEYBOARD;
slouken@8709
   455
                } else {
slouken@8709
   456
                    return;
slouken@8709
   457
                }
slouken@8709
   458
            } else {
slouken@9160
   459
                /* We could be linked with libudev on a system that doesn't have udev running */
slouken@9160
   460
                devclass = guess_device_class(dev);
slouken@8709
   461
            }
gabomdq@7753
   462
        }
slouken@7788
   463
    } else {
gabomdq@7753
   464
        return;
gabomdq@7753
   465
    }
gabomdq@7753
   466
    
gabomdq@7753
   467
    /* Process callbacks */
gabomdq@7753
   468
    for (item = _this->first; item != NULL; item = item->next) {
gabomdq@7753
   469
        item->callback(type, devclass, path);
gabomdq@7753
   470
    }
gabomdq@7753
   471
}
gabomdq@7753
   472
gabomdq@7753
   473
void 
gabomdq@7753
   474
SDL_UDEV_Poll(void)
gabomdq@7753
   475
{
gabomdq@7753
   476
    struct udev_device *dev = NULL;
gabomdq@7753
   477
    const char *action = NULL;
gabomdq@7753
   478
gabomdq@7753
   479
    if (_this == NULL) {
gabomdq@7753
   480
        return;
gabomdq@7753
   481
    }
gabomdq@7753
   482
gabomdq@7753
   483
    while (SDL_UDEV_hotplug_update_available()) {
gabomdq@7753
   484
        dev = _this->udev_monitor_receive_device(_this->udev_mon);
gabomdq@7753
   485
        if (dev == NULL) {
gabomdq@7753
   486
            break;
gabomdq@7753
   487
        }
gabomdq@7753
   488
        action = _this->udev_device_get_action(dev);
gabomdq@7753
   489
gabomdq@7753
   490
        if (SDL_strcmp(action, "add") == 0) {
slouken@9616
   491
            /* Wait for the device to finish initialization */
slouken@9616
   492
            SDL_Delay(100);
slouken@9616
   493
gabomdq@7753
   494
            device_event(SDL_UDEV_DEVICEADDED, dev);
gabomdq@7753
   495
        } else if (SDL_strcmp(action, "remove") == 0) {
gabomdq@7753
   496
            device_event(SDL_UDEV_DEVICEREMOVED, dev);
gabomdq@7753
   497
        }
gabomdq@7753
   498
        
gabomdq@7753
   499
        _this->udev_device_unref(dev);
gabomdq@7753
   500
    }
gabomdq@7753
   501
}
gabomdq@7753
   502
gabomdq@7753
   503
int 
gabomdq@7753
   504
SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
gabomdq@7753
   505
{
gabomdq@7753
   506
    SDL_UDEV_CallbackList *item;
gabomdq@7753
   507
    item = (SDL_UDEV_CallbackList *) SDL_calloc(1, sizeof (SDL_UDEV_CallbackList));
gabomdq@7753
   508
    if (item == NULL) {
gabomdq@7753
   509
        return SDL_OutOfMemory();
gabomdq@7753
   510
    }
gabomdq@7753
   511
    
gabomdq@7753
   512
    item->callback = cb;
gabomdq@7753
   513
gabomdq@7753
   514
    if (_this->last == NULL) {
gabomdq@7753
   515
        _this->first = _this->last = item;
gabomdq@7753
   516
    } else {
gabomdq@7753
   517
        _this->last->next = item;
gabomdq@7753
   518
        _this->last = item;
gabomdq@7753
   519
    }
gabomdq@7753
   520
    
gabomdq@7753
   521
    return 1;
gabomdq@7753
   522
}
gabomdq@7753
   523
gabomdq@7753
   524
void 
gabomdq@7753
   525
SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
gabomdq@7753
   526
{
gabomdq@7753
   527
    SDL_UDEV_CallbackList *item;
gabomdq@7753
   528
    SDL_UDEV_CallbackList *prev = NULL;
gabomdq@7753
   529
gabomdq@7753
   530
    for (item = _this->first; item != NULL; item = item->next) {
gabomdq@7753
   531
        /* found it, remove it. */
gabomdq@7753
   532
        if (item->callback == cb) {
gabomdq@7753
   533
            if (prev != NULL) {
gabomdq@7753
   534
                prev->next = item->next;
gabomdq@7753
   535
            } else {
gabomdq@7753
   536
                SDL_assert(_this->first == item);
gabomdq@7753
   537
                _this->first = item->next;
gabomdq@7753
   538
            }
gabomdq@7753
   539
            if (item == _this->last) {
gabomdq@7753
   540
                _this->last = prev;
gabomdq@7753
   541
            }
gabomdq@7753
   542
            SDL_free(item);
gabomdq@7753
   543
            return;
gabomdq@7753
   544
        }
gabomdq@7753
   545
        prev = item;
gabomdq@7753
   546
    }
gabomdq@7753
   547
    
gabomdq@7753
   548
}
gabomdq@7753
   549
gabomdq@7753
   550
slouken@7788
   551
#endif /* SDL_USE_LIBUDEV */
slouken@10660
   552
slouken@10660
   553
/* vi: set ts=4 sw=4 expandtab: */