Skip to content

Commit 9097368

Browse files
committed
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.
1 parent de7561c commit 9097368

File tree

13 files changed

+76
-33
lines changed

13 files changed

+76
-33
lines changed

src/device.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,61 @@
2121
#include <linux/input.h>
2222
#include <sys/inotify.h>
2323

24-
/*
25-
* Abstract away evdev and inotify.
26-
*
24+
/*
25+
* Abstract away evdev and inotify.
26+
*
2727
* We could make this cleaner by creating a single file descriptor via epoll
2828
* but this would break FreeBSD compatibility without a dedicated kqueue
29-
* implementation. A thread based approach was also considered,
30-
* but inter-thread communication adds too much overhead (~100us).
29+
* implementation. A thread based approach was also considered, but
30+
* inter-thread communication adds too much overhead (~100us).
3131
*
3232
* Overview:
33-
*
33+
*
3434
* A 'devmon' is a file descriptor which can be created with devmon_create()
3535
* and subsequently monitored for new devices read with devmon_read_device().
36-
*
37-
* A 'device' always corresponds to a keyboard from which activity can be
38-
* monitored with device->fd and events subsequently read using
36+
*
37+
* A 'device' always corresponds to a keyboard or mouse from which activity can
38+
* be monitored with device->fd and events subsequently read using
3939
* device_read_event().
4040
*
4141
* If the event returned by device_read_event() is of type DEV_REMOVED then the
4242
* corresponding device should be considered invalid by the caller.
4343
*/
4444

45-
static int is_keyboard(int fd)
45+
/*
46+
* returns 1 if keyboard, 2 if mouse, and 0 if neither.
47+
*/
48+
static int device_type(int fd)
4649
{
47-
uint32_t keymask;
50+
uint32_t mask[BTN_LEFT/32+1] = {0};
4851

49-
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keymask), &keymask) < 0) {
52+
if (ioctl(fd, EVIOCGBIT(EV_KEY, (BTN_LEFT/32+1)*4), mask) < 0) {
5053
perror("ioctl");
5154
return 0;
5255
}
5356

5457
/* The first 31 bits correspond to [KEY_ESC-KEY_S] */
55-
return keymask == 0xFFFFFFFE;
58+
if (mask[0] == 0xFFFFFFFE)
59+
return 1;
60+
else if (mask[BTN_LEFT/32] >> BTN_LEFT%32)
61+
return 2;
62+
else
63+
return 0;
5664
}
5765

