From 6f5d9902c8d96a2d79c87d32865186fdbc59a1e9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 11 Nov 2015 14:03:05 +1000 Subject: [PATCH] tablet: hook up tip events Behavior for axis events in the same event frame as the BTN_TOUCH is to always send axis events before any tip state. Behavior for button events in the same event frame as the BTN_TOUCH is to order button events to happen when the tip is in proximity, i.e. after the tip event on tip down and before the tip event on tip up. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-tablet.c | 37 ++++- src/evdev-tablet.h | 6 +- test/litest.c | 15 ++ test/litest.h | 3 + test/tablet.c | 371 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 422 insertions(+), 10 deletions(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 72ec31d6..476dee2b 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -425,6 +425,8 @@ tablet_update_button(struct tablet_dispatch *tablet, uint32_t enable) { switch (evcode) { + case BTN_TOUCH: + return; case BTN_LEFT: case BTN_RIGHT: case BTN_MIDDLE: @@ -433,7 +435,6 @@ tablet_update_button(struct tablet_dispatch *tablet, case BTN_FORWARD: case BTN_BACK: case BTN_TASK: - case BTN_TOUCH: case BTN_STYLUS: case BTN_STYLUS2: break; @@ -496,11 +497,10 @@ tablet_process_key(struct tablet_dispatch *tablet, break; case BTN_TOUCH: if (e->value) - tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT); + tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT); else - tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT); - - /* Fall through */ + tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT); + break; case BTN_LEFT: case BTN_RIGHT: case BTN_MIDDLE: @@ -622,7 +622,6 @@ tool_set_bits_from_libwacom(const struct tablet_dispatch *tablet, copy_button_cap(tablet, tool, BTN_STYLUS2); if (libwacom_stylus_get_num_buttons(s) >= 1) copy_button_cap(tablet, tool, BTN_STYLUS); - copy_button_cap(tablet, tool, BTN_TOUCH); } if (libwacom_stylus_has_wheel(s)) @@ -707,7 +706,6 @@ tool_set_bits(const struct tablet_dispatch *tablet, case LIBINPUT_TOOL_TYPE_ERASER: copy_button_cap(tablet, tool, BTN_STYLUS); copy_button_cap(tablet, tool, BTN_STYLUS2); - copy_button_cap(tablet, tool, BTN_TOUCH); break; case LIBINPUT_TOOL_TYPE_MOUSE: case LIBINPUT_TOOL_TYPE_LENS: @@ -843,7 +841,8 @@ sanitize_tablet_axes(struct tablet_dispatch *tablet) clear_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_DISTANCE); tablet->axes[LIBINPUT_TABLET_AXIS_DISTANCE] = 0; } else if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_AXIS_PRESSURE) && - !tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) { + (!tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) && + !tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT))) { /* Make sure that the last axis value sent to the caller is a 0 */ if (tablet->axes[LIBINPUT_TABLET_AXIS_PRESSURE] == 0) clear_bit(tablet->changed_axes, @@ -881,6 +880,8 @@ tablet_flush(struct tablet_dispatch *tablet, 0, sizeof(tablet->button_state.stylus_buttons)); tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); + if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) + tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT); } else if (tablet_has_status(tablet, TABLET_AXES_UPDATED) || tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) { sanitize_tablet_axes(tablet); @@ -890,6 +891,16 @@ tablet_flush(struct tablet_dispatch *tablet, tablet_unset_status(tablet, TABLET_AXES_UPDATED); } + if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) { + tablet_notify_tip(&device->base, + time, + tool, + LIBINPUT_TOOL_TIP_DOWN, + tablet->axes); + tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); + tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT); + } + if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) { tablet_notify_buttons(tablet, device, @@ -908,6 +919,16 @@ tablet_flush(struct tablet_dispatch *tablet, tablet_unset_status(tablet, TABLET_BUTTONS_PRESSED); } + if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) { + tablet_notify_tip(&device->base, + time, + tool, + LIBINPUT_TOOL_TIP_UP, + tablet->axes); + tablet_unset_status(tablet, TABLET_TOOL_LEAVING_CONTACT); + tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT); + } + if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes)); tablet_notify_proximity(&device->base, diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index 64e16c76..9901b2a1 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -38,7 +38,9 @@ enum tablet_status { TABLET_TOOL_IN_CONTACT = 1 << 3, TABLET_TOOL_LEAVING_PROXIMITY = 1 << 4, TABLET_TOOL_OUT_OF_PROXIMITY = 1 << 5, - TABLET_TOOL_ENTERING_PROXIMITY = 1 << 6 + TABLET_TOOL_ENTERING_PROXIMITY = 1 << 6, + TABLET_TOOL_ENTERING_CONTACT = 1 << 7, + TABLET_TOOL_LEAVING_CONTACT = 1 << 8, }; struct button_state { @@ -48,7 +50,7 @@ struct button_state { struct tablet_dispatch { struct evdev_dispatch base; struct evdev_device *device; - unsigned char status; + unsigned int status; unsigned char changed_axes[NCHARS(LIBINPUT_TABLET_AXIS_MAX + 1)]; double axes[LIBINPUT_TABLET_AXIS_MAX + 1]; double deltas[LIBINPUT_TABLET_AXIS_MAX + 1]; diff --git a/test/litest.c b/test/litest.c index 4bbabb20..292cddba 100644 --- a/test/litest.c +++ b/test/litest.c @@ -2330,6 +2330,21 @@ litest_is_gesture_event(struct libinput_event *event, return gevent; } +struct libinput_event_tablet * litest_is_tablet_event( + struct libinput_event *event, + enum libinput_event_type type) +{ + struct libinput_event_tablet *tevent; + + litest_assert(event != NULL); + litest_assert_int_eq(libinput_event_get_type(event), type); + + tevent = libinput_event_get_tablet_event(event); + litest_assert(tevent != NULL); + + return tevent; +} + void litest_assert_tablet_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state) diff --git a/test/litest.h b/test/litest.h index bf165f07..a7a447e4 100644 --- a/test/litest.h +++ b/test/litest.h @@ -402,6 +402,9 @@ struct libinput_event_gesture * litest_is_gesture_event( struct libinput_event *event, enum libinput_event_type type, int nfingers); +struct libinput_event_tablet * litest_is_tablet_event( + struct libinput_event *event, + enum libinput_event_type type); void litest_assert_button_event(struct libinput *li, unsigned int button, diff --git a/test/tablet.c b/test/tablet.c index 444290c2..a954e531 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -34,6 +34,370 @@ #include "evdev-tablet.h" #include "litest.h" +START_TEST(tip_down_up) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_DOWN); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_UP); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + + litest_assert_empty_queue(li); + +} +END_TEST + +START_TEST(tip_down_prox_in) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_pop_event_frame(dev); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_get_proximity_state(tablet_event), + LIBINPUT_TOOL_PROXIMITY_IN); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_DOWN); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + +} +END_TEST + +START_TEST(tip_up_prox_out) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_tablet_proximity_out(dev); + litest_pop_event_frame(dev); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_UP); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_get_proximity_state(tablet_event), + LIBINPUT_TOOL_PROXIMITY_OUT); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + +} +END_TEST + +START_TEST(tip_up_btn_change) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_STYLUS, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_BUTTON); + ck_assert_int_eq(libinput_event_tablet_get_button(tablet_event), + BTN_STYLUS); + ck_assert_int_eq(libinput_event_tablet_get_button_state(tablet_event), + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_UP); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + /* same thing with a release at tip-up */ + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_KEY, BTN_STYLUS, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_BUTTON); + ck_assert_int_eq(libinput_event_tablet_get_button(tablet_event), + BTN_STYLUS); + ck_assert_int_eq(libinput_event_tablet_get_button_state(tablet_event), + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_UP); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(tip_down_btn_change) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_STYLUS, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_DOWN); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_BUTTON); + ck_assert_int_eq(libinput_event_tablet_get_button(tablet_event), + BTN_STYLUS); + ck_assert_int_eq(libinput_event_tablet_get_button_state(tablet_event), + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + /* same thing with a release at tip-down */ + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_KEY, BTN_STYLUS, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_DOWN); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_BUTTON); + ck_assert_int_eq(libinput_event_tablet_get_button(tablet_event), + BTN_STYLUS); + ck_assert_int_eq(libinput_event_tablet_get_button_state(tablet_event), + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(tip_down_motion) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + double x, y, last_x, last_y; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_tablet_motion(dev, 70, 70, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_pop_event_frame(dev); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_AXIS); + last_x = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_X); + last_y = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_Y); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_DOWN); + x = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_X); + y = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_Y); + ck_assert_double_eq(last_x, x); + ck_assert_double_eq(last_y, y); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(tip_up_motion) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + double x, y, last_x, last_y; + + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_tablet_motion(dev, 70, 70, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_pop_event_frame(dev); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_AXIS); + last_x = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_X); + last_y = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_Y); + libinput_event_destroy(event); + + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TIP); + ck_assert_int_eq(libinput_event_tablet_get_tip_state(tablet_event), + LIBINPUT_TOOL_TIP_UP); + x = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_X); + y = libinput_event_tablet_get_axis_value(tablet_event, + LIBINPUT_TABLET_AXIS_Y); + ck_assert_double_eq(last_x, x); + ck_assert_double_eq(last_y, y); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(proximity_in_out) { struct litest_device *dev = litest_current_device(); @@ -1789,6 +2153,13 @@ litest_setup_tests(void) litest_add("tablet:proximity", proximity_in_out, LITEST_TABLET, LITEST_ANY); litest_add("tablet:proximity", proximity_has_axes, LITEST_TABLET, LITEST_ANY); litest_add("tablet:proximity", bad_distance_events, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY); + litest_add("tablet:tip", tip_down_up, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_down_prox_in, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_up_prox_out, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_down_btn_change, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_up_btn_change, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_down_motion, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:tip", tip_up_motion, LITEST_TABLET, LITEST_ANY); litest_add("tablet:motion", motion, LITEST_TABLET, LITEST_ANY); litest_add("tablet:motion", motion_delta, LITEST_TABLET, LITEST_ANY); litest_add("tablet:motion", motion_delta_partial, LITEST_TABLET, LITEST_ANY);