Remove BTN_DPAD_* keys from ID_INPUT_KEY test (#5701)
authorNathaniel R. Lewis <nlewis@gaikai.com>
Thu, 6 Apr 2017 19:13:14 +0000 (12:13 -0700)
committerMartin Pitt <martinpitt@users.noreply.github.com>
Thu, 6 Apr 2017 19:13:14 +0000 (21:13 +0200)
At present, devices implementing the BTN_DPAD_UP/DOWN/LEFT/RIGHT
codes will be incorrectly classified as key devices.  This causes
devices respecting the Linux gamepad spec (such as the DS3 as of
kernel 4.12) to be classified as keyboards by X11.

This is caused by the test_key function checking all codes on
[KEY_OK, BTN_TRIGGER_HAPPY).  Unfortunately the BTN_DPAD_* codes
are placed between KEY_LIGHTS_TOGGLE and KEY_ALS_TOGGLE.  This
patch splits the upper key block check into the block before and
after the BTN_DPAD_* codes.  An array is used to avoid dedicated,
per block loops in the event that more event codes are added in
the future.

src/udev/udev-builtin-input_id.c

index 51f364b..4303b25 100644 (file)
 #define LONG(x) ((x)/BITS_PER_LONG)
 #define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
 
+/* available as of kernel 3.11 */
+#ifndef BTN_DPAD_UP
+#define BTN_DPAD_UP 0x220
+#endif /* BTN_DPAD_UP */
+
+/* available as of kernel 3.13 */
+#ifndef KEY_ALS_TOGGLE
+#define KEY_ALS_TOGGLE 0x230
+#endif /* KEY_ALS_TOGGLE */
+
+struct range {
+        unsigned start;
+        unsigned end;
+};
+
+/* key code ranges above BTN_MISC (start is inclusive, stop is exclusive)*/
+static const struct range high_key_blocks[] = {
+        { KEY_OK, BTN_DPAD_UP },
+        { KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY }
+};
+
 static inline int abs_size_mm(const struct input_absinfo *absinfo) {
         /* Resolution is defined to be in units/mm for ABS_X/Y */
         return (absinfo->maximum - absinfo->minimum) / absinfo->resolution;
@@ -260,13 +281,16 @@ static bool test_key(struct udev_device *dev,
                 found |= bitmask_key[i];
                 log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0);
         }
-        /* If there are no keys in the lower block, check the higher block */
+        /* If there are no keys in the lower block, check the higher blocks */
         if (!found) {
-                for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) {
-                        if (test_bit(i, bitmask_key)) {
-                                log_debug("test_key: Found key %x in high block", i);
-                                found = 1;
-                                break;
+                unsigned block;
+                for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
+                        for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
+                                if (test_bit(i, bitmask_key)) {
+                                        log_debug("test_key: Found key %x in high block", i);
+                                        found = 1;
+                                        break;
+                                }
                         }
                 }
         }