tablet: always enable touch arbitration with touchscreens/ext. touchpads
authorPeter Hutterer <peter.hutterer@who-t.net>
Fri, 17 Mar 2023 03:08:31 +0000 (13:08 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 30 Mar 2023 05:53:23 +0000 (05:53 +0000)
Right now for touch arbitration to work, we require the device group to
be the same (i.e. they're hanging off the same physical bus). That's not
always the case and statistically we have a lot more devices that have
a built-in tablet + touchscreen than we have Intuos-like external
tablets.

So let's default to the more common case - enabling arbitration with the
first touchscreen/external touchpad we find. If a subsequent device is
"better", swap it out. Right now, the only heuristic we have here is the
device group check but in the future we could get more precise.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
src/evdev-tablet.c
test/test-tablet.c

index 4bc126329aed6b8cb482a199437b5efb6f8a6b92..c8d7ebb5f5317a7583d95d38d96a3e13ab0d8b7c 100644 (file)
@@ -2150,6 +2150,30 @@ tablet_setup_touch_arbitration(struct evdev_device *device,
 {
        struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
 
+        /* We enable touch arbitration with the first touch screen/external
+         * touchpad we see. This may be wrong in some cases, so we have some
+         * heuristics in case we find a "better" device.
+         */
+        if (tablet->touch_device != NULL) {
+               struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
+               struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
+
+               /* same phsical device? -> better, otherwise keep the one we have */
+               if (group1 != group2)
+                       return;
+
+               /* We found a better device, let's swap it out */
+               struct libinput *li = tablet_libinput_context(tablet);
+               tablet_set_touch_device_enabled(tablet,
+                                               ARBITRATION_NOT_ACTIVE,
+                                               NULL,
+                                               libinput_now(li));
+               evdev_log_debug(device,
+                               "touch-arbitration: removing pairing for %s<->%s\n",
+                               device->devname,
+                               tablet->touch_device->devname);
+       }
+
        evdev_log_debug(device,
                        "touch-arbitration: activated for %s<->%s\n",
                        device->devname,
@@ -2162,16 +2186,20 @@ tablet_setup_rotation(struct evdev_device *device,
                      struct evdev_device *new_device)
 {
        struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
-
-       evdev_log_debug(device,
-                       "tablet-rotation: %s will rotate %s\n",
-                       device->devname,
-                       new_device->devname);
-       tablet->rotation.touch_device = new_device;
-
-       if (libinput_device_config_left_handed_get(&new_device->base)) {
-               tablet->rotation.touch_device_left_handed_state = true;
-               tablet_change_rotation(device, DO_NOTIFY);
+       struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
+       struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
+
+       if (tablet->rotation.touch_device == NULL && (group1 == group2)) {
+               evdev_log_debug(device,
+                               "tablet-rotation: %s will rotate %s\n",
+                               device->devname,
+                               new_device->devname);
+               tablet->rotation.touch_device = new_device;
+
+               if (libinput_device_config_left_handed_get(&new_device->base)) {
+                       tablet->rotation.touch_device_left_handed_state = true;
+                       tablet_change_rotation(device, DO_NOTIFY);
+               }
        }
 }
 
@@ -2181,10 +2209,6 @@ tablet_device_added(struct evdev_device *device,
 {
        bool is_touchscreen, is_ext_touchpad;
 
-       if (libinput_device_get_device_group(&device->base) !=
-           libinput_device_get_device_group(&added_device->base))
-               return;
-
        is_touchscreen = evdev_device_has_capability(added_device,
                                                     LIBINPUT_DEVICE_CAP_TOUCH);
        is_ext_touchpad = evdev_device_has_capability(added_device,
index 5a92ea6ea7488177ab290524344dd9d37e808c07..9a6602e6ef89a42bec28ed06b4f131deafe979c7 100644 (file)
@@ -5338,6 +5338,34 @@ START_TEST(touch_arbitration_late_touch_lift)
 }
 END_TEST
 
+START_TEST(touch_arbitration_swap_device)
+{
+       struct litest_device *tablet = litest_current_device();
+       struct libinput *li = tablet->libinput;
+
+       enum litest_device_type paired = paired_device(tablet);
+       if (paired == LITEST_NO_DEVICE)
+               return;
+
+       /* First, add a normal touchscreen */
+       struct litest_device *touchscreen = litest_add_device(li, LITEST_GENERIC_MULTITOUCH_SCREEN);
+       libinput_device_config_gesture_set_hold_enabled(touchscreen->libinput_device,
+                                                       LIBINPUT_CONFIG_HOLD_DISABLED);
+       litest_drain_events(li);
+       assert_touch_is_arbitrated(tablet, touchscreen);
+
+       /* Now add a better device to override the pairing */
+       struct litest_device *finger = litest_add_device(li, paired);
+       libinput_device_config_gesture_set_hold_enabled(finger->libinput_device,
+                                                       LIBINPUT_CONFIG_HOLD_DISABLED);
+       litest_drain_events(li);
+       assert_touch_is_arbitrated(tablet, finger);
+
+       litest_delete_device(touchscreen);
+       litest_delete_device(finger);
+}
+END_TEST
+
 #if HAVE_LIBWACOM
 static void
 verify_left_handed_tablet_motion(struct litest_device *tablet,
@@ -6209,6 +6237,7 @@ TEST_COLLECTION(tablet)
        litest_add(touch_arbitration_late_touch_lift, LITEST_TABLET, LITEST_ANY);
        litest_add(touch_arbitration_outside_rect, LITEST_TABLET | LITEST_DIRECT, LITEST_ANY);
        litest_add(touch_arbitration_remove_after, LITEST_TABLET | LITEST_DIRECT, LITEST_ANY);
+       litest_add(touch_arbitration_swap_device, LITEST_TABLET, LITEST_ANY);
 
        litest_add_ranged(tablet_rotation_left_handed, LITEST_TABLET, LITEST_ANY, &lh_transitions);
        litest_add_ranged(tablet_rotation_left_handed_configuration, LITEST_TABLET, LITEST_ANY, &lh_transitions);