[PATCH v2 libinput] tablet: sync tools already in proximity at startup

Peter Hutterer peter.hutterer at who-t.net
Mon Mar 2 16:59:51 PST 2015


If a tool is in proximity when we init, send a proximity event immediately.

This is only partially reliable due to the current kernel behavior:
* if the tool comes into proximity when there is no evdev client, the device
  won't send any events and must be lifted out-of-proximity first.
* if the tool was in proximity (with an evdev client attached), but goes out
  of proximity and back in with no client connected, we get an immediate
  proximity out event from the kernel once we connect to the device and no
  further events after that.

Otherwise, things work as expected. The above should be fixed in the kernel
anyway.

Note that this changes the order of events during a udev seat init, before we
had all DEVICE_ADDED events in a row, now the proximity event may be
interspersed.

Reported-by: Jason Gerecke <killertofu at gmail.com>
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-mt-touchpad.c |  1 +
 src/evdev-tablet.c      | 78 ++++++++++++++++++++++++++++++++++++++++++++++---
 src/evdev-tablet.h      |  1 +
 src/evdev.c             |  5 ++++
 src/evdev.h             |  5 ++++
 test/tablet.c           | 53 +++++++++++++++++++++++++++++++++
 6 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index b90d84c..b3a8b08 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -897,6 +897,7 @@ static struct evdev_dispatch_interface tp_interface = {
 	tp_device_removed, /* device_suspended, treat as remove */
 	tp_device_added,   /* device_resumed, treat as add */
 	tp_tag_device,
+	NULL,              /* post_added */
 };
 
 static void
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index 6d3b9b7..12cca79 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -190,7 +190,7 @@ tablet_update_tool(struct tablet_dispatch *tablet,
 		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
 		tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
 	}
-	else
+	else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
 		tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
 }
 
@@ -467,6 +467,27 @@ tablet_evcode_to_tool(int code)
 	return type;
 }
 
