Skip to content

Commit

Permalink
Clear oneshot on click
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rvaiya committed Mar 26, 2022
1 parent de7561c commit 9097368
Show file tree
Hide file tree
Showing 13 changed files with 76 additions and 33 deletions.
49 changes: 32 additions & 17 deletions src/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,61 @@
#include <linux/input.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
* 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) {
Expand All @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct device {
*/
int fd;

uint8_t is_keyboard;
uint16_t product_id;
uint16_t vendor_id;
char name[64];
Expand Down
4 changes: 2 additions & 2 deletions src/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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];
Expand Down
23 changes: 22 additions & 1 deletion src/keyd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 9 additions & 7 deletions src/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
2 changes: 1 addition & 1 deletion t/oneshot.t
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ b up

control down
b down
control up
b up
control up
2 changes: 1 addition & 1 deletion t/oneshot11.t
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ a up

control down
j down
control up
j up
control up
a down
a up
2 changes: 2 additions & 0 deletions t/oneshot14.t
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ shift down
shift up
a down
a up
shift down
shift up
b down
b up
2 changes: 1 addition & 1 deletion t/oneshot5.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ i up
shift down
control down
i down
i up
control up
shift up
i up
2 changes: 1 addition & 1 deletion t/oneshot6.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ i up
shift down
control down
i down
i up
control up
shift up
i up
2 changes: 1 addition & 1 deletion t/oneshot9.t
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ i up

control down
i down
control up
i up
control up
2 changes: 2 additions & 0 deletions t/oneshotn.t
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ control down
control up
b down
b up
control down
control up
2 changes: 1 addition & 1 deletion t/oneshotn3.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ i up
control down
shift down
i down
i up
control up
shift up
i up

0 comments on commit 9097368

Please sign in to comment.