[PATCH v2 libinput 3/5] tablet: add pressure threshold handling
Peter Hutterer
peter.hutterer at who-t.net
Tue Jan 5 18:03:31 PST 2016
On tablets with ABS_PRESSURE use a pressure value to determine tip state, not
BTN_TOUCH. This enables us (down the road) to have device-specific pressure
thresholds. For now we use a 5% default for all devices.
The threshold is a range, if we go past the upper range we initiate the tip
down, if we go below the lower range we release the tip again.
This affects two current tests:
* Once we have pressure offsets and pressure thresholds, we're biased towards
pressure. So we can only check that distance is zero when there is a pressure
value, not the other way round.
* When the pressure threshold is exceeded on proximity in with a nonzero
distance, we can only warn and handle the pressure as normal. Since this is a
niche case anyway anything fancier is likely unnecessary.
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v1:
- fix pressure offset (don't double-add the minimum)
- fix pressure thresholds for non-zero axis minimum
doc/tablet-support.dox | 16 +++++++++
src/evdev-tablet.c | 98 ++++++++++++++++++++++++++++++++++++++++----------
src/libinput-private.h | 8 +++++
test/tablet.c | 67 ++++++++++++----------------------
4 files changed, 126 insertions(+), 63 deletions(-)
diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox
index c95d805..5e6f393 100644
--- a/doc/tablet-support.dox
+++ b/doc/tablet-support.dox
@@ -35,6 +35,22 @@ Tablet tools may send button events; these are exclusively for extra buttons
unrelated to the tip. A button event is independent of the tip and can while
the tip is down or up.
+Some tablet tools' pressure detection is too sensitive, causing phantom
+touches when the user only slightly brushes the surfaces. For example, some
+tools are capable of detecting 1 gram of pressure.
+
+libinput uses a device-specific pressure threshold to determine when the tip
+is considered logically down. As a result, libinput may send a nonzero
+pressure value while the tip is logically up. Most application can and
+should ignore pressure information until they receive the event of type @ref
+LIBINPUT_EVENT_TABLET_TOOL_TIP. Applications that require extremely
+fine-grained pressure sensitivity should use the pressure data instead of
+the tip events.
+
+Note that the pressure threshold to trigger a logical tip event may be zero
+on some devices. On tools without pressure sensitivity, determining when a
+tip is down is device-specific.
+
@section tablet-axes Special axes on tablet tools
A tablet tool usually provides additional information beyond x/y positional
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index faf7f8e..444ee4a 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -617,10 +617,15 @@ tablet_process_key(struct tablet_dispatch *tablet,
e->value);
break;
case BTN_TOUCH:
- if (e->value)
- tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
- else
- tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
+ if (!bit_is_set(tablet->axis_caps,
+ LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) {
+ if (e->value)
+ tablet_set_status(tablet,
+ TABLET_TOOL_ENTERING_CONTACT);
+ else
+ tablet_set_status(tablet,
+ TABLET_TOOL_LEAVING_CONTACT);
+ }
break;
case BTN_LEFT:
case BTN_RIGHT:
@@ -844,6 +849,12 @@ tool_set_bits(const struct tablet_dispatch *tablet,
}
}
+static inline int
+axis_range_percentage(const struct input_absinfo *a, double percent)
+{
+ return (a->maximum - a->minimum) * percent/100.0 + a->minimum;
+}
+
static struct libinput_tablet_tool *
tablet_get_tool(struct tablet_dispatch *tablet,
enum libinput_tablet_tool_type type,
@@ -894,12 +905,21 @@ tablet_get_tool(struct tablet_dispatch *tablet,
tool->pressure_offset = 0;
tool->has_pressure_offset = false;
+ tool->pressure_threshold.lower = 0;
+ tool->pressure_threshold.upper = 1;
pressure = libevdev_get_abs_info(tablet->device->evdev,
ABS_PRESSURE);
- if (pressure)
+ if (pressure) {
tool->pressure_offset = pressure->minimum;
+ /* 5% of the pressure range */
+ tool->pressure_threshold.upper =
+ axis_range_percentage(pressure, 5);
+ tool->pressure_threshold.lower =
+ pressure->minimum;
+ }
+
tool_set_bits(tablet, tool);
list_insert(tool_list, &tool->link);
@@ -965,7 +985,8 @@ tablet_notify_buttons(struct tablet_dispatch *tablet,
}
static void
-sanitize_pressure_distance(struct tablet_dispatch *tablet)
+sanitize_pressure_distance(struct tablet_dispatch *tablet,
+ struct libinput_tablet_tool *tool)
{
bool tool_in_contact;
const struct input_absinfo *distance,
@@ -974,9 +995,14 @@ sanitize_pressure_distance(struct tablet_dispatch *tablet)
distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
pressure = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE);
- tool_in_contact = (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) ||
- tablet_has_status(tablet,
- TABLET_TOOL_ENTERING_CONTACT));
+ if (!pressure || !distance)
+ return;
+
+ if (!bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE) &&
+ !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
+ return;
+
+ tool_in_contact = (pressure->value > tool->pressure_offset);
/* Keep distance and pressure mutually exclusive */
if (distance &&
@@ -1017,16 +1043,11 @@ sanitize_mouse_lens_rotation(struct tablet_dispatch *tablet)
set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
}
-static inline int
-axis_range_percentage(const struct input_absinfo *a, int percent)
-{
- return (a->maximum - a->minimum) * percent/100 + a->minimum;
-}
-
static void
-sanitize_tablet_axes(struct tablet_dispatch *tablet)
+sanitize_tablet_axes(struct tablet_dispatch *tablet,
+ struct libinput_tablet_tool *tool)
{
- sanitize_pressure_distance(tablet);
+ sanitize_pressure_distance(tablet, tool);
sanitize_mouse_lens_rotation(tablet);
}
@@ -1086,6 +1107,46 @@ detect_pressure_offset(struct tablet_dispatch *tablet,
}
static void
+detect_tool_contact(struct tablet_dispatch *tablet,
+ struct evdev_device *device,
+ struct libinput_tablet_tool *tool)
+{
+ const struct input_absinfo *p;
+ int pressure;
+
+ if (!bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
+ return;
+
+ /* if we have pressure, always use that for contact, not BTN_TOUCH */
+ if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT))
+ log_bug_libinput(device->base.seat->libinput,
+ "Invalid status: entering contact\n");
+ if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) &&
+ !tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY))
+ log_bug_libinput(device->base.seat->libinput,
+ "Invalid status: leaving contact\n");
+
+ p = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE);
+ if (!p) {
+ log_bug_libinput(device->base.seat->libinput,
+ "Missing pressure axis\n");
+ return;
+ }
+ pressure = p->value;
+
+ if (tool->has_pressure_offset)
+ pressure -= (tool->pressure_offset - p->minimum);
+
+ if (pressure <= tool->pressure_threshold.lower &&
+ tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) {
+ tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
+ } else if (pressure >= tool->pressure_threshold.upper &&
+ !tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) {
+ tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
+ }
+}
+
+static void
tablet_mark_all_axes_changed(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool)
{
@@ -1188,7 +1249,8 @@ tablet_flush(struct tablet_dispatch *tablet,
TABLET_TOOL_ENTERING_PROXIMITY))
tablet_mark_all_axes_changed(tablet, tool);
detect_pressure_offset(tablet, device, tool);
- sanitize_tablet_axes(tablet);
+ detect_tool_contact(tablet, device, tool);
+ sanitize_tablet_axes(tablet, tool);
tablet_check_notify_axes(tablet, device, time, tool);
tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 3d714af..5238e64 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -63,6 +63,12 @@ struct normalized_range_coords {
double x, y;
};
+/* A threshold with an upper and lower limit */
+struct threshold {
+ int upper;
+ int lower;
+};
+
struct libinput_interface_backend {
int (*resume)(struct libinput *libinput);
void (*suspend)(struct libinput *libinput);
@@ -277,6 +283,8 @@ struct libinput_tablet_tool {
int refcount;
void *user_data;
+ /* The pressure threshold assumes a pressure_offset of 0 */
+ struct threshold pressure_threshold;
int pressure_offset; /* in device coordinates */
bool has_pressure_offset;
};
diff --git a/test/tablet.c b/test/tablet.c
index 76dfe80..421deb0 100644
--- a/test/tablet.c
+++ b/test/tablet.c
@@ -2528,7 +2528,7 @@ START_TEST(tablet_pressure_distance_exclusive)
struct libinput_event_tablet_tool *tev;
struct axis_replacement axes[] = {
{ ABS_DISTANCE, 10 },
- { ABS_PRESSURE, 20 }, /* see the litest device */
+ { ABS_PRESSURE, 0 },
{ -1, -1 },
};
double pressure, distance;
@@ -2536,6 +2536,7 @@ START_TEST(tablet_pressure_distance_exclusive)
litest_tablet_proximity_in(dev, 5, 100, axes);
litest_drain_events(li);
+ litest_axis_set_value(axes, ABS_PRESSURE, 2);
litest_tablet_motion(dev, 70, 70, axes);
libinput_dispatch(li);
@@ -2545,28 +2546,8 @@ START_TEST(tablet_pressure_distance_exclusive)
pressure = libinput_event_tablet_tool_get_pressure(tev);
distance = libinput_event_tablet_tool_get_distance(tev);
- ck_assert_double_eq(pressure, 0.0);
- ck_assert_double_ne(distance, 0.0);
-
- libinput_event_destroy(event);
-
- litest_axis_set_value(axes, ABS_DISTANCE, 15);
- litest_axis_set_value(axes, ABS_PRESSURE, 25);
- litest_event(dev, EV_KEY, BTN_TOUCH, 1);
- litest_tablet_motion(dev, 30, 30, axes);
- libinput_dispatch(li);
-
- litest_wait_for_event_of_type(li,
- LIBINPUT_EVENT_TABLET_TOOL_AXIS,
- -1);
- event = libinput_get_event(li);
- tev = libinput_event_get_tablet_tool_event(event);
-
- pressure = libinput_event_tablet_tool_get_pressure(tev);
- distance = libinput_event_tablet_tool_get_distance(tev);
-
- ck_assert_double_eq(distance, 0.0);
ck_assert_double_ne(pressure, 0.0);
+ ck_assert_double_eq(distance, 0.0);
libinput_event_destroy(event);
}
@@ -2933,6 +2914,18 @@ START_TEST(tablet_pressure_offset_increase)
}
END_TEST
+static void pressure_threshold_warning(struct libinput *libinput,
+ enum libinput_log_priority priority,
+ const char *format,
+ va_list args)
+{
+ int *warning_triggered = (int*)libinput_get_user_data(libinput);
+
+ if (priority == LIBINPUT_LOG_PRIORITY_ERROR &&
+ strstr(format, "pressure offset greater"))
+ (*warning_triggered)++;
+}
+
START_TEST(tablet_pressure_offset_exceed_threshold)
{
struct litest_device *dev = litest_current_device();
@@ -2945,40 +2938,24 @@ START_TEST(tablet_pressure_offset_exceed_threshold)
{ -1, -1 },
};
double pressure;
+ int warning_triggered = 0;
litest_drain_events(li);
- litest_disable_log_handler(li);
+ libinput_set_user_data(li, &warning_triggered);
+
+ libinput_log_set_handler(li, pressure_threshold_warning);
litest_tablet_proximity_in(dev, 5, 100, axes);
libinput_dispatch(li);
event = libinput_get_event(li);
tev = litest_is_tablet_event(event,
LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
pressure = libinput_event_tablet_tool_get_pressure(tev);
- ck_assert_double_eq(pressure, 0.0);
+ ck_assert_double_gt(pressure, 0.0);
libinput_event_destroy(event);
+
+ ck_assert_int_eq(warning_triggered, 1);
litest_restore_log_handler(li);
-
- litest_axis_set_value(axes, ABS_DISTANCE, 0);
- litest_axis_set_value(axes, ABS_PRESSURE, 31);
- litest_push_event_frame(dev);
- litest_tablet_motion(dev, 70, 70, axes);
- litest_event(dev, EV_KEY, BTN_TOUCH, 1);
- litest_pop_event_frame(dev);
- libinput_dispatch(li);
- litest_drain_events(li);
-
- litest_axis_set_value(axes, ABS_PRESSURE, 30);
- litest_tablet_motion(dev, 70, 70, axes);
- libinput_dispatch(li);
-
- event = libinput_get_event(li);
- tev = litest_is_tablet_event(event,
- LIBINPUT_EVENT_TABLET_TOOL_AXIS);
- pressure = libinput_event_tablet_tool_get_pressure(tev);
- ck_assert_double_gt(pressure, 0.0);
-
- libinput_event_destroy(event);
}
END_TEST
--
2.5.0
More information about the wayland-devel
mailing list