[PATCH libinput] tablet: handle custom proximity handling

Hans de Goede hdegoede at redhat.com
Thu Dec 3 05:37:26 PST 2015


Hi,

On 03-12-15 00:57, Peter Hutterer wrote:
> For the puck/lens cursor tool we need to artificially reduce proximity
> detection. These tools are usually used in a relative mode (i.e. like a mouse)
> and thus require lifting and resetting the tool multiple times to move across
> the screen. The tablets' distance detection goes too far, requiring the user
> to lift the device several cm on every move. This is uncomfortable.
>
> Introduce an artificial distance threshold for the devices with the default
> value taken from the X.Org wacom driver. If a tool is in proximity but outside
> of this range, fake proximity events accordingly.
>
> If a button was pressed while we were out of range we discard that event and
> send it later when we enter proximity again.
>
> This is the simple implementation that only takes one proximity out value (the
> one from the wacom driver) and applies it to all. Those devices that support a
> button/lens tool and have a different default threshold are well out of date.
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

Patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede at redhat.com>

Regards,

Hans

p.s.

I've a (serial!) graphire tablet, and I'm sure there are also quite a few
usb graphire tablets out there, so we do need to deal with supporting the
mouse on the graphire tablet eventually.



> ---
>   src/evdev-tablet.c | 103 ++++++++++++++++++++++++-
>   src/evdev-tablet.h |   3 +
>   test/litest.c      |  18 +++++
>   test/litest.h      |   3 +-
>   test/tablet.c      | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>   5 files changed, 346 insertions(+), 2 deletions(-)
>
> diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
> index 7e3298c..777ffad 100644
> --- a/src/evdev-tablet.c
> +++ b/src/evdev-tablet.c
> @@ -67,6 +67,22 @@ tablet_get_released_buttons(struct tablet_dispatch *tablet,
>   					~(state->stylus_buttons[i]);
>   }
>
> +/* Merge the previous state with the current one so all buttons look like
> + * they just got pressed in this frame */
> +static inline void
> +tablet_force_button_presses(struct tablet_dispatch *tablet)
> +{
> +	struct button_state *state = &tablet->button_state,
> +			    *prev_state = &tablet->prev_button_state;
> +	size_t i;
> +
> +	for (i = 0; i < sizeof(state->stylus_buttons); i++) {
> +		state->stylus_buttons[i] = state->stylus_buttons[i] |
> +						prev_state->stylus_buttons[i];
> +		prev_state->stylus_buttons[i] = 0;
> +	}
> +}
> +
>   static int
>   tablet_device_has_axis(struct tablet_dispatch *tablet,
>   		       enum libinput_tablet_tool_axis axis)
> @@ -994,6 +1010,62 @@ sanitize_tablet_axes(struct tablet_dispatch *tablet)
>   }
>
>   static void
> +tablet_update_proximity_state(struct tablet_dispatch *tablet,
> +			      struct evdev_device *device)
> +{
> +	const struct input_absinfo *distance;
> +	int dist_max = tablet->cursor_proximity_threshold;
> +	int dist;
> +
> +	distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
> +	if (!distance)
> +		return;
> +
> +	dist = distance->value;
> +	if (dist == 0)
> +		return;
> +
> +	/* Tool got into permitted range */
> +	if (dist < dist_max &&
> +	    (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
> +	     tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))) {
> +		tablet_unset_status(tablet,
> +				    TABLET_TOOL_OUT_OF_RANGE);
> +		tablet_unset_status(tablet,
> +				    TABLET_TOOL_OUT_OF_PROXIMITY);
> +		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
> +		tablet_mark_all_axes_changed(tablet, device);
> +
> +		tablet_set_status(tablet, TABLET_BUTTONS_PRESSED);
> +		tablet_force_button_presses(tablet);
> +		return;
> +	}
> +
> +	if (dist < dist_max)
> +		return;
> +
> +	/* Still out of range/proximity */
> +	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
> +	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
> +	    return;
> +
> +	/* Tool entered prox but is outside of permitted range */
> +	if (tablet_has_status(tablet,
> +			      TABLET_TOOL_ENTERING_PROXIMITY)) {
> +		tablet_set_status(tablet,
> +				  TABLET_TOOL_OUT_OF_RANGE);
> +		tablet_unset_status(tablet,
> +				    TABLET_TOOL_ENTERING_PROXIMITY);
> +		return;
> +	}
> +
> +	/* Tool was in prox and is now outside of range. Set leaving
> +	 * proximity, on the next event it will be OUT_OF_PROXIMITY and thus
> +	 * caught by the above conditions */
> +	tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
> +}
> +
> +static void
>   tablet_flush(struct tablet_dispatch *tablet,
>   	     struct evdev_device *device,
>   	     uint64_t time)
> @@ -1004,7 +1076,12 @@ tablet_flush(struct tablet_dispatch *tablet,
>   				tablet->current_tool_id,
>   				tablet->current_tool_serial);
>
> -	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
> +	if (tool->type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
> +	    tool->type == LIBINPUT_TABLET_TOOL_TYPE_LENS)
> +		tablet_update_proximity_state(tablet, device);
> +
> +	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY) ||
> +	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE))
>   		return;
>
>   	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
> @@ -1197,6 +1274,29 @@ tablet_init_calibration(struct tablet_dispatch *tablet,
>   		evdev_init_calibration(device, &tablet->base);
>   }
>
> +static void
> +tablet_init_proximity_threshold(struct tablet_dispatch *tablet,
> +				struct evdev_device *device)
> +{
> +	/* This rules out most of the bamboos and other devices, we're
> +	 * pretty much down to
> +	 */
> +	if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_MOUSE) &&
> +	    !libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_LENS))
> +		return;
> +
> +	/* 42 is the default proximity threshold the xf86-input-wacom driver
> +	 * uses for Intuos/Cintiq models. Graphire models have a threshold
> +	 * of 10 but since they haven't been manufactured in ages and the
> +	 * intersection of users having a graphire, running libinput and
> +	 * wanting to use the mouse/lens cursor tool is small enough to not
> +	 * worry about it for now. If we need to, we can introduce a udev
> +	 * property later.
> +	 */
> +
> +	tablet->cursor_proximity_threshold = 42;
> +}
> +
>   static int
>   tablet_init(struct tablet_dispatch *tablet,
>   	    struct evdev_device *device)
> @@ -1210,6 +1310,7 @@ tablet_init(struct tablet_dispatch *tablet,
>   	list_init(&tablet->tool_list);
>
>   	tablet_init_calibration(tablet, device);
> +	tablet_init_proximity_threshold(tablet, device);
>
>   	for (axis = LIBINPUT_TABLET_TOOL_AXIS_X;
>   	     axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
> diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h
> index 162b536..918bd51 100644
> --- a/src/evdev-tablet.h
> +++ b/src/evdev-tablet.h
> @@ -41,6 +41,7 @@ enum tablet_status {
>   	TABLET_TOOL_ENTERING_PROXIMITY = 1 << 6,
>   	TABLET_TOOL_ENTERING_CONTACT = 1 << 7,
>   	TABLET_TOOL_LEAVING_CONTACT = 1 << 8,
> +	TABLET_TOOL_OUT_OF_RANGE = 1 << 9,
>   };
>
>   struct button_state {
> @@ -65,6 +66,8 @@ struct tablet_dispatch {
>   	enum libinput_tablet_tool_type current_tool_type;
>   	uint32_t current_tool_id;
>   	uint32_t current_tool_serial;
> +
> +	uint32_t cursor_proximity_threshold;
>   };
>
>   static inline enum libinput_tablet_tool_axis
> diff --git a/test/litest.c b/test/litest.c
> index 01d97d9..48291e5 100644
> --- a/test/litest.c
> +++ b/test/litest.c
> @@ -2370,6 +2370,24 @@ litest_assert_tablet_button_event(struct libinput *li, unsigned int button,
>   	libinput_event_destroy(event);
>   }
>
> +void litest_assert_tablet_proximity_event(struct libinput *li,
> +					  enum libinput_tablet_tool_proximity_state state)
> +{
> +	struct libinput_event *event;
> +	struct libinput_event_tablet_tool *tev;
> +	enum libinput_event_type type = LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY;
> +
> +	litest_wait_for_event(li);
> +	event = libinput_get_event(li);
> +
> +	litest_assert_notnull(event);
> +	litest_assert_int_eq(libinput_event_get_type(event), type);
> +	tev = libinput_event_get_tablet_tool_event(event);
> +	litest_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev),
> +			     state);
> +	libinput_event_destroy(event);
> +}
> +
>   void
>   litest_assert_scroll(struct libinput *li,
>   		     enum libinput_pointer_axis axis,
> diff --git a/test/litest.h b/test/litest.h
> index ed9ddfe..f1ba4ae 100644
> --- a/test/litest.h
> +++ b/test/litest.h
> @@ -419,7 +419,8 @@ void litest_assert_only_typed_events(struct libinput *li,
>   void litest_assert_tablet_button_event(struct libinput *li,
>   				       unsigned int button,
>   				       enum libinput_button_state state);
> -
> +void litest_assert_tablet_proximity_event(struct libinput *li,
> +					  enum libinput_tablet_tool_proximity_state state);
>   struct libevdev_uinput * litest_create_uinput_device(const char *name,
>   						     struct input_id *id,
>   						     ...);
> diff --git a/test/tablet.c b/test/tablet.c
> index c82be49..ba5b1ea 100644
> --- a/test/tablet.c
> +++ b/test/tablet.c
> @@ -854,6 +854,222 @@ START_TEST(proximity_has_axes)
>   }
>   END_TEST
>
> +START_TEST(proximity_range_enter)
> +{
> +	struct litest_device *dev = litest_current_device();
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 60 },
> +		{ -1, -1 }
> +	};
> +
> +	if (!libevdev_has_event_code(dev->evdev,
> +				    EV_KEY,
> +				    BTN_TOOL_MOUSE))
> +		return;
> +
> +	litest_drain_events(li);
> +
> +	litest_push_event_frame(dev);
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +	litest_pop_event_frame(dev);
> +	litest_assert_empty_queue(li);
> +
> +	axes[0].value = 20;
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	libinput_dispatch(li);
> +
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
> +	axes[0].value = 60;
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	libinput_dispatch(li);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
> +
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_empty_queue(li);
> +}
> +END_TEST
> +
> +START_TEST(proximity_range_in_out)
> +{
> +	struct litest_device *dev = litest_current_device();
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 20 },
> +		{ -1, -1 }
> +	};
> +
> +	if (!libevdev_has_event_code(dev->evdev,
> +				    EV_KEY,
> +				    BTN_TOOL_MOUSE))
> +		return;
> +
> +	litest_drain_events(li);
> +
> +	litest_push_event_frame(dev);
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +	litest_pop_event_frame(dev);
> +	libinput_dispatch(li);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
> +
> +	axes[0].value = 60;
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	libinput_dispatch(li);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
> +
> +	litest_tablet_motion(dev, 30, 30, axes);
> +	litest_assert_empty_queue(li);
> +
> +	axes[0].value = 20;
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	libinput_dispatch(li);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
> +
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
> +	litest_assert_empty_queue(li);
> +}
> +END_TEST
> +
> +START_TEST(proximity_range_button_click)
> +{
> +	struct litest_device *dev = litest_current_device();
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 60 },
> +		{ -1, -1 }
> +	};
> +
> +	if (!libevdev_has_event_code(dev->evdev,
> +				    EV_KEY,
> +				    BTN_TOOL_MOUSE))
> +		return;
> +
> +	litest_drain_events(li);
> +
> +	litest_push_event_frame(dev);
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +	litest_pop_event_frame(dev);
> +	litest_drain_events(li);
> +
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	libinput_dispatch(li);
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 0);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	libinput_dispatch(li);
> +
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_empty_queue(li);
> +}
> +END_TEST
> +
> +START_TEST(proximity_range_button_press)
> +{
> +	struct litest_device *dev = litest_current_device();
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 20 },
> +		{ -1, -1 }
> +	};
> +
> +	if (!libevdev_has_event_code(dev->evdev,
> +				    EV_KEY,
> +				    BTN_TOOL_MOUSE))
> +		return;
> +
> +	litest_push_event_frame(dev);
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +	litest_pop_event_frame(dev);
> +	litest_drain_events(li);
> +
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	libinput_dispatch(li);
> +
> +	litest_assert_tablet_button_event(li,
> +					  BTN_STYLUS,
> +					  LIBINPUT_BUTTON_STATE_PRESSED);
> +
> +	axes[0].value = 60;
> +	litest_tablet_motion(dev, 15, 15, axes);
> +	libinput_dispatch(li);
> +
> +	/* expect fake button release */
> +	litest_assert_tablet_button_event(li,
> +					  BTN_STYLUS,
> +					  LIBINPUT_BUTTON_STATE_RELEASED);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
> +
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 0);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	libinput_dispatch(li);
> +
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_empty_queue(li);
> +}
> +END_TEST
> +
> +START_TEST(proximity_range_button_release)
> +{
> +	struct litest_device *dev = litest_current_device();
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 60 },
> +		{ -1, -1 }
> +	};
> +
> +	if (!libevdev_has_event_code(dev->evdev,
> +				    EV_KEY,
> +				    BTN_TOOL_MOUSE))
> +		return;
> +
> +	litest_push_event_frame(dev);
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +	litest_pop_event_frame(dev);
> +	litest_drain_events(li);
> +
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	litest_assert_empty_queue(li);
> +
> +	axes[0].value = 20;
> +	litest_tablet_motion(dev, 15, 15, axes);
> +	libinput_dispatch(li);
> +
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
> +	/* expect fake button press */
> +	litest_assert_tablet_button_event(li,
> +					  BTN_STYLUS,
> +					  LIBINPUT_BUTTON_STATE_PRESSED);
> +	litest_assert_empty_queue(li);
> +
> +	litest_event(dev, EV_KEY, BTN_STYLUS, 0);
> +	litest_event(dev, EV_SYN, SYN_REPORT, 0);
> +	libinput_dispatch(li);
> +	litest_assert_tablet_button_event(li,
> +					  BTN_STYLUS,
> +					  LIBINPUT_BUTTON_STATE_RELEASED);
> +
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_tablet_proximity_event(li,
> +					     LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
> +}
> +END_TEST
> +
>   START_TEST(motion)
>   {
>   	struct litest_device *dev = litest_current_device();
> @@ -2695,6 +2911,11 @@ 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:proximity", proximity_range_enter, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
> +	litest_add("tablet:proximity", proximity_range_in_out, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
> +	litest_add("tablet:proximity", proximity_range_button_click, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
> +	litest_add("tablet:proximity", proximity_range_button_press, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
> +	litest_add("tablet:proximity", proximity_range_button_release, 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);
>


More information about the wayland-devel mailing list