From 1dcc6fda6b9f99c9896bdb8b12bf0257e23aba89 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Fri, 19 Jan 2024 15:43:53 +0100 Subject: [PATCH] lx_emul: improve motion-device handling in evdev The key element of the improvement is differentiated processing of events of the following device types. Mouse: relative motion Pointer: absolute motion (Qemu usb-tablet and IP-KVM devices) Touchpad: relative motion via absolute touchpad coordinates Touchtool: absolute motion (e.g., stylus) Touchscreen: absolute motion and finger (multi-) touch Processing is done in two stages for one "input packet". First, all events of the packet are recorded into the current evdev state with device-type specific operations. Then, appropriate Genode input events are generated from the accumulated evdev state in the submission stage (again by device-type specific functions). A simple version of tap-to-click was added to the touchpad support. Fixes #5105 --- repos/dde_linux/run/usb_hid_raw.run | 10 +- .../lib/lx_emul/shadow/drivers/input/evdev.c | 896 +++++++++++++----- .../lib/lx_emul/shadow/drivers/input/evdev.h | 223 +++++ repos/ports/run/vbox5_genode_usb_hid_raw.run | 10 +- 4 files changed, 867 insertions(+), 272 deletions(-) create mode 100644 repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.h diff --git a/repos/dde_linux/run/usb_hid_raw.run b/repos/dde_linux/run/usb_hid_raw.run index 8e4733dac7..e39e05f328 100644 --- a/repos/dde_linux/run/usb_hid_raw.run +++ b/repos/dde_linux/run/usb_hid_raw.run @@ -232,8 +232,8 @@ trim_lines 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+1 key count: 1 +[init -> event_dump] Input event #2 REL_MOTION -1+1 key count: 0 +[init -> event_dump] Input event #3 PRESS BTN_LEFT 65534 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 @@ -242,11 +242,11 @@ compare_output_to { [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [HID 03eb:204d] [init -> usb_hid_drv] input: HID 03eb:204d -[init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) +[init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) MOUSE [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [HID 03eb:204d] [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 #7 REL_MOTION -1+1 key count: 0 +[init -> event_dump] Input event #8 PRESS BTN_LEFT 65534 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 368efec88f..7cb11220ae 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 @@ -26,57 +26,609 @@ #include /* - * TODO differentiate touchpads, trackpads, touchscreen, etc. + * Input devices with motion events * - * (from Documentation/input/event-codes.rst) - * - * INPUT_PROP_DIRECT + INPUT_PROP_POINTER - * -------------------------------------- + * (from Documentation/input/event-codes.rst and multi-touch-protocol.rst) * * 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. + * transformations, such as scaling, flipping and rotating). + * -> touchscreen, tablet (stylus/pen) * - * 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. + * Non-direct input devices may require non-trivial transformation, such as + * absolute to relative transformation. + * -> mouse, touchpad * - * 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. + * Historically a touch device with BTN_TOOL_FINGER and BTN_TOUCH was + * interpreted as a touchpad by userspace, while a similar device without + * BTN_TOOL_FINGER was interpreted as a touchscreen. For backwards + * compatibility with current userspace it is recommended to follow this + * distinction. + * + * In Linux, stylus/pen tool proximity is reported by BTN_TOOL_PEN/RUBBER plus + * ABS_DISTANCE events. The actual contact to the surface emits an additional + * BTN_TOUCH event. For multi-touch devices, the "tool" is also reported via + * BTN_TOOL_FINGER/DOUBLETAP etc. + * + * Thus, these devices must be differentiated. + * + * Mouse: relative motion + * Pointer: absolute motion (Qemu usb-tablet and IP-KVM devices) + * Touchpad: relative motion via absolute touchpad coordinates + * Touchtool: absolute motion (e.g., stylus) + * Touchscreen: absolute motion and finger (multi-) touch */ +static bool is_rel_dev(struct input_dev *dev) +{ + return test_bit(EV_REL, dev->evbit) && test_bit(REL_X, dev->relbit); +} + +static bool is_abs_dev(struct input_dev *dev) +{ + return test_bit(EV_ABS, dev->evbit) && test_bit(ABS_X, dev->absbit); +} + +static bool is_touch_dev(struct input_dev *dev) +{ + return test_bit(BTN_TOUCH, dev->keybit); +} + +static bool is_tool_dev(struct input_dev *dev) +{ + return test_bit(BTN_TOOL_PEN, dev->keybit) + || test_bit(BTN_TOOL_RUBBER, dev->keybit) + || test_bit(BTN_TOOL_BRUSH, dev->keybit) + || test_bit(BTN_TOOL_PENCIL, dev->keybit) + || test_bit(BTN_TOOL_AIRBRUSH, dev->keybit) + || test_bit(BTN_TOOL_MOUSE, dev->keybit) + || test_bit(BTN_TOOL_LENS, dev->keybit); +} + +enum evdev_motion { + MOTION_NONE, + MOTION_MOUSE, /* relative motion */ + MOTION_POINTER, /* absolute motion */ + MOTION_TOUCHPAD, /* relative motion based on absolute axes */ + MOTION_TOUCHTOOL, /* absolute motion */ + MOTION_TOUCHSCREEN, /* absolute motion */ +}; + +enum evdev_motion evdev_motion(struct input_dev const *dev) +{ + if (is_rel_dev(dev)) + return MOTION_MOUSE; + + if (!is_abs_dev(dev)) + return MOTION_NONE; + + if (!is_touch_dev(dev)) + return MOTION_POINTER; + + if (test_bit(BTN_TOOL_FINGER, dev->keybit)) + return MOTION_TOUCHPAD; + + if (is_tool_dev(dev)) + return MOTION_TOUCHTOOL; + + return MOTION_TOUCHSCREEN; +} + + struct evdev_mt_slot { - int tracking_id; /* -1 means unused */ + int 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, }; + +/* + * Maximum number of touch slots supported. + * + * Many Linux drivers report 2 to 10 slots, the Magic Trackpad reports 16. The + * Surface driver reports 64, which we just ignore. + */ +enum { MAX_MT_SLOTS = 16 }; struct evdev_mt { + bool pending; unsigned num_slots; unsigned cur_slot; struct evdev_mt_slot slots[MAX_MT_SLOTS]; }; +#define array_for_each_element(element, array) \ + for ((element) = (array); \ + (element) < ((array) + ARRAY_SIZE((array))); \ + (element)++) + +#define for_each_mt_slot(slot, mt) \ + array_for_each_element(slot, (mt)->slots) + + +struct evdev_key +{ + bool pending; + unsigned code; + bool press; + + typeof(jiffies) jiffies; +}; +#define INIT_KEY (struct evdev_key){ false, 0, false } +#define EVDEV_KEY(code, press) (struct evdev_key){ true, code, press, jiffies } + +struct evdev_keys +{ + unsigned pending; /* pending keys counter */ + struct evdev_key key[16]; /* max 16 keys per packet */ +}; + +#define for_each_key(key, keys, pending_only) \ + array_for_each_element(key, (keys)->key) \ + if (!(pending_only) || (key)->pending) + +#define for_each_pending_key(key, keys) \ + if ((keys)->pending) \ + for_each_key(key, keys, true) + + +struct evdev_xy +{ + bool pending; + int x, y; +}; +#define INIT_XY (struct evdev_xy){ false, 0, 0 } + + +struct evdev_touchpad +{ + typeof(jiffies) touch_time; + bool btn_left_pressed; /* state of (physical) BTN_LEFT */ +}; + + 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; + enum evdev_motion motion; + + /* record of all events in one packet - submitted on SYN */ + unsigned tool; /* BTN_TOOL_* or 0 */ + struct evdev_keys keys; + struct evdev_xy rel; + struct evdev_xy wheel; + struct evdev_xy abs; + struct evdev_mt mt; + + /* device-specific state machine */ + union { + struct evdev_touchpad touchpad; + }; }; +/* helper functions (require declarations above) */ +#include "evdev.h" + + +static bool record_mouse(struct evdev *evdev, struct input_value const *v) +{ + if (v->type != EV_REL || evdev->motion != MOTION_MOUSE) + return false; + + switch (v->code) { + case REL_X: evdev->rel.pending = true; evdev->rel.x += v->value; break; + case REL_Y: evdev->rel.pending = true; evdev->rel.y += v->value; break; + case REL_HWHEEL: evdev->wheel.pending = true; evdev->wheel.x += v->value; break; + case REL_WHEEL: evdev->wheel.pending = true; evdev->wheel.y += v->value; break; + + default: + return false; + } + + return true; +} + + +static bool record_abs(struct evdev *evdev, struct input_value const *v) +{ + if (v->type != EV_ABS) + return false; + + switch (v->code) { + case ABS_X: evdev->abs.pending = true; evdev->abs.x = v->value; break; + case ABS_Y: evdev->abs.pending = true; evdev->abs.y = v->value; break; + + default: + return false; + } + + return true; +} + + +static bool record_wheel(struct evdev *evdev, struct input_value const *v) +{ + if (v->type != EV_REL) + return false; + + switch (v->code) { + case REL_HWHEEL: evdev->wheel.pending = true; evdev->wheel.x += v->value; break; + case REL_WHEEL: evdev->wheel.pending = true; evdev->wheel.y += v->value; break; + + default: + return false; + } + + return true; +} + + +static bool record_pointer(struct evdev *evdev, struct input_value const *v) +{ + if (evdev->motion != MOTION_POINTER) + return false; + + return record_abs(evdev, v) || record_wheel(evdev, v); +} + + +static bool record_touchtool(struct evdev *evdev, struct input_value const *v) +{ + if (evdev->motion != MOTION_TOUCHTOOL) + return false; + + return record_abs(evdev, v) || record_wheel(evdev, v); +} + + +static bool record_mt(struct evdev_mt *mt, struct input_value const *v) +{ + if (v->type != EV_ABS || !mt->num_slots) + return false; + + switch (v->code) { + case ABS_MT_SLOT: + mt->cur_slot = (v->value >= 0 ? v->value : 0); + /* nothing pending yet */ + break; + + case ABS_MT_TRACKING_ID: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot].id = v->value >= 0 ? mt->cur_slot : -1; + mt->pending = true; + } + break; + + case ABS_MT_POSITION_X: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot].x = v->value; + mt->pending = true; + } + break; + + case ABS_MT_POSITION_Y: + if (mt->cur_slot < mt->num_slots) { + mt->slots[mt->cur_slot].y = v->value; + mt->pending = true; + } + break; + + default: + return false; + } + + return true; +} + + +static bool record_touchpad(struct evdev *evdev, struct input_value const *v) +{ + if (evdev->motion != MOTION_TOUCHPAD) + return false; + + /* monitor (physical) button state clashing with tap-to-click */ + if (v->type == EV_KEY && v->code == BTN_LEFT) + evdev->touchpad.btn_left_pressed = !!v->value; + + /* only multi-touch pads supported currently */ + return record_mt(&evdev->mt, v); +} + + +static bool record_touchscreen(struct evdev *evdev, struct input_value const *v) +{ + if (evdev->motion != MOTION_TOUCHSCREEN) + return false; + + /* only multi-touch screens supported currently */ + return record_mt(&evdev->mt, v); +} + + +static bool is_tool_key(unsigned code) +{ + switch (code) { + case BTN_TOOL_PEN: + case BTN_TOOL_RUBBER: + case BTN_TOOL_BRUSH: + case BTN_TOOL_PENCIL: + case BTN_TOOL_AIRBRUSH: + case BTN_TOOL_FINGER: + case BTN_TOOL_MOUSE: + case BTN_TOOL_LENS: + case BTN_TOOL_QUINTTAP: + case BTN_TOOL_DOUBLETAP: + case BTN_TOOL_TRIPLETAP: + case BTN_TOOL_QUADTAP: + return true; + + default: + return false; + } +} + +static bool record_key(struct evdev *evdev, struct input_value const *v) +{ + struct evdev_keys * const keys = &evdev->keys; + + if (v->type != EV_KEY) + return false; + + if (is_tool_key(v->code)) { + evdev->tool = v->value ? v->code : 0; + } else { + struct evdev_key *key; + for_each_key(key, keys, false) { + if (key->pending) + continue; + + *key = EVDEV_KEY(v->code, !!v->value); + keys->pending++; + break; + } + } + + return true; +} + + +static void submit_press_release(struct evdev_key *key, struct evdev_keys *keys, + struct genode_event_submit *submit) +{ + if (!key->pending) + return; + + if (key->press) + submit->press(submit, lx_emul_event_keycode(key->code)); + else + submit->release(submit, lx_emul_event_keycode(key->code)); + + *key = INIT_KEY; + keys->pending--; +} + + +static void submit_keys(struct evdev_keys *keys, struct genode_event_submit *submit) +{ + struct evdev_key *key; + + if (!keys->pending) + return; + + for_each_pending_key(key, keys) + submit_press_release(key, keys, submit); +} + + +static void submit_mouse(struct evdev *evdev, struct genode_event_submit *submit) +{ + if (evdev->motion != MOTION_MOUSE) + return; + + if (evdev->rel.pending) { + submit->rel_motion(submit, evdev->rel.x, evdev->rel.y); + evdev->rel = INIT_XY; + } + + if (evdev->wheel.pending) { + submit->wheel(submit, evdev->wheel.x, evdev->wheel.y); + evdev->wheel = INIT_XY; + } +} + + +static void submit_pointer(struct evdev *evdev, struct genode_event_submit *submit) +{ + if (evdev->motion != MOTION_POINTER) + return; + + if (evdev->abs.pending) { + submit->abs_motion(submit, evdev->abs.x, evdev->abs.y); + evdev->abs.pending = false; + } + + if (evdev->wheel.pending) { + submit->wheel(submit, evdev->wheel.x, evdev->wheel.y); + evdev->wheel = INIT_XY; + } +} + + +static void submit_touchtool(struct evdev *evdev, struct genode_event_submit *submit) +{ + struct evdev_keys * const keys = &evdev->keys; + + struct evdev_key *key; + + if (evdev->motion != MOTION_TOUCHTOOL) + return; + + if (evdev->abs.pending) { + submit->abs_motion(submit, evdev->abs.x, evdev->abs.y); + evdev->abs.pending = false; + } + + /* submit recorded tool on BTN_TOUCH */ + for_each_pending_key(key, keys) { + if (key->code != BTN_TOUCH) + continue; + + key->code = evdev->tool; + submit_press_release(key, keys, submit); + break; + } +} + + +static void touchpad_tap_to_click(struct evdev_keys *keys, struct evdev_touchpad *tp, + struct genode_event_submit *submit) +{ + enum { TAP_TIME = 130 /* max touch duration in ms */ }; + + struct evdev_key *key; + + if (!keys->pending) + return; + + for_each_pending_key(key, keys) { + if (key->code != BTN_TOUCH) + continue; + + if (key->press && !tp->btn_left_pressed) { + tp->touch_time = key->jiffies; + } else { + if (time_before(key->jiffies, tp->touch_time + msecs_to_jiffies(TAP_TIME))) { + submit->press(submit, lx_emul_event_keycode(BTN_LEFT)); + submit->release(submit, lx_emul_event_keycode(BTN_LEFT)); + } + tp->touch_time = 0; + } + + *key = INIT_KEY; + keys->pending--; + break; + } +} + + +static void submit_touchpad(struct evdev *evdev, struct genode_event_submit *submit) +{ + struct evdev_mt * const mt = &evdev->mt; + + struct evdev_mt_slot *slot; + + if (evdev->motion != MOTION_TOUCHPAD) + return; + + /* + * TODO device state model + * + * - click without small motion (if pad is pressable button) + * - two-finger scrolling + * - edge scrolling + * - virtual-button regions + * + * https://wayland.freedesktop.org/libinput/doc/latest/tapping.html + */ + + if (mt->pending) { + for_each_mt_slot(slot, mt) { + if (slot->id == -1) { + *slot = INIT_MT_SLOT; + continue; + } + + 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; + } + + mt->pending = false; + } + + touchpad_tap_to_click(&evdev->keys, &evdev->touchpad, submit); +} + + +static void submit_touchscreen(struct evdev *evdev, struct genode_event_submit *submit) +{ + struct evdev_mt * const mt = &evdev->mt; + struct evdev_keys * const keys = &evdev->keys; + + struct evdev_key *key; + struct evdev_mt_slot *slot; + + if (evdev->motion != MOTION_TOUCHSCREEN) + return; + + if (mt->pending) { + for_each_mt_slot(slot, mt) { + if (slot->id == -1 && slot->ox != -1 && slot->oy != -1) { + submit->touch_release(submit, slot->id); + + *slot = INIT_MT_SLOT; + continue; + } + + /* skip unchanged slots */ + if (slot->ox == slot->x && slot->oy == slot->y) + continue; + + if (slot->x != -1 && slot->y != -1) { + struct genode_event_touch_args args = { + .finger = slot->id, + .xpos = slot->x, + .ypos = slot->y, + .width = 1 + }; + submit->touch(submit, &args); + } + + slot->ox = slot->x; + slot->oy = slot->y; + } + + mt->pending = false; + } + + /* filter BTN_TOUCH */ + for_each_pending_key(key, keys) { + if (key->code != BTN_TOUCH) + continue; + + *key = INIT_KEY; + keys->pending--; + break; + } +} + + +static bool submit_on_syn(struct evdev *evdev, struct input_value const *v, + struct genode_event_submit *submit) +{ + if (v->type != EV_SYN || v->code != SYN_REPORT) + return false; + + /* motion devices */ + submit_mouse(evdev, submit); + submit_pointer(evdev, submit); + submit_touchpad(evdev, submit); + submit_touchtool(evdev, submit); + submit_touchscreen(evdev, submit); + + /* submit keys not handled above */ + submit_keys(&evdev->keys, submit); + + return true; +} + + struct genode_event_generator_ctx { struct evdev *evdev; @@ -85,222 +637,6 @@ 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) { @@ -317,9 +653,12 @@ static void evdev_event_generator(struct genode_event_generator_ctx *ctx, /* filter input_repeat_key() */ if ((v->type == EV_KEY) && (v->value > 1)) continue; - processed |= handle_key(evdev, v, submit); - processed |= record_abs(evdev, v); - processed |= record_rel(evdev, v); + processed |= record_mouse(evdev, v); + processed |= record_pointer(evdev, v); + processed |= record_touchpad(evdev, v); + processed |= record_touchtool(evdev, v); + processed |= record_touchscreen(evdev, v); + processed |= record_key(evdev, v); processed |= submit_on_syn(evdev, v, submit); if (!processed) @@ -351,6 +690,55 @@ static void evdev_event(struct input_handle *handle, } +static void init_motion(struct evdev *evdev) +{ + struct input_dev *dev = evdev->handle.dev; + + evdev->motion = evdev_motion(dev); + + switch (evdev->motion) { + case MOTION_NONE: + case MOTION_MOUSE: + case MOTION_POINTER: + /* nothing to do */ + break; + + case MOTION_TOUCHPAD: + case MOTION_TOUCHTOOL: + case MOTION_TOUCHSCREEN: + if (dev->mt) { + struct evdev_mt *mt = &evdev->mt; + + struct evdev_mt_slot *slot; + + mt->num_slots = min(dev->mt->num_slots, MAX_MT_SLOTS); + mt->cur_slot = 0; + for_each_mt_slot(slot, mt) + *slot = INIT_MT_SLOT; + + /* disable undesired events */ + clear_bit(ABS_X, dev->absbit); + clear_bit(ABS_Y, dev->absbit); + clear_bit(ABS_PRESSURE, dev->absbit); + clear_bit(ABS_MT_TOUCH_MAJOR, dev->absbit); + clear_bit(ABS_MT_TOUCH_MINOR, dev->absbit); + clear_bit(ABS_MT_WIDTH_MAJOR, dev->absbit); + clear_bit(ABS_MT_WIDTH_MINOR, dev->absbit); + clear_bit(ABS_MT_ORIENTATION, dev->absbit); + clear_bit(ABS_MT_TOOL_TYPE, dev->absbit); + clear_bit(ABS_MT_PRESSURE, dev->absbit); + clear_bit(ABS_MT_TOOL_X, dev->absbit); + clear_bit(ABS_MT_TOOL_Y, dev->absbit); + } else { + /* disable undesired events */ + clear_bit(ABS_PRESSURE, dev->absbit); + clear_bit(ABS_DISTANCE, dev->absbit); + } + break; + } +} + + static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { @@ -369,25 +757,7 @@ 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); - } + init_motion(evdev); /* disable undesired events */ clear_bit(EV_MSC, dev->evbit); @@ -402,10 +772,12 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_unregister_handle; - printk("Connected device: %s (%s at %s)\n", + printk("Connected device: %s (%s at %s) %s%s\n", dev_name(&dev->dev), dev->name ?: "unknown", - dev->phys ?: "unknown"); + dev->phys ?: "unknown", + dev->mt ? "MULTITOUCH " : "", + evdev->motion != MOTION_NONE ? NAME_OF_MOTION(evdev->motion) : ""); return 0; diff --git a/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.h b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.h new file mode 100644 index 0000000000..6838a9a7c4 --- /dev/null +++ b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/evdev.h @@ -0,0 +1,223 @@ +/* + * \brief Linux emulation environment: evdev helpers + * \author Christian Helmuth + * \date 2024-01-29 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _SHADOW__DRIVERS__INPUT__EVDEV_H_ +#define _SHADOW__DRIVERS__INPUT__EVDEV_H_ + + +struct name { char s[32]; }; +#define NAME_INIT(name, fmt, ...) snprintf(name.s, sizeof(name.s), fmt, ## __VA_ARGS__) + +static struct name name_of_motion(enum evdev_motion motion) +{ + struct name result = { { 0 } }; + + switch (motion) { + case MOTION_NONE: NAME_INIT(result, "NONE"); break; + case MOTION_MOUSE: NAME_INIT(result, "MOUSE"); break; + case MOTION_POINTER: NAME_INIT(result, "POINTER"); break; + case MOTION_TOUCHPAD: NAME_INIT(result, "TOUCHPAD"); break; + case MOTION_TOUCHTOOL: NAME_INIT(result, "TOUCHTOOL"); break; + case MOTION_TOUCHSCREEN: NAME_INIT(result, "TOUCHSCREEN"); break; + } + + return result; +} +#define NAME_OF_MOTION(type) name_of_motion(type).s + +static struct name name_of_prop(unsigned prop) +{ + struct name result = { { 0 } }; + + switch (prop) { + case INPUT_PROP_POINTER: NAME_INIT(result, "POINTER"); break; + case INPUT_PROP_DIRECT: NAME_INIT(result, "DIRECT"); break; + case INPUT_PROP_BUTTONPAD: NAME_INIT(result, "BUTTONPAD"); break; + case INPUT_PROP_SEMI_MT: NAME_INIT(result, "SEMI_MT"); break; + case INPUT_PROP_TOPBUTTONPAD: NAME_INIT(result, "TOPBUTTONPAD"); break; + case INPUT_PROP_POINTING_STICK: NAME_INIT(result, "POINTING_STICK"); break; + case INPUT_PROP_ACCELEROMETER: NAME_INIT(result, "ACCELEROMETER"); break; + default: NAME_INIT(result, "%2u", prop); + } + + return result; +} +#define NAME_OF_PROP(type) name_of_prop(type).s + +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; + case EV_SW: NAME_INIT(result, "SW "); break; + case EV_LED: NAME_INIT(result, "LED"); break; + case EV_REP: NAME_INIT(result, "REP"); 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_MIDDLE: NAME_INIT(result, "BTN_MIDDLE"); break; + case BTN_SIDE: NAME_INIT(result, "BTN_SIDE"); break; + case BTN_EXTRA: NAME_INIT(result, "BTN_EXTRA"); break; + case BTN_FORWARD: NAME_INIT(result, "BTN_FORWARD"); break; + case BTN_BACK: NAME_INIT(result, "BTN_BACK"); break; + case BTN_TASK: NAME_INIT(result, "BTN_TASK"); break; + case BTN_TOOL_PEN: NAME_INIT(result, "BTN_TOOL_PEN"); break; + case BTN_TOOL_RUBBER: NAME_INIT(result, "BTN_TOOL_RUBBER"); break; + case BTN_TOOL_FINGER: NAME_INIT(result, "BTN_TOOL_FINGER"); break; + case BTN_TOUCH: NAME_INIT(result, "BTN_TOUCH"); break; + case BTN_STYLUS: NAME_INIT(result, "BTN_STYLUS"); break; + case BTN_STYLUS2: NAME_INIT(result, "BTN_STYLUS2"); 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; + case REL_WHEEL_HI_RES: NAME_INIT(result, "WHEEL_HI_RES"); break; + case REL_HWHEEL_HI_RES: NAME_INIT(result, "HWHEEL_HI_RES"); 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_PRESSURE: NAME_INIT(result, "PRESSURE"); break; + case ABS_DISTANCE: NAME_INIT(result, "DISTANCE"); break; + case ABS_MISC: NAME_INIT(result, "MISC"); break; + case ABS_MT_SLOT: NAME_INIT(result, "MT_SLOT"); break; + case ABS_MT_TOUCH_MAJOR: NAME_INIT(result, "MT_TOUCH_MAJOR"); break; + case ABS_MT_TOUCH_MINOR: NAME_INIT(result, "MT_TOUCH_MINOR"); break; + case ABS_MT_WIDTH_MAJOR: NAME_INIT(result, "MT_WIDTH_MAJOR"); break; + case ABS_MT_WIDTH_MINOR: NAME_INIT(result, "MT_WIDTH_MINOR"); break; + case ABS_MT_ORIENTATION: NAME_INIT(result, "MT_ORIENTATION"); 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; + case ABS_MT_PRESSURE: NAME_INIT(result, "MT_PRESSURE"); break; + case ABS_MT_DISTANCE: NAME_INIT(result, "MT_DISTANCE"); break; + case ABS_MT_TOOL_X: NAME_INIT(result, "MT_TOOL_X"); break; + case ABS_MT_TOOL_Y: NAME_INIT(result, "MT_TOOL_Y"); 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 void log_event_in_packet(unsigned cur, unsigned max, + struct input_value const *v, + struct evdev const *evdev) +{ + printk("--- Event[%u/%u] %s '%s' %s type=%s code=%s value=%d\n", cur, max, + dev_name(&evdev->handle.dev->dev), evdev->handle.dev->name, NAME_OF_MOTION(evdev->motion), + NAME_OF_TYPE(v->type), NAME_OF_CODE(v->type, v->code), v->value); +} + +static void log_device_info(struct input_dev *dev) +{ + unsigned bit; + + printk("device: %s (%s at %s)\n", dev_name(&dev->dev), dev->name ?: "unknown", dev->phys ?: "unknown"); + printk(" propbit:"); + for_each_set_bit(bit, dev->propbit, INPUT_PROP_CNT) + printk(" %s", NAME_OF_PROP(bit)); + printk("\n"); + printk(" evbit: "); + for_each_set_bit(bit, dev->evbit, EV_CNT) + printk(" %s", NAME_OF_TYPE(bit)); + printk("\n"); + + if (test_bit(EV_REL, dev->evbit)) { + printk(" relbit: "); + for_each_set_bit(bit, dev->relbit, REL_CNT) + printk(" %s", NAME_OF_CODE(EV_REL, bit)); + printk("\n"); + } + if (test_bit(EV_ABS, dev->evbit)) { + printk(" absbit: "); + for_each_set_bit(bit, dev->absbit, ABS_CNT) + printk(" %s", NAME_OF_CODE(EV_ABS, bit)); + printk("\n"); + } + if (test_bit(EV_ABS, dev->evbit) || test_bit(EV_REL, dev->evbit)) { + printk(" keybit: "); + for_each_set_bit(bit, dev->keybit, KEY_CNT) + printk(" %s", NAME_OF_CODE(EV_KEY, bit)); + printk("\n"); + } + if (test_bit(EV_LED, dev->evbit)) { + unsigned count = 0; + for_each_set_bit(bit, dev->ledbit, LED_CNT) + count++; + printk(" leds: %u\n", count); + } + printk(" hint_events_per_packet: %u max_vals: %u\n", dev->hint_events_per_packet, dev->max_vals); + if (dev->mt) { + printk(" dev->mt->flags=%x\n", dev->mt->flags); + printk(" dev->mt->num_slots=%u\n", dev->mt->num_slots); + } + if (dev->absinfo) { + printk(" absinfo: %px\n", dev->absinfo); + } +} + +#endif /* _SHADOW__DRIVERS__INPUT__EVDEV_H_ */ diff --git a/repos/ports/run/vbox5_genode_usb_hid_raw.run b/repos/ports/run/vbox5_genode_usb_hid_raw.run index bf8cb8d472..b2878e5e30 100644 --- a/repos/ports/run/vbox5_genode_usb_hid_raw.run +++ b/repos/ports/run/vbox5_genode_usb_hid_raw.run @@ -307,8 +307,8 @@ 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+1 key count: 1 +[init -> log_terminal] [init -> event_dump] Input event #2 REL_MOTION -1+1 key count: 0 +[init -> log_terminal] [init -> event_dump] Input event #3 PRESS BTN_LEFT 65534 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 @@ -318,11 +318,11 @@ compare_output_to { [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] Connected device: inputX (HID 03eb:204d) MOUSE [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 #7 REL_MOTION -1+1 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 RELEASE BTN_LEFT key count: 0 }