From 1a99977ca027962824cc93be851508c5eeee19d5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 15 Dec 2015 08:39:56 +1000 Subject: tablet: a tip event can replace an axis event When we're only dealing with BTN_TOUCH we can make the tip event independent of the axis event. Now that we handle pressure thresholds to trigger tip state this does not work, we'd have to send an axis event with the new pressure and then a tip event. Since the pressure triggers the tip event this seems disconnected. Make the tip event officially capable of carrying axes. A caller can then decide how to forward this to the next layer. Signed-off-by: Peter Hutterer Acked-by: Jason Gerecke --- doc/tablet-support.dox | 3 +- src/evdev-tablet.c | 168 +++++++++++++++++++++++++++++++------------------ src/libinput-private.h | 1 + src/libinput.c | 4 ++ src/libinput.h | 28 ++++++++- test/tablet.c | 101 ++++++++++++----------------- 6 files changed, 179 insertions(+), 126 deletions(-) diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox index 5e6f3932..7207a473 100644 --- a/doc/tablet-support.dox +++ b/doc/tablet-support.dox @@ -45,7 +45,8 @@ pressure value while the tip is logically up. Most application can and should ignore pressure information until they receive the event of type @ref LIBINPUT_EVENT_TABLET_TOOL_TIP. Applications that require extremely fine-grained pressure sensitivity should use the pressure data instead of -the tip events. +the tip events to determine a logical tip down state and treat the tip +events like axis events otherwise. Note that the pressure threshold to trigger a logical tip event may be zero on some devices. On tools without pressure sensitivity, determining when a diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 070ac9a1..1dc607be 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -452,13 +452,14 @@ tablet_handle_wheel(struct tablet_dispatch *tablet, return tablet->axes[a]; } -static void +static bool tablet_check_notify_axes(struct tablet_dispatch *tablet, struct evdev_device *device, - uint64_t time, - struct libinput_tablet_tool *tool) + struct libinput_tablet_tool *tool, + double *axes_out, + size_t axes_sz, + int *wheel_discrete_out) { - struct libinput_device *base = &device->base; double axes[LIBINPUT_TABLET_TOOL_AXIS_MAX + 1] = {0}; int wheel_discrete = 0; struct device_coords point; @@ -466,8 +467,9 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, const char tmp[sizeof(tablet->changed_axes)] = {0}; if (memcmp(tmp, tablet->changed_axes, sizeof(tmp)) == 0) - return; + return false; + assert(axes_sz == sizeof(axes)); point = tablet_handle_xy(tablet, device); axes[LIBINPUT_TABLET_TOOL_AXIS_X] = point.x; axes[LIBINPUT_TABLET_TOOL_AXIS_Y] = point.y; @@ -500,41 +502,10 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, axes[LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL] = tablet_handle_wheel(tablet, device, &wheel_discrete); - /* We need to make sure that we check that the tool is not out of - * proximity before we send any axis updates. This is because many - * tablets will send axis events with incorrect values if the tablet - * tool is close enough so that the tablet can partially detect that - * it's there, but can't properly receive any data from the tool. */ - if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY) && - !tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { - if (tablet_has_status(tablet, - TABLET_TOOL_ENTERING_PROXIMITY)) { - tablet_notify_proximity(&device->base, - time, - tool, - LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, - tablet->changed_axes, - axes); - } else { - enum libinput_tablet_tool_tip_state tip_state; - - if (tablet_has_status(tablet, - TABLET_TOOL_IN_CONTACT)) - tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN; - else - tip_state = LIBINPUT_TABLET_TOOL_TIP_UP; - - tablet_notify_axis(base, - time, - tool, - tip_state, - tablet->changed_axes, - axes, - wheel_discrete); - } - } + memcpy(axes_out, axes, sizeof(axes)); + *wheel_discrete_out = wheel_discrete; - memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes)); + return true; } static void @@ -1215,6 +1186,98 @@ tablet_update_proximity_state(struct tablet_dispatch *tablet, tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); } +static void +tablet_send_axis_proximity_tip_down_events(struct tablet_dispatch *tablet, + struct evdev_device *device, + struct libinput_tablet_tool *tool, + uint64_t time) +{ + double axes[LIBINPUT_TABLET_TOOL_AXIS_MAX + 1] = {0}; + int wheel_discrete = 0; + + /* We need to make sure that we check that the tool is not out of + * proximity before we send any axis updates. This is because many + * tablets will send axis events with incorrect values if the tablet + * tool is close enough so that the tablet can partially detect that + * it's there, but can't properly receive any data from the tool. */ + if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) + goto out; + else if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { + /* Tool is leaving proximity, we can't rely on the last axis + * information (it'll be mostly 0), so we just get the + * current state and skip over updating the axes. + */ + static_assert(sizeof(axes) == sizeof(tablet->axes), + "Mismatching array sizes"); + memcpy(axes, tablet->axes, sizeof(axes)); + + /* Dont' send an axis event, but we may have a tip event + * update */ + tablet_unset_status(tablet, TABLET_AXES_UPDATED); + } else if (!tablet_check_notify_axes(tablet, + device, + tool, + axes, + sizeof(axes), + &wheel_discrete)) { + goto out; + } + + if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) { + tablet_notify_proximity(&device->base, + time, + tool, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, + tablet->changed_axes, + axes); + tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); + tablet_unset_status(tablet, TABLET_AXES_UPDATED); + } + + if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) { + tablet_notify_tip(&device->base, + time, + tool, + LIBINPUT_TABLET_TOOL_TIP_DOWN, + tablet->changed_axes, + tablet->axes); + tablet_unset_status(tablet, TABLET_AXES_UPDATED); + tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); + tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT); + } else if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) { + tablet_notify_tip(&device->base, + time, + tool, + LIBINPUT_TABLET_TOOL_TIP_UP, + tablet->changed_axes, + tablet->axes); + tablet_unset_status(tablet, TABLET_AXES_UPDATED); + tablet_unset_status(tablet, TABLET_TOOL_LEAVING_CONTACT); + tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT); + } else if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) { + enum libinput_tablet_tool_tip_state tip_state; + + if (tablet_has_status(tablet, + TABLET_TOOL_IN_CONTACT)) + tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN; + else + tip_state = LIBINPUT_TABLET_TOOL_TIP_UP; + + tablet_notify_axis(&device->base, + time, + tool, + tip_state, + tablet->changed_axes, + axes, + wheel_discrete); + tablet_unset_status(tablet, TABLET_AXES_UPDATED); + } + +out: + memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes)); + tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); +} + static void tablet_flush(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -1250,21 +1313,12 @@ tablet_flush(struct tablet_dispatch *tablet, detect_pressure_offset(tablet, device, tool); detect_tool_contact(tablet, device, tool); sanitize_tablet_axes(tablet, tool); - tablet_check_notify_axes(tablet, device, time, tool); - - tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); - tablet_unset_status(tablet, TABLET_AXES_UPDATED); } - if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) { - tablet_notify_tip(&device->base, - time, - tool, - LIBINPUT_TABLET_TOOL_TIP_DOWN, - tablet->axes); - tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); - tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT); - } + tablet_send_axis_proximity_tip_down_events(tablet, + device, + tool, + time); if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) { tablet_notify_buttons(tablet, @@ -1284,16 +1338,6 @@ 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_TABLET_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/libinput-private.h b/src/libinput-private.h index 5238e646..2dbc555a 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -509,6 +509,7 @@ tablet_notify_tip(struct libinput_device *device, uint64_t time, struct libinput_tablet_tool *tool, enum libinput_tablet_tool_tip_state tip_state, + unsigned char *changed_axes, double *axes); void diff --git a/src/libinput.c b/src/libinput.c index d73637a8..e9883ffd 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -2226,6 +2226,7 @@ tablet_notify_tip(struct libinput_device *device, uint64_t time, struct libinput_tablet_tool *tool, enum libinput_tablet_tool_tip_state tip_state, + unsigned char *changed_axes, double *axes) { struct libinput_event_tablet_tool *tip_event; @@ -2243,6 +2244,9 @@ tablet_notify_tip(struct libinput_device *device, memcpy(tip_event->axes, axes, sizeof(tip_event->axes)); + memcpy(tip_event->changed_axes, + changed_axes, + sizeof(tip_event->changed_axes)); post_device_event(device, time, diff --git a/src/libinput.h b/src/libinput.h index ab57b4cb..7b9236de 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -273,6 +273,10 @@ enum libinput_event_type { * changes from this initial state. It is possible for a tool to * enter and leave proximity without sending an event of type @ref * LIBINPUT_EVENT_TABLET_TOOL_AXIS. + * + * An event of type @ref LIBINPUT_EVENT_TABLET_TOOL_AXIS is sent + * when the tip state does not change. See the documentation for + * @ref LIBINPUT_EVENT_TABLET_TOOL_TIP for more details. */ LIBINPUT_EVENT_TABLET_TOOL_AXIS = 600, /** @@ -309,10 +313,30 @@ enum libinput_event_type { * LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY for the tip down event, and * immediately before for the tip up event. * - * If a button and/or axis state change occurs at the same time as a - * tip state change, the order of events is device-dependent. + * The decision when a tip touches the surface is device-dependent + * and may be derived from pressure data or other means. If the tip + * state is changed by axes changing state, the + * @ref LIBINPUT_EVENT_TABLET_TOOL_TIP event includes the changed + * axes and no additional axis event is sent for this state change. + * In other words, a caller must look at both @ref + * LIBINPUT_EVENT_TABLET_TOOL_AXIS and @ref + * LIBINPUT_EVENT_TABLET_TOOL_TIP events to know the current state + * of the axes. + * + * If a button state change occurs at the same time as a tip state + * change, the order of events is device-dependent. */ LIBINPUT_EVENT_TABLET_TOOL_TIP, + /** + * Signals that a tool has changed a logical button state on a + * device with the @ref LIBINPUT_DEVICE_CAP_TABLET_TOOL capability. + * + * Button state changes occur on their own and do not include axis + * state changes. If button and axis state changes occur within the + * same logical hardware event, the order of the @ref + * LIBINPUT_EVENT_TABLET_TOOL_BUTTON and @ref + * LIBINPUT_EVENT_TABLET_TOOL_AXIS event is device-specific. + */ LIBINPUT_EVENT_TABLET_TOOL_BUTTON, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800, diff --git a/test/tablet.c b/test/tablet.c index 421deb06..4976d1eb 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * Copyright © 2014 Stephen Chandler "Lyude" Paul * * Permission to use, copy, modify, distribute, and sell this software and @@ -58,11 +58,6 @@ START_TEST(tip_down_up) libinput_dispatch(li); - event = libinput_get_event(li); - tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); - libinput_event_destroy(event); - event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); @@ -79,11 +74,6 @@ START_TEST(tip_down_up) litest_pop_event_frame(dev); libinput_dispatch(li); - event = libinput_get_event(li); - tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); - libinput_event_destroy(event); - event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); @@ -125,7 +115,6 @@ START_TEST(tip_down_prox_in) LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); libinput_event_destroy(event); - libinput_dispatch(li); event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); @@ -212,9 +201,12 @@ START_TEST(tip_up_btn_change) litest_pop_event_frame(dev); libinput_dispatch(li); + event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); + LIBINPUT_EVENT_TABLET_TOOL_TIP); + ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), + LIBINPUT_TABLET_TOOL_TIP_UP); libinput_event_destroy(event); event = libinput_get_event(li); @@ -226,13 +218,6 @@ START_TEST(tip_up_btn_change) LIBINPUT_BUTTON_STATE_PRESSED); libinput_event_destroy(event); - event = libinput_get_event(li); - tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_TIP); - ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), - LIBINPUT_TABLET_TOOL_TIP_UP); - libinput_event_destroy(event); - litest_assert_empty_queue(li); litest_axis_set_value(axes, ABS_DISTANCE, 0); @@ -253,9 +238,12 @@ START_TEST(tip_up_btn_change) litest_pop_event_frame(dev); libinput_dispatch(li); + event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); + LIBINPUT_EVENT_TABLET_TOOL_TIP); + ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), + LIBINPUT_TABLET_TOOL_TIP_UP); libinput_event_destroy(event); event = libinput_get_event(li); @@ -267,14 +255,6 @@ START_TEST(tip_up_btn_change) 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_TOOL_TIP); - ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), - LIBINPUT_TABLET_TOOL_TIP_UP); - libinput_event_destroy(event); - litest_assert_empty_queue(li); } END_TEST @@ -303,10 +283,6 @@ START_TEST(tip_down_btn_change) litest_pop_event_frame(dev); libinput_dispatch(li); - event = libinput_get_event(li); - tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); - libinput_event_destroy(event); event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, @@ -345,10 +321,6 @@ START_TEST(tip_down_btn_change) litest_pop_event_frame(dev); libinput_dispatch(li); - event = libinput_get_event(li); - tablet_event = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); - libinput_event_destroy(event); event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, @@ -384,9 +356,18 @@ START_TEST(tip_down_motion) }; double x, y, last_x, last_y; - litest_tablet_proximity_in(dev, 10, 10, axes); litest_drain_events(li); + litest_tablet_proximity_in(dev, 10, 10, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tablet_event = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + last_x = libinput_event_tablet_tool_get_x(tablet_event); + last_y = libinput_event_tablet_tool_get_y(tablet_event); + libinput_event_destroy(event); + + /* move x/y on tip down, make sure x/y changed */ litest_axis_set_value(axes, ABS_DISTANCE, 0); litest_axis_set_value(axes, ABS_PRESSURE, 20); litest_push_event_frame(dev); @@ -395,24 +376,18 @@ START_TEST(tip_down_motion) 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_TOOL_AXIS); - last_x = libinput_event_tablet_tool_get_x(tablet_event); - last_y = libinput_event_tablet_tool_get_y(tablet_event); - libinput_event_destroy(event); - libinput_dispatch(li); event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), LIBINPUT_TABLET_TOOL_TIP_DOWN); + ck_assert(libinput_event_tablet_tool_x_has_changed(tablet_event)); + ck_assert(libinput_event_tablet_tool_y_has_changed(tablet_event)); x = libinput_event_tablet_tool_get_x(tablet_event); y = libinput_event_tablet_tool_get_y(tablet_event); - ck_assert_double_eq(last_x, x); - ck_assert_double_eq(last_y, y); + ck_assert_double_lt(last_x, x); + ck_assert_double_lt(last_y, y); libinput_event_destroy(event); litest_assert_empty_queue(li); @@ -427,44 +402,48 @@ START_TEST(tip_up_motion) struct libinput_event_tablet_tool *tablet_event; struct axis_replacement axes[] = { { ABS_DISTANCE, 0 }, - { ABS_PRESSURE, 20 }, + { ABS_PRESSURE, 0 }, { -1, -1 } }; double x, y, last_x, last_y; - litest_push_event_frame(dev); litest_tablet_proximity_in(dev, 10, 10, axes); - 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); litest_drain_events(li); - litest_axis_set_value(axes, ABS_PRESSURE, 0); + litest_axis_set_value(axes, ABS_PRESSURE, 20); litest_push_event_frame(dev); litest_tablet_motion(dev, 70, 70, axes); - litest_event(dev, EV_KEY, BTN_TOUCH, 0); + 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_TOOL_AXIS); + LIBINPUT_EVENT_TABLET_TOOL_TIP); last_x = libinput_event_tablet_tool_get_x(tablet_event); last_y = libinput_event_tablet_tool_get_y(tablet_event); libinput_event_destroy(event); + /* move x/y on tip up, make sure x/y changed */ + litest_axis_set_value(axes, ABS_PRESSURE, 0); + litest_push_event_frame(dev); + litest_tablet_motion(dev, 40, 40, axes); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_pop_event_frame(dev); + libinput_dispatch(li); event = libinput_get_event(li); tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); ck_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tablet_event), LIBINPUT_TABLET_TOOL_TIP_UP); + ck_assert(libinput_event_tablet_tool_x_has_changed(tablet_event)); + ck_assert(libinput_event_tablet_tool_y_has_changed(tablet_event)); x = libinput_event_tablet_tool_get_x(tablet_event); y = libinput_event_tablet_tool_get_y(tablet_event); - ck_assert_double_eq(last_x, x); - ck_assert_double_eq(last_y, y); + ck_assert_double_ne(last_x, x); + ck_assert_double_ne(last_y, y); libinput_event_destroy(event); litest_assert_empty_queue(li); @@ -2841,7 +2820,7 @@ START_TEST(tablet_pressure_offset_decrease) event = libinput_get_event(li); tev = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); + LIBINPUT_EVENT_TABLET_TOOL_TIP); pressure = libinput_event_tablet_tool_get_pressure(tev); @@ -2905,7 +2884,7 @@ START_TEST(tablet_pressure_offset_increase) event = libinput_get_event(li); tev = litest_is_tablet_event(event, - LIBINPUT_EVENT_TABLET_TOOL_AXIS); + LIBINPUT_EVENT_TABLET_TOOL_TIP); pressure = libinput_event_tablet_tool_get_pressure(tev); -- cgit v1.2.3