[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