summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2018-09-28 11:47:32 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2019-01-31 05:17:28 +0000
commitf612c1ef0c8d2f41c1693c5c4801f13a4df836a1 (patch)
tree430a8e978251288ef4cdb4359ac842eb4e901140
parentf325ca921dede43b87286b2fff510c5dfacb3c93 (diff)
downloadlibinput-f612c1ef0c8d2f41c1693c5c4801f13a4df836a1.tar.gz
tablet: add tilt-based touch arbitration for screen tablets
If the tilt angle on tip down is not 0 set the touch arbitration to a rectangle around the assumed position of the hand. This assumed position is right of the tip for a rightwards tilt and left of the tip for a leftwards tilt (i.e. left-handed mode). The rectangle is 200x200mm with a 20x50mm NW of the tip or NE for left-handed. In other words, if the period below is the tip, the rectangle looks like this: +-----------+ +-----------+ | . | <- for rightwards tilt | . | | | | | | | | | | | for leftwards tilt -> | | +-----------+ +-----------+ Touches within that rectangle are canceled, new touches are ignored. As the tip moves around the rectangle is updated but touches are only cancelled on the original tip down. While the tip is down, new touches are ignored in the exclusion area but pre-existing touches are not cancelled. This is currently only implemented in the fallback interface, i.e. it will only work for Cintiqs. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r--src/evdev-fallback.c20
-rw-r--r--src/evdev-tablet.c120
-rw-r--r--src/evdev-tablet.h1
-rw-r--r--test/litest.c17
-rw-r--r--test/litest.h9
-rw-r--r--test/test-tablet.c81
6 files changed, 225 insertions, 23 deletions
diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
index 50c4afd1..9d15a91f 100644
--- a/src/evdev-fallback.c
+++ b/src/evdev-fallback.c
@@ -1219,6 +1219,24 @@ fallback_interface_sync_initial_state(struct evdev_device *device,
}
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,
enum evdev_arbitration_state which,
@@ -1453,7 +1471,7 @@ struct evdev_dispatch_interface fallback_interface = {
.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,
};
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index e94fad53..d6f12e71 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -1376,6 +1376,69 @@ tablet_update_proximity_state(struct tablet_dispatch *tablet,
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,
@@ -1549,7 +1612,8 @@ tablet_send_events(struct tablet_dispatch *tablet,
* 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);
@@ -1618,27 +1682,25 @@ tablet_flush(struct tablet_dispatch *tablet,
}
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);
}
@@ -1647,18 +1709,33 @@ tablet_toggle_touch_device(struct tablet_dispatch *tablet,
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);
}
@@ -1837,7 +1914,10 @@ tablet_suspend(struct evdev_dispatch *dispatch,
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);
diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h
index 76f53959..89761952 100644
--- a/src/evdev-tablet.h
+++ b/src/evdev-tablet.h
@@ -84,6 +84,7 @@ struct tablet_dispatch {
/* 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;
diff --git a/test/litest.c b/test/litest.c
index b000a216..8fdf03eb 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -1885,6 +1885,23 @@ litest_slot_start(struct litest_device *d,
}
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 x,
diff --git a/test/litest.h b/test/litest.h
index 420fdc69..07af2f87 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -531,6 +531,15 @@ litest_touch_move_extended(struct litest_device *d,
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,
double x,
diff --git a/test/test-tablet.c b/test/test-tablet.c
index 2d5b2d8b..d71fa068 100644
--- a/test/test-tablet.c
+++ b/test/test-tablet.c
@@ -4247,6 +4247,8 @@ START_TEST(touch_arbitration)
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 }
@@ -4267,8 +4269,8 @@ START_TEST(touch_arbitration)
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);
@@ -4306,6 +4308,80 @@ START_TEST(touch_arbitration)
}
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();
@@ -4913,6 +4989,7 @@ TEST_COLLECTION(tablet)
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);