diff --git a/src/device.c b/src/device.c index f8e2694..70d8151 100644 --- a/src/device.c +++ b/src/device.c @@ -21,49 +21,61 @@ #include #include -/* - * Abstract away evdev and inotify. - * +/* + * Abstract away evdev and inotify. + * * We could make this cleaner by creating a single file descriptor via epoll * but this would break FreeBSD compatibility without a dedicated kqueue - * implementation. A thread based approach was also considered, - * but inter-thread communication adds too much overhead (~100us). + * implementation. A thread based approach was also considered, but + * inter-thread communication adds too much overhead (~100us). * * Overview: - * + * * A 'devmon' is a file descriptor which can be created with devmon_create() * and subsequently monitored for new devices read with devmon_read_device(). - * - * A 'device' always corresponds to a keyboard from which activity can be - * monitored with device->fd and events subsequently read using + * + * A 'device' always corresponds to a keyboard or mouse from which activity can + * be monitored with device->fd and events subsequently read using * device_read_event(). * * If the event returned by device_read_event() is of type DEV_REMOVED then the * corresponding device should be considered invalid by the caller. */ -static int is_keyboard(int fd) +/* + * returns 1 if keyboard, 2 if mouse, and 0 if neither. + */ +static int device_type(int fd) { - uint32_t keymask; + uint32_t mask[BTN_LEFT/32+1] = {0}; - if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keymask), &keymask) < 0) { + if (ioctl(fd, EVIOCGBIT(EV_KEY, (BTN_LEFT/32+1)*4), mask) < 0) { perror("ioctl"); return 0; } /* The first 31 bits correspond to [KEY_ESC-KEY_S] */ - return keymask == 0xFFFFFFFE; + if (mask[0] == 0xFFFFFFFE) + return 1; + else if (mask[BTN_LEFT/32] >> BTN_LEFT%32) + return 2; + else + return 0; } static int device_init(const char *path, struct device *dev) { int fd; + int type; + if ((fd = open(path, O_RDONLY | O_NONBLOCK, 0600)) < 0) { fprintf(stderr, "failed to open %s\n", path); return -1; } - if (is_keyboard(fd)) { + type = device_type(fd); + + if (type) { struct input_id info; if (ioctl(fd, EVIOCGNAME(sizeof(dev->name)), dev->name) == -1) { @@ -80,6 +92,7 @@ static int device_init(const char *path, struct device *dev) dev->path[sizeof(dev->path)-1] = 0; dev->fd = fd; + dev->is_keyboard = type == 1; dev->vendor_id = info.vendor; dev->product_id = info.product; @@ -143,7 +156,7 @@ int device_scan(struct device devices[MAX_DEVICES]) return ndevs; } -/* +/* * NOTE: Only a single devmon fd may exist. Implementing this properly * would involve bookkeeping state for each fd, but this is * unnecessary for our use. @@ -169,7 +182,7 @@ int devmon_create() return fd; } -/* +/* * A non blocking call which returns any devices available on the provided * monitor descriptor. The return value should not be freed or modified by the calling * code. Returns NULL if no devices are available. @@ -215,7 +228,7 @@ int device_ungrab(struct device *dev) return ioctl(dev->fd, EVIOCGRAB, (void *) 0); } -/* +/* * Read a device event from the given device or return * NULL if none are available (may happen in the * case of a spurious wakeup). @@ -254,6 +267,8 @@ struct device_event *device_read_event(struct device *dev) ev.code = KEYD_MOUSE_2; else if (ev.code == KEY_FN) ev.code = KEYD_FN; + else if (ev.code >= BTN_DIGI && ev.code <= BTN_TOOL_QUADTAP) + ; else { fprintf(stderr, "ERROR: unsupported evdev code: 0x%x\n", ev.code); return NULL; diff --git a/src/device.h b/src/device.h index 0851273..b061130 100644 --- a/src/device.h +++ b/src/device.h @@ -21,6 +21,7 @@ struct device { */ int fd; + uint8_t is_keyboard; uint16_t product_id; uint16_t vendor_id; char name[64]; diff --git a/src/keyboard.c b/src/keyboard.c index f9217b0..2eb540d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -30,7 +30,7 @@ static long get_time() static void kbd_send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) { - if (code == KEYD_NOOP) + if (code == KEYD_NOOP || code == KEYD_EXTERNAL_MOUSE_BUTTON) return; if (pressed) @@ -377,10 +377,10 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descri } else { kbd_send_key(kbd, d->args[0].code, 0); send_mods(kbd, descriptor_layer_mods, 1); + clear_oneshot = 1; } oneshot_latch = 0; - clear_oneshot = 1; break; case OP_LAYER: layer = &layers[d->args[0].idx]; diff --git a/src/keyd.c b/src/keyd.c index 9a4cbce..d466fa5 100644 --- a/src/keyd.c +++ b/src/keyd.c @@ -55,6 +55,16 @@ static void daemon_add_cb(struct device *dev) dev->data = NULL; + /* + * NOTE: Some mice can emit keys and consequently appear as keyboards. + * Conversely, some keyboards with a builtin trackpad can emit mouse + * events. There doesn't appear to be a reliable way to distinguish + * between these two, so we take a permissive approach and leave it up to + * the user to blacklist mice which emit key events. + */ + if (!dev->is_keyboard) + return; + printf("device added: %04x:%04x %s (%s)\n", dev->vendor_id, dev->product_id, @@ -106,7 +116,18 @@ static void panic_check(uint8_t code, uint8_t pressed) static int daemon_event_cb(struct device *dev, uint8_t code, uint8_t pressed) { - struct keyboard *kbd = dev ? dev->data : active_kbd; + struct keyboard *kbd = NULL; + + if (!dev) { + /* timeout */ + kbd = active_kbd; + } else if (dev->data) { + kbd = dev->data; + } else if (code >= KEYD_LEFT_MOUSE && code <= KEYD_MOUSE_2) { + code = KEYD_EXTERNAL_MOUSE_BUTTON; + + kbd = active_kbd; + } if (!kbd) return 0; diff --git a/src/keys.h b/src/keys.h index 4c2885c..072d26d 100644 --- a/src/keys.h +++ b/src/keys.h @@ -281,13 +281,15 @@ struct modifier_table_ent { /* These deviate from uinput codes. */ -#define KEYD_NOOP 195 -#define KEYD_LEFT_MOUSE 249 -#define KEYD_RIGHT_MOUSE 250 -#define KEYD_MIDDLE_MOUSE 251 -#define KEYD_MOUSE_1 252 -#define KEYD_MOUSE_2 253 -#define KEYD_FN 254 +#define KEYD_EXTERNAL_MOUSE_BUTTON 196 + +#define KEYD_NOOP 195 +#define KEYD_LEFT_MOUSE 249 +#define KEYD_MIDDLE_MOUSE 250 +#define KEYD_RIGHT_MOUSE 251 +#define KEYD_MOUSE_1 252 +#define KEYD_MOUSE_2 253 +#define KEYD_FN 254 extern const struct modifier_table_ent modifier_table[MAX_MOD]; extern const struct keycode_table_ent keycode_table[256]; diff --git a/t/oneshot.t b/t/oneshot.t index f1b20ab..e10d4e1 100644 --- a/t/oneshot.t +++ b/t/oneshot.t @@ -5,5 +5,5 @@ b up control down b down -control up b up +control up diff --git a/t/oneshot11.t b/t/oneshot11.t index 91e1788..7292406 100644 --- a/t/oneshot11.t +++ b/t/oneshot11.t @@ -9,7 +9,7 @@ a up control down j down -control up j up +control up a down a up diff --git a/t/oneshot14.t b/t/oneshot14.t index 2751e5f..cafa121 100644 --- a/t/oneshot14.t +++ b/t/oneshot14.t @@ -11,5 +11,7 @@ shift down shift up a down a up +shift down +shift up b down b up diff --git a/t/oneshot5.t b/t/oneshot5.t index 9537ace..9e1e7a0 100644 --- a/t/oneshot5.t +++ b/t/oneshot5.t @@ -8,6 +8,6 @@ i up shift down control down i down +i up control up shift up -i up diff --git a/t/oneshot6.t b/t/oneshot6.t index 187fa08..5d09d01 100644 --- a/t/oneshot6.t +++ b/t/oneshot6.t @@ -8,6 +8,6 @@ i up shift down control down i down +i up control up shift up -i up diff --git a/t/oneshot9.t b/t/oneshot9.t index 42991d3..9a3594a 100644 --- a/t/oneshot9.t +++ b/t/oneshot9.t @@ -5,5 +5,5 @@ i up control down i down -control up i up +control up diff --git a/t/oneshotn.t b/t/oneshotn.t index 3fed490..e1de12d 100644 --- a/t/oneshotn.t +++ b/t/oneshotn.t @@ -7,3 +7,5 @@ control down control up b down b up +control down +control up diff --git a/t/oneshotn3.t b/t/oneshotn3.t index 0624fe9..edc2a13 100644 --- a/t/oneshotn3.t +++ b/t/oneshotn3.t @@ -8,6 +8,6 @@ i up control down shift down i down +i up control up shift up -i up