From 3a566262fee757855d7a81c682fc327c5b7b954b Mon Sep 17 00:00:00 2001 From: Christian Helmuth <christian.helmuth@genode-labs.com> Date: Thu, 7 Sep 2023 15:52:02 +0200 Subject: [PATCH] lx_emul: multi-touch trackpad support in evdev.c Also addresses remarks in issue #3019. --- repos/dde_linux/run/usb_hid_raw.run | 20 +- .../lib/lx_emul/shadow/drivers/input/evdev.c | 350 +++++++++++++++--- repos/ports/run/vbox5_genode_usb_hid_raw.run | 56 +-- 3 files changed, 338 insertions(+), 88 deletions(-) diff --git a/repos/dde_linux/run/usb_hid_raw.run b/repos/dde_linux/run/usb_hid_raw.run index 06e6bd38a7..a935210b47 100644 --- a/repos/dde_linux/run/usb_hid_raw.run +++ b/repos/dde_linux/run/usb_hid_raw.run @@ -172,7 +172,7 @@ install_config { # set fd [open [run_dir]/genode/usb_host_drv.config w] puts $fd { -<config bios_handoff="yes"> +<config bios_handoff="no"> <report devices="yes"/> <policy label_prefix="usb_hid_drv" class="0x3"/> </config>} @@ -201,7 +201,7 @@ grep_output {^\[init } unify_output {(?n) on usb-usbbus.*$} "" unify_output {(?n) at usb-usbbus.*\)} ")" unify_output {(?n)hid-generic.*input:} "hid-generic: input:" -unify_output {(?n)usb-[0-9]-[0-9]:} "usb-X-X:" +unify_output {(?n)usb-[0-9]-[0-9]} "usb-X-X" unify_output {(?n)device number [0-9]+} "device number X" unify_output {(?n)input[0-9]} "inputX" unify_output {(?n) as /devices/.*} "" @@ -212,9 +212,8 @@ compare_output_to { [init -> event_dump] Input event #0 PRESS KEY_X 65534 key count: 1 [init -> event_dump] Input event #1 RELEASE KEY_X key count: 0 [init -> event_dump] Input event #2 PRESS BTN_LEFT 65534 key count: 1 -[init -> event_dump] Input event #3 REL_MOTION -1+0 key count: 1 -[init -> event_dump] Input event #4 REL_MOTION +0+1 key count: 1 -[init -> event_dump] Input event #5 RELEASE BTN_LEFT key count: 0 +[init -> event_dump] Input event #3 REL_MOTION -1+1 key count: 1 +[init -> event_dump] Input event #4 RELEASE BTN_LEFT key count: 0 [init -> usb_hid_drv] usb usb-X-X: USB disconnect, device number X [init -> usb_hid_drv] Disconnected device: inputX [init -> usb_hid_drv] Disconnected device: inputX @@ -224,10 +223,9 @@ compare_output_to { [init -> usb_hid_drv] input: HID 03eb:204d [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [HID 03eb:204d] -[init -> event_dump] Input event #6 PRESS KEY_X 65534 key count: 1 -[init -> event_dump] Input event #7 RELEASE KEY_X key count: 0 -[init -> event_dump] Input event #8 PRESS BTN_LEFT 65534 key count: 1 -[init -> event_dump] Input event #9 REL_MOTION -1+0 key count: 1 -[init -> event_dump] Input event #10 REL_MOTION +0+1 key count: 1 -[init -> event_dump] Input event #11 RELEASE BTN_LEFT key count: 0 +[init -> event_dump] Input event #5 PRESS KEY_X 65534 key count: 1 +[init -> event_dump] Input event #6 RELEASE KEY_X key count: 0 +[init -> event_dump] Input event #7 PRESS BTN_LEFT 65534 key count: 1 +[init -> event_dump] Input event #8 REL_MOTION -1+1 key count: 1 +[init -> event_dump] Input event #9 RELEASE BTN_LEFT key count: 0 } diff --git a/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.c b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.c index 71931e71a0..4b4f14555f 100644 --- a/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.c +++ b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.c @@ -21,50 +21,62 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/init.h> #include <linux/device.h> +/* + * TODO differentiate touchpads, trackpads, touchscreen, etc. + * + * (from Documentation/input/event-codes.rst) + * + * INPUT_PROP_DIRECT + INPUT_PROP_POINTER + * -------------------------------------- + * + * The INPUT_PROP_DIRECT property indicates that device coordinates should be + * directly mapped to screen coordinates (not taking into account trivial + * transformations, such as scaling, flipping and rotating). Non-direct input + * devices require non-trivial transformation, such as absolute to relative + * transformation for touchpads. Typical direct input devices: touchscreens, + * drawing tablets; non-direct devices: touchpads, mice. + * + * The INPUT_PROP_POINTER property indicates that the device is not transposed + * on the screen and thus requires use of an on-screen pointer to trace user's + * movements. Typical pointer devices: touchpads, tablets, mice; non-pointer + * device: touchscreen. + * + * If neither INPUT_PROP_DIRECT or INPUT_PROP_POINTER are set, the property is + * considered undefined and the device type should be deduced in the + * traditional way, using emitted event types. + */ + +struct evdev_mt_slot +{ + int tracking_id; /* -1 means unused */ + int x, y, ox, oy; +}; +#define INIT_MT_SLOT (struct evdev_mt_slot){ -1, -1, -1, -1, -1 } + +/* just stay with primary and secondary touch for now */ +enum { MAX_MT_SLOTS = 2, PRIMARY = 0, SECONDARY = 1, }; + +struct evdev_mt +{ + unsigned num_slots; + unsigned cur_slot; + struct evdev_mt_slot slots[MAX_MT_SLOTS]; +}; struct evdev { struct genode_event *event; struct input_handle handle; + unsigned pending; + int rel_x, rel_y, rel_wx, rel_wy; + struct evdev_mt mt; }; -static void submit_rel_motion(struct genode_event_submit *submit, - unsigned code, int value) -{ - switch (code) { - case REL_X: submit->rel_motion(submit, value, 0); break; - case REL_Y: submit->rel_motion(submit, 0, value); break; - - case REL_HWHEEL: submit->wheel(submit, value, 0); break; - case REL_WHEEL: submit->wheel(submit, 0, value); break; - - /* skip for now because of large values */ - case REL_HWHEEL_HI_RES: break; - case REL_WHEEL_HI_RES: break; - - default: - printk("Unsupported relative motion event code=%d dropped\n", code); - } -} - - -static void submit_key(struct genode_event_submit *submit, - unsigned code, bool press) -{ - /* map BTN_TOUCH to BTN_LEFT */ - if (code == BTN_TOUCH) code = BTN_LEFT; - - if (press) - submit->press(submit, lx_emul_event_keycode(code)); - else - submit->release(submit, lx_emul_event_keycode(code)); -} - - struct genode_event_generator_ctx { struct evdev *evdev; @@ -73,31 +85,244 @@ struct genode_event_generator_ctx }; +struct name { char s[32]; }; +#define NAME_INIT(name, fmt, ...) snprintf(name.s, sizeof(name.s), fmt, ## __VA_ARGS__) + + +static struct name name_of_type(unsigned type) +{ + struct name result = { { 0 } }; + + switch (type) { + case EV_SYN: NAME_INIT(result, "SYN"); break; + case EV_KEY: NAME_INIT(result, "KEY"); break; + case EV_REL: NAME_INIT(result, "REL"); break; + case EV_ABS: NAME_INIT(result, "ABS"); break; + case EV_MSC: NAME_INIT(result, "MSC"); break; + default: NAME_INIT(result, "%3u", type); + } + + return result; +} +#define NAME_OF_TYPE(type) name_of_type(type).s + + +static struct name name_of_code(unsigned type, unsigned code) +{ + struct name result = { { 0 } }; + + switch (type) { + case EV_SYN: + switch (code) { + case SYN_REPORT: NAME_INIT(result, "REPORT"); break; + + default: NAME_INIT(result, "%u", code); + } break; + + case EV_KEY: + switch (code) { + case BTN_LEFT: NAME_INIT(result, "BTN_LEFT"); break; + case BTN_RIGHT: NAME_INIT(result, "BTN_RIGHT"); break; + case BTN_TOOL_FINGER: NAME_INIT(result, "BTN_TOOL_FINGER"); break; + case BTN_TOUCH: NAME_INIT(result, "BTN_TOUCH"); break; + case BTN_TOOL_DOUBLETAP: NAME_INIT(result, "BTN_TOOL_DOUBLETAP"); break; + case BTN_TOOL_TRIPLETAP: NAME_INIT(result, "BTN_TOOL_TRIPLETAP"); break; + case BTN_TOOL_QUADTAP: NAME_INIT(result, "BTN_TOOL_QUADTAP"); break; + case BTN_TOOL_QUINTTAP: NAME_INIT(result, "BTN_TOOL_QUINTTAP"); break; + + default: NAME_INIT(result, "%u", code); + } break; + + case EV_REL: + switch (code) { + case REL_X: NAME_INIT(result, "X"); break; + case REL_Y: NAME_INIT(result, "Y"); break; + case REL_HWHEEL: NAME_INIT(result, "HWHEEL"); break; + case REL_WHEEL: NAME_INIT(result, "WHEEL"); break; + case REL_MISC: NAME_INIT(result, "MISC"); break; + + default: NAME_INIT(result, "%u", code); + } break; + + case EV_ABS: + switch (code) { + case ABS_X: NAME_INIT(result, "X"); break; + case ABS_Y: NAME_INIT(result, "Y"); break; + case ABS_MISC: NAME_INIT(result, "MISC"); break; + case ABS_MT_SLOT: NAME_INIT(result, "MT_SLOT"); break; + case ABS_MT_POSITION_X: NAME_INIT(result, "MT_POSITION_X"); break; + case ABS_MT_POSITION_Y: NAME_INIT(result, "MT_POSITION_Y"); break; + case ABS_MT_TOOL_TYPE: NAME_INIT(result, "MT_TOOL_TYPE"); break; + case ABS_MT_TRACKING_ID: NAME_INIT(result, "MT_TRACKING_ID"); break; + + default: NAME_INIT(result, "%u", code); + } break; + + case EV_MSC: + switch (code) { + case MSC_SCAN: NAME_INIT(result, "SCAN"); break; + case MSC_TIMESTAMP: NAME_INIT(result, "TIMESTAMP"); break; + + default: NAME_INIT(result, "%u", code); + } break; + + default: NAME_INIT(result, "%u", code); + } + + return result; +} +#define NAME_OF_CODE(type, code) name_of_code(type, code).s + + +static bool handle_key(struct evdev *evdev, struct input_value const *v, + struct genode_event_submit *submit) +{ + unsigned code = v->code; + + if (v->type != EV_KEY) + return false; + + /* map BTN_TOUCH to BTN_LEFT */ + if (code == BTN_TOUCH) code = BTN_LEFT; + + if (v->value) + submit->press(submit, lx_emul_event_keycode(code)); + else + submit->release(submit, lx_emul_event_keycode(code)); + + return true; +} + + +static bool record_rel(struct evdev *evdev, struct input_value const *v) +{ + if (v->type != EV_REL) + return false; + + switch (v->code) { + case REL_X: evdev->rel_x += v->value; break; + case REL_Y: evdev->rel_y += v->value; break; + case REL_HWHEEL: evdev->rel_wx += v->value; break; + case REL_WHEEL: evdev->rel_wy += v->value; break; + + default: + return false; + } + evdev->pending++; + + return true; +} + + +static bool record_abs(struct evdev *evdev, struct input_value const *v) +{ + struct evdev_mt * const mt = &evdev->mt; + + if (v->type != EV_ABS) + return false; + + if (mt->num_slots) { + switch (v->code) { + case ABS_MT_SLOT: + mt->cur_slot = (v->value >= 0 ? v->value : 0); + break; + + case ABS_MT_TRACKING_ID: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot] = INIT_MT_SLOT; + evdev->pending++; + } + break; + + case ABS_MT_POSITION_X: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot].x = v->value; + evdev->pending++; + } + break; + + case ABS_MT_POSITION_Y: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot].y = v->value; + evdev->pending++; + } + break; + + default: + return false; + } + } else { + /* XXX absolute events not supported currently */ + return false; + } + + return true; +} + + +static void submit_mt_motion(struct evdev_mt_slot *slot, + struct genode_event_submit *submit) +{ + if (slot->ox != -1 && slot->oy != -1) + submit->rel_motion(submit, slot->x - slot->ox, slot->y - slot->oy); + + slot->ox = slot->x; + slot->oy = slot->y; +} + + +static bool submit_on_syn(struct evdev *evdev, struct input_value const *v, + struct genode_event_submit *submit) +{ + struct evdev_mt * const mt = &evdev->mt; + + if (v->type != EV_SYN || v->code != SYN_REPORT) + return false; + if (!evdev->pending) + return true; + + if (mt->num_slots) { + submit_mt_motion(&mt->slots[PRIMARY], submit); + submit_mt_motion(&mt->slots[SECONDARY], submit); + } else { + if (evdev->rel_x || evdev->rel_y) { + submit->rel_motion(submit, evdev->rel_x, evdev->rel_y); + evdev->rel_x = evdev->rel_y = 0; + } + if (evdev->rel_wx || evdev->rel_wy) { + submit->wheel(submit, evdev->rel_wx, evdev->rel_wy); + evdev->rel_wx = evdev->rel_wy = 0; + } + /* XXX absolute events not supported currently */ + } + + evdev->pending = 0; + + return true; +} + static void evdev_event_generator(struct genode_event_generator_ctx *ctx, struct genode_event_submit *submit) { - int i; + for (int i = 0; i < ctx->count; i++) { + struct evdev *evdev = ctx->evdev; - for (i = 0; i < ctx->count; i++) { - unsigned const type = ctx->values[i].type; - unsigned const code = ctx->values[i].code; - unsigned const value = ctx->values[i].value; + struct input_value const *v = &ctx->values[i]; + + bool processed = false; /* filter input_repeat_key() */ - if ((type == EV_KEY) && (value > 1)) continue; + if ((v->type == EV_KEY) && (v->value > 1)) continue; - /* filter EV_SYN and EV_MSC */ - if (type == EV_SYN || type == EV_MSC) continue; + processed |= handle_key(evdev, v, submit); + processed |= record_abs(evdev, v); + processed |= record_rel(evdev, v); + processed |= submit_on_syn(evdev, v, submit); - switch (type) { - case EV_KEY: submit_key(submit, code, value); break; - case EV_REL: submit_rel_motion(submit, code, value); break; - - default: - printk("Unsupported Event[%u/%u] device=%s, type=%d, code=%d, value=%d dropped\n", - i + 1, ctx->count, dev_name(&ctx->evdev->handle.dev->dev), type, code, value); - continue; - } + if (!processed) + printk("Dropping unsupported Event[%u/%u] device=%s type=%s code=%s value=%d\n", + i + 1, ctx->count, evdev->handle.dev->name, + NAME_OF_TYPE(v->type), NAME_OF_CODE(v->type, v->code), v->value); } } @@ -141,6 +366,31 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, evdev->handle.handler = handler; evdev->handle.name = dev->name; + if (dev->mt) { + struct evdev_mt *mt = &evdev->mt; + + mt->num_slots = min(dev->mt->num_slots, MAX_MT_SLOTS); + mt->cur_slot = 0; + for (int i = 0; i < sizeof(mt->slots)/sizeof(*mt->slots); i++) + mt->slots[i] = INIT_MT_SLOT; + + /* disable undesired events */ + clear_bit(ABS_MT_TOOL_TYPE, dev->absbit); + clear_bit(ABS_X, dev->absbit); + clear_bit(ABS_Y, dev->absbit); + clear_bit(BTN_TOOL_FINGER, dev->keybit); + clear_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + clear_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + clear_bit(BTN_TOOL_QUADTAP, dev->keybit); + clear_bit(BTN_TOOL_QUINTTAP, dev->keybit); + clear_bit(BTN_TOUCH, dev->keybit); + } + + /* disable undesired events */ + clear_bit(EV_MSC, dev->evbit); + clear_bit(REL_HWHEEL_HI_RES, dev->relbit); + clear_bit(REL_WHEEL_HI_RES, dev->relbit); + error = input_register_handle(&evdev->handle); if (error) goto err_free_handle; diff --git a/repos/ports/run/vbox5_genode_usb_hid_raw.run b/repos/ports/run/vbox5_genode_usb_hid_raw.run index 3f59c7f041..bf8cb8d472 100644 --- a/repos/ports/run/vbox5_genode_usb_hid_raw.run +++ b/repos/ports/run/vbox5_genode_usb_hid_raw.run @@ -288,39 +288,41 @@ regexp {(\[init -\> log_terminal\] \[init -\> event_dump\] Input event #0\t.*)} run_genode_until {.*\[init -\> event_dump\] Input event #11.*\n} 60 [output_spawn_id] -unify_output { dev_info:} "" -unify_output { usb [0-9\-]+:} "" -unify_output { number [0-9]+} "" -unify_output {(?n)on usb-usbbus.*$} "" -unify_output {(?n)using .*$} "" -unify_output {(?n)^.*__wait_event.*$} "" -unify_output {(?n)^.*Failed to submit URB.*$} "" -unify_output {(?n)^.*dev_warn.*$} "" +# pay only attention to the output of init and its children +grep_output {^\[init } + +unify_output {(?n) on usb-usbbus.*$} "" +unify_output {(?n) at usb-usbbus.*\)} ")" +unify_output {(?n)hid-generic.*input:} "hid-generic: input:" +unify_output {(?n)usb-[0-9]-[0-9]} "usb-X-X" +unify_output {(?n)device number [0-9]+} "device number X" +unify_output {(?n)input[0-9]} "inputX" +unify_output {(?n) as /devices/.*} "" +unify_output {(?n)^\[init -\> usb_drv.*} "" +unify_output {(?n)^\[init -\> log_terminal\] \[init -\> usb_drv.*} "" unify_output {(?n)^.*dangling allocation.*$} "" -unify_output {(?n)^.*Warning:.*$} "" unify_output {(?n)^.*usbfs: process .* did not claim interface.*$} "" -unify_output { [0-9][0-9][0-9][0-9]:[0-9][0-9][0-9][0-9] } " " -filter_out_color_escape_sequences trim_lines compare_output_to { [init -> log_terminal] [init -> event_dump] Input event #0 PRESS KEY_X 65534 key count: 1 [init -> log_terminal] [init -> event_dump] Input event #1 RELEASE KEY_X key count: 0 [init -> log_terminal] [init -> event_dump] Input event #2 PRESS BTN_LEFT 65534 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #3 REL_MOTION -1+0 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #4 REL_MOTION +0+1 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #5 RELEASE BTN_LEFT key count: 0 -[init -> usb_drv] USB disconnect, device -[init -> log_terminal] [init -> usb_drv -> usb_drv] USB disconnect, device -[init -> usb_drv] new full-speed USB device -[init -> virtualbox] Attach USB device usb-2-8 -[init -> log_terminal] [init -> usb_drv -> usb_drv] new full-speed USB device -[init -> log_terminal] [init -> usb_hid_drv] input: USB HID v1.11 Keyboard [HID 03eb:204d] -[init -> log_terminal] [init -> usb_hid_drv] input: USB HID v1.11 Mouse [HID 03eb:204d] -[init -> log_terminal] [init -> event_dump] Input event #6 PRESS KEY_X 65534 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #7 RELEASE KEY_X key count: 0 -[init -> log_terminal] [init -> event_dump] Input event #8 PRESS BTN_LEFT 65534 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #9 REL_MOTION -1+0 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #10 REL_MOTION +0+1 key count: 1 -[init -> log_terminal] [init -> event_dump] Input event #11 RELEASE BTN_LEFT key count: 0 +[init -> log_terminal] [init -> event_dump] Input event #3 REL_MOTION -1+1 key count: 1 +[init -> log_terminal] [init -> event_dump] Input event #4 RELEASE BTN_LEFT key count: 0 +[init -> log_terminal] [init -> usb_hid_drv] usb usb-X-X: USB disconnect, device number X +[init -> log_terminal] [init -> usb_hid_drv] Disconnected device: inputX +[init -> log_terminal] [init -> usb_hid_drv] Disconnected device: inputX +[init -> virtualbox] Attach USB device usb-X-X +[init -> log_terminal] [init -> usb_hid_drv] input: HID 03eb:204d +[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) +[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [HID 03eb:204d] +[init -> log_terminal] [init -> usb_hid_drv] input: HID 03eb:204d +[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) +[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [HID 03eb:204d] +[init -> log_terminal] [init -> event_dump] Input event #5 PRESS KEY_X 65534 key count: 1 +[init -> log_terminal] [init -> event_dump] Input event #6 RELEASE KEY_X key count: 0 +[init -> log_terminal] [init -> event_dump] Input event #7 PRESS BTN_LEFT 65534 key count: 1 +[init -> log_terminal] [init -> event_dump] Input event #8 REL_MOTION -1+1 key count: 1 +[init -> log_terminal] [init -> event_dump] Input event #9 RELEASE BTN_LEFT key count: 0 }