Clear oneshot on click

This patch passively monitors mouse events and passes them
through to the active keyboard in order to facilitate
clearing oneshot modifiers on click.

A byproduct of this is an increase the number of spurious wakeups
(caused by mouse movement since we can't exclusively monitor click
events) , but this seems to have an minimal performance impact
in practice.
master
Raheman Vaiya 4 years ago
parent de7561cc2d
commit 9097368672
  1. 49
      src/device.c
  2. 1
      src/device.h
  3. 4
      src/keyboard.c
  4. 23
      src/keyd.c
  5. 16
      src/keys.h
  6. 2
      t/oneshot.t
  7. 2
      t/oneshot11.t
  8. 2
      t/oneshot14.t
  9. 2
      t/oneshot5.t
  10. 2
      t/oneshot6.t
  11. 2
      t/oneshot9.t
  12. 2
      t/oneshotn.t
  13. 2
      t/oneshotn3.t

@ -21,49 +21,61 @@
#include <linux/input.h> #include <linux/input.h>
#include <sys/inotify.h> #include <sys/inotify.h>
/* /*
* Abstract away evdev and inotify. * Abstract away evdev and inotify.
* *
* We could make this cleaner by creating a single file descriptor via epoll * We could make this cleaner by creating a single file descriptor via epoll
* but this would break FreeBSD compatibility without a dedicated kqueue * but this would break FreeBSD compatibility without a dedicated kqueue
* implementation. A thread based approach was also considered, * implementation. A thread based approach was also considered, but
* but inter-thread communication adds too much overhead (~100us). * inter-thread communication adds too much overhead (~100us).
* *
* Overview: * Overview:
* *
* A 'devmon' is a file descriptor which can be created with devmon_create() * A 'devmon' is a file descriptor which can be created with devmon_create()
* and subsequently monitored for new devices read with devmon_read_device(). * and subsequently monitored for new devices read with devmon_read_device().
* *
* A 'device' always corresponds to a keyboard from which activity can be * A 'device' always corresponds to a keyboard or mouse from which activity can
* monitored with device->fd and events subsequently read using * be monitored with device->fd and events subsequently read using
* device_read_event(). * device_read_event().
* *
* If the event returned by device_read_event() is of type DEV_REMOVED then the * If the event returned by device_read_event() is of type DEV_REMOVED then the
* corresponding device should be considered invalid by the caller. * 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"); perror("ioctl");
return 0; return 0;
} }
/* The first 31 bits correspond to [KEY_ESC-KEY_S] */ /* 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) static int device_init(const char *path, struct device *dev)
{ {
int fd; int fd;
int type;
if ((fd = open(path, O_RDONLY | O_NONBLOCK, 0600)) < 0) { if ((fd = open(path, O_RDONLY | O_NONBLOCK, 0600)) < 0) {
fprintf(stderr, "failed to open %s\n", path); fprintf(stderr, "failed to open %s\n", path);
return -1; return -1;
} }
if (is_keyboard(fd)) { type = device_type(fd);
if (type) {
struct input_id info; struct input_id info;
if (ioctl(fd, EVIOCGNAME(sizeof(dev->name)), dev->name) == -1) { 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->path[sizeof(dev->path)-1] = 0;
dev->fd = fd; dev->fd = fd;
dev->is_keyboard = type == 1;
dev->vendor_id = info.vendor; dev->vendor_id = info.vendor;
dev->product_id = info.product; dev->product_id = info.product;
@ -143,7 +156,7 @@ int device_scan(struct device devices[MAX_DEVICES])
return ndevs; return ndevs;
} }
/* /*
* NOTE: Only a single devmon fd may exist. Implementing this properly * NOTE: Only a single devmon fd may exist. Implementing this properly
* would involve bookkeeping state for each fd, but this is * would involve bookkeeping state for each fd, but this is
* unnecessary for our use. * unnecessary for our use.
@ -169,7 +182,7 @@ int devmon_create()
return fd; return fd;
} }
/* /*
* A non blocking call which returns any devices available on the provided * 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 * monitor descriptor. The return value should not be freed or modified by the calling
* code. Returns NULL if no devices are available. * 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); return ioctl(dev->fd, EVIOCGRAB, (void *) 0);
} }
/* /*
* Read a device event from the given device or return * Read a device event from the given device or return
* NULL if none are available (may happen in the * NULL if none are available (may happen in the
* case of a spurious wakeup). * case of a spurious wakeup).
@ -254,6 +267,8 @@ struct device_event *device_read_event(struct device *dev)
ev.code = KEYD_MOUSE_2; ev.code = KEYD_MOUSE_2;
else if (ev.code == KEY_FN) else if (ev.code == KEY_FN)
ev.code = KEYD_FN; ev.code = KEYD_FN;
else if (ev.code >= BTN_DIGI && ev.code <= BTN_TOOL_QUADTAP)
;
else { else {
fprintf(stderr, "ERROR: unsupported evdev code: 0x%x\n", ev.code); fprintf(stderr, "ERROR: unsupported evdev code: 0x%x\n", ev.code);
return NULL; return NULL;

@ -21,6 +21,7 @@ struct device {
*/ */
int fd; int fd;
uint8_t is_keyboard;
uint16_t product_id; uint16_t product_id;
uint16_t vendor_id; uint16_t vendor_id;
char name[64]; char name[64];

