[PATCH libinput] tablet: handle custom proximity handling

Peter Hutterer peter.hutterer at who-t.net
Thu Dec 3 15:04:02 PST 2015


On Thu, Dec 03, 2015 at 02:37:26PM +0100, Hans de Goede wrote:
> 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.

maybe. I don't doubt that the tablets are still out there and used, I just
thing that there is only a small number of those tablets being used together
with the mouse :)

Cheers,
   Peter

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