[PATCH v2 libinput 6/6] tablet: add touch arbitration

Jason Gerecke killertofu at gmail.com
Tue Sep 6 18:21:42 UTC 2016


On 09/05/2016 06:04 PM, Peter Hutterer wrote:
> So far we've relied on the wacom kernel module to do touch arbitration for us
> but that won't be the case in upcoming kernels. Implement touch arbitration in
> userspace by pairing the two devices and suspending the touch device whenever
> a tool comes into proximity.
> 
> In the future more sophisticated arbitration can be done (e.g. only touches
> which are close to the pen) but let's burn that bridge when we have to cross
> it.
> 
> Note that touch arbitration is "device suspend light", i.e. we leave the
> device enabled and the fd is active. Tablet interactions are comparatively
> short-lived, so closing the fd and asking logind for a new one every time the
> pen changes proximity is suboptimal. Instead, we just keep a boolean around
> and discard all events while it is set.
> 
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

The revised series looks good to me -- thanks again :)

Reviewed-by: Jason Gerecke <jason.gerecke at wacom.com>

Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....

> ---
> Changes to v1:
> - drop the previous evdev_device_suspend() approach and use a ignore_events
>   boolean in the dispatch instead.
> - add test for suspending/resuming the touch device during arbitration
> - use a common helper for each arbitration test. the tests only differ in
>   the second device created and what events we expect so let's re-use
>   everything else
> 
>  doc/tablet-support.dox  |  13 ++
>  src/evdev-mt-touchpad.c |  21 +++
>  src/evdev-mt-touchpad.h |   4 +
>  src/evdev-tablet-pad.c  |   1 +
>  src/evdev-tablet.c      |  80 ++++++++++-
>  src/evdev-tablet.h      |   3 +
>  src/evdev.c             |  21 +++
>  src/evdev.h             |   8 ++
>  test/tablet.c           | 344 ++++++++++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 492 insertions(+), 3 deletions(-)
> 
> diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox
> index cda0d70..19c5d8f 100644
> --- a/doc/tablet-support.dox
> +++ b/doc/tablet-support.dox
> @@ -331,4 +331,17 @@ button and ring events on the right. When one of the three mode toggle
>  buttons on the right is pressed, the right mode switches to that button's
>  mode but the left mode remains unchanged.
>  
> + at section tablet-touch-arbitration Tablet touch arbitration
> +
> +"Touch arbitration" is the terminology used when touch events are suppressed
> +while the pen is in proximity. Since it is almost impossible to use a stylus
> +or other tool without triggering touches with the hand holding the tool,
> +touch arbitration serves to reduce the number of accidental inputs.
> +The wacom kernel driver currently provides touch arbitration but for other
> +devices arbitration has to be done in userspace.
> +
> +libinput uses the @ref libinput_device_group to decide on touch arbitration
> +and automatically discards touch events whenever a tool is in proximity.
> +The exact behavior is device-dependent.
> +
>  */
> diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
> index 65b0abf..2354061 100644
> --- a/src/evdev-mt-touchpad.c
> +++ b/src/evdev-mt-touchpad.c
> @@ -1165,6 +1165,9 @@ tp_interface_process(struct evdev_dispatch *dispatch,
>  	struct tp_dispatch *tp =
>  		(struct tp_dispatch *)dispatch;
>  
> +	if (tp->ignore_events)
> +		return;
> +
>  	switch (e->type) {
>  	case EV_ABS:
>  		if (tp->has_mt)
> @@ -1679,6 +1682,23 @@ evdev_tag_touchpad(struct evdev_device *device,
>  	}
>  }
>  
> +static void
> +tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
> +			  struct evdev_device *device,
> +			  bool enable)
> +{
> +	struct tp_dispatch *tp = (struct tp_dispatch*)dispatch;
> +	bool ignore_events = !enable;
> +
> +	if (ignore_events == tp->ignore_events)
> +		return;
> +
> +	if (ignore_events)
> +		tp_clear_state(tp);
> +
> +	tp->ignore_events = ignore_events;
> +}
> +
>  static struct evdev_dispatch_interface tp_interface = {
>  	tp_interface_process,
>  	tp_interface_suspend,
> @@ -1689,6 +1709,7 @@ static struct evdev_dispatch_interface tp_interface = {
>  	tp_interface_device_removed, /* device_suspended, treat as remove */
>  	tp_interface_device_added,   /* device_resumed, treat as add */
>  	NULL,                        /* post_added */
> +	tp_interface_toggle_touch,
>  };
>  
>  static void
> diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
> index 8a8d2db..de9bdb5 100644
> --- a/src/evdev-mt-touchpad.h
> +++ b/src/evdev-mt-touchpad.h
> @@ -231,6 +231,10 @@ struct tp_dispatch {
>  	bool semi_mt;
>  	bool reports_distance;			/* does the device support true hovering */
>  
> +	/* true if we're reading events (i.e. not suspended) but we're
> +	 * ignoring them */
> +	bool ignore_events;
> +
>  	unsigned int num_slots;			/* number of slots */
>  	unsigned int ntouches;			/* no slots inc. fakes */
>  	struct tp_touch *touches;		/* len == ntouches */
> diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
> index 0e95408..82542bc 100644
> --- a/src/evdev-tablet-pad.c
> +++ b/src/evdev-tablet-pad.c
> @@ -512,6 +512,7 @@ static struct evdev_dispatch_interface pad_interface = {
>  	NULL, /* device_suspended */
>  	NULL, /* device_resumed */
>  	NULL, /* post_added */
> +	NULL, /* toggle_touch */
>  };
>  
>  static void
> diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
> index 254d669..14023b6 100644
> --- a/src/evdev-tablet.c
> +++ b/src/evdev-tablet.c
> @@ -1434,6 +1434,39 @@ tablet_flush(struct tablet_dispatch *tablet,
>  }
>  
>  static inline void
> +tablet_set_touch_device_enabled(struct evdev_device *touch_device,
> +				bool enable)
> +{
> +	struct evdev_dispatch *dispatch;
> +
> +	if (touch_device == NULL)
> +		return;
> +
> +	dispatch = touch_device->dispatch;
> +	if (dispatch->interface->toggle_touch)
> +		dispatch->interface->toggle_touch(dispatch,
> +						  touch_device,
> +						  enable);
> +}
> +
> +static inline void
> +tablet_toggle_touch_device(struct tablet_dispatch *tablet,
> +			   struct evdev_device *tablet_device)
> +{
> +	bool enable_events;
> +
> +	enable_events = tablet_has_status(tablet,
> +					  TABLET_TOOL_OUT_OF_RANGE) ||
> +			tablet_has_status(tablet, TABLET_NONE) ||
> +			tablet_has_status(tablet,
> +					  TABLET_TOOL_LEAVING_PROXIMITY) ||
> +			tablet_has_status(tablet,
> +					  TABLET_TOOL_OUT_OF_PROXIMITY);
> +
> +	tablet_set_touch_device_enabled(tablet->touch_device, enable_events);
> +}
> +
> +static inline void
>  tablet_reset_state(struct tablet_dispatch *tablet)
>  {
>  	/* Update state */
> @@ -1466,6 +1499,7 @@ tablet_process(struct evdev_dispatch *dispatch,
>  		break;
>  	case EV_SYN:
>  		tablet_flush(tablet, device, time);
> +		tablet_toggle_touch_device(tablet, device);
>  		tablet_reset_state(tablet);
>  		break;
>  	default:
> @@ -1478,6 +1512,16 @@ tablet_process(struct evdev_dispatch *dispatch,
>  }
>  
>  static void
> +tablet_suspend(struct evdev_dispatch *dispatch,
> +	       struct evdev_device *device)
> +{
> +	struct tablet_dispatch *tablet =
> +		(struct tablet_dispatch *)dispatch;
> +
> +	tablet_set_touch_device_enabled(tablet->touch_device, true);
> +}
> +
> +static void
>  tablet_destroy(struct evdev_dispatch *dispatch)
>  {
>  	struct tablet_dispatch *tablet =
> @@ -1492,6 +1536,35 @@ tablet_destroy(struct evdev_dispatch *dispatch)
>  }
>  
>  static void
> +tablet_device_added(struct evdev_device *device,
> +		    struct evdev_device *added_device)
> +{
> +	struct tablet_dispatch *tablet =
> +		(struct tablet_dispatch*)device->dispatch;
> +
> +	if (libinput_device_get_device_group(&device->base) !=
> +	    libinput_device_get_device_group(&added_device->base))
> +		return;
> +
> +	/* Touch screens or external touchpads only */
> +	if (evdev_device_has_capability(added_device, LIBINPUT_DEVICE_CAP_TOUCH) ||
> +	    (evdev_device_has_capability(added_device, LIBINPUT_DEVICE_CAP_POINTER) &&
> +	     (added_device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)))
> +	    tablet->touch_device = added_device;
> +}
> +
> +static void
> +tablet_device_removed(struct evdev_device *device,
> +		      struct evdev_device *removed_device)
> +{
> +	struct tablet_dispatch *tablet =
> +		(struct tablet_dispatch*)device->dispatch;
> +
> +	if (tablet->touch_device == removed_device)
> +		tablet->touch_device = NULL;
> +}
> +
> +static void
>  tablet_check_initial_proximity(struct evdev_device *device,
>  			       struct evdev_dispatch *dispatch)
>  {
> @@ -1532,14 +1605,15 @@ tablet_check_initial_proximity(struct evdev_device *device,
>  
>  static struct evdev_dispatch_interface tablet_interface = {
>  	tablet_process,
> -	NULL, /* suspend */
> +	tablet_suspend,
>  	NULL, /* remove */
>  	tablet_destroy,
> -	NULL, /* device_added */
> -	NULL, /* device_removed */
> +	tablet_device_added,
> +	tablet_device_removed,
>  	NULL, /* device_suspended */
>  	NULL, /* device_resumed */
>  	tablet_check_initial_proximity,
> +	NULL, /* toggle_touch */
>  };
>  
>  static void
> diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h
> index f66de5d..2279e03 100644
> --- a/src/evdev-tablet.h
> +++ b/src/evdev-tablet.h
> @@ -71,6 +71,9 @@ struct tablet_dispatch {
>  	uint32_t cursor_proximity_threshold;
>  
>  	struct libinput_device_config_calibration calibration;
> +
> +	/* The paired touch device on devices with both pen & touch */
> +	struct evdev_device *touch_device;
>  };
>  
>  static inline enum libinput_tablet_tool_axis
> diff --git a/src/evdev.c b/src/evdev.c
> index 3b48c3e..03384c8 100644
> --- a/src/evdev.c
> +++ b/src/evdev.c
> @@ -1055,6 +1055,9 @@ fallback_process(struct evdev_dispatch *evdev_dispatch,
>  	struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch;
>  	enum evdev_event_type sent;
>  
> +	if (dispatch->ignore_events)
> +		return;
> +
>  	switch (event->type) {
>  	case EV_REL:
>  		fallback_process_relative(dispatch, device, event, time);
> @@ -1184,6 +1187,23 @@ fallback_suspend(struct evdev_dispatch *evdev_dispatch,
>  }
>  
>  static void
> +fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch,
> +		      struct evdev_device *device,
> +		      bool enable)
> +{
> +	struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch;
> +	bool ignore_events = !enable;
> +
> +	if (ignore_events == dispatch->ignore_events)
> +		return;
> +
> +	if (ignore_events)
> +		fallback_return_to_neutral_state(dispatch, device);
> +
> +	dispatch->ignore_events = ignore_events;
> +}
> +
> +static void
>  fallback_destroy(struct evdev_dispatch *evdev_dispatch)
>  {
>  	struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch;
> @@ -1243,6 +1263,7 @@ struct evdev_dispatch_interface fallback_interface = {
>  	NULL, /* device_suspended */
>  	NULL, /* device_resumed */
>  	NULL, /* post_added */
> +	fallback_toggle_touch, /* toggle_touch */
>  };
>  
>  static uint32_t
> diff --git a/src/evdev.h b/src/evdev.h
> index 10b0e58..b240615 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -265,6 +265,10 @@ struct evdev_dispatch_interface {
>  	 * was sent */
>  	void (*post_added)(struct evdev_device *device,
>  			   struct evdev_dispatch *dispatch);
> +
> +	void (*toggle_touch)(struct evdev_dispatch *dispatch,
> +			     struct evdev_device *device,
> +			     bool enable);
>  };
>  
>  struct evdev_dispatch {
> @@ -308,6 +312,10 @@ struct fallback_dispatch {
>  	unsigned long hw_key_mask[NLONGS(KEY_CNT)];
>  
>  	enum evdev_event_type pending_event;
> +
> +	/* true if we're reading events (i.e. not suspended) but we're
> +	   ignoring them */
> +	bool ignore_events;
>  };
>  
>  struct evdev_device *
> diff --git a/test/tablet.c b/test/tablet.c
> index 30588a4..212ef0c 100644
> --- a/test/tablet.c
> +++ b/test/tablet.c
> @@ -3767,6 +3767,338 @@ START_TEST(relative_calibration)
>  }
>  END_TEST
>  
> +static void
> +touch_arbitration(struct litest_device *dev,
> +		  enum litest_device_type other,
> +		  bool is_touchpad)
> +{
> +	struct litest_device *finger;
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 10 },
> +		{ ABS_PRESSURE, 0 },
> +		{ -1, -1 }
> +	};
> +
> +	finger = litest_add_device(li, other);
> +	litest_drain_events(li);
> +
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 20, 40, axes);
> +	litest_drain_events(li);
> +
> +	litest_touch_down(finger, 0, 30, 30);
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +	litest_assert_empty_queue(li);
> +
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 20, 40, axes);
> +	litest_assert_only_typed_events(li,
> +					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
> +	litest_tablet_proximity_out(dev);
> +	litest_assert_only_typed_events(li,
> +					LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
> +
> +	/* finger still down */
> +	litest_touch_move_to(finger, 0, 80, 80, 30, 30, 10, 1);
> +	litest_touch_up(finger, 0);
> +	litest_assert_empty_queue(li);
> +
> +	/* lift finger, expect expect events */
> +	litest_touch_down(finger, 0, 30, 30);
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(finger, 0);
> +	libinput_dispatch(li);
> +
> +	if (is_touchpad)
> +		litest_assert_only_typed_events(li,
> +						LIBINPUT_EVENT_POINTER_MOTION);
> +	else
> +		litest_assert_touch_sequence(li);
> +
> +	litest_delete_device(finger);
> +}
> +
> +START_TEST(intuos_touch_arbitration)
> +{
> +	touch_arbitration(litest_current_device(), LITEST_WACOM_FINGER, true);
> +}
> +END_TEST
> +
> +START_TEST(cintiq_touch_arbitration)
> +{
> +	touch_arbitration(litest_current_device(),
> +			  LITEST_WACOM_CINTIQ_13HDT_FINGER,
> +			  false);
> +}
> +END_TEST
> +
> +static void
> +touch_arbitration_stop_touch(struct litest_device *dev,
> +			     enum litest_device_type other,
> +			     bool is_touchpad)
> +{
> +	struct litest_device *finger;
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 10 },
> +		{ ABS_PRESSURE, 0 },
> +		{ -1, -1 }
> +	};
> +
> +	finger = litest_add_device(li, other);
> +	litest_touch_down(finger, 0, 30, 30);
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 20, 40, axes);
> +	litest_drain_events(li);
> +
> +	litest_touch_move_to(finger, 0, 80, 80, 30, 30, 10, 1);
> +	/* start another finger to make sure that one doesn't send events
> +	   either */
> +	litest_touch_down(finger, 1, 30, 30);
> +	litest_touch_move_to(finger, 1, 30, 30, 80, 80, 10, 1);
> +	litest_assert_empty_queue(li);
> +
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 20, 40, axes);
> +	litest_assert_only_typed_events(li,
> +					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
> +	litest_tablet_proximity_out(dev);
> +	litest_drain_events(li);
> +
> +	/* Finger needs to be lifted for events to happen*/
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +	litest_assert_empty_queue(li);
> +	litest_touch_move_to(finger, 1, 80, 80, 30, 30, 10, 1);
> +	litest_assert_empty_queue(li);
> +	litest_touch_up(finger, 0);
> +	litest_touch_move_to(finger, 1, 30, 30, 80, 80, 10, 1);
> +	litest_assert_empty_queue(li);
> +	litest_touch_up(finger, 1);
> +	litest_touch_down(finger, 0, 30, 30);
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(finger, 0);
> +	libinput_dispatch(li);
> +
> +	if (is_touchpad)
> +		litest_assert_only_typed_events(li,
> +						LIBINPUT_EVENT_POINTER_MOTION);
> +	else
> +		litest_assert_touch_sequence(li);
> +
> +	litest_delete_device(finger);
> +	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
> +}
> +
> +START_TEST(intuos_touch_arbitration_stop_touch)
> +{
> +	touch_arbitration_stop_touch(litest_current_device(),
> +				     LITEST_WACOM_FINGER,
> +				     true);
> +}
> +END_TEST
> +
> +START_TEST(cintiq_touch_arbitration_stop_touch)
> +{
> +	touch_arbitration_stop_touch(litest_current_device(),
> +				     LITEST_WACOM_CINTIQ_13HDT_FINGER,
> +				     false);
> +}
> +END_TEST
> +
> +static void
> +touch_arbitration_suspend_touch(struct litest_device *dev,
> +				enum litest_device_type other,
> +				bool is_touchpad)
> +{
> +	struct litest_device *tablet;
> +	struct libinput *li = dev->libinput;
> +	enum libinput_config_status status;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 10 },
> +		{ ABS_PRESSURE, 0 },
> +		{ -1, -1 }
> +	};
> +
> +	tablet = litest_add_device(li, other);
> +
> +	/* we can't force a device suspend, but we can at least make sure
> +	   the device doesn't send events */
> +	status = libinput_device_config_send_events_set_mode(
> +			     dev->libinput_device,
> +			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
> +	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
> +
> +	litest_drain_events(li);
> +
> +	litest_tablet_proximity_in(tablet, 10, 10, axes);
> +	litest_tablet_motion(tablet, 10, 10, axes);
> +	litest_tablet_motion(tablet, 20, 40, axes);
> +	litest_drain_events(li);
> +
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(dev, 0);
> +	litest_assert_empty_queue(li);
> +
> +	/* Remove tablet device to unpair, still disabled though */
> +	litest_delete_device(tablet);
> +	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
> +
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(dev, 0);
> +	litest_assert_empty_queue(li);
> +
> +	/* Touch device is still disabled */
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(dev, 0);
> +	litest_assert_empty_queue(li);
> +
> +	status = libinput_device_config_send_events_set_mode(
> +			     dev->libinput_device,
> +			     LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
> +	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
> +
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(dev, 0);
> +	libinput_dispatch(li);
> +
> +	if (is_touchpad)
> +		litest_assert_only_typed_events(li,
> +						LIBINPUT_EVENT_POINTER_MOTION);
> +	else
> +		litest_assert_touch_sequence(li);
> +}
> +
> +START_TEST(intuos_touch_arbitration_suspend_touch_device)
> +{
> +	touch_arbitration_suspend_touch(litest_current_device(),
> +					LITEST_WACOM_INTUOS,
> +					true);
> +}
> +END_TEST
> +
> +START_TEST(cintiq_touch_arbitration_suspend_touch_device)
> +{
> +	touch_arbitration_suspend_touch(litest_current_device(),
> +					LITEST_WACOM_CINTIQ_13HDT_PEN,
> +					false);
> +}
> +END_TEST
> +
> +static void
> +touch_arbitration_remove_touch(struct litest_device *dev,
> +			       enum litest_device_type other,
> +			       bool is_touchpad)
> +{
> +	struct litest_device *finger;
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 10 },
> +		{ ABS_PRESSURE, 0 },
> +		{ -1, -1 }
> +	};
> +
> +	finger = litest_add_device(li, other);
> +	litest_touch_down(finger, 0, 30, 30);
> +	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10, 1);
> +
> +	litest_tablet_proximity_in(dev, 10, 10, axes);
> +	litest_drain_events(li);
> +
> +	litest_delete_device(finger);
> +	libinput_dispatch(li);
> +	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
> +	litest_assert_empty_queue(li);
> +
> +	litest_tablet_motion(dev, 10, 10, axes);
> +	litest_tablet_motion(dev, 20, 40, axes);
> +	litest_assert_only_typed_events(li,
> +					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
> +}
> +
> +START_TEST(intuos_touch_arbitration_remove_touch)
> +{
> +	touch_arbitration_remove_touch(litest_current_device(),
> +				       LITEST_WACOM_INTUOS,
> +				       true);
> +}
> +END_TEST
> +
> +START_TEST(cintiq_touch_arbitration_remove_touch)
> +{
> +	touch_arbitration_remove_touch(litest_current_device(),
> +				       LITEST_WACOM_CINTIQ_13HDT_FINGER,
> +				       false);
> +}
> +END_TEST
> +
> +static void
> +touch_arbitration_remove_tablet(struct litest_device *dev,
> +				enum litest_device_type other,
> +				bool is_touchpad)
> +{
> +	struct litest_device *tablet;
> +	struct libinput *li = dev->libinput;
> +	struct axis_replacement axes[] = {
> +		{ ABS_DISTANCE, 10 },
> +		{ ABS_PRESSURE, 0 },
> +		{ -1, -1 }
> +	};
> +
> +	tablet = litest_add_device(li, other);
> +	libinput_dispatch(li);
> +	litest_tablet_proximity_in(tablet, 10, 10, axes);
> +	litest_tablet_motion(tablet, 10, 10, axes);
> +	litest_tablet_motion(tablet, 20, 40, axes);
> +	litest_drain_events(li);
> +
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_assert_empty_queue(li);
> +
> +	litest_delete_device(tablet);
> +	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
> +
> +	/* Touch is still down, don't enable */
> +	litest_touch_move_to(dev, 0, 80, 80, 30, 30, 10, 1);
> +	litest_touch_up(dev, 0);
> +	litest_assert_empty_queue(li);
> +
> +	litest_touch_down(dev, 0, 30, 30);
> +	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10, 1);
> +	litest_touch_up(dev, 0);
> +	libinput_dispatch(li);
> +
> +	if (is_touchpad)
> +		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
> +	else
> +		litest_assert_touch_sequence(li);
> +}
> +
> +START_TEST(intuos_touch_arbitration_remove_tablet)
> +{
> +	touch_arbitration_remove_tablet(litest_current_device(),
> +					LITEST_WACOM_INTUOS,
> +					true);
> +}
> +END_TEST
> +
> +START_TEST(cintiq_touch_arbitration_remove_tablet)
> +{
> +	touch_arbitration_remove_tablet(litest_current_device(),
> +					LITEST_WACOM_CINTIQ_13HDT_PEN,
> +					false);
> +}
> +END_TEST
> +
>  void
>  litest_setup_tests_tablet(void)
>  {
> @@ -3844,4 +4176,16 @@ litest_setup_tests_tablet(void)
>  	litest_add("tablet:relative", relative_no_delta_prox_in, LITEST_TABLET, LITEST_ANY);
>  	litest_add("tablet:relative", relative_delta, LITEST_TABLET, LITEST_ANY);
>  	litest_add("tablet:relative", relative_calibration, LITEST_TABLET, LITEST_ANY);
> +
> +	litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration, LITEST_WACOM_INTUOS);
> +	litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration_stop_touch, LITEST_WACOM_INTUOS);
> +	litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration_suspend_touch_device, LITEST_WACOM_FINGER);
> +	litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration_remove_touch, LITEST_WACOM_INTUOS);
> +	litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration_remove_tablet, LITEST_WACOM_FINGER);
> +
> +	litest_add_for_device("tablet:touch-arbitration", cintiq_touch_arbitration, LITEST_WACOM_CINTIQ_13HDT_PEN);
> +	litest_add_for_device("tablet:touch-arbitration", cintiq_touch_arbitration_stop_touch, LITEST_WACOM_CINTIQ_13HDT_PEN);
> +	litest_add_for_device("tablet:touch-arbitration", cintiq_touch_arbitration_suspend_touch_device, LITEST_WACOM_CINTIQ_13HDT_FINGER);
> +	litest_add_for_device("tablet:touch-arbitration", cintiq_touch_arbitration_remove_touch, LITEST_WACOM_CINTIQ_13HDT_PEN);
> +	litest_add_for_device("tablet:touch-arbitration", cintiq_touch_arbitration_remove_tablet, LITEST_WACOM_CINTIQ_13HDT_FINGER);
>  }
> 


More information about the wayland-devel mailing list