udev: Factor out SDL_EVDEV_GuessDeviceClass

This works on capability bitfields that can either come from udev or
from ioctls, so it is equally applicable to both udev and non-udev
input device detection.

Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie 2020-11-11 19:14:34 -08:00
parent 13e7d1a958
commit 8db3171b98
10 changed files with 153 additions and 69 deletions

View File

@ -1266,6 +1266,7 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT RISCOS)
endif() endif()
# Always compiled for Linux, unconditionally: # Always compiled for Linux, unconditionally:
set(SOURCE_FILES ${SOURCE_FILES} "${SDL2_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.c")
set(SOURCE_FILES ${SOURCE_FILES} "${SDL2_SOURCE_DIR}/src/core/linux/SDL_threadprio.c") set(SOURCE_FILES ${SOURCE_FILES} "${SDL2_SOURCE_DIR}/src/core/linux/SDL_threadprio.c")
# src/core/unix/*.c is included in a generic if(UNIX) section, elsewhere. # src/core/unix/*.c is included in a generic if(UNIX) section, elsewhere.

4
configure vendored
View File

@ -24838,9 +24838,11 @@ $as_echo "#define SDL_TIMER_UNIX 1" >>confdefs.h
fi fi
# Set up files for evdev input # Set up files for evdev input
if test x$use_input_events = xyes; then if test x$use_input_events = xyes; then
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev*.c" SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev.c"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev_kbd.c"
fi fi
# Set up other core UNIX files # Set up other core UNIX files
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev_capabilities.c"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_threadprio.c" SOURCES="$SOURCES $srcdir/src/core/linux/SDL_threadprio.c"
SOURCES="$SOURCES $srcdir/src/core/unix/*.c" SOURCES="$SOURCES $srcdir/src/core/unix/*.c"
;; ;;

View File

@ -3658,9 +3658,11 @@ case "$host" in
fi fi
# Set up files for evdev input # Set up files for evdev input
if test x$use_input_events = xyes; then if test x$use_input_events = xyes; then
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev*.c" SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev.c"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev_kbd.c"
fi fi
# Set up other core UNIX files # Set up other core UNIX files
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_evdev_capabilities.c"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_threadprio.c" SOURCES="$SOURCES $srcdir/src/core/linux/SDL_threadprio.c"
SOURCES="$SOURCES $srcdir/src/core/unix/*.c" SOURCES="$SOURCES $srcdir/src/core/unix/*.c"
;; ;;

View File

@ -44,6 +44,7 @@
#include "SDL_scancode.h" #include "SDL_scancode.h"
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#include "../../events/scancodes_linux.h" /* adds linux_scancode_table */ #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
#include "../../core/linux/SDL_evdev_capabilities.h"
#include "../../core/linux/SDL_udev.h" #include "../../core/linux/SDL_udev.h"
/* These are not defined in older Linux kernel headers */ /* These are not defined in older Linux kernel headers */

View File

@ -0,0 +1,80 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 2020 Collabora Ltd.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_evdev_capabilities.h"
#if HAVE_LIBUDEV_H
extern int
SDL_EVDEV_GuessDeviceClass(unsigned long bitmask_ev[NBITS(EV_MAX)],
unsigned long bitmask_abs[NBITS(ABS_MAX)],
unsigned long bitmask_key[NBITS(KEY_MAX)],
unsigned long bitmask_rel[NBITS(REL_MAX)])
{
int devclass = 0;
unsigned long keyboard_mask;
if (test_bit(EV_ABS, bitmask_ev) &&
test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
; /* ID_INPUT_TABLET */
} else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
; /* ID_INPUT_TOUCHPAD */
} else if (test_bit(BTN_MOUSE, bitmask_key)) {
devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
} else if (test_bit(BTN_TOUCH, bitmask_key)) {
/* TODO: better determining between touchscreen and multitouch touchpad,
see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
}
if (test_bit(BTN_TRIGGER, bitmask_key) ||
test_bit(BTN_A, bitmask_key) ||
test_bit(BTN_1, bitmask_key) ||
test_bit(ABS_RX, bitmask_abs) ||
test_bit(ABS_RY, bitmask_abs) ||
test_bit(ABS_RZ, bitmask_abs) ||
test_bit(ABS_THROTTLE, bitmask_abs) ||
test_bit(ABS_RUDDER, bitmask_abs) ||
test_bit(ABS_WHEEL, bitmask_abs) ||
test_bit(ABS_GAS, bitmask_abs) ||
test_bit(ABS_BRAKE, bitmask_abs)) {
devclass |= SDL_UDEV_DEVICE_JOYSTICK; /* ID_INPUT_JOYSTICK */
}
}
if (test_bit(EV_REL, bitmask_ev) &&
test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
test_bit(BTN_MOUSE, bitmask_key)) {
devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
}
/* the first 32 bits are ESC, numbers, and Q to D; if we have any of
* those, consider it a keyboard device; do not test KEY_RESERVED, though */
keyboard_mask = 0xFFFFFFFE;
if ((bitmask_key[0] & keyboard_mask) != 0)
devclass |= SDL_UDEV_DEVICE_KEYBOARD; /* ID_INPUT_KEYBOARD */
return devclass;
}
#endif

