diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index fe5db8e0..27177b53 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -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) diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index e3490cd8..3d34c446 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -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; diff --git a/src/evdev-totem.c b/src/evdev-totem.c index 94a3932b..412192de 100644 --- a/src/evdev-totem.c +++ b/src/evdev-totem.c @@ -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; } diff --git a/src/libinput-private.h b/src/libinput-private.h index 6a0267f9..baca85aa 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -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, diff --git a/src/libinput.c b/src/libinput.c index 7bf26c7d..783926d0 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -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); } diff --git a/test/test-tablet.c b/test/test-tablet.c index f33dc406..e7daa219 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -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);