Commit 8db3171b98f0f72a39312f34966e857e73f8a86c

Simon McVittie 2020-11-11T19:14:34

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>

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78e4099..d378d36 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1266,6 +1266,7 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT RISCOS)
     endif()
 
     # 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")
 
     # src/core/unix/*.c is included in a generic if(UNIX) section, elsewhere.
diff --git a/configure b/configure
index d522521..458d379 100755
--- a/configure
+++ b/configure
@@ -24838,9 +24838,11 @@ $as_echo "#define SDL_TIMER_UNIX 1" >>confdefs.h
         fi
         # Set up files for evdev input
         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
         # 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/unix/*.c"
         ;;
diff --git a/configure.ac b/configure.ac
index c9c0295..550af85 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3658,9 +3658,11 @@ case "$host" in
         fi
         # Set up files for evdev input
         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       
         # 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/unix/*.c"
         ;;
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index f7c376a..1fdf241 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -44,6 +44,7 @@
 #include "SDL_scancode.h"
 #include "../../events/SDL_events_c.h"
 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
+#include "../../core/linux/SDL_evdev_capabilities.h"
 #include "../../core/linux/SDL_udev.h"
 
 /* These are not defined in older Linux kernel headers */
diff --git a/src/core/linux/SDL_evdev_capabilities.c b/src/core/linux/SDL_evdev_capabilities.c
new file mode 100644
index 0000000..d96b9ed
--- /dev/null
+++ b/src/core/linux/SDL_evdev_capabilities.c
@@ -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
diff --git a/src/core/linux/SDL_evdev_capabilities.h b/src/core/linux/SDL_evdev_capabilities.h
new file mode 100644
index 0000000..cef1513
--- /dev/null
+++ b/src/core/linux/SDL_evdev_capabilities.h
@@ -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: */
diff --git a/src/core/linux/SDL_udev.c b/src/core/linux/SDL_udev.c
index 9c9e950..b45ed10 100644
--- a/src/core/linux/SDL_udev.c
+++ b/src/core/linux/SDL_udev.c
@@ -32,6 +32,7 @@
 #include <linux/input.h>
 
 #include "SDL_assert.h"
+#include "SDL_evdev_capabilities.h"
 #include "SDL_loadso.h"
 #include "SDL_timer.h"
 #include "SDL_hints.h"
@@ -291,12 +292,6 @@ SDL_UDEV_LoadLibrary(void)
     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)
 {
     const char *value;
@@ -330,13 +325,11 @@ static void get_caps(struct udev_device *dev, struct udev_device *pdev, const ch
 static int
 guess_device_class(struct udev_device *dev)
 {
-    int devclass = 0;
     struct udev_device *pdev;
     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)];
-    unsigned long keyboard_mask;
 
     /* walk up the parental chain until we find the real input device; the
      * 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/key", bitmask_key, SDL_arraysize(bitmask_key));
 
-    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;
+    return SDL_EVDEV_GuessDeviceClass(&bitmask_ev[0],
+                                      &bitmask_abs[0],
+                                      &bitmask_key[0],
+                                      &bitmask_rel[0]);
 }
 
 static void 
diff --git a/src/core/linux/SDL_udev.h b/src/core/linux/SDL_udev.h
index e610853..83b4610 100644
--- a/src/core/linux/SDL_udev.h
+++ b/src/core/linux/SDL_udev.h
@@ -46,17 +46,6 @@ typedef enum
     SDL_UDEV_DEVICEREMOVED
 } 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 struct SDL_UDEV_CallbackList {
diff --git a/src/haptic/linux/SDL_syshaptic.c b/src/haptic/linux/SDL_syshaptic.c
index 81ea0bf..6e9533b 100644
--- a/src/haptic/linux/SDL_syshaptic.c
+++ b/src/haptic/linux/SDL_syshaptic.c
@@ -28,6 +28,7 @@
 #include "SDL_joystick.h"
 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
 #include "../../joystick/linux/SDL_sysjoystick_c.h"     /* For joystick hwdata */
+#include "../../core/linux/SDL_evdev_capabilities.h"
 #include "../../core/linux/SDL_udev.h"
 
 #include <unistd.h>             /* close */
@@ -86,8 +87,6 @@ static SDL_hapticlist_item *SDL_hapticlist = NULL;
 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
 static int numhaptics = 0;
 
-#define test_bit(nr, addr) \
-   (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
 #define EV_TEST(ev,f) \
    if (test_bit((ev), features)) ret |= (f);
 /*
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 0d10799..a94bfe3 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -78,6 +78,7 @@
 #define BTN_DPAD_RIGHT  0x223
 #endif
 
+#include "../../core/linux/SDL_evdev_capabilities.h"
 #include "../../core/linux/SDL_udev.h"
 
 #if 0
@@ -124,10 +125,6 @@ static int numjoysticks = 0;
 static Uint32 last_joy_detect_time;
 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
 FixupDeviceInfoForMapping(int fd, struct input_id *inpid)
 {