src/core/linux/SDL_udev.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 18 Aug 2017 20:00:29 -0400
changeset 11323 46861f3fc187
parent 11296 44853f387017
child 11552 2603f41d6a7e
permissions -rw-r--r--
cmake: added a FIXME for later.

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