From 411a3a4766144c93795c2bda9aa9f89a3602c896 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 7 Mar 2017 16:00:45 +1000 Subject: [PATCH] tablet: add axis smoothing Taking the tablet events as-is produces the occasional wobble in what should be a straight line. Bug 99961 has a jpg attachment to illustrate that. Emulate the wacom driver behavior and average x/y across the last 4 values to smoothen out these dents. https://bugs.freedesktop.org/show_bug.cgi?id=99961 Signed-off-by: Peter Hutterer Acked-by: Ping Cheng --- src/evdev-tablet.c | 48 +++++++++++++++++++++------- src/evdev-tablet.h | 3 +- test/test-tablet.c | 78 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index daef7ac8..f87ccfe3 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -423,10 +423,10 @@ static inline struct normalized_coords tablet_tool_process_delta(struct tablet_dispatch *tablet, struct libinput_tablet_tool *tool, const struct evdev_device *device, + struct tablet_axes *axes, uint64_t time) { const struct normalized_coords zero = { 0.0, 0.0 }; - const struct tablet_axes *last, *current; struct device_coords delta = { 0, 0 }; struct device_float_coords accel; @@ -434,13 +434,12 @@ tablet_tool_process_delta(struct tablet_dispatch *tablet, TABLET_TOOL_ENTERING_PROXIMITY) && (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) || bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y))) { - current = &tablet->axes; - last = tablet_history_get(tablet, 0); - - delta.x = current->point.x - last->point.x; - delta.y = current->point.y - last->point.y; + delta.x = axes->point.x - tablet->last_smooth_point.x; + delta.y = axes->point.y - tablet->last_smooth_point.y; } + tablet->last_smooth_point = axes->point; + accel.x = 1.0 * delta.x; accel.y = 1.0 * delta.y; @@ -591,6 +590,31 @@ tablet_update_wheel(struct tablet_dispatch *tablet, } } +static void +tablet_smoothen_axes(const struct tablet_dispatch *tablet, + struct tablet_axes *axes) +{ + size_t i; + size_t count = tablet_history_size(tablet); + struct tablet_axes smooth = { 0 }; + + for (i = 0; i < count; i++) { + const struct tablet_axes *a = tablet_history_get(tablet, i); + + smooth.point.x += a->point.x; + smooth.point.y += a->point.y; + + smooth.tilt.x += a->tilt.x; + smooth.tilt.y += a->tilt.y; + } + + axes->point.x = smooth.point.x/count; + axes->point.y = smooth.point.y/count; + + axes->tilt.x = smooth.tilt.x/count; + axes->tilt.y = smooth.tilt.y/count; +} + static bool tablet_check_notify_axes(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -626,18 +650,20 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, axes.wheel_discrete = tablet->axes.wheel_discrete; axes.rotation = tablet->axes.rotation; - axes.delta = tablet_tool_process_delta(tablet, tool, device, time); - - *axes_out = axes; - rc = true; out: tablet_history_push(tablet, &tablet->axes); + tablet_smoothen_axes(tablet, &axes); + + /* The delta relies on the last *smooth* point, so we do it last */ + axes.delta = tablet_tool_process_delta(tablet, tool, device, &axes, time); + + *axes_out = axes; return rc; } -/**/ + static void tablet_update_button(struct tablet_dispatch *tablet, uint32_t evcode, diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index 344b4412..43ed8978 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -55,7 +55,8 @@ struct tablet_dispatch { struct evdev_device *device; unsigned int status; unsigned char changed_axes[NCHARS(LIBINPUT_TABLET_TOOL_AXIS_MAX + 1)]; - struct tablet_axes axes; + struct tablet_axes axes; /* for assembling the current state */ + struct device_coords last_smooth_point; struct { unsigned int index; unsigned int count; diff --git a/test/test-tablet.c b/test/test-tablet.c index 7e2545d3..215a9f6b 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -1013,6 +1013,13 @@ START_TEST(proximity_has_axes) litest_axis_set_value(axes, ABS_DISTANCE, 20); litest_axis_set_value(axes, ABS_TILT_X, 15); litest_axis_set_value(axes, ABS_TILT_Y, 25); + + /* work around axis smoothing */ + litest_tablet_motion(dev, 20, 30, axes); + litest_tablet_motion(dev, 20, 29, axes); + litest_tablet_motion(dev, 20, 31, axes); + litest_drain_events(li); + litest_tablet_motion(dev, 20, 30, axes); libinput_dispatch(li); event = libinput_get_event(li); @@ -1044,8 +1051,10 @@ START_TEST(proximity_has_axes) x = libinput_event_tablet_tool_get_x(tablet_event); y = libinput_event_tablet_tool_get_y(tablet_event); - litest_assert_double_eq(x, last_x); - litest_assert_double_eq(y, last_y); + litest_assert_double_ge(x, last_x - 1); + litest_assert_double_le(x, last_x + 1); + litest_assert_double_ge(y, last_y - 1); + litest_assert_double_le(y, last_y + 1); if (libinput_tablet_tool_has_distance(tool)) { ck_assert(!libinput_event_tablet_tool_distance_has_changed( @@ -1428,6 +1437,16 @@ START_TEST(left_handed) libinput_event_destroy(event); + /* work around smoothing */ + litest_axis_set_value(axes, ABS_DISTANCE, 9); + litest_tablet_motion(dev, 100, 0, axes); + litest_axis_set_value(axes, ABS_DISTANCE, 7); + litest_tablet_motion(dev, 100, 0, axes); + litest_axis_set_value(axes, ABS_DISTANCE, 10); + litest_tablet_motion(dev, 100, 0, axes); + litest_drain_events(li); + + litest_axis_set_value(axes, ABS_DISTANCE, 5); litest_tablet_motion(dev, 100, 0, axes); libinput_dispatch(li); @@ -1468,6 +1487,16 @@ START_TEST(left_handed) libinput_event_destroy(event); + /* work around smoothing */ + litest_axis_set_value(axes, ABS_DISTANCE, 9); + litest_tablet_motion(dev, 100, 0, axes); + litest_axis_set_value(axes, ABS_DISTANCE, 7); + litest_tablet_motion(dev, 100, 0, axes); + litest_axis_set_value(axes, ABS_DISTANCE, 10); + litest_tablet_motion(dev, 100, 0, axes); + litest_drain_events(li); + + litest_axis_set_value(axes, ABS_DISTANCE, 5); litest_tablet_motion(dev, 100, 0, axes); libinput_dispatch(li); @@ -1768,6 +1797,7 @@ START_TEST(motion_outside_bounds) struct libinput_event *event; struct libinput_event_tablet_tool *tablet_event; double val; + int i; struct axis_replacement axes[] = { { ABS_DISTANCE, 10 }, @@ -1778,6 +1808,15 @@ START_TEST(motion_outside_bounds) litest_tablet_proximity_in(dev, 50, 50, axes); litest_drain_events(li); + /* Work around smoothing */ + for (i = 5; i > 0; i--) { + litest_event(dev, EV_ABS, ABS_X, 0 + 5 * i); + litest_event(dev, EV_ABS, ABS_Y, 1000); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + } + litest_drain_events(li); + /* On the 24HD x/y of 0 is outside the limit */ litest_event(dev, EV_ABS, ABS_X, 0); litest_event(dev, EV_ABS, ABS_Y, 1000); @@ -1797,6 +1836,15 @@ START_TEST(motion_outside_bounds) libinput_event_destroy(event); + /* Work around smoothing */ + for (i = 5; i > 0; i--) { + litest_event(dev, EV_ABS, ABS_X, 1000); + litest_event(dev, EV_ABS, ABS_Y, 0 + 5 * i); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + } + litest_drain_events(li); + /* On the 24HD x/y of 0 is outside the limit */ litest_event(dev, EV_ABS, ABS_X, 1000); litest_event(dev, EV_ABS, ABS_Y, 0); @@ -3341,12 +3389,18 @@ START_TEST(tablet_pressure_min_max) litest_tablet_motion(dev, 5, 100, axes); litest_drain_events(li); + /* need to fill the motion history */ litest_axis_set_value(axes, ABS_PRESSURE, 100); + litest_tablet_motion(dev, 5, 100, axes); + litest_tablet_motion(dev, 6, 100, axes); + litest_tablet_motion(dev, 7, 100, axes); + litest_tablet_motion(dev, 8, 100, axes); + litest_drain_events(li); + litest_tablet_motion(dev, 5, 100, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); - ck_assert(libinput_event_tablet_tool_pressure_has_changed(tev)); p = libinput_event_tablet_tool_get_pressure(tev); ck_assert_double_ge(p, 1.0); libinput_event_destroy(event); @@ -3630,7 +3684,12 @@ START_TEST(tilt_x) for (tilt = 0; tilt <= 100; tilt += 5) { litest_axis_set_value(axes, ABS_TILT_X, tilt); + /* work around smoothing */ litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 10, 11, axes); + litest_tablet_motion(dev, 10, 10, axes); + litest_drain_events(li); + litest_tablet_motion(dev, 10, 11, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, @@ -3699,6 +3758,11 @@ START_TEST(tilt_y) for (tilt = 0; tilt <= 100; tilt += 5) { litest_axis_set_value(axes, ABS_TILT_Y, tilt); + /* work around smoothing */ + litest_tablet_motion(dev, 10, 11, axes); + litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 10, 11, axes); + litest_drain_events(li); litest_tablet_motion(dev, 10, 10, axes); libinput_dispatch(li); event = libinput_get_event(li); @@ -3815,7 +3879,7 @@ START_TEST(relative_delta) ck_assert(dy == 0.0); libinput_event_destroy(event); - litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 5, 10, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, @@ -3837,7 +3901,7 @@ START_TEST(relative_delta) ck_assert(dy > 0.0); libinput_event_destroy(event); - litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 10, 5, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, @@ -3888,7 +3952,7 @@ START_TEST(relative_calibration) ck_assert(dy == 0.0); libinput_event_destroy(event); - litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 5, 10, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, @@ -3910,7 +3974,7 @@ START_TEST(relative_calibration) ck_assert(dy < 0.0); libinput_event_destroy(event); - litest_tablet_motion(dev, 10, 10, axes); + litest_tablet_motion(dev, 10, 5, axes); libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event,