[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