@ -30,7 +30,7 @@ static long get_time()
static void kbd_send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) 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; return;
if (pressed) if (pressed)
@ -377,10 +377,10 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descri
} else { } else {
kbd_send_key(kbd, d->args[0].code, 0); kbd_send_key(kbd, d->args[0].code, 0);
send_mods(kbd, descriptor_layer_mods, 1); send_mods(kbd, descriptor_layer_mods, 1);
clear_oneshot = 1;
} }
oneshot_latch = 0; oneshot_latch = 0;
clear_oneshot = 1;
break; break;
case OP_LAYER: case OP_LAYER:
layer = &layers[d->args[0].idx]; layer = &layers[d->args[0].idx];

@ -55,6 +55,16 @@ static void daemon_add_cb(struct device *dev)
dev->data = NULL; 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", printf("device added: %04x:%04x %s (%s)\n",
dev->vendor_id, dev->vendor_id,
dev->product_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) 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) if (!kbd)
return 0; return 0;

@ -281,13 +281,15 @@ struct modifier_table_ent {
/* These deviate from uinput codes. */ /* These deviate from uinput codes. */
#define KEYD_NOOP 195 #define KEYD_EXTERNAL_MOUSE_BUTTON 196
#define KEYD_LEFT_MOUSE 249
#define KEYD_RIGHT_MOUSE 250 #define KEYD_NOOP 195
#define KEYD_MIDDLE_MOUSE 251 #define KEYD_LEFT_MOUSE 249
#define KEYD_MOUSE_1 252 #define KEYD_MIDDLE_MOUSE 250
#define KEYD_MOUSE_2 253 #define KEYD_RIGHT_MOUSE 251
#define KEYD_FN 254 #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 modifier_table_ent modifier_table[MAX_MOD];
extern const struct keycode_table_ent keycode_table[256]; extern const struct keycode_table_ent keycode_table[256];

@ -5,5 +5,5 @@ b up
control down control down
b down b down
control up
b up b up
control up

@ -9,7 +9,7 @@ a up
control down control down
j down j down
control up
j up j up
control up
a down a down
a up a up

@ -11,5 +11,7 @@ shift down
shift up shift up
a down a down
a up a up
shift down
shift up
b down b down
b up b up

@ -8,6 +8,6 @@ i up
shift down shift down
control down control down
i down i down
i up
control up control up
shift up shift up
i up

@ -8,6 +8,6 @@ i up
shift down shift down
control down control down
i down i down
i up
control up control up
shift up shift up
i up

@ -5,5 +5,5 @@ i up
control down control down
i down i down
control up
i up i up
control up

@ -7,3 +7,5 @@ control down
control up control up
b down b down
b up b up
control down
control up

@ -8,6 +8,6 @@ i up
control down control down
shift down shift down
i down i down
i up
control up control up
shift up shift up
i up

Loading…
Cancel
Save