Optimize grab logic

Use EVIOCGKEY to obtain keystate of the underlying device instead of
exclusively relying on internal state + timeouts. The old approach was
racey and slow. This should improve start up/reload times and make
initialization less buggy.
master
Raheman Vaiya 4 years ago
parent 23d16a8279
commit 7ac7ec9a7e
  1. 35
      src/evdev.c
  2. 80
      src/keyd.c
  3. 1
      src/keyd.h

@ -130,3 +130,38 @@ int evdev_device_id(const char *devnode, uint16_t *vendor, uint16_t *product)
return 0;
}
int evdev_grab_keyboard(int fd)
{
size_t i;
uint8_t state[KEY_MAX / 8 + 1];
/*
* await neutral key state to ensure any residual
* key up events propagate.
*/
while (1) {
int n = 0;
memset(state, 0, sizeof(state));
if (ioctl(fd, EVIOCGKEY(sizeof state), state) < 0) {
perror("ioctl EVIOCGKEY");
return -1;
}
for (i = 0; i < KEY_MAX; i++) {
if ((state[i/8] >> (i % 8)) & 0x1)
n++;
}
if (n == 0)
break;
}
if (ioctl(fd, EVIOCGRAB, (void *) 1) < 0) {
perror("EVIOCGRAB");
return -1;
}
return 0;
}

@ -103,71 +103,6 @@ static void send_repetitions()
}
}
/* Block on the given keyboard nodes until no keys are depressed. */
static void await_keyboard_neutrality(char **devs, int n)
{
int fds[MAX_KEYBOARDS];
int maxfd = 0;
int i;
dbg("Awaiting keyboard neutrality.");
for (i = 0; i < n; i++) {
if ((fds[i] = open(devs[i], O_RDONLY | O_NONBLOCK)) < 0)
die("open");
if (fds[i] > maxfd)
maxfd = fds[i];
}
/*
* There is a race condition here since it is possible for a key down
* event to be generated before keyd is launched, in that case we hope a
* repeat event is generated within the first 300ms. If we miss the
* keydown event and the repeat event is not generated within the first
* 300ms it is possible for this to yield a false positive. In practice
* this seems to work fine. Given the stateless nature of evdev I am not
* aware of a better way to achieve this.
*/
while (1) {
struct timeval tv = {
.tv_usec = 300000
};
struct input_event ev;
int i;
fd_set fdset;
FD_ZERO(&fdset);
for (i = 0; i < n; i++)
FD_SET(fds[i], &fdset);
select(maxfd + 1, &fdset, NULL, NULL, &tv);
for (i = 0; i < n; i++) {
if (FD_ISSET(fds[i], &fdset)) {
while (read(fds[i], &ev, sizeof ev) > 0) {
if (ev.type == EV_KEY) {
keystate[ev.code] = ev.value;
dbg("keystate[%d]: %d", ev.code, ev.value);
}
}
}
}
for (i = 0; i < MAX_KEYS; i++)
if (keystate[i])
break;
if (i == MAX_KEYS)
break;
}
for (i = 0; i < n; i++)
close(fds[i]);
dbg("Keyboard neutrality achieved");
}
void reset_vkbd()
{
size_t code;
@ -243,13 +178,10 @@ static int manage_keyboard(const char *devnode)
return -1;
}
/* Grab the keyboard. */
if (ioctl(fd, EVIOCGRAB, (void *) 1) < 0) {
if (evdev_grab_keyboard(fd) < 0) {
info("Failed to grab %04x:%04x, ignoring...\n", vendor_id, product_id);
perror("EVIOCGRAB");
close(fd);
free(kbd);
return -1;
}
@ -269,14 +201,12 @@ static int manage_keyboard(const char *devnode)
return 0;
}
static void scan_keyboards(int wait)
static void scan_keyboards()
{
int i, n;
char *devs[MAX_KEYBOARDS];
evdev_get_keyboard_nodes(devs, &n);
if (wait)
await_keyboard_neutrality(devs, n);
for (i = 0; i < n; i++)
manage_keyboard(devs[i]);
@ -298,7 +228,7 @@ void reload_config()
keyboards = NULL;
scan_keyboards(0);
scan_keyboards();
}
static int destroy_keyboard(const char *devnode)
@ -556,7 +486,7 @@ static void main_loop()
nice(-20);
scan_keyboards(1);
scan_keyboards();
inotifyfd = create_inotify_fd();
sd = create_server_socket();

@ -20,5 +20,6 @@ int evdev_get_keyboard_nodes(char **devs, int *ndevs);
int evdev_is_keyboard(const char *devnode);
int evdev_device_id(const char *devnode, uint16_t *vendor, uint16_t *product);
const char *evdev_device_name(const char *devnode);
int evdev_grab_keyboard(int fd);
#endif

Loading…
Cancel
Save