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