+static inline int
+tablet_tool_to_evcode(enum libinput_tool_type type)
+{
+	int code;
+
+	switch (type) {
+	case LIBINPUT_TOOL_PEN:		code = BTN_TOOL_PEN;		break;
+	case LIBINPUT_TOOL_ERASER:	code = BTN_TOOL_RUBBER;		break;
+	case LIBINPUT_TOOL_BRUSH:	code = BTN_TOOL_BRUSH;		break;
+	case LIBINPUT_TOOL_PENCIL:	code = BTN_TOOL_PENCIL;		break;
+	case LIBINPUT_TOOL_AIRBRUSH:	code = BTN_TOOL_AIRBRUSH;	break;
+	case LIBINPUT_TOOL_FINGER:	code = BTN_TOOL_FINGER;		break;
+	case LIBINPUT_TOOL_MOUSE:	code = BTN_TOOL_MOUSE;		break;
+	case LIBINPUT_TOOL_LENS:	code = BTN_TOOL_LENS;		break;
+	default:
+		abort();
+	}
+
+	return code;
+}
+
 static void
 tablet_process_key(struct tablet_dispatch *tablet,
 		   struct evdev_device *device,
@@ -869,6 +890,9 @@ tablet_flush(struct tablet_dispatch *tablet,
 				tablet->current_tool_id,
 				tablet->current_tool_serial);
 
+	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
+		return;
+
 	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
 		/* Release all stylus buttons */
 		memset(tablet->button_state.stylus_buttons,
@@ -916,7 +940,11 @@ tablet_flush(struct tablet_dispatch *tablet,
 
 		tablet_change_to_left_handed(device);
 	}
+}
 
+static inline void
+tablet_reset_state(struct tablet_dispatch *tablet)
+{
 	/* Update state */
 	memcpy(&tablet->prev_button_state,
 	       &tablet->button_state,
@@ -947,6 +975,7 @@ tablet_process(struct evdev_dispatch *dispatch,
 		break;
 	case EV_SYN:
 		tablet_flush(tablet, device, time);
+		tablet_reset_state(tablet);
 		break;
 	default:
 		log_error(device->base.seat->libinput,
@@ -971,6 +1000,49 @@ tablet_destroy(struct evdev_dispatch *dispatch)
 	free(tablet);
 }
 
+static void
+tablet_check_initial_proximity(struct evdev_device *device,
+			       struct evdev_dispatch *dispatch)
+{
+	bool tool_in_prox = false;
+	int code, state;
+	enum libinput_tool_type tool;
+	struct tablet_dispatch *tablet = (struct tablet_dispatch*)dispatch;
+
+	for (tool = LIBINPUT_TOOL_PEN; tool <= LIBINPUT_TOOL_MAX; tool++) {
+		code = tablet_tool_to_evcode(tool);
+
+		/* we only expect one tool to be in proximity at a time */
+		if (libevdev_fetch_event_value(device->evdev,
+						EV_KEY,
+						code,
+						&state) && state) {
+			tool_in_prox = true;
+			break;
+		}
+	}
+
+	if (!tool_in_prox) {
+		tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
+		return;
+	}
+
+	tablet_update_tool(tablet, device, tool, state);
+
+	tablet->current_tool_id =
+		libevdev_get_event_value(device->evdev,
+					 EV_ABS,
+					 ABS_MISC);
+	tablet->current_tool_serial =
+		libevdev_get_event_value(device->evdev,
+					 EV_MSC,
+					 MSC_SERIAL);
+
+	tablet_flush(tablet,
+		     device,
+		     libinput_now(device->base.seat->libinput));
+}
+
 static struct evdev_dispatch_interface tablet_interface = {
 	tablet_process,
 	NULL, /* remove */
@@ -980,6 +1052,7 @@ static struct evdev_dispatch_interface tablet_interface = {
 	NULL, /* device_suspended */
 	NULL, /* device_resumed */
 	NULL, /* tag_device */
+	tablet_check_initial_proximity,
 };
 
 static int
@@ -1002,9 +1075,6 @@ tablet_init(struct tablet_dispatch *tablet,
 	}
 
 	tablet_mark_all_axes_changed(tablet, device);
-
-	tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
-
 	return 0;
 }
 
diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h
index ea103b0..76a9677 100644
--- a/src/evdev-tablet.h
+++ b/src/evdev-tablet.h
@@ -28,6 +28,7 @@
 
 #define LIBINPUT_TABLET_AXIS_NONE 0
 #define LIBINPUT_TOOL_NONE 0
+#define LIBINPUT_TOOL_MAX LIBINPUT_TOOL_LENS
 
 enum tablet_status {
 	TABLET_NONE = 0,
diff --git a/src/evdev.c b/src/evdev.c
index b90ea7c..2c4d1f1 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -803,6 +803,7 @@ struct evdev_dispatch_interface fallback_interface = {
 	NULL, /* device_suspended */
 	NULL, /* device_resumed */
 	fallback_tag_device,
+	NULL, /* post_added */
 };
 
 static uint32_t
@@ -1592,6 +1593,10 @@ evdev_notify_added_device(struct evdev_device *device)
 	}
 
 	notify_added_device(&device->base);
+
+	if (device->dispatch->interface->post_added)
+		device->dispatch->interface->post_added(device,
+							device->dispatch);
 }
 
 static int
diff --git a/src/evdev.h b/src/evdev.h
index 7469675..26f321e 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -196,6 +196,11 @@ struct evdev_dispatch_interface {
 	/* Tag device with one of EVDEV_TAG */
 	void (*tag_device)(struct evdev_device *device,
 			   struct udev_device *udev_device);
+
+	/* Called immediately after the LIBINPUT_EVENT_DEVICE_ADDED event
+	 * was sent */
+	void (*post_added)(struct evdev_device *device,
+			   struct evdev_dispatch *dispatch);
 };
 
 struct evdev_dispatch {
diff --git a/test/tablet.c b/test/tablet.c
index d7486cb..ba61e0e 100644
--- a/test/tablet.c
+++ b/test/tablet.c
@@ -1157,6 +1157,58 @@ START_TEST(tool_capabilities)
 }
 END_TEST
 
+START_TEST(tool_in_prox_before_start)
+{
+	struct libinput *li;
+	struct litest_device *dev = litest_current_device();
+	struct libinput_event *event;
+	struct axis_replacement axes[] = {
+		{ ABS_DISTANCE, 10 },
+		{ ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 0 },
+		{ -1, -1 }
+	};
+	const char *devnode;
+
+	litest_tablet_proximity_in(dev, 10, 10, axes);
+
+	/* for simplicity, we create a new litest context */
+	devnode = libevdev_uinput_get_devnode(dev->uinput);
+	li = litest_create_context();
+	libinput_path_add_device(li, devnode);
+
+	litest_wait_for_event_of_type(li,
+				      LIBINPUT_EVENT_DEVICE_ADDED,
+				      -1);
+	event = libinput_get_event(li);
+	libinput_event_destroy(event);
+
+	litest_wait_for_event_of_type(li,
+				      LIBINPUT_EVENT_TABLET_PROXIMITY,
+				      -1);
+	event = libinput_get_event(li);
+	libinput_event_destroy(event);
+	litest_assert_empty_queue(li);
+
+	litest_tablet_motion(dev, 10, 20, axes);
+	litest_tablet_motion(dev, 30, 40, axes);
+
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_AXIS);
+	litest_assert_empty_queue(li);
+	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_BUTTON);
+	litest_tablet_proximity_out(dev);
+
+	litest_wait_for_event_of_type(li,
+				      LIBINPUT_EVENT_TABLET_PROXIMITY,
+				      -1);
+	libinput_unref(li);
+}
+END_TEST
+
 START_TEST(mouse_tool)
 {
 	struct litest_device *dev = litest_current_device();
@@ -1615,6 +1667,7 @@ main(int argc, char **argv)
 {
 	litest_add("tablet:tool", tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
 	litest_add_no_device("tablet:tool", tool_capabilities);
+	litest_add("tablet:tool", tool_in_prox_before_start, LITEST_TABLET, LITEST_ANY);
 	litest_add("tablet:tool_serial", tool_serial, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
 	litest_add("tablet:tool_serial", serial_changes_tool, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
 	litest_add("tablet:tool_serial", invalid_serials, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
-- 
2.1.0



More information about the wayland-devel mailing list