lx_emul: multi-touch trackpad support in evdev.c

Also addresses remarks in issue #3019.
This commit is contained in:
Christian Helmuth 2023-09-07 15:52:02 +02:00
parent 12fc9a0fbb
commit 3a566262fe
3 changed files with 338 additions and 88 deletions

View File

@ -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
}

View File

@ -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;

View File

@ -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
}