View File

@ -0,0 +1,58 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 2020 Collabora Ltd.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_evdev_capabilities_h_
#define SDL_evdev_capabilities_h_
#if HAVE_LIBUDEV_H
#include <linux/input.h>
/* A device can be any combination of these classes */
typedef enum
{
SDL_UDEV_DEVICE_UNKNOWN = 0x0000,
SDL_UDEV_DEVICE_MOUSE = 0x0001,
SDL_UDEV_DEVICE_KEYBOARD = 0x0002,
SDL_UDEV_DEVICE_JOYSTICK = 0x0004,
SDL_UDEV_DEVICE_SOUND = 0x0008,
SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010
} SDL_UDEV_deviceclass;
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define EVDEV_OFF(x) ((x)%BITS_PER_LONG)
#define EVDEV_LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array) ((array[EVDEV_LONG(bit)] >> EVDEV_OFF(bit)) & 1)
extern int SDL_EVDEV_GuessDeviceClass(unsigned long bitmask_ev[NBITS(EV_MAX)],
unsigned long bitmask_abs[NBITS(ABS_MAX)],
unsigned long bitmask_key[NBITS(KEY_MAX)],
unsigned long bitmask_rel[NBITS(REL_MAX)]);
#endif /* HAVE_LIBUDEV_H */
#endif /* SDL_evdev_capabilities_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -32,6 +32,7 @@
#include <linux/input.h> #include <linux/input.h>
#include "SDL_assert.h" #include "SDL_assert.h"
#include "SDL_evdev_capabilities.h"
#include "SDL_loadso.h" #include "SDL_loadso.h"
#include "SDL_timer.h" #include "SDL_timer.h"
#include "SDL_hints.h" #include "SDL_hints.h"
@ -291,12 +292,6 @@ SDL_UDEV_LoadLibrary(void)
return retval; return retval;
} }
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len) static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
{ {
const char *value; const char *value;
@ -330,13 +325,11 @@ static void get_caps(struct udev_device *dev, struct udev_device *pdev, const ch
static int static int
guess_device_class(struct udev_device *dev) guess_device_class(struct udev_device *dev)
{ {
int devclass = 0;
struct udev_device *pdev; struct udev_device *pdev;
unsigned long bitmask_ev[NBITS(EV_MAX)]; unsigned long bitmask_ev[NBITS(EV_MAX)];
unsigned long bitmask_abs[NBITS(ABS_MAX)]; unsigned long bitmask_abs[NBITS(ABS_MAX)];
unsigned long bitmask_key[NBITS(KEY_MAX)]; unsigned long bitmask_key[NBITS(KEY_MAX)];
unsigned long bitmask_rel[NBITS(REL_MAX)]; unsigned long bitmask_rel[NBITS(REL_MAX)];
unsigned long keyboard_mask;
/* walk up the parental chain until we find the real input device; the /* walk up the parental chain until we find the real input device; the
* argument is very likely a subdevice of this, like eventN */ * argument is very likely a subdevice of this, like eventN */
@ -353,48 +346,10 @@ guess_device_class(struct udev_device *dev)
get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel)); get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key)); get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
if (test_bit(EV_ABS, bitmask_ev) && return SDL_EVDEV_GuessDeviceClass(&bitmask_ev[0],
test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) { &bitmask_abs[0],
if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) { &bitmask_key[0],
; /* ID_INPUT_TABLET */ &bitmask_rel[0]);
} else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
; /* ID_INPUT_TOUCHPAD */
} else if (test_bit(BTN_MOUSE, bitmask_key)) {
devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
} else if (test_bit(BTN_TOUCH, bitmask_key)) {
/* TODO: better determining between touchscreen and multitouch touchpad,
see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
}
if (test_bit(BTN_TRIGGER, bitmask_key) ||
test_bit(BTN_A, bitmask_key) ||
test_bit(BTN_1, bitmask_key) ||
test_bit(ABS_RX, bitmask_abs) ||
test_bit(ABS_RY, bitmask_abs) ||
test_bit(ABS_RZ, bitmask_abs) ||
test_bit(ABS_THROTTLE, bitmask_abs) ||
test_bit(ABS_RUDDER, bitmask_abs) ||
test_bit(ABS_WHEEL, bitmask_abs) ||
test_bit(ABS_GAS, bitmask_abs) ||
test_bit(ABS_BRAKE, bitmask_abs)) {
devclass |= SDL_UDEV_DEVICE_JOYSTICK; /* ID_INPUT_JOYSTICK */
}
}
if (test_bit(EV_REL, bitmask_ev) &&
test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
test_bit(BTN_MOUSE, bitmask_key)) {
devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
}
/* the first 32 bits are ESC, numbers, and Q to D; if we have any of
* those, consider it a keyboard device; do not test KEY_RESERVED, though */
keyboard_mask = 0xFFFFFFFE;
if ((bitmask_key[0] & keyboard_mask) != 0)
devclass |= SDL_UDEV_DEVICE_KEYBOARD; /* ID_INPUT_KEYBOARD */
return devclass;
} }
static void static void

