Skip to content

Commit

Permalink
Implement mouse passthrough for hybrid devices (#175)
Browse files Browse the repository at this point in the history
This brings us one step closer to full mouse support (#162, #158)
  • Loading branch information
rvaiya committed May 27, 2022
1 parent 248e047 commit 0840338
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 105 deletions.
132 changes: 88 additions & 44 deletions src/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,30 @@
* corresponding device should be considered invalid by the caller.
*/

/*
* returns 1 if keyboard, 2 if mouse, and 0 if neither.
*/
static int device_type(int fd)
static uint8_t resolve_device_type(int fd)
{
uint32_t mask[BTN_LEFT/32+1] = {0};
uint8_t has_rel;
uint8_t type = 0;

if (ioctl(fd, EVIOCGBIT(EV_KEY, (BTN_LEFT/32+1)*4), mask) < 0) {
perror("ioctl");
return 0;
}

if (ioctl(fd, EVIOCGBIT(EV_REL, 1), &has_rel) < 0) {
perror("ioctl");
return 0;
}

if (has_rel)
type |= DEVT_MOUSE;

/* The first 31 bits correspond to [KEY_ESC-KEY_S] */
if (mask[0] == 0xFFFFFFFE)
return 1;
else if (mask[BTN_LEFT/32] >> BTN_LEFT%32)
return 2;
else
return 0;
type |= DEVT_KEYBOARD;

return type;
}

static int device_init(const char *path, struct device *dev)
Expand All @@ -75,7 +80,7 @@ static int device_init(const char *path, struct device *dev)
return -1;
}

type = device_type(fd);
type = resolve_device_type(fd);

if (type) {
struct input_id info;
Expand All @@ -94,7 +99,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->type = type;
dev->vendor_id = info.vendor;
dev->product_id = info.product;

Expand Down Expand Up @@ -285,44 +290,83 @@ struct device_event *device_read_event(struct device *dev)
}
}

if (ev.type == EV_REL && ev.code == REL_WHEEL) {
devev.type = DEV_KEY;
devev.code = ev.value > 0 ? KEYD_SCROLL_UP : KEYD_SCROLL_DOWN;
devev.pressed = 1;
switch (ev.type) {
case EV_REL:
switch (ev.code) {
case REL_WHEEL:
devev.type = DEV_MOUSE_SCROLL;
devev.y = ev.value;
devev.x = 0;

return &devev;
} else if (ev.type != EV_KEY || ev.value == 2) {
return NULL;
}
break;
case REL_HWHEEL:
devev.type = DEV_MOUSE_SCROLL;
devev.y = 0;
devev.x = ev.value;

/*
* KEYD_* codes <256 correspond to their evdev
* counterparts.
*/
if (ev.code >= 256) {
if (ev.code == BTN_LEFT)
ev.code = KEYD_LEFT_MOUSE;
else if (ev.code == BTN_MIDDLE)
ev.code = KEYD_MIDDLE_MOUSE;
else if (ev.code == BTN_RIGHT)
ev.code = KEYD_RIGHT_MOUSE;
else if (ev.code == BTN_SIDE)
ev.code = KEYD_MOUSE_1;
else if (ev.code == BTN_EXTRA)
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);
break;
case REL_X:
devev.type = DEV_MOUSE_MOVE;
devev.x = ev.value;
devev.y = 0;

break;
case REL_Y:
devev.type = DEV_MOUSE_MOVE;
devev.y = ev.value;
devev.x = 0;

break;
case REL_WHEEL_HI_RES:
case REL_HWHEEL_HI_RES:
/* TODO: implement me */
break;
default:
fprintf(stderr, "Unrecognized EV_REL code: %d\n", ev.code);
return NULL;
}
}

devev.type = DEV_KEY;
devev.code = ev.code;
devev.pressed = ev.value;
break;
case EV_KEY:
/* Ignore repeat events */
if (ev.value == 2)
return NULL;

/*
* KEYD_* codes <256 correspond to their evdev
* counterparts.
*/
if (ev.code >= 256) {
if (ev.code == BTN_LEFT)
ev.code = KEYD_LEFT_MOUSE;
else if (ev.code == BTN_MIDDLE)
ev.code = KEYD_MIDDLE_MOUSE;
else if (ev.code == BTN_RIGHT)
ev.code = KEYD_RIGHT_MOUSE;
else if (ev.code == BTN_SIDE)
ev.code = KEYD_MOUSE_1;
else if (ev.code == BTN_EXTRA)
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;
}
}

devev.type = DEV_KEY;
devev.code = ev.code;
devev.pressed = ev.value;

break;
default:
return NULL;
}

return &devev;
}
Expand Down
16 changes: 10 additions & 6 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@

#include <stdint.h>

#define DEV_MOUSE 0
#define DEV_KEY 1
#define DEV_REMOVED 2
#define DEV_MOUSE_MOVE 0
#define DEV_MOUSE_SCROLL 1
#define DEV_KEY 2
#define DEV_REMOVED 3

#define DEVT_MOUSE 0x1
#define DEVT_KEYBOARD 0x2

#define MAX_DEVICES 64

Expand All @@ -21,7 +25,7 @@ struct device {
*/
int fd;

uint8_t is_keyboard;
uint8_t type;
uint16_t product_id;
uint16_t vendor_id;
char name[64];
Expand All @@ -36,8 +40,8 @@ struct device_event {

uint8_t code;
uint8_t pressed;
uint8_t x;
uint8_t y;
uint32_t x;
uint32_t y;
};


Expand Down
118 changes: 77 additions & 41 deletions src/keyd.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static void (*device_add_cb) (struct device *dev);
static void (*device_remove_cb) (struct device *dev);

/* returns a minimum timeout value */
static int (*device_event_cb) (struct device *dev, uint8_t code, uint8_t processed);
static int (*device_event_cb) (struct device *dev, struct device_event *ev);

/* globals */

Expand Down Expand Up @@ -116,7 +116,7 @@ static void daemon_add_cb(struct device *dev)
* 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)
if (!(dev->type & DEVT_KEYBOARD))
return;

