From: Peter Hutterer Date: Tue, 13 Jun 2023 03:12:53 +0000 (+1000) Subject: tablet: apply pressure offset handling for non-distance tablets X-Git-Tag: 1.24.0~15 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a244b9e32bdd31ea469899473440fe0dd70c4c3b;p=platform%2Fupstream%2Flibinput.git tablet: apply pressure offset handling for non-distance tablets Previously we only applied pressure offset handling for tablets that supported ABS_DISTANCE. Detecting a pressure offset when the tool doesn't actually touch the surface is easy after all. But tablets without distance handling may also have a pressure offset, so let's try to detect this. This is obviously harder since the pen will always touch the tablet's surface whenever it is in proximity and thus will always have *some* pressure applied to it. The process here is to merely observe the minimum pressure value during the first two strokes of the pen. On the third prox in, that minimum pressure value is taken as the offset. If the pressure drops below the offset, the offset is adjusted downwards [1] so over time we'll get closer to the pen's real offset. [1] this is already done for distance tablets too Signed-off-by: Peter Hutterer --- diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 9cdb5896..42be7407 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1085,7 +1085,7 @@ tool_set_pressure_thresholds(struct tablet_dispatch *tablet, struct libinput_tablet_tool *tool) { struct evdev_device *device = tablet->device; - const struct input_absinfo *pressure; + const struct input_absinfo *pressure, *distance; struct quirks_context *quirks = NULL; struct quirks *q = NULL; struct quirk_range r; @@ -1101,7 +1101,14 @@ tool_set_pressure_thresholds(struct tablet_dispatch *tablet, quirks = evdev_libinput_context(device)->quirks; q = quirks_fetch_for_device(quirks, device->udev_device); - tool->pressure.offset = pressure->minimum; + distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); + if (distance) { + tool->pressure.offset = pressure->minimum; + tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_DONE; + } else { + tool->pressure.offset = pressure->maximum; + tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_PROXIN1; + } /* 5 and 1% of the pressure range */ hi = axis_range_percentage(pressure, 5); @@ -1337,18 +1344,24 @@ update_pressure_offset(struct tablet_dispatch *tablet, libevdev_get_abs_info(device->evdev, ABS_PRESSURE); if (!pressure || - !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE) || - !tool->pressure.has_offset) + !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) return; /* If we have an event that falls below the current offset, adjust * the offset downwards. A fast contact can start with a * higher-than-needed pressure offset and then we'd be tied into a * high pressure offset for the rest of the session. + * + * If we are still pending the offset decision, only update the observed + * offset value, don't actually set it to have an offset. */ int offset = pressure->value; - if (offset < tool->pressure.offset) - set_pressure_offset(tool, offset); + if (tool->pressure.has_offset) { + if (offset < tool->pressure.offset) + set_pressure_offset(tool, offset); + } else if (tool->pressure.heuristic_state != PRESSURE_HEURISTIC_STATE_DONE) { + tool->pressure.offset = min(offset, tool->pressure.offset); + } } static void @@ -1366,16 +1379,43 @@ detect_pressure_offset(struct tablet_dispatch *tablet, pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); - if (!pressure || !distance) + if (!pressure) return; offset = pressure->value; if (offset <= pressure->minimum) return; - /* If we're closer than 50% of the distance axis, skip pressure - * offset detection, too likely to be wrong */ - if (distance->value < axis_range_percentage(distance, 50)) + if (distance) { + /* If we're closer than 50% of the distance axis, skip pressure + * offset detection, too likely to be wrong */ + if (distance->value < axis_range_percentage(distance, 50)) + return; + } else { + /* A device without distance will always have some pressure on + * contact. Offset detection is delayed for a few proximity ins + * in the hope we'll find the minimum value until then. That + * offset is updated during motion events so by the time the + * deciding prox-in arrives we should know the minimum offset. + */ + if (offset > pressure->minimum) + tool->pressure.offset = min(offset, tool->pressure.offset); + + switch (tool->pressure.heuristic_state) { + case PRESSURE_HEURISTIC_STATE_PROXIN1: + case PRESSURE_HEURISTIC_STATE_PROXIN2: + tool->pressure.heuristic_state++; + return; + case PRESSURE_HEURISTIC_STATE_DECIDE: + tool->pressure.heuristic_state++; + offset = tool->pressure.offset; + break; + case PRESSURE_HEURISTIC_STATE_DONE: + return; + } + } + + if (offset <= pressure->minimum) return; if (offset > axis_range_percentage(pressure, 20)) { diff --git a/src/libinput-private.h b/src/libinput-private.h index 0a75a3c3..f3f441cb 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -442,6 +442,13 @@ struct tablet_axes { struct phys_ellipsis size; }; +enum pressure_heuristic_state { + PRESSURE_HEURISTIC_STATE_PROXIN1, /** First proximity in event */ + PRESSURE_HEURISTIC_STATE_PROXIN2, /** Second proximity in event */ + PRESSURE_HEURISTIC_STATE_DECIDE, /** Decide on offset now */ + PRESSURE_HEURISTIC_STATE_DONE, /** Decision's been made, live with it */ +}; + struct libinput_tablet_tool { struct list link; uint32_t serial; @@ -456,6 +463,8 @@ struct libinput_tablet_tool { struct threshold threshold; /* in device coordinates */ int offset; /* in device coordinates */ bool has_offset; + + enum pressure_heuristic_state heuristic_state; } pressure; }; diff --git a/test/test-tablet.c b/test/test-tablet.c index 354d5d9e..edf47224 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -3829,6 +3829,22 @@ START_TEST(tablet_pressure_offset_set) { -1, -1 }, }; + litest_drain_events(li); + + if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_DISTANCE)) { + /* First two prox ins won't do anything, coming with 10% should give + * us ~10% pressure */ + for (int i = 0; i < 2; i++) { + litest_tablet_proximity_in(dev, 5, 100, axes); + libinput_dispatch(li); + + assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY, 0.20); + assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_TIP, 0.20); + litest_tablet_proximity_out(dev); + litest_drain_events(li); + } + } + /* This activates the pressure offset */ litest_tablet_proximity_in(dev, 5, 100, axes); litest_drain_events(li); @@ -3900,6 +3916,13 @@ START_TEST(tablet_pressure_offset_decrease) litest_tablet_proximity_out(dev); litest_drain_events(li); + /* offset 15 on prox in - this one is so we trigger on the next prox + * in for the no-distance tablets */ + litest_axis_set_value(axes, ABS_PRESSURE, 15); + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_tablet_proximity_out(dev); + litest_drain_events(li); + /* a reduced pressure value must reduce the offset */ litest_axis_set_value(axes, ABS_PRESSURE, 10); litest_tablet_proximity_in(dev, 5, 100, axes); @@ -3959,7 +3982,13 @@ START_TEST(tablet_pressure_offset_increase) litest_tablet_proximity_out(dev); litest_drain_events(li); - /* offset 30 on second prox in - must not change the offset */ + /* offset 25 on second prox in - must not change the offset */ + litest_axis_set_value(axes, ABS_PRESSURE, 25); + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_tablet_proximity_out(dev); + litest_drain_events(li); + + /* offset 30 on third prox in - must not change the offset */ litest_axis_set_value(axes, ABS_PRESSURE, 30); litest_tablet_proximity_in(dev, 5, 100, axes); litest_drain_events(li); @@ -4092,6 +4121,15 @@ START_TEST(tablet_pressure_offset_exceed_threshold) int warning_triggered = 0; struct litest_user_data *user_data = libinput_get_user_data(li); + /* Tablet without distance: offset takes effect on third prox-in */ + if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_DISTANCE)) { + for (int i = 0; i < 2; i++) { + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_tablet_proximity_out(dev); + libinput_dispatch(li); + } + } + litest_drain_events(li); user_data->private = &warning_triggered; @@ -6150,6 +6188,7 @@ TEST_COLLECTION(tablet) litest_add(tablet_calibration_set_matrix_delta, LITEST_TABLET, LITEST_TOTEM|LITEST_PRECALIBRATED); litest_add(tablet_pressure_min_max, LITEST_TABLET, LITEST_ANY); + /* Tests for pressure offset with distance */ litest_add_for_device(tablet_pressure_range, LITEST_WACOM_INTUOS); litest_add_for_device(tablet_pressure_offset_set, LITEST_WACOM_INTUOS); litest_add_for_device(tablet_pressure_offset_decrease, LITEST_WACOM_INTUOS); @@ -6157,6 +6196,14 @@ TEST_COLLECTION(tablet) litest_add_for_device(tablet_pressure_offset_exceed_threshold, LITEST_WACOM_INTUOS); litest_add_for_device(tablet_pressure_offset_none_for_zero_distance, LITEST_WACOM_INTUOS); litest_add_for_device(tablet_pressure_offset_none_for_small_distance, LITEST_WACOM_INTUOS); + /* Tests for pressure offset without distance */ + litest_add_for_device(tablet_pressure_range, LITEST_WACOM_HID4800_PEN); + litest_add_for_device(tablet_pressure_offset_set, LITEST_WACOM_HID4800_PEN); + litest_add_for_device(tablet_pressure_offset_decrease, LITEST_WACOM_HID4800_PEN); + litest_add_for_device(tablet_pressure_offset_increase, LITEST_WACOM_HID4800_PEN); + litest_add_for_device(tablet_pressure_offset_exceed_threshold, LITEST_WACOM_HID4800_PEN); + + litest_add_for_device(tablet_distance_range, LITEST_WACOM_INTUOS); litest_add(relative_no_profile, LITEST_TABLET, LITEST_ANY);