5866
static int device_init(const char *path, struct device *dev)
5967
{
6068
int fd;
69+
int type;
70+
6171
if ((fd = open(path, O_RDONLY | O_NONBLOCK, 0600)) < 0) {
6272
fprintf(stderr, "failed to open %s\n", path);
6373
return -1;
6474
}
6575

66-
if (is_keyboard(fd)) {
76+
type = device_type(fd);
77+
78+
if (type) {
6779
struct input_id info;
6880

6981
if (ioctl(fd, EVIOCGNAME(sizeof(dev->name)), dev->name) == -1) {
@@ -80,6 +92,7 @@ static int device_init(const char *path, struct device *dev)
8092
dev->path[sizeof(dev->path)-1] = 0;
8193

8294
dev->fd = fd;
95+
dev->is_keyboard = type == 1;
8396
dev->vendor_id = info.vendor;
8497
dev->product_id = info.product;
8598

@@ -143,7 +156,7 @@ int device_scan(struct device devices[MAX_DEVICES])
143156
return ndevs;
144157
}
145158

146-
/*
159+
/*
147160
* NOTE: Only a single devmon fd may exist. Implementing this properly
148161
* would involve bookkeeping state for each fd, but this is
149162
* unnecessary for our use.
@@ -169,7 +182,7 @@ int devmon_create()
169182
return fd;
170183
}
171184

172-
/*
185+
/*
173186
* A non blocking call which returns any devices available on the provided
174187
* monitor descriptor. The return value should not be freed or modified by the calling
175188
* code. Returns NULL if no devices are available.
@@ -215,7 +228,7 @@ int device_ungrab(struct device *dev)
215228
return ioctl(dev->fd, EVIOCGRAB, (void *) 0);
216229
}
217230

218-
/*
231+
/*
219232
* Read a device event from the given device or return
220233
* NULL if none are available (may happen in the
221234
* case of a spurious wakeup).
@@ -254,6 +267,8 @@ struct device_event *device_read_event(struct device *dev)
254267
ev.code = KEYD_MOUSE_2;
255268
else if (ev.code == KEY_FN)
256269
ev.code = KEYD_FN;
270+
else if (ev.code >= BTN_DIGI && ev.code <= BTN_TOOL_QUADTAP)
271+
;
257272
else {
258273
fprintf(stderr, "ERROR: unsupported evdev code: 0x%x\n", ev.code);
259274
return NULL;

src/device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct device {
2121
*/
2222
int fd;
2323

24+
uint8_t is_keyboard;
2425
uint16_t product_id;
2526
uint16_t vendor_id;
2627
char name[64];

src/keyboard.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static long get_time()
3030

3131
static void kbd_send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed)
3232
{
33-
if (code == KEYD_NOOP)
33+
if (code == KEYD_NOOP || code == KEYD_EXTERNAL_MOUSE_BUTTON)
3434
return;
3535

3636
if (pressed)
@@ -377,10 +377,10 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descri
377377
} else {
378378
kbd_send_key(kbd, d->args[0].code, 0);
379379
send_mods(kbd, descriptor_layer_mods, 1);
380+
clear_oneshot = 1;
380381
}
381382

382383
oneshot_latch = 0;
383-
clear_oneshot = 1;
384384
break;
385385
case OP_LAYER:
386386
layer = &layers[d->args[0].idx];

src/keyd.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ static void daemon_add_cb(struct device *dev)
5555

5656
dev->data = NULL;
5757

58+
/*
59+
* NOTE: Some mice can emit keys and consequently appear as keyboards.
60+
* Conversely, some keyboards with a builtin trackpad can emit mouse
61+
* events. There doesn't appear to be a reliable way to distinguish
62+
* between these two, so we take a permissive approach and leave it up to
63+
* the user to blacklist mice which emit key events.
64+
*/
65+
if (!dev->is_keyboard)
66+
return;
67+
5868
printf("device added: %04x:%04x %s (%s)\n",
5969
dev->vendor_id,
6070
dev->product_id,
@@ -106,7 +116,18 @@ static void panic_check(uint8_t code, uint8_t pressed)
106116

107117
static int daemon_event_cb(struct device *dev, uint8_t code, uint8_t pressed)
108118
{
109-
struct keyboard *kbd = dev ? dev->data : active_kbd;
119+
struct keyboard *kbd = NULL;
120+
121+
if (!dev) {
122+
/* timeout */
123+
kbd = active_kbd;
124+
} else if (dev->data) {
125+
kbd = dev->data;
126+
} else if (code >= KEYD_LEFT_MOUSE && code <= KEYD_MOUSE_2) {
127+
code = KEYD_EXTERNAL_MOUSE_BUTTON;
128+
129+
kbd = active_kbd;
130+
}
110131

111132
if (!kbd)
112133
return 0;

src/keys.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,15 @@ struct modifier_table_ent {
281281

282282
/* These deviate from uinput codes. */
283283

284-
#define KEYD_NOOP 195
285-
#define KEYD_LEFT_MOUSE 249
286-
#define KEYD_RIGHT_MOUSE 250
287-
#define KEYD_MIDDLE_MOUSE 251
288-
#define KEYD_MOUSE_1 252
289-
#define KEYD_MOUSE_2 253
290-
#define KEYD_FN 254
284+
#define KEYD_EXTERNAL_MOUSE_BUTTON 196
285+
286+
#define KEYD_NOOP 195
287+
#define KEYD_LEFT_MOUSE 249
288+
#define KEYD_MIDDLE_MOUSE 250
289+
#define KEYD_RIGHT_MOUSE 251
290+
#define KEYD_MOUSE_1 252
291+
#define KEYD_MOUSE_2 253
292+
#define KEYD_FN 254
291293

292294
extern const struct modifier_table_ent modifier_table[MAX_MOD];
293295
extern const struct keycode_table_ent keycode_table[256];

t/oneshot.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ b up
55

66
control down
77
b down
8-
control up
98
b up
9+
control up

t/oneshot11.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ a up
99

1010
control down
1111
j down
12-
control up
1312
j up
13+
control up
1414
a down
1515
a up

t/oneshot14.t

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ shift down
1111
shift up
1212
a down
1313
a up
14+
shift down
15+
shift up
1416
b down
1517
b up

t/oneshot5.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ i up
88
shift down
99
control down
1010
i down
11+
i up
1112
control up
1213
shift up
13-
i up

t/oneshot6.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ i up
88
shift down
99
control down
1010
i down
11+
i up
1112
control up
1213
shift up
13-
i up

0 commit comments

Comments
 (0)