View File

@ -46,17 +46,6 @@ typedef enum
SDL_UDEV_DEVICEREMOVED SDL_UDEV_DEVICEREMOVED
} SDL_UDEV_deviceevent; } SDL_UDEV_deviceevent;
/* A device can be any combination of these classes */
typedef enum
{
SDL_UDEV_DEVICE_UNKNOWN = 0x0000,
SDL_UDEV_DEVICE_MOUSE = 0x0001,
SDL_UDEV_DEVICE_KEYBOARD = 0x0002,
SDL_UDEV_DEVICE_JOYSTICK = 0x0004,
SDL_UDEV_DEVICE_SOUND = 0x0008,
SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010
} SDL_UDEV_deviceclass;
typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
typedef struct SDL_UDEV_CallbackList { typedef struct SDL_UDEV_CallbackList {

View File

@ -28,6 +28,7 @@
#include "SDL_joystick.h" #include "SDL_joystick.h"
#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
#include "../../joystick/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */ #include "../../joystick/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */
#include "../../core/linux/SDL_evdev_capabilities.h"
#include "../../core/linux/SDL_udev.h" #include "../../core/linux/SDL_udev.h"
#include <unistd.h> /* close */ #include <unistd.h> /* close */
@ -86,8 +87,6 @@ static SDL_hapticlist_item *SDL_hapticlist = NULL;
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
static int numhaptics = 0; static int numhaptics = 0;
#define test_bit(nr, addr) \
(((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
#define EV_TEST(ev,f) \ #define EV_TEST(ev,f) \
if (test_bit((ev), features)) ret |= (f); if (test_bit((ev), features)) ret |= (f);
/* /*

View File

@ -78,6 +78,7 @@
#define BTN_DPAD_RIGHT 0x223 #define BTN_DPAD_RIGHT 0x223
#endif #endif
#include "../../core/linux/SDL_evdev_capabilities.h"
#include "../../core/linux/SDL_udev.h" #include "../../core/linux/SDL_udev.h"
#if 0 #if 0
@ -124,10 +125,6 @@ static int numjoysticks = 0;
static Uint32 last_joy_detect_time; static Uint32 last_joy_detect_time;
static time_t last_input_dir_mtime; static time_t last_input_dir_mtime;
#define test_bit(nr, addr) \
(((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
static void static void
FixupDeviceInfoForMapping(int fd, struct input_id *inpid) FixupDeviceInfoForMapping(int fd, struct input_id *inpid)
{ {