[PATCH v2 libinput 6/7] tablet: hook up relative motion events
Peter Hutterer
peter.hutterer at who-t.net
Mon Jan 18 17:22:47 PST 2016
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
Acked-by: Jason Gerecke <jason.gerecke at wacom.com>
---
Changes to v1:
- updated for removal of the dx/dy unaccel coordinates
doc/pointer-acceleration.dox | 6 ++
src/evdev-tablet.c | 116 +++++++++++++++++++++---
src/evdev.c | 13 +++
src/evdev.h | 4 +
src/filter.c | 94 ++++++++++++++++++++
src/filter.h | 3 +
src/libinput-util.h | 10 +++
test/tablet.c | 204 +++++++++++++++++++++++++++++++++++++++++++
8 files changed, 440 insertions(+), 10 deletions(-)
diff --git a/doc/pointer-acceleration.dox b/doc/pointer-acceleration.dox
index 7ec5e74..2fbb4cc 100644
--- a/doc/pointer-acceleration.dox
+++ b/doc/pointer-acceleration.dox
@@ -124,4 +124,10 @@ velocity of the pointer and each delta (dx, dy) results in an accelerated delta
(dx * factor, dy * factor). This provides 1:1 movement between the device
and the pointer on-screen.
+ at section ptraccel-tablet Pointer acceleration on tablets
+
+Pointer acceleration for relative motion on tablet devices is a flat
+acceleration, with the speed seeting slowing down or speeding up the pointer
+motion by a constant factor. Tablets do not allow for switchable profiles.
+
*/
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index 1870e7e..ae20550 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -285,19 +285,29 @@ normalize_wheel(struct tablet_dispatch *tablet,
return value * device->scroll.wheel_click_angle;
}
-static inline struct device_coords
-tablet_handle_xy(struct tablet_dispatch *tablet, struct evdev_device *device)
+static inline void
+tablet_handle_xy(struct tablet_dispatch *tablet,
+ struct evdev_device *device,
+ struct device_coords *point_out,
+ struct device_coords *delta_out)
{
struct device_coords point;
+ struct device_coords delta = { 0, 0 };
const struct input_absinfo *absinfo;
+ int value;
if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X)) {
absinfo = libevdev_get_abs_info(device->evdev, ABS_X);
if (device->left_handed.enabled)
- tablet->axes.point.x = invert_axis(absinfo);
+ value = invert_axis(absinfo);
else
- tablet->axes.point.x = absinfo->value;
+ value = absinfo->value;
+
+ if (!tablet_has_status(tablet,
+ TABLET_TOOL_ENTERING_PROXIMITY))
+ delta.x = value - tablet->axes.point.x;
+ tablet->axes.point.x = value;
}
point.x = tablet->axes.point.x;
@@ -305,15 +315,41 @@ tablet_handle_xy(struct tablet_dispatch *tablet, struct evdev_device *device)
absinfo = libevdev_get_abs_info(device->evdev, ABS_Y);
if (device->left_handed.enabled)
- tablet->axes.point.y = invert_axis(absinfo);
+ value = invert_axis(absinfo);
else
- tablet->axes.point.y = absinfo->value;
+ value = absinfo->value;
+
+ if (!tablet_has_status(tablet,
+ TABLET_TOOL_ENTERING_PROXIMITY))
+ delta.y = value - tablet->axes.point.y;
+ tablet->axes.point.y = value;
}
point.y = tablet->axes.point.y;
evdev_transform_absolute(device, &point);
+ evdev_transform_relative(device, &delta);
- return point;
+ *delta_out = delta;
+ *point_out = point;
+}
+
+static inline struct normalized_coords
+tablet_process_delta(struct tablet_dispatch *tablet,
+ const struct evdev_device *device,
+ const struct device_coords *delta,
+ uint64_t time)
+{
+ struct normalized_coords accel;
+
+ /* The tablet accel code uses mm as input */
+ accel.x = 1.0 * delta->x/device->abs.absinfo_x->resolution;
+ accel.y = 1.0 * delta->y/device->abs.absinfo_y->resolution;
+
+ if (normalized_is_zero(accel))
+ return accel;
+
+ return filter_dispatch(device->pointer.filter,
+ &accel, tablet, time);
}
static inline double
@@ -445,19 +481,22 @@ static bool
tablet_check_notify_axes(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct libinput_tablet_tool *tool,
- struct tablet_axes *axes_out)
+ struct tablet_axes *axes_out,
+ uint64_t time)
{
struct tablet_axes axes = {0};
const char tmp[sizeof(tablet->changed_axes)] = {0};
+ struct device_coords delta;
if (memcmp(tmp, tablet->changed_axes, sizeof(tmp)) == 0)
return false;
- axes.point = tablet_handle_xy(tablet, device);
+ tablet_handle_xy(tablet, device, &axes.point, &delta);
axes.pressure = tablet_handle_pressure(tablet, device, tool);
axes.distance = tablet_handle_distance(tablet, device);
axes.slider = tablet_handle_slider(tablet, device);
axes.tilt = tablet_handle_tilt(tablet, device);
+ axes.delta = tablet_process_delta(tablet, device, &delta, time);
/* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are
* already normalized and set if we have the mouse/lens tool */
@@ -1186,7 +1225,8 @@ tablet_send_axis_proximity_tip_down_events(struct tablet_dispatch *tablet,
} else if (!tablet_check_notify_axes(tablet,
device,
tool,
- &axes)) {
+ &axes,
+ time)) {
goto out;
}
@@ -1463,11 +1503,64 @@ tablet_init_proximity_threshold(struct tablet_dispatch *tablet,
tablet->cursor_proximity_threshold = 42;
}
+static uint32_t
+tablet_accel_config_get_profiles(struct libinput_device *libinput_device)
+{
+ return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+}
+
+static enum libinput_config_status
+tablet_accel_config_set_profile(struct libinput_device *libinput_device,
+ enum libinput_config_accel_profile profile)
+{
+ return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+}
+
+static enum libinput_config_accel_profile
+tablet_accel_config_get_profile(struct libinput_device *libinput_device)
+{
+ return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+}
+
+static enum libinput_config_accel_profile
+tablet_accel_config_get_default_profile(struct libinput_device *libinput_device)
+{
+ return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+}
+
+static int
+tablet_init_accel(struct tablet_dispatch *tablet, struct evdev_device *device)
+{
+ const struct input_absinfo *x, *y;
+ struct motion_filter *filter;
+ int rc;
+
+ x = device->abs.absinfo_x;
+ y = device->abs.absinfo_y;
+
+ filter = create_pointer_accelerator_filter_tablet(x->resolution,
+ y->resolution);
+
+ rc = evdev_device_init_pointer_acceleration(device, filter);
+ if (rc != 0)
+ return rc;
+
+ /* we override the profile hooks for accel configuration with hooks
+ * that don't allow selection of profiles */
+ device->pointer.config.get_profiles = tablet_accel_config_get_profiles;
+ device->pointer.config.set_profile = tablet_accel_config_set_profile;
+ device->pointer.config.get_profile = tablet_accel_config_get_profile;
+ device->pointer.config.get_default_profile = tablet_accel_config_get_default_profile;
+
+ return 0;
+}
+
static int
tablet_init(struct tablet_dispatch *tablet,
struct evdev_device *device)
{
enum libinput_tablet_tool_axis axis;
+ int rc;
tablet->base.interface = &tablet_interface;
tablet->device = device;
@@ -1477,6 +1570,9 @@ tablet_init(struct tablet_dispatch *tablet,
tablet_init_calibration(tablet, device);
tablet_init_proximity_threshold(tablet, device);
+ rc = tablet_init_accel(tablet, device);
+ if (rc != 0)
+ return rc;
for (axis = LIBINPUT_TABLET_TOOL_AXIS_X;
axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
diff --git a/src/evdev.c b/src/evdev.c
index 85ca640..8fa9626 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -222,6 +222,19 @@ evdev_transform_absolute(struct evdev_device *device,
matrix_mult_vec(&device->abs.calibration, &point->x, &point->y);
}
+void
+evdev_transform_relative(struct evdev_device *device,
+ struct device_coords *point)
+{
+ struct matrix rel_matrix;
+
+ if (!device->abs.apply_calibration)
+ return;
+
+ matrix_to_relative(&rel_matrix, &device->abs.calibration);
+ matrix_mult_vec(&rel_matrix, &point->x, &point->y);
+}
+
static inline double
scale_axis(const struct input_absinfo *absinfo, double val, double to_range)
{
diff --git a/src/evdev.h b/src/evdev.h
index 09c05e9..785c754 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -292,6 +292,10 @@ evdev_transform_absolute(struct evdev_device *device,
struct device_coords *point);
void
+evdev_transform_relative(struct evdev_device *device,
+ struct device_coords *point);
+
+void
evdev_init_calibration(struct evdev_device *device,
struct evdev_dispatch *dispatch);
diff --git a/src/filter.c b/src/filter.c
index 0d0b95d..4c39b0e 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -163,6 +163,13 @@ struct pointer_accelerator_flat {
double dpi_factor;
};
+struct tablet_accelerator_flat {
+ struct motion_filter base;
+
+ double factor;
+ int xres, yres;
+};
+
static void
feed_trackers(struct pointer_accelerator *accel,
const struct normalized_coords *delta,
@@ -964,3 +971,90 @@ create_pointer_accelerator_filter_flat(int dpi)
return &filter->base;
}
+
+/* The tablet accel code uses mm as input */
+static struct normalized_coords
+tablet_accelerator_filter_flat(struct motion_filter *filter,
+ const struct normalized_coords *mm,
+ void *data, uint64_t time)
+{
+ struct tablet_accelerator_flat *accel_filter =
+ (struct tablet_accelerator_flat *)filter;
+ struct normalized_coords accelerated;
+
+ /* Tablet input is in mm, output is supposed to be in logical
+ * pixels roughly equivalent to a mouse/touchpad.
+ *
+ * This is a magical constant found by trial and error. On a 96dpi
+ * screen 0.4mm of movement correspond to 1px logical pixel which
+ * is almost identical to the tablet mapped to screen in absolute
+ * mode. Tested on a Intuos5, other tablets may vary.
+ */
+ const double DPI_CONVERSION = 96.0/25.4 * 2.5; /* unitless factor */
+
+ accelerated.x = mm->x * accel_filter->factor * DPI_CONVERSION;
+ accelerated.y = mm->y * accel_filter->factor * DPI_CONVERSION;
+
+ return accelerated;
+}
+
+static bool
+tablet_accelerator_set_speed(struct motion_filter *filter,
+ double speed_adjustment)
+{
+ struct tablet_accelerator_flat *accel_filter =
+ (struct tablet_accelerator_flat *)filter;
+
+ assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
+
+ accel_filter->factor = speed_adjustment + 1.0;
+
+ return true;
+}
+
+static void
+tablet_accelerator_destroy(struct motion_filter *filter)
+{
+ struct tablet_accelerator_flat *accel_filter =
+ (struct tablet_accelerator_flat *)filter;
+
+ free(accel_filter);
+}
+
+struct motion_filter_interface accelerator_interface_tablet = {
+ .type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
+ .filter = tablet_accelerator_filter_flat,
+ .filter_constant = NULL,
+ .restart = NULL,
+ .destroy = tablet_accelerator_destroy,
+ .set_speed = tablet_accelerator_set_speed,
+};
+
+static struct tablet_accelerator_flat *
+create_tablet_filter_flat(int xres, int yres)
+{
+ struct tablet_accelerator_flat *filter;
+
+ filter = zalloc(sizeof *filter);
+ if (filter == NULL)
+ return NULL;
+
+ filter->xres = xres;
+ filter->yres = yres;
+
+ return filter;
+}
+
+struct motion_filter *
+create_pointer_accelerator_filter_tablet(int xres, int yres)
+{
+ struct tablet_accelerator_flat *filter;
+
+ filter = create_tablet_filter_flat(xres, yres);
+ if (!filter)
+ return NULL;
+
+ filter->base.interface = &accelerator_interface_tablet;
+
+ return &filter->base;
+}
diff --git a/src/filter.h b/src/filter.h
index e156642..c1b43a5 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -101,6 +101,9 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_trackpoint(int dpi);
+struct motion_filter *
+create_pointer_accelerator_filter_tablet(int xres, int yres);
+
/*
* Pointer acceleration profiles.
*/
diff --git a/src/libinput-util.h b/src/libinput-util.h
index 25ab5f1..efdaafc 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -269,6 +269,16 @@ matrix_to_farray6(const struct matrix *m, float out[6])
out[5] = m->val[1][2];
}
+static inline void
+matrix_to_relative(struct matrix *dest, const struct matrix *src)
+{
+ matrix_init_identity(dest);
+ dest->val[0][0] = src->val[0][0];
+ dest->val[0][1] = src->val[0][1];
+ dest->val[1][0] = src->val[1][0];
+ dest->val[1][1] = src->val[1][1];
+}
+
/**
* Simple wrapper for asprintf that ensures the passed in-pointer is set
* to NULL upon error.
diff --git a/test/tablet.c b/test/tablet.c
index 4976d1e..59aefe5 100644
--- a/test/tablet.c
+++ b/test/tablet.c
@@ -3205,6 +3205,205 @@ START_TEST(tilt_y)
}
END_TEST
+START_TEST(relative_no_profile)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_accel_profile profile;
+ enum libinput_config_status status;
+ uint32_t profiles;
+
+ ck_assert(libinput_device_config_accel_is_available(device));
+
+ profile = libinput_device_config_accel_get_default_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+
+ profiles = libinput_device_config_accel_get_profiles(device);
+ ck_assert_int_eq(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, 0);
+ ck_assert_int_eq(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, 0);
+
+ status = libinput_device_config_accel_set_profile(device,
+ LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+
+ status = libinput_device_config_accel_set_profile(device,
+ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+}
+END_TEST
+
+START_TEST(relative_no_delta_prox_in)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_tablet_tool *tev;
+ struct axis_replacement axes[] = {
+ { ABS_DISTANCE, 10 },
+ { ABS_PRESSURE, 0 },
+ { -1, -1 }
+ };
+ double dx, dy;
+
+ litest_drain_events(li);
+
+ litest_tablet_proximity_in(dev, 10, 10, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(relative_delta)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_tablet_tool *tev;
+ struct axis_replacement axes[] = {
+ { ABS_DISTANCE, 10 },
+ { ABS_PRESSURE, 0 },
+ { -1, -1 }
+ };
+ double dx, dy;
+
+ litest_tablet_proximity_in(dev, 10, 10, axes);
+ litest_drain_events(li);
+
+ litest_tablet_motion(dev, 20, 10, axes);
+ libinput_dispatch(li);
+
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx > 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 10, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx < 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 20, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx == 0.0);
+ ck_assert(dy > 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 10, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx == 0.0);
+ ck_assert(dy < 0.0);
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(relative_calibration)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_tablet_tool *tev;
+ struct axis_replacement axes[] = {
+ { ABS_DISTANCE, 10 },
+ { ABS_PRESSURE, 0 },
+ { -1, -1 }
+ };
+ double dx, dy;
+ float calibration[] = { -1, 0, 1, 0, -1, 1 };
+ enum libinput_config_status status;
+
+ if (!libinput_device_config_calibration_has_matrix(dev->libinput_device))
+ return;
+
+ status = libinput_device_config_calibration_set_matrix(
+ dev->libinput_device,
+ calibration);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ litest_tablet_proximity_in(dev, 10, 10, axes);
+ litest_drain_events(li);
+
+ litest_tablet_motion(dev, 20, 10, axes);
+ libinput_dispatch(li);
+
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx < 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 10, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx > 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 20, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx == 0.0);
+ ck_assert(dy < 0.0);
+ libinput_event_destroy(event);
+
+ litest_tablet_motion(dev, 10, 10, axes);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ tev = litest_is_tablet_event(event,
+ LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+ dx = libinput_event_tablet_tool_get_dx(tev);
+ dy = libinput_event_tablet_tool_get_dy(tev);
+ ck_assert(dx == 0.0);
+ ck_assert(dy > 0.0);
+ libinput_event_destroy(event);
+}
+END_TEST
+
void
litest_setup_tests(void)
{
@@ -3271,4 +3470,9 @@ litest_setup_tests(void)
litest_add_for_device("tablet:pressure", tablet_pressure_offset_exceed_threshold, LITEST_WACOM_INTUOS);
litest_add_for_device("tablet:pressure", tablet_pressure_offset_none_for_zero_distance, LITEST_WACOM_INTUOS);
litest_add_for_device("tablet:pressure", tablet_pressure_offset_none_for_small_distance, LITEST_WACOM_INTUOS);
+
+ litest_add("tablet:relative", relative_no_profile, LITEST_TABLET, LITEST_ANY);
+ litest_add("tablet:relative", relative_no_delta_prox_in, LITEST_TABLET, LITEST_ANY);
+ litest_add("tablet:relative", relative_delta, LITEST_TABLET, LITEST_ANY);
+ litest_add("tablet:relative", relative_calibration, LITEST_TABLET, LITEST_ANY);
}
--
2.5.0
More information about the wayland-devel
mailing list