From 411f95b17fc82d02b91a006371396b1d54db45ad Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 26 Feb 2015 13:55:08 +1000 Subject: [PATCH] tablet: sync tools already in proximity at startup If a tool is in proximity when we init, send a proximity event immediately. This is only partially reliable due to the current kernel behavior: * if the tool comes into proximity when there is no evdev client, the device won't send any events and must be lifted out-of-proximity first. Patch is in the works, see https://patchwork.kernel.org/patch/5924611/ * before 3.19, if the tool was in proximity (with an evdev client attached), but goes out of proximity and back in with no client connected, we get an immediate proximity out event from the kernel once we connect to the device and no further events after that. See kernel commit b905811a49bcd6e6726ce5bbb591f57aaddfd3be Otherwise, things work as expected. The above should be fixed in the kernel anyway. Note that this changes the order of events during a udev seat init, before we had all DEVICE_ADDED events in a row, now the proximity event may be interspersed. Reported-by: Jason Gerecke Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires --- src/evdev-mt-touchpad.c | 1 + src/evdev-tablet.c | 52 +++++++++++++++++++++++++++++++++++++++- src/evdev-tablet.h | 21 ++++++++++++++++ src/evdev.c | 5 ++++ src/evdev.h | 5 ++++ test/tablet.c | 53 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b90d84c5..b3a8b08c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -897,6 +897,7 @@ static struct evdev_dispatch_interface tp_interface = { tp_device_removed, /* device_suspended, treat as remove */ tp_device_added, /* device_resumed, treat as add */ tp_tag_device, + NULL, /* post_added */ }; static void diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 9b8b77ee..8c11409e 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -190,7 +190,7 @@ tablet_update_tool(struct tablet_dispatch *tablet, tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY); } - else + else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); } @@ -868,6 +868,9 @@ tablet_flush(struct tablet_dispatch *tablet, tablet->current_tool_id, tablet->current_tool_serial); + if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) + return; + if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { /* Release all stylus buttons */ memset(tablet->button_state.stylus_buttons, @@ -915,7 +918,11 @@ tablet_flush(struct tablet_dispatch *tablet, tablet_change_to_left_handed(device); } +} +static inline void +tablet_reset_state(struct tablet_dispatch *tablet) +{ /* Update state */ memcpy(&tablet->prev_button_state, &tablet->button_state, @@ -946,6 +953,7 @@ tablet_process(struct evdev_dispatch *dispatch, break; case EV_SYN: tablet_flush(tablet, device, time); + tablet_reset_state(tablet); break; default: log_error(device->base.seat->libinput, @@ -970,6 +978,47 @@ tablet_destroy(struct evdev_dispatch *dispatch) free(tablet); } +static void +tablet_check_initial_proximity(struct evdev_device *device, + struct evdev_dispatch *dispatch) +{ + bool tool_in_prox = false; + int code, state; + enum libinput_tool_type tool; + struct tablet_dispatch *tablet = (struct tablet_dispatch*)dispatch; + + for (tool = LIBINPUT_TOOL_PEN; tool <= LIBINPUT_TOOL_MAX; tool++) { + code = tablet_tool_to_evcode(tool); + + /* we only expect one tool to be in proximity at a time */ + if (libevdev_fetch_event_value(device->evdev, + EV_KEY, + code, + &state) && state) { + tool_in_prox = true; + break; + } + } + + if (!tool_in_prox) + return; + + tablet_update_tool(tablet, device, tool, state); + + tablet->current_tool_id = + libevdev_get_event_value(device->evdev, + EV_ABS, + ABS_MISC); + tablet->current_tool_serial = + libevdev_get_event_value(device->evdev, + EV_MSC, + MSC_SERIAL); + + tablet_flush(tablet, + device, + libinput_now(device->base.seat->libinput)); +} + static struct evdev_dispatch_interface tablet_interface = { tablet_process, NULL, /* remove */ @@ -979,6 +1028,7 @@ static struct evdev_dispatch_interface tablet_interface = { NULL, /* device_suspended */ NULL, /* device_resumed */ NULL, /* tag_device */ + tablet_check_initial_proximity, }; static int diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index ea103b0d..2ba08fae 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -28,6 +28,7 @@ #define LIBINPUT_TABLET_AXIS_NONE 0 #define LIBINPUT_TOOL_NONE 0 +#define LIBINPUT_TOOL_MAX LIBINPUT_TOOL_LENS enum tablet_status { TABLET_NONE = 0, @@ -156,4 +157,24 @@ axis_to_evcode(const enum libinput_tablet_axis axis) return evcode; } +static inline int +tablet_tool_to_evcode(enum libinput_tool_type type) +{ + int code; + + switch (type) { + case LIBINPUT_TOOL_PEN: code = BTN_TOOL_PEN; break; + case LIBINPUT_TOOL_ERASER: code = BTN_TOOL_RUBBER; break; + case LIBINPUT_TOOL_BRUSH: code = BTN_TOOL_BRUSH; break; + case LIBINPUT_TOOL_PENCIL: code = BTN_TOOL_PENCIL; break; + case LIBINPUT_TOOL_AIRBRUSH: code = BTN_TOOL_AIRBRUSH; break; + case LIBINPUT_TOOL_FINGER: code = BTN_TOOL_FINGER; break; + case LIBINPUT_TOOL_MOUSE: code = BTN_TOOL_MOUSE; break; + case LIBINPUT_TOOL_LENS: code = BTN_TOOL_LENS; break; + default: + abort(); + } + + return code; +} #endif diff --git a/src/evdev.c b/src/evdev.c index b90ea7c7..2c4d1f15 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -803,6 +803,7 @@ struct evdev_dispatch_interface fallback_interface = { NULL, /* device_suspended */ NULL, /* device_resumed */ fallback_tag_device, + NULL, /* post_added */ }; static uint32_t @@ -1592,6 +1593,10 @@ evdev_notify_added_device(struct evdev_device *device) } notify_added_device(&device->base); + + if (device->dispatch->interface->post_added) + device->dispatch->interface->post_added(device, + device->dispatch); } static int diff --git a/src/evdev.h b/src/evdev.h index 74696751..26f321ef 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -196,6 +196,11 @@ struct evdev_dispatch_interface { /* Tag device with one of EVDEV_TAG */ void (*tag_device)(struct evdev_device *device, struct udev_device *udev_device); + + /* Called immediately after the LIBINPUT_EVENT_DEVICE_ADDED event + * was sent */ + void (*post_added)(struct evdev_device *device, + struct evdev_dispatch *dispatch); }; struct evdev_dispatch { diff --git a/test/tablet.c b/test/tablet.c index d7486cb9..ba61e0e0 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -1157,6 +1157,58 @@ START_TEST(tool_capabilities) } END_TEST +START_TEST(tool_in_prox_before_start) +{ + struct libinput *li; + struct litest_device *dev = litest_current_device(); + struct libinput_event *event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { ABS_TILT_X, 0 }, + { ABS_TILT_Y, 0 }, + { -1, -1 } + }; + const char *devnode; + + litest_tablet_proximity_in(dev, 10, 10, axes); + + /* for simplicity, we create a new litest context */ + devnode = libevdev_uinput_get_devnode(dev->uinput); + li = litest_create_context(); + libinput_path_add_device(li, devnode); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_DEVICE_ADDED, + -1); + event = libinput_get_event(li); + libinput_event_destroy(event); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_TABLET_PROXIMITY, + -1); + event = libinput_get_event(li); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + + litest_tablet_motion(dev, 10, 20, axes); + litest_tablet_motion(dev, 30, 40, axes); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_AXIS); + litest_assert_empty_queue(li); + litest_event(dev, EV_KEY, BTN_STYLUS, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_STYLUS, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_BUTTON); + litest_tablet_proximity_out(dev); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_TABLET_PROXIMITY, + -1); + libinput_unref(li); +} +END_TEST + START_TEST(mouse_tool) { struct litest_device *dev = litest_current_device(); @@ -1615,6 +1667,7 @@ main(int argc, char **argv) { litest_add("tablet:tool", tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add_no_device("tablet:tool", tool_capabilities); + litest_add("tablet:tool", tool_in_prox_before_start, LITEST_TABLET, LITEST_ANY); litest_add("tablet:tool_serial", tool_serial, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add("tablet:tool_serial", serial_changes_tool, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add("tablet:tool_serial", invalid_serials, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);