diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2024-01-15 17:07:17 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2024-01-30 14:29:25 +1000 |
commit | 4bc27543e9079c90b4cb3968737c0a0b14364413 (patch) | |
tree | 04a1cb609657454dfcb1ebe038f04e039ed7573d | |
parent | 47f0bce7ec269190e8c15eff4b89bdaee619a8e5 (diff) |
tablet: add tablet tool pressure range configuration
Add a configuration option to reduce the available hardware range to a
fraction thereof. This is done by copying the absinfo struct for the
pressure value and adjusting that copy's minimum/maximum value for
scaling into the target normalized range.
The 1%/5% tip thresholds are kept but pressure offset detection is
disabled if there is a custom pressure range.
Unlike the pressure curve which is implemented in the compositor, the
pressure min/max range needs to be in libinput, primarily because the
tip threshold needs to adjust to any new minimum, allowing for
light touches with a pen without triggering tip down even at a higher
hardware pressure.
-rw-r--r-- | doc/user/configuration.rst | 14 | ||||
-rw-r--r-- | doc/user/tablet-support.rst | 46 | ||||
-rw-r--r-- | src/evdev-tablet.c | 182 | ||||
-rw-r--r-- | src/libinput-private.h | 22 | ||||
-rw-r--r-- | src/libinput.c | 69 | ||||
-rw-r--r-- | src/libinput.h | 130 | ||||
-rw-r--r-- | src/libinput.sym | 11 | ||||
-rw-r--r-- | test/test-tablet.c | 315 |
8 files changed, 750 insertions, 39 deletions
diff --git a/doc/user/configuration.rst b/doc/user/configuration.rst index 16207285..8ce31f21 100644 --- a/doc/user/configuration.rst +++ b/doc/user/configuration.rst @@ -177,3 +177,17 @@ the non-dominant hand. Note that where a device rotation is higher than 160 but less than 200 degrees, the direction of wheels is also inverted. For all other angles, the wheel direction is left as-is. + +.. _config-tablet-pressure-range: + +------------------------------------------------------------------------------ +Tablet tool pressure range +------------------------------------------------------------------------------ + +The pressure range on a :ref:`Tablet tool <tablet-tools>` can be reduced +from the full available hardware range to a subset of that range. The effect +of this is that the tablet will not register pressure below the given +the given threshold is met, and will reach the maximum logical pressure +before the maximum hardware-supported pressure is reached. + +See :ref:`tablet-pressure-range` for more info. diff --git a/doc/user/tablet-support.rst b/doc/user/tablet-support.rst index 32bf162b..fededac0 100644 --- a/doc/user/tablet-support.rst +++ b/doc/user/tablet-support.rst @@ -189,6 +189,52 @@ specifically: Pressure offsets are not detected on **LIBINPUT_TABLET_TOOL_TYPE_MOUSE** and **LIBINPUT_TABLET_TOOL_TYPE_LENS** tools. + +.. _tablet-pressure-range: + +------------------------------------------------------------------------------ +Custom tablet tool pressure ranges +------------------------------------------------------------------------------ + +On tablets supporting pressure, libinput provides that hardware pressure as +a logical range of ``0.0`` up to ``1.0`` for the maximum supported pressure. +By default, the hardware range thus maps into the following logical range:: + + + hw minimum hw maximum + hw range: |------|-----------------------------------| + logical range: |----|-----------------------------------| + 0.0 | 1.0 + Tip + +Note that libinput always has some built-in thresholds to filter out erroneous +touches with near-zero pressure but otherwise the hardware range maps as-is +into the logical range. The :ref:`tip event <tablet-tip>` threshold is defined +within this range. + +For some use-cases the full hardware range is not suitable, it may require either +too light a pressure for the user to interact or it may require too hard a +pressure before the logical maximum is reached. libinput provides +the **libinput_tablet_tool_config_pressure_range_set()** function that allows +reducing the usable range of the tablet:: + + hw minimum hw maximum + hw range: |----------|-------------------------------| + adjusted range: |------|---------------------| + logical range: |----|---------------------| + 0.0 | 1.0 + Tip + +A reduced range as shown above will result in + +- all hw pressure below the new minimum to register as logical pressure ``0.0`` +- all hw pressure above the new maximum to register as logical pressure ``1.0`` +- the tip event threshold to be relative to the new minimum + +In other words, adjusting the pressure range of a tablet tool is equivalent to +reducing the hardware range of said tool. Note that where a custom pressure +range is set, detection of :ref:`tablet-pressure-offset` is disabled. + .. _tablet-serial-numbers: ------------------------------------------------------------------------------ diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index d3b6f825..c2dce5bf 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -346,6 +346,7 @@ normalize_distance(const struct input_absinfo *absinfo) static inline double normalize_pressure(const struct input_absinfo *absinfo, + int abs_value, struct libinput_tablet_tool *tool) { /** @@ -360,9 +361,10 @@ normalize_pressure(const struct input_absinfo *absinfo, * threshold is 0 pressure. */ struct input_absinfo abs = *absinfo; + abs.minimum = tool->pressure.threshold.lower; - return absinfo_normalize(&abs); + return absinfo_normalize_value(&abs, abs_value); } static inline double @@ -522,15 +524,15 @@ tablet_update_pressure(struct tablet_dispatch *tablet, struct evdev_device *device, struct libinput_tablet_tool *tool) { - const struct input_absinfo *absinfo; - - if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_PRESSURE)) + const struct input_absinfo *abs = libevdev_get_abs_info(device->evdev, + ABS_PRESSURE); + if (!abs) return; - if (bit_is_set(tablet->changed_axes, - LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) { - absinfo = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); - tablet->axes.pressure = normalize_pressure(absinfo, tool); + if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) { + tablet->axes.pressure = normalize_pressure(&tool->pressure.abs_pressure, + abs->value, + tool); } } @@ -1076,26 +1078,94 @@ axis_range_percentage(const struct input_absinfo *a, double percent) return (a->maximum - a->minimum) * percent/100.0 + a->minimum; } +static bool +tablet_get_quirked_pressure_thresholds(struct tablet_dispatch *tablet, + int *hi, + int *lo) +{ + struct evdev_device *device = tablet->device; + struct quirks_context *quirks = evdev_libinput_context(device)->quirks; + struct quirks *q = quirks_fetch_for_device(quirks, device->udev_device); + struct quirk_range r; + bool status = false; + + /* Note: the quirk term "range" refers to the hi/lo settings, not the + * full available range for the pressure axis */ + if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) { + if (r.lower < r.upper) { + *hi = r.lower; + *lo = r.upper; + status = true; + } else { + evdev_log_info(device, "Invalid pressure range, using defaults\n"); + } + } + + quirks_unref(q); + return status; +} + +static void +apply_pressure_range_configuration(struct tablet_dispatch *tablet, + struct libinput_tablet_tool *tool) +{ + struct evdev_device *device = tablet->device; + + if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_PRESSURE) || + (tool->pressure.range.min == tool->pressure.wanted_range.min && + tool->pressure.range.max == tool->pressure.wanted_range.max)) + return; + + double min = tool->pressure.wanted_range.min; + double max = tool->pressure.wanted_range.max; + + struct input_absinfo abs = *libevdev_get_abs_info(device->evdev, ABS_PRESSURE); + + int minimum = axis_range_percentage(&abs, min * 100.0); + int maximum = axis_range_percentage(&abs, max * 100.0); + + abs.minimum = minimum; + abs.maximum = maximum; + + /* Only use the quirk pressure range if we don't have a custom range */ + int hi, lo; + if (tool->pressure.wanted_range.min != 0.0 || + tool->pressure.wanted_range.max != 1.0 || + !tablet_get_quirked_pressure_thresholds(tablet, &hi, &lo)) { + /* 5 and 1% of the pressure range */ + hi = axis_range_percentage(&abs, 5); + lo = axis_range_percentage(&abs, 1); + } + + tool->pressure.abs_pressure = abs; + tool->pressure.threshold.upper = hi; + tool->pressure.threshold.lower = lo; + tool->pressure.range.min = tool->pressure.wanted_range.min; + tool->pressure.range.max = tool->pressure.wanted_range.max; + + /* Disable any heuristics */ + if (tool->pressure.has_configured_range) { + tool->pressure.has_offset = true; + tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_DONE; + } +} + static inline void -tool_set_pressure_thresholds(struct tablet_dispatch *tablet, - struct libinput_tablet_tool *tool) +tool_init_pressure_thresholds(struct tablet_dispatch *tablet, + struct libinput_tablet_tool *tool) { struct evdev_device *device = tablet->device; const struct input_absinfo *pressure, *distance; - struct quirks_context *quirks = NULL; - struct quirks *q = NULL; - struct quirk_range r; - int lo = 0, hi = 1; tool->pressure.offset = 0; tool->pressure.has_offset = false; pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); - if (!pressure) - goto out; - - quirks = evdev_libinput_context(device)->quirks; - q = quirks_fetch_for_device(quirks, device->udev_device); + if (!pressure) { + tool->pressure.threshold.upper = 1; + tool->pressure.threshold.lower = 0; + return; + } distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); if (distance) { @@ -1106,24 +1176,40 @@ tool_set_pressure_thresholds(struct tablet_dispatch *tablet, tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_PROXIN1; } - /* 5 and 1% of the pressure range */ - hi = axis_range_percentage(pressure, 5); - lo = axis_range_percentage(pressure, 1); + apply_pressure_range_configuration(tablet, tool); +} - if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) { - if (r.lower >= r.upper) { - evdev_log_info(device, - "Invalid pressure range, using defaults\n"); - } else { - hi = r.upper; - lo = r.lower; - } - } -out: - tool->pressure.threshold.upper = hi; - tool->pressure.threshold.lower = lo; +static int +pressure_range_is_available(struct libinput_tablet_tool *tool) +{ + return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); +} - quirks_unref(q); +static enum libinput_config_status +pressure_range_set(struct libinput_tablet_tool *tool, double min, double max) +{ + if (min < 0.0 || min >= 1.0 || max <= 0.0 || max > 1.0 || max <= min) + return LIBINPUT_CONFIG_STATUS_INVALID; + + tool->pressure.wanted_range.min = min; + tool->pressure.wanted_range.max = max; + tool->pressure.has_configured_range = true; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static void +pressure_range_get(struct libinput_tablet_tool *tool, double *min, double *max) +{ + *min = tool->pressure.wanted_range.min; + *max = tool->pressure.wanted_range.max; +} + +static void +pressure_range_get_default(struct libinput_tablet_tool *tool, double *min, double *max) +{ + *min = 0.0; + *max = 1.0; } static struct libinput_tablet_tool * @@ -1139,14 +1225,31 @@ tablet_new_tool(struct tablet_dispatch *tablet, .serial = serial, .tool_id = tool_id, .refcount = 1, + + .pressure.range.min = 0.0, + .pressure.range.max = 0.0, /* to trigger configuration */ + .pressure.wanted_range.min = 0.0, + .pressure.wanted_range.max = 1.0, + + .config.pressure_range.is_available = pressure_range_is_available, + .config.pressure_range.set = pressure_range_set, + .config.pressure_range.get = pressure_range_get, + .config.pressure_range.get_default = pressure_range_get_default, }; + /* Copy the pressure axis for configuring the range later */ + struct evdev_device *device = tablet->device; + const struct input_absinfo *abs = libevdev_get_abs_info(device->evdev, + ABS_PRESSURE); + if (abs) + tool->pressure.abs_pressure = *abs; + /* FIXME: known bug - the pressure threshold is only set once on the * first tablet, if a tool is used across multiple tablets with * different pressure ranges this will be wrong. This case is niche * enough that we can fix it if we ever run into it. */ - tool_set_pressure_thresholds(tablet, tool); + tool_init_pressure_thresholds(tablet, tool); tool_set_bits(tablet, tool); return tool; @@ -1272,6 +1375,8 @@ sanitize_pressure_distance(struct tablet_dispatch *tablet, *pressure; distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE); + /* Note: for pressure/distance sanitization we use the real pressure + axis, not our configured one */ pressure = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE); if (!pressure || !distance) @@ -1354,7 +1459,7 @@ update_pressure_offset(struct tablet_dispatch *tablet, const struct input_absinfo *pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); - if (!pressure || + if (!pressure || tool->pressure.has_configured_range || !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) return; @@ -1383,7 +1488,7 @@ detect_pressure_offset(struct tablet_dispatch *tablet, const struct input_absinfo *pressure, *distance; int offset; - if (tool->pressure.has_offset || + if (tool->pressure.has_offset || tool->pressure.has_configured_range || !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) return; @@ -1999,6 +2104,7 @@ reprocess: tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT); + apply_pressure_range_configuration(tablet, tool); } else if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) { tablet_mark_all_axes_changed(tablet, tool); update_pressure_offset(tablet, device, tool); diff --git a/src/libinput-private.h b/src/libinput-private.h index f3f441cb..b218b390 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -72,6 +72,11 @@ struct normalized_range_coords { double x, y; }; +/* A [0.0, 1.0] normalized range */ +struct normalized_range { + double min, max; +}; + /* A pair of angles in degrees */ struct wheel_angle { double x, y; @@ -449,6 +454,13 @@ enum pressure_heuristic_state { PRESSURE_HEURISTIC_STATE_DONE, /** Decision's been made, live with it */ }; +struct libinput_tablet_tool_config_pressure_range { + int (*is_available)(struct libinput_tablet_tool *tool); + enum libinput_config_status (*set)(struct libinput_tablet_tool *tool, double min, double max); + void (*get)(struct libinput_tablet_tool *tool, double *min, double *max); + void (*get_default)(struct libinput_tablet_tool *tool, double *min, double *max); +}; + struct libinput_tablet_tool { struct list link; uint32_t serial; @@ -460,12 +472,22 @@ struct libinput_tablet_tool { void *user_data; struct { + /* The configured axis we actually work with */ + struct input_absinfo abs_pressure; + struct normalized_range range; + struct normalized_range wanted_range; + bool has_configured_range; + struct threshold threshold; /* in device coordinates */ int offset; /* in device coordinates */ bool has_offset; enum pressure_heuristic_state heuristic_state; } pressure; + + struct { + struct libinput_tablet_tool_config_pressure_range pressure_range; + } config; }; struct libinput_tablet_pad_mode_group { diff --git a/src/libinput.c b/src/libinput.c index f656dd9c..42be3f92 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -4728,6 +4728,75 @@ libinput_device_config_rotation_get_default_angle(struct libinput_device *device return device->config.rotation->get_default_angle(device); } +LIBINPUT_EXPORT int +libinput_tablet_tool_config_pressure_range_is_available(struct libinput_tablet_tool *tool) +{ + if (!tool->config.pressure_range.is_available) + return 0; + + return tool->config.pressure_range.is_available(tool); +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_tablet_tool_config_pressure_range_set(struct libinput_tablet_tool *tool, + double minimum, + double maximum) +{ + if (!libinput_tablet_tool_config_pressure_range_is_available(tool)) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + if (minimum < 0.0 || minimum >= 1.0 || + maximum <= 0.0 || maximum > 1.0 || + maximum <= minimum) + return LIBINPUT_CONFIG_STATUS_INVALID; + + return tool->config.pressure_range.set(tool, minimum, maximum); +} + +LIBINPUT_EXPORT double +libinput_tablet_tool_config_pressure_range_get_minimum(struct libinput_tablet_tool *tool) +{ + double min = 0.0, max = 1.0; + + if (libinput_tablet_tool_config_pressure_range_is_available(tool)) + tool->config.pressure_range.get(tool, &min, &max); + + return min; +} + +LIBINPUT_EXPORT double +libinput_tablet_tool_config_pressure_range_get_maximum(struct libinput_tablet_tool *tool) +{ + double min = 0.0, max = 1.0; + + if (libinput_tablet_tool_config_pressure_range_is_available(tool)) + tool->config.pressure_range.get(tool, &min, &max); + + return max; +} + +LIBINPUT_EXPORT double +libinput_tablet_tool_config_pressure_range_get_default_minimum(struct libinput_tablet_tool *tool) +{ + double min = 0.0, max = 1.0; + + if (libinput_tablet_tool_config_pressure_range_is_available(tool)) + tool->config.pressure_range.get_default(tool, &min, &max); + + return min; +} + +LIBINPUT_EXPORT double +libinput_tablet_tool_config_pressure_range_get_default_maximum(struct libinput_tablet_tool *tool) +{ + double min = 0.0, max = 1.0; + + if (libinput_tablet_tool_config_pressure_range_is_available(tool)) + tool->config.pressure_range.get_default(tool, &min, &max); + + return max; +} + #if HAVE_LIBWACOM WacomDeviceDatabase * libinput_libwacom_ref(struct libinput *li) diff --git a/src/libinput.h b/src/libinput.h index 6258a1af..67445946 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -6398,6 +6398,136 @@ libinput_device_config_rotation_get_angle(struct libinput_device *device); unsigned int libinput_device_config_rotation_get_default_angle(struct libinput_device *device); +/** + * @ingroup config + * + * Check if a tablet tool can have a custom pressure range. + * + * @param tool The libinput tool + * @return Non-zero if a device has an adjustible pressure range, zero otherwise. + * + * @see libinput_tablet_tool_config_pressure_range_set + * @see libinput_tablet_tool_config_pressure_range_get_minimum + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_minimum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + * + * @since 1.25 + */ +int +libinput_tablet_tool_config_pressure_range_is_available(struct libinput_tablet_tool *tool); + +/** + * @ingroup config + * + * Set the pressure range for this tablet tool. This maps the given logical + * pressure range into the available hardware pressure range so that a hardware + * pressure of the given minimum value maps into a logical pressure of 0.0 (as + * returned by libinput_event_tablet_tool_get_pressure()) and the hardware + * pressure of the given maximum value is mapped into the logical pressure + * of 1.0 (as returned by . libinput_event_tablet_tool_get_pressure()) + * + * The minimum value must be less than the maximum value, libinput may + * libinput may require the values to have a specific distance to each other, + * i.e. that (maximium - minimum > N) for an implementation-defined value of N. + * + * @param tool The libinput tool + * @param minimum The minimum pressure value in the range [0.0, 1.0) + * @param maximum The maximum pressure value in the range (0.0, 1.0] + * + * @return A config status code + * + * @see libinput_tablet_tool_config_pressure_range_is_available + * @see libinput_tablet_tool_config_pressure_range_get_minimum + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_minimum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + */ +enum libinput_config_status +libinput_tablet_tool_config_pressure_range_set(struct libinput_tablet_tool *tool, + double minimum, + double maximum); + +/** + * @ingroup config + * + * Get the minimum pressure value for this tablet tool, normalized to the + * range [0.0, 1.0] of the available hardware pressure. + * + * If the tool does not support pressure range configuration, the return value + * of this function is always 0.0. + * + * @param tool The libinput tool + * @return The minimum pressure value for this tablet tool + * + * @see libinput_tablet_tool_config_pressure_range_is_available + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_minimum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + */ +double +libinput_tablet_tool_config_pressure_range_get_minimum(struct libinput_tablet_tool *tool); + +/** + * @ingroup config + * + * Get the maximum pressure value for this tablet tool, normalized to the + * range [0.0, 1.0] of the available hardware pressure. + * + * If the tool does not support pressure range configuration, the return value + * of this function is always 1.0. + * + * @param tool The libinput tool + * @return The maximum pressure value for this tablet tool + * + * @see libinput_tablet_tool_config_pressure_range_is_available + * @see libinput_tablet_tool_config_pressure_range_get_minimum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + */ +double +libinput_tablet_tool_config_pressure_range_get_maximum(struct libinput_tablet_tool *tool); + +/** + * @ingroup config + * + * Get the minimum pressure value for this tablet tool, normalized to the + * range [0.0, 1.0] of the available hardware pressure. + * + * If the tool does not support pressure range configuration, the return value + * of this function is always 0.0. + * + * @param tool The libinput tool + * @return The minimum pressure value for this tablet tool + * + * @see libinput_tablet_tool_config_pressure_range_is_available + * @see libinput_tablet_tool_config_pressure_range_get_minimum + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + */ +double +libinput_tablet_tool_config_pressure_range_get_default_minimum(struct libinput_tablet_tool *tool); + +/** + * @ingroup config + * + * Get the maximum pressure value for this tablet tool, normalized to the + * range [0.0, 1.0] of the available hardware pressure. + * + * If the tool does not support pressure range configuration, the return value + * of this function is always 1.0. + * + * @param tool The libinput tool + * @return The maximum pressure value for this tablet tool + * + * @see libinput_tablet_tool_config_pressure_range_is_available + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_maximum + * @see libinput_tablet_tool_config_pressure_range_get_default_maximum + */ +double +libinput_tablet_tool_config_pressure_range_get_default_maximum(struct libinput_tablet_tool *tool); + #ifdef __cplusplus } #endif diff --git a/src/libinput.sym b/src/libinput.sym index b89059c1..78739059 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -332,4 +332,13 @@ LIBINPUT_1.23 { libinput_config_accel_destroy; libinput_device_config_accel_apply; libinput_config_accel_set_points; -} LIBINPUT_1.21;
\ No newline at end of file +} LIBINPUT_1.21; + +LIBINPUT_1.26 { + libinput_tablet_tool_config_pressure_range_is_available; + libinput_tablet_tool_config_pressure_range_set; + libinput_tablet_tool_config_pressure_range_get_minimum; + libinput_tablet_tool_config_pressure_range_get_maximum; + libinput_tablet_tool_config_pressure_range_get_default_minimum; + libinput_tablet_tool_config_pressure_range_get_default_maximum; +} LIBINPUT_1.23; diff --git a/test/test-tablet.c b/test/test-tablet.c index b94f3568..804720d3 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -4150,6 +4150,317 @@ START_TEST(tablet_pressure_range) } END_TEST +START_TEST(tablet_pressure_config) +{ + 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, 0 }, + { ABS_PRESSURE, 10 }, + { -1, -1 }, + }; + + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_drain_events(li); + libinput_dispatch(li); + + 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); + struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(tev); + + ck_assert(libinput_tablet_tool_config_pressure_range_is_available(tool)); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_minimum(tool), 0.0); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_maximum(tool), 1.0); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_minimum(tool), 0.0); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_maximum(tool), 1.0); + + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.0), + LIBINPUT_CONFIG_STATUS_SUCCESS); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.2, 0.5), + LIBINPUT_CONFIG_STATUS_SUCCESS); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, -0.1, 1.0), + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.0), + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 1.0, 1.0), + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.1), + LIBINPUT_CONFIG_STATUS_INVALID); + + /* The last successful one */ + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_minimum(tool), 0.2); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_maximum(tool), 0.5); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_minimum(tool), 0.0); + ck_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_maximum(tool), 1.0); + + libinput_event_destroy(event); +} +END_TEST + +START_TEST(tablet_pressure_config_set_minimum) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet_tool *tev; + struct libinput_tablet_tool *tool; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 0 }, + { ABS_PRESSURE, 10 }, + { -1, -1 }, + }; + double p, old_pressure; + + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_drain_events(li); + libinput_dispatch(li); + + 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); + tool = libinput_event_tablet_tool_get_tool(tev); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_gt(p, 0.0); + old_pressure = p; + + ck_assert(libinput_tablet_tool_config_pressure_range_is_available(tool)); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 1.0), + LIBINPUT_CONFIG_STATUS_SUCCESS); + libinput_event_destroy(event); + + /* config doesn't take effect until we're out of prox */ + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_eq(p, old_pressure); + libinput_event_destroy(event); + } + + litest_tablet_proximity_out(dev); + litest_timeout_tablet_proxout(); + litest_drain_events(li); + + /* 10% hw value is below our thresholds, so logical zero */ + litest_axis_set_value(axes, ABS_PRESSURE, 10); + litest_tablet_proximity_in(dev, 70, 70, axes); + litest_drain_events(li); + + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_eq(p, 0.00); + libinput_event_destroy(event); + } + + /* 50% hw value mapped into a reduced range of 60% from hw range, + plus the 1% minimum offset, so our output pressure is actually ~15% */ + litest_axis_set_value(axes, ABS_PRESSURE, 50); + 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_TIP); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_gt(p, 0.15); + ck_assert_double_le(p, 0.16); + libinput_event_destroy(event); + + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_ge(p, 0.15); + ck_assert_double_le(p, 0.16); + libinput_event_destroy(event); + } +} +END_TEST + +START_TEST(tablet_pressure_config_set_maximum) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet_tool *tev; + struct libinput_tablet_tool *tool; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 0 }, + { ABS_PRESSURE, 10 }, + { -1, -1 }, + }; + double p, old_pressure; + + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_drain_events(li); + libinput_dispatch(li); + + 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); + tool = libinput_event_tablet_tool_get_tool(tev); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_gt(p, 0.0); + old_pressure = p; + + ck_assert(libinput_tablet_tool_config_pressure_range_is_available(tool)); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.6), + LIBINPUT_CONFIG_STATUS_SUCCESS); + libinput_event_destroy(event); + + /* config doesn't take effect until we're out of prox */ + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_eq(p, old_pressure); + libinput_event_destroy(event); + } + + litest_tablet_proximity_out(dev); + litest_timeout_tablet_proxout(); + libinput_dispatch(li); + + litest_axis_set_value(axes, ABS_PRESSURE, 10); + litest_tablet_proximity_in(dev, 70, 70, axes); + litest_drain_events(li); + + /* 10% hw value mapped into a reduced range of 60% from hw range, + plus the 1% minimum offset so our output pressure is actually ~15% */ + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_ge(p, 0.15); + ck_assert_double_le(p, 0.16); + ck_assert_double_gt(p, old_pressure); + libinput_event_destroy(event); + } + + /* 50% hw value mapped into a reduced range of 60% from hw range, + plus the 1% minimum offset, so our output pressure is actually ~83% */ + litest_axis_set_value(axes, ABS_PRESSURE, 50); + + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_ge(p, 0.82); + ck_assert_double_le(p, 0.84); + libinput_event_destroy(event); + } + + for (int hwp = 60; hwp < 100; hwp += 10) { + litest_axis_set_value(axes, ABS_PRESSURE, hwp); + + for (int pos = 71; pos < 80; pos++) { + litest_tablet_motion(dev, pos, pos, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_eq(p, 1.0); + libinput_event_destroy(event); + } + } +} +END_TEST + +START_TEST(tablet_pressure_config_set_range) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet_tool *tev; + struct libinput_tablet_tool *tool; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 0 }, + { ABS_PRESSURE, 10 }, + { -1, -1 }, + }; + double p, old_pressure; + + litest_tablet_proximity_in(dev, 5, 100, axes); + litest_drain_events(li); + libinput_dispatch(li); + + 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); + tool = libinput_event_tablet_tool_get_tool(tev); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_gt(p, 0.0); + old_pressure = p; + + ck_assert(libinput_tablet_tool_config_pressure_range_is_available(tool)); + ck_assert_int_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 0.6), + LIBINPUT_CONFIG_STATUS_SUCCESS); + libinput_event_destroy(event); + + /* config doesn't take effect until we're out of prox */ + for (int i = 71; i < 80; i++) { + litest_tablet_motion(dev, i, i, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + p = libinput_event_tablet_tool_get_pressure(tev); + ck_assert_double_eq(p, old_pressure); + libinput_event_destroy(event); + } + + litest_tablet_proximity_out(dev); + litest_timeout_tablet_proxout(); + litest_drain_events(li); + + litest_tablet_proximity_in(dev, 70, 70, axes); + litest_drain_events(li); + + for (double pressure = 0.0, i = 71; pressure <= 100; pressure += 5, i += 0.2) { + litest_axis_set_value(axes, ABS_PRESSURE, pressure); + litest_tablet_motion(dev, i, i, axes); + libinput_dispatch(li); + event = libinput_get_event(li); + if (libinput_event_get_type(event) == LIBINPUT_EVENT_TABLET_TOOL_AXIS) + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS); + else + tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP); + p = libinput_event_tablet_tool_get_pressure(tev); + if (pressure <= 40) { + ck_assert_double_eq(p, 0.0); + } else if (pressure >= 60) { + ck_assert_double_eq(p, 1.0); + } else { + ck_assert_double_ge(p, (pressure - 1 - 40)/20.0); + ck_assert_double_le(p, (pressure - 40)/20.0); + } + libinput_event_destroy(event); + } +} +END_TEST + static void pressure_threshold_warning(struct libinput *libinput, enum libinput_log_priority priority, @@ -6335,6 +6646,10 @@ TEST_COLLECTION(tablet) 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(tablet_pressure_config, LITEST_TABLET, LITEST_TOTEM); + litest_add(tablet_pressure_config_set_minimum, LITEST_TABLET, LITEST_TOTEM); + litest_add(tablet_pressure_config_set_maximum, LITEST_TABLET, LITEST_TOTEM); + litest_add(tablet_pressure_config_set_range, LITEST_TABLET, LITEST_TOTEM); litest_add_for_device(tablet_distance_range, LITEST_WACOM_INTUOS); |