}
}
+static void
+fallback_interface_update_rect(struct evdev_dispatch *evdev_dispatch,
+ struct evdev_device *device,
+ const struct phys_rect *phys_rect,
+ uint64_t time)
+{
+ struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+ struct device_coord_rect rect = {0};
+
+ assert(phys_rect);
+
+ /* Existing touches do not change, we just update the rect and only
+ * new touches in these areas will be ignored. If you want to paint
+ * over your finger, be my guest. */
+ rect = evdev_phys_rect_to_units(device, phys_rect);
+ dispatch->arbitration.rect = rect;
+}
+
static void
fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
.device_resumed = fallback_interface_device_added, /* treat as add */
.post_added = fallback_interface_sync_initial_state,
.touch_arbitration_toggle = fallback_interface_toggle_touch,
- .touch_arbitration_update_rect = NULL,
+ .touch_arbitration_update_rect = fallback_interface_update_rect,
.get_switch_state = fallback_interface_get_switch_state,
};
tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
}
+static struct phys_rect
+tablet_calculate_arbitration_rect(struct tablet_dispatch *tablet)
+{
+ struct evdev_device *device = tablet->device;
+ struct phys_rect r = {0};
+ struct phys_coords mm;
+
+ mm = evdev_device_units_to_mm(device, &tablet->axes.point);
+
+ /* The rect we disable is 20mm left of the tip, 50mm north of the
+ * tip, and 200x200mm large.
+ * If the stylus is tilted left (tip further right than the eraser
+ * end) assume left-handed mode.
+ *
+ * Obviously if we'd run out of the boundaries, we rescale the rect
+ * accordingly.
+ */
+ if (tablet->axes.tilt.x > 0) {
+ r.x = mm.x - 20;
+ r.w = 200;
+ } else {
+ r.x = mm.x + 20;
+ r.w = 200;
+ r.x -= r.w;
+ }
+
+ if (r.x < 0) {
+ r.w -= r.x;
+ r.x = 0;
+ }
+
+ r.y = mm.y - 50;
+ r.h = 200;
+ if (r.y < 0) {
+ r.h -= r.y;
+ r.y = 0;
+ }
+
+ return r;
+}
+
+static inline void
+tablet_update_touch_device_rect(struct tablet_dispatch *tablet,
+ const struct tablet_axes *axes,
+ uint64_t time)
+{
+ struct evdev_dispatch *dispatch;
+ struct phys_rect rect = {0};
+
+ if (tablet->touch_device == NULL ||
+ tablet->arbitration != ARBITRATION_IGNORE_RECT)
+ return;
+
+ rect = tablet_calculate_arbitration_rect(tablet);
+
+ dispatch = tablet->touch_device->dispatch;
+ if (dispatch->interface->touch_arbitration_update_rect)
+ dispatch->interface->touch_arbitration_update_rect(dispatch,
+ tablet->touch_device,
+ &rect,
+ time);
+}
+
static inline bool
tablet_send_proximity_in(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
* update */
tablet_unset_status(tablet, TABLET_AXES_UPDATED);
} else {
- tablet_check_notify_axes(tablet, device, tool, &axes, time);
+ if (tablet_check_notify_axes(tablet, device, tool, &axes, time))
+ tablet_update_touch_device_rect(tablet, &axes, time);
}
assert(tablet->axes.delta.x == 0);
}
static inline void
-tablet_set_touch_device_enabled(struct evdev_device *touch_device,
- bool enable,
+tablet_set_touch_device_enabled(struct tablet_dispatch *tablet,
+ enum evdev_arbitration_state which,
+ const struct phys_rect *rect,
uint64_t time)
{
+ struct evdev_device *touch_device = tablet->touch_device;
struct evdev_dispatch *dispatch;
- enum evdev_arbitration_state which;
if (touch_device == NULL)
return;
- if (enable)
- which = ARBITRATION_NOT_ACTIVE;
- else
- which = ARBITRATION_IGNORE_ALL;
+ tablet->arbitration = which;
dispatch = touch_device->dispatch;
if (dispatch->interface->touch_arbitration_toggle)
dispatch->interface->touch_arbitration_toggle(dispatch,
touch_device,
which,
- NULL,
+ rect,
time);
}
struct evdev_device *tablet_device,
uint64_t time)
{
- bool enable_events;
+ enum evdev_arbitration_state which;
+ struct phys_rect r = {0};
+ struct phys_rect *rect = NULL;
- enable_events = tablet_has_status(tablet,
- TABLET_TOOL_OUT_OF_RANGE) ||
- tablet_has_status(tablet, TABLET_NONE) ||
- tablet_has_status(tablet,
- TABLET_TOOL_LEAVING_PROXIMITY) ||
- tablet_has_status(tablet,
- TABLET_TOOL_OUT_OF_PROXIMITY);
+ if (tablet_has_status(tablet,
+ TABLET_TOOL_OUT_OF_RANGE) ||
+ tablet_has_status(tablet, TABLET_NONE) ||
+ tablet_has_status(tablet,
+ TABLET_TOOL_LEAVING_PROXIMITY) ||
+ tablet_has_status(tablet,
+ TABLET_TOOL_OUT_OF_PROXIMITY)) {
+ which = ARBITRATION_NOT_ACTIVE;
+ } else if (tablet->axes.tilt.x == 0) {
+ which = ARBITRATION_IGNORE_ALL;
+ } else if (tablet->arbitration != ARBITRATION_IGNORE_RECT) {
+ /* This enables rect-based arbitration, updates are sent
+ * elsewhere */
+ r = tablet_calculate_arbitration_rect(tablet);
+ rect = &r;
+ which = ARBITRATION_IGNORE_RECT;
+ } else {
+ return;
+ }
- tablet_set_touch_device_enabled(tablet->touch_device,
- enable_events,
+ tablet_set_touch_device_enabled(tablet,
+ which,
+ rect,
time);
}
struct libinput *li = tablet_libinput_context(tablet);
uint64_t now = libinput_now(li);
- tablet_set_touch_device_enabled(tablet->touch_device, true, now);
+ tablet_set_touch_device_enabled(tablet,
+ ARBITRATION_NOT_ACTIVE,
+ NULL,
+ now);
if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
/* The paired touch device on devices with both pen & touch */
struct evdev_device *touch_device;
+ enum evdev_arbitration_state arbitration;
struct {
bool need_to_force_prox_out;
d->semi_mt.touches[slot].y = y;
}
+void
+litest_touch_sequence(struct litest_device *d,
+ unsigned int slot,
+ double x_from,
+ double y_from,
+ double x_to,
+ double y_to,
+ int steps)
+{
+ litest_touch_down(d, slot, x_from, y_from);
+ litest_touch_move_to(d, slot,
+ x_from, y_from,
+ x_to, y_to,
+ steps);
+ litest_touch_up(d, slot);
+}
+
void
litest_touch_down(struct litest_device *d,
unsigned int slot,
double y,
struct axis_replacement *axes);
+void
+litest_touch_sequence(struct litest_device *d,
+ unsigned int slot,
+ double x1,
+ double y1,
+ double x2,
+ double y2,
+ int steps);
+
void
litest_touch_down(struct litest_device *d,
unsigned int slot,
struct litest_device *finger;
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
+ { ABS_TILT_X, 80 },
+ { ABS_TILT_Y, 80 },
{ ABS_DISTANCE, 10 },
{ ABS_PRESSURE, 0 },
{ -1, -1 }
litest_tablet_motion(dev, 20, 40, axes);
litest_drain_events(li);
- litest_touch_down(finger, 0, 30, 30);
- litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10);
+ litest_touch_down(finger, 0, 21, 41);
+ litest_touch_move_to(finger, 0, 21, 41, 80, 80, 10);
litest_assert_empty_queue(li);
litest_tablet_motion(dev, 10, 10, axes);
}
END_TEST
+START_TEST(touch_arbitration_outside_rect)
+{
+ struct litest_device *dev = litest_current_device();
+ enum litest_device_type other;
+ struct litest_device *finger;
+ struct libinput *li = dev->libinput;
+ struct axis_replacement axes[] = {
+ { ABS_TILT_X, 80 },
+ { ABS_TILT_Y, 80 },
+ { ABS_DISTANCE, 10 },
+ { ABS_PRESSURE, 0 },
+ { -1, -1 }
+ };
+ double x, y;
+ bool is_touchpad;
+
+ other = paired_device(dev);
+ if (other == LITEST_NO_DEVICE)
+ return;
+
+ finger = litest_add_device(li, other);
+ litest_drain_events(li);
+
+ is_touchpad = !libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT);
+ if (is_touchpad)
+ return;
+
+ x = 20;
+ y = 45;
+
+ litest_tablet_proximity_in(dev, x, y - 1, axes);
+ litest_drain_events(li);
+
+ /* these are in percent, but the pen/finger have different
+ * resolution and the rect works in mm, so the numbers below are
+ * hand-picked for the test device */
+ litest_tablet_motion(dev, x, y, axes);
+ litest_drain_events(li);
+
+ /* left of rect */
+ litest_touch_sequence(finger, 0, x - 10, y + 2, x - 10, y + 20, 30);
+ libinput_dispatch(li);
+ litest_assert_touch_sequence(li);
+
+ /* above rect */
+ litest_touch_sequence(finger, 0, x + 2, y - 35, x + 20, y - 10, 30);
+ libinput_dispatch(li);
+ litest_assert_touch_sequence(li);
+
+ /* right of rect */
+ litest_touch_sequence(finger, 0, x + 80, y + 2, x + 20, y + 10, 30);
+ libinput_dispatch(li);
+ litest_assert_touch_sequence(li);
+
+#if 0
+ /* This *should* work but the Cintiq test devices is <200mm
+ high, so we can't test for anything below the tip */
+ x = 20;
+ y = 10;
+ litest_tablet_proximity_out(dev);
+ litest_tablet_motion(dev, x, y, axes);
+ litest_tablet_proximity_in(dev, x, y - 1, axes);
+ litest_drain_events(li);
+
+ /* below rect */
+ litest_touch_sequence(finger, 0, x + 2, y + 80, x + 20, y + 20, 30);
+ libinput_dispatch(li);
+ litest_assert_touch_sequence(li);
+#endif
+
+ litest_delete_device(finger);
+}
+END_TEST
+
START_TEST(touch_arbitration_stop_touch)
{
struct litest_device *dev = litest_current_device();
litest_add("tablet:touch-arbitration", touch_arbitration_remove_tablet, LITEST_TOUCH, LITEST_ANY);
litest_add("tablet:touch-arbitration", touch_arbitration_keep_ignoring, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:touch-arbitration", touch_arbitration_late_touch_lift, LITEST_TABLET, LITEST_ANY);
+ litest_add("tablet:touch-arbitration", touch_arbitration_outside_rect, LITEST_TABLET | LITEST_DIRECT, LITEST_ANY);
litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen, LITEST_HUION_TABLET);
litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen_no_timeout_during_usage, LITEST_HUION_TABLET);