tablet: implement support for area configuration for external tablets

For external tablets like the Intuos series we now expose the area
rectangle configuration and the (minimum) implementation required to
make this work.

Because an area configuration may apply late and tablet events usually
get scaled by the compositor we need to store the current axis extents
in each event. This is to behave correctly in this events sequence:

1. tool proximity in
2. caller changes config, config is pending
3. tool moves, generates events
4. tool goes out of prox, new config applies
5. caller processes motion events from step 3

If the caller in step five uses any of the get_x_transformed calls these
need to be scaled relative to the original area, not the one set in
step 2.

The current implementation merely clips into the area so moving a stylus
outside the area will be equivalent to moving it along the respective
edge of the area. It's not a true dead zone yet.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1013>
This commit is contained in:
Peter Hutterer 2024-06-13 10:31:37 +10:00
parent cf5b7bee01
commit 0b28eeea5a
6 changed files with 438 additions and 47 deletions

View file

@ -260,6 +260,40 @@ tablet_process_absolute(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 void
tablet_change_area(struct evdev_device *device)
{
struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
if (memcmp(&tablet->area.rect, &tablet->area.want_rect, sizeof(tablet->area.rect)) == 0)
return;
if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
return;
tablet->area.rect = tablet->area.want_rect;
evdev_log_debug(device,
"tablet-area: area is %.2f/%.2f - %.2f/%.2f\n",
tablet->area.rect.x1,
tablet->area.rect.y1,
tablet->area.rect.x2,
tablet->area.rect.y2);
const struct input_absinfo *absx = device->abs.absinfo_x;
const struct input_absinfo *absy = device->abs.absinfo_y;
tablet->area.x.minimum = axis_range_percentage(absx, tablet->area.rect.x1 * 100);
tablet->area.x.maximum = axis_range_percentage(absx, tablet->area.rect.x2 * 100);
tablet->area.y.minimum = axis_range_percentage(absy, tablet->area.rect.y1 * 100);
tablet->area.y.maximum = axis_range_percentage(absy, tablet->area.rect.y2 * 100);
}
static void
tablet_apply_rotation(struct evdev_device *device)
{
@ -442,6 +476,31 @@ normalize_wheel(struct tablet_dispatch *tablet,
return value * device->scroll.wheel_click_angle.x;
}
static void
apply_tablet_area(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct device_coords *point)
{
if (tablet->area.rect.x1 == 0.0 && tablet->area.rect.x2 == 1.0 &&
tablet->area.rect.y1 == 0.0 && tablet->area.rect.y2 == 1.0)
return;
/* The point is somewhere on the tablet in device coordinates,
* but we need it relative to the x/y offset.
* So clip it first, then offset it to our area min/max.
*
* Right now we're just clipping, we don't completely
* ignore events. What we should do is ignore events outside
* altogether and generate prox in/out events when we actually
* enter the area.
*/
point->x = min(point->x, tablet->area.x.maximum);
point->y = min(point->y, tablet->area.y.maximum);
point->x = max(point->x, tablet->area.x.minimum);
point->y = max(point->y, tablet->area.y.minimum);
}
static inline void
tablet_update_xy(struct tablet_dispatch *tablet,
struct evdev_device *device)
@ -473,7 +532,10 @@ tablet_update_xy(struct tablet_dispatch *tablet,
tablet->axes.point.y = value;
/* calibration and area are currently mutually exclusive so
* one of those is a noop */
evdev_transform_absolute(device, &tablet->axes.point);
apply_tablet_area(tablet, device, &tablet->axes.point);
}
}
@ -1070,12 +1132,6 @@ 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 bool
tablet_get_quirked_pressure_thresholds(struct tablet_dispatch *tablet,
int *hi,
@ -1338,7 +1394,9 @@ tablet_notify_button_mask(struct tablet_dispatch *tablet,
tip_state,
&tablet->axes,
i,
state);
state,
&tablet->area.x,
&tablet->area.y);
}
}
@ -1737,7 +1795,9 @@ tablet_send_proximity_in(struct tablet_dispatch *tablet,
tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
tablet->changed_axes,
axes);
axes,
&tablet->area.x,
&tablet->area.y);
tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
tablet_unset_status(tablet, TABLET_AXES_UPDATED);
@ -1763,7 +1823,9 @@ tablet_send_proximity_out(struct tablet_dispatch *tablet,
tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
tablet->changed_axes,
axes);
axes,
&tablet->area.x,
&tablet->area.y);
tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
tablet_unset_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
@ -1788,7 +1850,9 @@ tablet_send_tip(struct tablet_dispatch *tablet,
tool,
LIBINPUT_TABLET_TOOL_TIP_DOWN,
tablet->changed_axes,
axes);
axes,
&tablet->area.x,
&tablet->area.y);
tablet_unset_status(tablet, TABLET_AXES_UPDATED);
tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT);
@ -1806,7 +1870,9 @@ tablet_send_tip(struct tablet_dispatch *tablet,
tool,
LIBINPUT_TABLET_TOOL_TIP_UP,
tablet->changed_axes,
axes);
axes,
&tablet->area.x,
&tablet->area.y);
tablet_unset_status(tablet, TABLET_AXES_UPDATED);
tablet_unset_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT);
@ -1844,7 +1910,9 @@ tablet_send_axes(struct tablet_dispatch *tablet,
tool,
tip_state,
tablet->changed_axes,
axes);
axes,
&tablet->area.x,
&tablet->area.y);
tablet_unset_status(tablet, TABLET_AXES_UPDATED);
tablet_reset_changed_axes(tablet);
axes->delta.x = 0;
@ -1914,6 +1982,7 @@ tablet_send_events(struct tablet_dispatch *tablet,
if (tablet_send_proximity_out(tablet, tool, device, &axes, time)) {
tablet_change_to_left_handed(device);
tablet_apply_rotation(device);
tablet_change_area(device);
tablet_history_reset(tablet);
}
}
@ -2501,6 +2570,71 @@ tablet_init_calibration(struct tablet_dispatch *tablet,
evdev_init_calibration(device, &tablet->calibration);
}
static int
tablet_area_has_rectangle(struct libinput_device *device)
{
return 1;
}
static enum libinput_config_status
tablet_area_set_rectangle(struct libinput_device *device,
const struct libinput_config_area_rectangle *rectangle)
{
struct evdev_device *evdev = evdev_device(device);
struct tablet_dispatch *tablet = tablet_dispatch(evdev->dispatch);
if (rectangle->x1 >= rectangle->x2 || rectangle->y1 >= rectangle->y2)
return LIBINPUT_CONFIG_STATUS_INVALID;
if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 ||
rectangle->y1 < 0.0 || rectangle->y2 > 1.0)
return LIBINPUT_CONFIG_STATUS_INVALID;
tablet->area.want_rect = *rectangle;
tablet_change_area(evdev);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static struct libinput_config_area_rectangle
tablet_area_get_rectangle(struct libinput_device *device)
{
struct evdev_device *evdev = evdev_device(device);
struct tablet_dispatch *tablet = tablet_dispatch(evdev->dispatch);
return tablet->area.rect;
}
static struct libinput_config_area_rectangle
tablet_area_get_default_rectangle(struct libinput_device *device)
{
struct libinput_config_area_rectangle rect = {
0.0, 0.0, 1.0, 1.0,
};
return rect;
}
static void
tablet_init_area(struct tablet_dispatch *tablet,
struct evdev_device *device)
{
tablet->area.rect = (struct libinput_config_area_rectangle) {
0.0, 0.0, 1.0, 1.0,
};
tablet->area.want_rect = tablet->area.rect;
tablet->area.x = *device->abs.absinfo_x;
tablet->area.y = *device->abs.absinfo_y;
if (!libevdev_has_property(device->evdev, INPUT_PROP_DIRECT)) {
device->base.config.area = &tablet->area.config;
tablet->area.config.has_rectangle = tablet_area_has_rectangle;
tablet->area.config.set_rectangle = tablet_area_set_rectangle;
tablet->area.config.get_rectangle = tablet_area_get_rectangle;
tablet->area.config.get_default_rectangle = tablet_area_get_default_rectangle;
}
}
static void
tablet_init_proximity_threshold(struct tablet_dispatch *tablet,
struct evdev_device *device)
@ -2791,6 +2925,7 @@ tablet_init(struct tablet_dispatch *tablet,
tablet_fix_tilt(tablet, device);
tablet_init_calibration(tablet, device, is_display_tablet);
tablet_init_area(tablet, device);
tablet_init_proximity_threshold(tablet, device);
rc = tablet_init_accel(tablet, device);
if (rc != 0)

View file

@ -93,6 +93,13 @@ struct tablet_dispatch {
uint32_t cursor_proximity_threshold;
struct libinput_device_config_calibration calibration;
struct {
struct libinput_device_config_area config;
struct libinput_config_area_rectangle rect;
struct libinput_config_area_rectangle want_rect;
struct input_absinfo x;
struct input_absinfo y;
} area;
/* The paired touch device on devices with both pen & touch */
struct evdev_device *touch_device;

View file

@ -401,14 +401,18 @@ totem_handle_slot_state(struct totem_dispatch *totem,
slot->tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
totem_slot_reset_changed_axes(totem, slot);
tablet_notify_tip(&device->base,
time,
slot->tool,
tip_state,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
slot->state = SLOT_STATE_UPDATE;
break;
case SLOT_STATE_UPDATE:
@ -419,7 +423,9 @@ totem_handle_slot_state(struct totem_dispatch *totem,
slot->tool,
tip_state,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
}
break;
case SLOT_STATE_END:
@ -452,7 +458,9 @@ totem_handle_slot_state(struct totem_dispatch *totem,
tip_state,
&axes,
BTN_0,
btn_state);
btn_state,
device->abs.absinfo_x,
device->abs.absinfo_y);
totem->button_state_previous = totem->button_state_now;
}
@ -468,14 +476,19 @@ totem_handle_slot_state(struct totem_dispatch *totem,
slot->tool,
tip_state,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
totem_slot_reset_changed_axes(totem, slot);
tablet_notify_proximity(&device->base,
time,
slot->tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
slot->state = SLOT_STATE_NONE;
break;
case SLOT_STATE_NONE:
@ -577,7 +590,9 @@ totem_interface_suspend(struct evdev_dispatch *dispatch,
tip_state,
&axes,
BTN_0,
LIBINPUT_BUTTON_STATE_RELEASED);
LIBINPUT_BUTTON_STATE_RELEASED,
device->abs.absinfo_x,
device->abs.absinfo_y);
totem->button_state_now = false;
totem->button_state_previous = false;
@ -589,14 +604,18 @@ totem_interface_suspend(struct evdev_dispatch *dispatch,
slot->tool,
LIBINPUT_TABLET_TOOL_TIP_UP,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
}
tablet_notify_proximity(&device->base,
now,
slot->tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
}
totem_set_touch_device_enabled(totem, true, now);
}
@ -683,14 +702,18 @@ totem_interface_initial_proximity(struct evdev_device *device,
slot->tool,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
totem_slot_reset_changed_axes(totem, slot);
tablet_notify_tip(&device->base,
now,
slot->tool,
LIBINPUT_TABLET_TOOL_TIP_DOWN,
slot->changed_axes,
&axes);
&axes,
device->abs.absinfo_x,
device->abs.absinfo_y);
slot->state = SLOT_STATE_UPDATE;
enable_touch = false;
}

View file

@ -777,7 +777,9 @@ tablet_notify_axis(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_tip_state tip_state,
unsigned char *changed_axes,
const struct tablet_axes *axes);
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y);
void
tablet_notify_proximity(struct libinput_device *device,
@ -785,7 +787,9 @@ tablet_notify_proximity(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_proximity_state state,
unsigned char *changed_axes,
const struct tablet_axes *axes);
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y);
void
tablet_notify_tip(struct libinput_device *device,
@ -793,7 +797,9 @@ tablet_notify_tip(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_tip_state tip_state,
unsigned char *changed_axes,
const struct tablet_axes *axes);
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y);
void
tablet_notify_button(struct libinput_device *device,
@ -802,7 +808,9 @@ tablet_notify_button(struct libinput_device *device,
enum libinput_tablet_tool_tip_state tip_state,
const struct tablet_axes *axes,
int32_t button,
enum libinput_button_state state);
enum libinput_button_state state,
const struct input_absinfo *x,
const struct input_absinfo *y);
void
tablet_pad_notify_button(struct libinput_device *device,

View file

@ -36,6 +36,7 @@
#include "libinput.h"
#include "libinput-private.h"
#include "util-input-event.h"
#include "evdev.h"
#include "timer.h"
#include "quirks.h"
@ -219,6 +220,10 @@ struct libinput_event_tablet_tool {
struct libinput_tablet_tool *tool;
enum libinput_tablet_tool_proximity_state proximity_state;
enum libinput_tablet_tool_tip_state tip_state;
struct {
struct input_absinfo x;
struct input_absinfo y;
} abs;
};
struct libinput_event_tablet_pad {
@ -1303,8 +1308,6 @@ libinput_event_tablet_tool_wheel_has_changed(
LIBINPUT_EXPORT double
libinput_event_tablet_tool_get_x(struct libinput_event_tablet_tool *event)
{
struct evdev_device *device = evdev_device(event->base.device);
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
@ -1313,15 +1316,13 @@ libinput_event_tablet_tool_get_x(struct libinput_event_tablet_tool *event)
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
return absinfo_convert_to_mm(device->abs.absinfo_x,
return absinfo_convert_to_mm(&event->abs.x,
event->axes.point.x);
}
LIBINPUT_EXPORT double
libinput_event_tablet_tool_get_y(struct libinput_event_tablet_tool *event)
{
struct evdev_device *device = evdev_device(event->base.device);
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
@ -1330,7 +1331,7 @@ libinput_event_tablet_tool_get_y(struct libinput_event_tablet_tool *event)
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
return absinfo_convert_to_mm(device->abs.absinfo_y,
return absinfo_convert_to_mm(&event->abs.y,
event->axes.point.y);
}
@ -1507,8 +1508,6 @@ LIBINPUT_EXPORT double
libinput_event_tablet_tool_get_x_transformed(struct libinput_event_tablet_tool *event,
uint32_t width)
{
struct evdev_device *device = evdev_device(event->base.device);
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
@ -1517,17 +1516,13 @@ libinput_event_tablet_tool_get_x_transformed(struct libinput_event_tablet_tool *
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
return evdev_device_transform_x(device,
event->axes.point.x,
width);
return absinfo_scale_axis(&event->abs.x, event->axes.point.x, width);
}
LIBINPUT_EXPORT double
libinput_event_tablet_tool_get_y_transformed(struct libinput_event_tablet_tool *event,
uint32_t height)
{
struct evdev_device *device = evdev_device(event->base.device);
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
@ -1536,9 +1531,7 @@ libinput_event_tablet_tool_get_y_transformed(struct libinput_event_tablet_tool *
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
return evdev_device_transform_y(device,
event->axes.point.y,
height);
return absinfo_scale_axis(&event->abs.y, event->axes.point.y, height);
}
LIBINPUT_EXPORT struct libinput_tablet_tool *
@ -2772,7 +2765,9 @@ tablet_notify_axis(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_tip_state tip_state,
unsigned char *changed_axes,
const struct tablet_axes *axes)
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y)
{
struct libinput_event_tablet_tool *axis_event;
@ -2784,6 +2779,8 @@ tablet_notify_axis(struct libinput_device *device,
.proximity_state = LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
.tip_state = tip_state,
.axes = *axes,
.abs.x = *x,
.abs.y = *y,
};
memcpy(axis_event->changed_axes,
@ -2802,7 +2799,9 @@ tablet_notify_proximity(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_proximity_state proximity_state,
unsigned char *changed_axes,
const struct tablet_axes *axes)
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y)
{
struct libinput_event_tablet_tool *proximity_event;
@ -2814,6 +2813,8 @@ tablet_notify_proximity(struct libinput_device *device,
.tip_state = LIBINPUT_TABLET_TOOL_TIP_UP,
.proximity_state = proximity_state,
.axes = *axes,
.abs.x = *x,
.abs.y = *y,
};
memcpy(proximity_event->changed_axes,
changed_axes,
@ -2831,7 +2832,9 @@ tablet_notify_tip(struct libinput_device *device,
struct libinput_tablet_tool *tool,
enum libinput_tablet_tool_tip_state tip_state,
unsigned char *changed_axes,
const struct tablet_axes *axes)
const struct tablet_axes *axes,
const struct input_absinfo *x,
const struct input_absinfo *y)
{
struct libinput_event_tablet_tool *tip_event;
@ -2843,6 +2846,8 @@ tablet_notify_tip(struct libinput_device *device,
.tip_state = tip_state,
.proximity_state = LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
.axes = *axes,
.abs.x = *x,
.abs.y = *y,
};
memcpy(tip_event->changed_axes,
changed_axes,
@ -2861,7 +2866,9 @@ tablet_notify_button(struct libinput_device *device,
enum libinput_tablet_tool_tip_state tip_state,
const struct tablet_axes *axes,
int32_t button,
enum libinput_button_state state)
enum libinput_button_state state,
const struct input_absinfo *x,
const struct input_absinfo *y)
{
struct libinput_event_tablet_tool *button_event;
int32_t seat_button_count;
@ -2881,6 +2888,8 @@ tablet_notify_button(struct libinput_device *device,
.proximity_state = LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
.tip_state = tip_state,
.axes = *axes,
.abs.x = *x,
.abs.y = *y,
};
post_device_event(device,
@ -4148,6 +4157,13 @@ libinput_device_config_area_set_rectangle(struct libinput_device *device,
if (!libinput_device_config_area_has_rectangle(device))
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
if (rectangle->x1 >= rectangle->x2 || rectangle->y1 >= rectangle->y2)
return LIBINPUT_CONFIG_STATUS_INVALID;
if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 ||
rectangle->y1 < 0.0 || rectangle->y2 > 1.0)
return LIBINPUT_CONFIG_STATUS_INVALID;
return device->config.area->set_rectangle(device, rectangle);
}

View file

@ -3891,6 +3891,203 @@ START_TEST(tablet_calibration_set_matrix)
}
END_TEST
START_TEST(tablet_area_has_rectangle)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *d = dev->libinput_device;
enum libinput_config_status status;
int rc;
struct libinput_config_area_rectangle rect;
int has_area = !libevdev_has_property(dev->evdev, INPUT_PROP_DIRECT);
rc = libinput_device_config_area_has_rectangle(d);
litest_assert_int_eq(rc, has_area);
rect = libinput_device_config_area_get_rectangle(d);
litest_assert_double_eq(rect.x1, 0.0);
litest_assert_double_eq(rect.y1, 0.0);
litest_assert_double_eq(rect.x2, 1.0);
litest_assert_double_eq(rect.y2, 1.0);
rect = libinput_device_config_area_get_default_rectangle(d);
litest_assert_double_eq(rect.x1, 0.0);
litest_assert_double_eq(rect.y1, 0.0);
litest_assert_double_eq(rect.x2, 1.0);
litest_assert_double_eq(rect.y2, 1.0);
status = libinput_device_config_area_set_rectangle(d, &rect);
if (has_area)
litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
else
litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
}
END_TEST
START_TEST(tablet_area_set_rectangle_invalid)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *d = dev->libinput_device;
int rc;
struct libinput_config_area_rectangle rect;
int has_area = !libevdev_has_property(dev->evdev, INPUT_PROP_DIRECT);
if (!has_area)
return LITEST_NOT_APPLICABLE;
rect.x1 = 1.0;
rect.x2 = 0.9;
rect.y1 = 0.0;
rect.y2 = 1.0;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
rect.x1 = 0.9;
rect.x2 = 1.0;
rect.y1 = 1.0;
rect.y2 = 0.9;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
rect.x1 = 0.9;
rect.x2 = 0.9;
rect.y1 = 0.9;
rect.y2 = 1.0;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
rect.x1 = 0.9;
rect.x2 = 1.0;
rect.y1 = 0.9;
rect.y2 = 0.9;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
rect.x1 = 0.9;
rect.x2 = 1.5;
rect.y1 = 0.0;
rect.y2 = 1.0;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
rect.x1 = 0.0;
rect.x2 = 1.0;
rect.y1 = 0.9;
rect.y2 = 1.4;
rc = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
}
END_TEST
static void
get_tool_xy(struct libinput *li, double *x, double *y)
{
struct libinput_event *event = libinput_get_event(li);
struct libinput_event_tablet_tool *tev;
litest_assert_ptr_notnull(event);
switch (libinput_event_get_type(event)) {
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
break;
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
break;
default:
abort();
}
*x = libinput_event_tablet_tool_get_x_transformed(tev, 100);
*y = libinput_event_tablet_tool_get_y_transformed(tev, 100);
libinput_event_destroy(event);
}
START_TEST(tablet_area_set_rectangle)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_device *d = dev->libinput_device;
struct axis_replacement axes[] = {
{ ABS_DISTANCE, 10 },
{ ABS_PRESSURE, 0 },
{ -1, -1 }
};
double x, y;
double *scaled, *unscaled;
bool use_vertical = !!_i; /* ranged test */
if (libevdev_has_property(dev->evdev, INPUT_PROP_DIRECT))
return LITEST_NOT_APPLICABLE;
struct libinput_config_area_rectangle rect;
if (use_vertical) {
rect = (struct libinput_config_area_rectangle){
0.25, 0.0, 0.75, 1.0,
};
scaled = &x;
unscaled = &y;
} else {
rect = (struct libinput_config_area_rectangle){
0.0, 0.25, 1.0, 0.75,
};
scaled = &y;
unscaled = &x;
}
enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
/* move vertically through the center */
litest_tablet_proximity_in(dev, 5, 5, axes);
libinput_dispatch(li);
get_tool_xy(li, &x, &y);
litest_assert_double_eq_epsilon(*scaled, 0.0, 2);
litest_assert_double_eq_epsilon(*unscaled, 5.0, 2);
for (int i = 10; i <= 100; i += 5) {
/* Negate any smoothing */
litest_tablet_motion(dev, i, i, axes);
litest_tablet_motion(dev, i - 1, i, axes);
litest_tablet_motion(dev, i, i - 1, axes);
litest_drain_events(li);
litest_tablet_motion(dev, i, i, axes);
libinput_dispatch(li);
get_tool_xy(li, &x, &y);
if (i <= 25)
litest_assert_double_eq(*scaled, 0.0);
else if (i > 75)
litest_assert_double_eq_epsilon(*scaled, 100.0, 1);
else
litest_assert_double_eq_epsilon(*scaled, (i - 25) * 2, 1);
litest_assert_double_eq_epsilon(*unscaled, i, 2);
}
/* Push through any smoothing */
litest_tablet_motion(dev, 100, 100, axes);
litest_tablet_motion(dev, 100, 100, axes);
libinput_dispatch(li);
litest_drain_events(li);
litest_tablet_proximity_out(dev);
litest_timeout_tablet_proxout();
libinput_dispatch(li);
get_tool_xy(li, &x, &y);
litest_assert_double_eq_epsilon(x, 100, 1);
litest_assert_double_eq_epsilon(y, 100, 1);
}
END_TEST
static void
assert_pressure(struct libinput *li, enum libinput_event_type type, double expected_pressure)
{
@ -6571,6 +6768,7 @@ TEST_COLLECTION(tablet)
struct range with_timeout = { 0, 2 };
struct range xyaxes = { ABS_X, ABS_Y + 1 };
struct range tilt_cases = {TILT_MINIMUM, TILT_MAXIMUM + 1};
struct range vert_horiz = { 0, 2 };
litest_add(tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
litest_add(tool_user_data, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
@ -6652,6 +6850,10 @@ TEST_COLLECTION(tablet)
litest_add(tablet_calibration_set_matrix, LITEST_TABLET, LITEST_TOTEM|LITEST_PRECALIBRATED);
litest_add(tablet_calibration_set_matrix_delta, LITEST_TABLET, LITEST_TOTEM|LITEST_PRECALIBRATED);
litest_add(tablet_area_has_rectangle, LITEST_TABLET, LITEST_ANY);
litest_add(tablet_area_set_rectangle_invalid, LITEST_TABLET, LITEST_ANY);
litest_add_ranged(tablet_area_set_rectangle, LITEST_TABLET, LITEST_ANY, &vert_horiz);
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);