printf("device added: %04x:%04x %s (%s)\n",
Expand Down Expand Up @@ -161,37 +161,61 @@ static void panic_check(uint8_t code, uint8_t pressed)
exit(-1);
}

static int daemon_event_cb(struct device *dev, uint8_t code, uint8_t pressed)
static int daemon_event_cb(struct device *dev, struct device_event *ev)
{
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_SCROLL_DOWN ||
code == KEYD_SCROLL_UP) {
code = KEYD_EXTERNAL_MOUSE_BUTTON;

kbd = active_kbd;
}

if (!kbd)
return 0;
struct keyboard *kbd;

panic_check(code, pressed);
active_kbd = kbd;
/* timeout */
if (!dev)
return kbd_process_key_event(active_kbd, 0, 0);

kbd = dev->data;

switch (ev->type) {
uint8_t code, pressed;
case DEV_KEY:
code = ev->code;
pressed = ev->pressed;

/* Notify the active keyboard of mouse presses to neutralize any oneshots. */
if ((code >= KEYD_LEFT_MOUSE && code <= KEYD_MOUSE_2) && active_kbd)
kbd_process_key_event(active_kbd, KEYD_EXTERNAL_MOUSE_BUTTON, pressed);

if (!kbd)
return 0;

panic_check(code, pressed);

if (dev->type & DEVT_KEYBOARD)
active_kbd = kbd;

dbg2("Processing %04x:%04x (%s): %s %s",
dev->vendor_id,
dev->product_id,
dev->name,
keycode_table[code].name ? keycode_table[code].
name : "undefined", pressed ? "down" : "up");

return kbd_process_key_event(kbd, code, pressed);
break;
case DEV_MOUSE_SCROLL:
/*
* Treat scroll events as mouse buttons so oneshot and the like get
* cleared.
*/
if (active_kbd) {
kbd_process_key_event(active_kbd, KEYD_EXTERNAL_MOUSE_BUTTON, 1);
kbd_process_key_event(active_kbd, KEYD_EXTERNAL_MOUSE_BUTTON, 0);
}

dbg2("Processing %04x:%04x (%s): %s %s",
dev->vendor_id,
dev->product_id,
dev->name,
keycode_table[code].name ? keycode_table[code].name : "undefined",
pressed ? "down" : "up");
vkbd_mouse_scroll(vkbd, ev->x, ev->y);
break;
case DEV_MOUSE_MOVE:
vkbd_mouse_move(vkbd, ev->x, ev->y);
break;
}

return kbd_process_key_event(kbd, code, pressed);
return 0;
}

static int ipc_cb(int fd, const char *input)
Expand Down Expand Up @@ -241,17 +265,29 @@ static void monitor_add_cb(struct device *dev)

}

static int monitor_event_cb(struct device *dev, uint8_t code, uint8_t pressed)
static int monitor_event_cb(struct device *dev, struct device_event *ev)
{
const char *name = keycode_table[code].name;

if (name) {
printf("%s\t%04x:%04x\t%s %s\n",
dev->name,
dev->vendor_id,
dev->product_id,
name,
pressed ? "down" : "up");
if (!ev)
return 0;

switch (ev->type) {
const char *name;
case DEV_MOUSE_MOVE:
printf("%s: move %d %d\n", dev->name, ev->x, ev->y);
break;
case DEV_MOUSE_SCROLL:
printf("%s: scroll %d %d\n", dev->name, ev->x, ev->y);
break;
case DEV_KEY:
name = keycode_table[ev->code].name;

if (name) {
printf("%s\t%04x:%04x\t%s %s\n",
dev->name,
dev->vendor_id,
dev->product_id, name, ev->pressed ? "down" : "up");
}
break;
}

return 0;
Expand Down Expand Up @@ -366,7 +402,7 @@ static int loop(int monitor_mode)
if (timeout) {
int elapsed = get_time_ms() - timeout_start;
if (elapsed >= timeout) {
timeout = device_event_cb(NULL, 0, 0);
timeout = device_event_cb(NULL, NULL);
timeout_start = get_time_ms();
}
}
Expand Down Expand Up @@ -410,7 +446,7 @@ static int loop(int monitor_mode)
devices[i].fd = -1;
prune = 1;
} else {
timeout = device_event_cb(dev, ev->code, ev->pressed);
timeout = device_event_cb(dev, ev);
timeout_start = get_time_ms();
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,6 @@ const struct keycode_table_ent keycode_table[256] = {
[KEYD_EXIT] = { "exit", NULL, NULL },
[KEYD_MOVE] = { "move", NULL, NULL },
[KEYD_EDIT] = { "edit", NULL, NULL },
[KEYD_SCROLLUP] = { "scrollup", NULL, NULL },
[KEYD_SCROLLDOWN] = { "scrolldown", NULL, NULL },
[KEYD_KPLEFTPAREN] = { "kpleftparen", NULL, NULL },
[KEYD_KPRIGHTPAREN] = { "kprightparen", NULL, NULL },
[KEYD_NEW] = { "new", NULL, NULL },
Expand Down
Loading

0 comments on commit 0840338

Please sign in to comment.