Apple touchpads don't use ABS_MT_PRESSURE but they are multitouch touchpads,
so the current pressure-based handling code doesn't apply because it expects
slot-based pressure for mt touchpads.

Apple does however send useful data for ABS_MT_WIDTH_MAJOR/MINOR, so let's use
that instead. The data provided in those is more-or-less random, so we need a
hwdb entry to track the acceptable thresholds.

Signed-off-by: Peter Hutterer <[email protected]>
---
 src/evdev-mt-touchpad.c             | 115 +++++++++++++++++++++++++++++++++++-
 src/evdev-mt-touchpad.h             |  12 ++++
 src/evdev.c                         |   2 +-
 test/litest-device-bcm5974.c        |  12 ++++
 test/litest-device-magic-trackpad.c |  26 ++++++--
 test/test-touchpad.c                | 106 +++++++++++++++++++++++++++++++++
 udev/90-libinput-model-quirks.hwdb  |   1 +
 udev/parse_hwdb.py                  |   1 +
 8 files changed, 269 insertions(+), 6 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 14bb5c84..0f009ef6 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -348,6 +348,16 @@ tp_process_absolute(struct tp_dispatch *tp,
                t->dirty = true;
                tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
                break;
+       case ABS_MT_TOUCH_MAJOR:
+               t->major = e->value;
+               t->dirty = true;
+               tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
+               break;
+       case ABS_MT_TOUCH_MINOR:
+               t->minor = e->value;
+               t->dirty = true;
+               tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
+               break;
        }
 }
 
@@ -1016,6 +1026,45 @@ tp_unhover_pressure(struct tp_dispatch *tp, uint64_t 
time)
 }
 
 static void
+tp_unhover_size(struct tp_dispatch *tp, uint64_t time)
+{
+       struct tp_touch *t;
+       int low = tp->touch_size.low,
+           high = tp->touch_size.high;
+       int i;
+
+       /* We require 5 slots for size handling, so we don't need to care
+        * about fake touches here */
+
+       for (i = 0; i < (int)tp->num_slots; i++) {
+               t = tp_get_touch(tp, i);
+
+               if (t->state == TOUCH_NONE)
+                       continue;
+
+               if (!t->dirty)
+                       continue;
+
+               if (t->state == TOUCH_HOVERING) {
+                       if ((t->major > high && t->minor > low) ||
+                           (t->major > low && t->minor > high)) {
+                               evdev_log_debug(tp->device,
+                                               "touch-size: begin touch\n");
+                               /* avoid jumps when landing a finger */
+                               tp_motion_history_reset(t);
+                               tp_begin_touch(tp, t, time);
+                       }
+               } else {
+                       if (t->major < low || t->minor < low) {
+                               evdev_log_debug(tp->device,
+                                               "touch-size: end touch\n");
+                               tp_end_touch(tp, t, time);
+                       }
+               }
+       }
+}
+
+static void
 tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
 {
        struct tp_touch *t;
@@ -1077,6 +1126,8 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
 {
        if (tp->pressure.use_pressure)
                tp_unhover_pressure(tp, time);
+       else if (tp->touch_size.use_touch_size)
+               tp_unhover_size(tp, time);
        else
                tp_unhover_fake_touches(tp, time);
 
@@ -1512,6 +1563,15 @@ tp_sync_touch(struct tp_dispatch *tp,
                t->pressure = libevdev_get_event_value(evdev,
                                                       EV_ABS,
                                                       ABS_PRESSURE);
+
+       libevdev_fetch_slot_value(evdev,
+                                 slot,
+                                 ABS_MT_TOUCH_MAJOR,
+                                 &t->major);
+       libevdev_fetch_slot_value(evdev,
+                                 slot,
+                                 ABS_MT_TOUCH_MINOR,
+                                 &t->minor);
 }
 
 static void
@@ -2650,10 +2710,59 @@ tp_init_pressure(struct tp_dispatch *tp,
                        "using pressure-based touch detection\n");
 }
 
+static bool
+tp_init_touch_size(struct tp_dispatch *tp,
+                  struct evdev_device *device)
+{
+       const char *prop;
+       int lo, hi;
+
+       if (!libevdev_has_event_code(device->evdev,
+                                    EV_ABS,
+                                    ABS_MT_TOUCH_MAJOR)) {
+               return false;
+       }
+
+       if (libevdev_get_num_slots(device->evdev) < 5) {
+               evdev_log_bug_libinput(device,
+                              "Expected 5+ slots for touch size detection\n");
+               return false;
+       }
+
+       prop = udev_device_get_property_value(device->udev_device,
+                                             "LIBINPUT_ATTR_TOUCH_SIZE_RANGE");
+       if (!prop)
+               return false;
+
+       if (!parse_range_property(prop, &hi, &lo)) {
+               evdev_log_bug_client(device,
+                                    "discarding invalid touch size range 
'%s'\n",
+                                    prop);
+               return false;
+       }
+
+       if (hi == 0 && lo == 0) {
+               evdev_log_info(device,
+                              "touch size based touch detection disabled\n");
+               return false;
+       }
+
+       /* Thresholds apply for both major or minor */
+       tp->touch_size.low = lo;
+       tp->touch_size.high = hi;
+       tp->touch_size.use_touch_size = true;
+
+       evdev_log_debug(device, "using size-based touch detection\n");
+
+       return true;
+}
+
 static int
 tp_init(struct tp_dispatch *tp,
        struct evdev_device *device)
 {
+       bool use_touch_size = false;
+
        tp->base.dispatch_type = DISPATCH_TOUCHPAD;
        tp->base.interface = &tp_interface;
        tp->device = device;
@@ -2668,7 +2777,11 @@ tp_init(struct tp_dispatch *tp,
 
        evdev_device_init_abs_range_warnings(device);
 
-       tp_init_pressure(tp, device);
+       if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD)
+               use_touch_size = tp_init_touch_size(tp, device);
+
+       if (!use_touch_size)
+               tp_init_pressure(tp, device);
 
        /* Set the dpi to that of the x axis, because that's what we normalize
           to when needed*/
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index d601f7e5..7391e6ce 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -148,6 +148,7 @@ struct tp_touch {
        uint64_t time;
        int pressure;
        bool is_tool_palm; /* MT_TOOL_PALM */
+       int major, minor;
 
        bool was_down; /* if distance == 0, false for pure hovering
                          touches */
@@ -248,6 +249,17 @@ struct tp_dispatch {
                int low;
        } pressure;
 
+       /* If touch size (either axis) goes above high -> touch down,
+          if touch size (either axis) goes below low -> touch up */
+       struct  {
+               bool use_touch_size;
+               int high;
+               int low;
+
+               /* convert device units to angle */
+               double orientation_to_angle;
+       } touch_size;
+
        struct device_coords hysteresis_margin;
 
        struct {
diff --git a/src/evdev.c b/src/evdev.c
index 3e8677bf..6e2d89dd 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1896,7 +1896,7 @@ evdev_process_event(struct evdev_device *device, struct 
input_event *e)
        struct evdev_dispatch *dispatch = device->dispatch;
        uint64_t time = tv2us(&e->time);
 
-#if 0
+#if 1
        if (libevdev_event_is_code(e, EV_SYN, SYN_REPORT))
                evdev_log_debug(device,
                          "-------------- EV_SYN ------------\n");
diff --git a/test/litest-device-bcm5974.c b/test/litest-device-bcm5974.c
index 7d872853..1e6a002f 100644
--- a/test/litest-device-bcm5974.c
+++ b/test/litest-device-bcm5974.c
@@ -40,6 +40,9 @@ static struct input_event down[] = {
        { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
        { .type = -1, .code = -1 },
 };
@@ -51,6 +54,9 @@ static struct input_event move[] = {
        { .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
        { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
        { .type = -1, .code = -1 },
 };
@@ -63,6 +69,12 @@ get_axis_default(struct litest_device *d, unsigned int 
evcode, int32_t *value)
        case ABS_MT_PRESSURE:
                *value = 30;
                return 0;
+       case ABS_MT_TOUCH_MAJOR:
+       case ABS_MT_TOUCH_MINOR:
+               *value = 200;
+               return 0;
+       case ABS_MT_ORIENTATION:
+               return 0;
        }
        return 1;
 }
diff --git a/test/litest-device-magic-trackpad.c 
b/test/litest-device-magic-trackpad.c
index cc371633..56d42344 100644
--- a/test/litest-device-magic-trackpad.c
+++ b/test/litest-device-magic-trackpad.c
@@ -37,10 +37,11 @@ static struct input_event down[] = {
        { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = 
LITEST_AUTO_ASSIGN },
-       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 272 },
-       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 400 },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
        { .type = -1, .code = -1 },
 };
@@ -49,17 +50,34 @@ static struct input_event move[] = {
        { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 272 },
-       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 400 },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = 
LITEST_AUTO_ASSIGN },
+       { .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 
LITEST_AUTO_ASSIGN },
        { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
        { .type = -1, .code = -1 },
 };
 
+static int
+get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
+{
+       switch (evcode) {
+       case ABS_MT_TOUCH_MAJOR:
+       case ABS_MT_TOUCH_MINOR:
+               *value = 200;
+               return 0;
+       case ABS_MT_ORIENTATION:
+               return 0;
+       }
+       return 1;
+}
+
 static struct litest_device_interface interface = {
        .touch_down_events = down,
        .touch_move_events = move,
+
+       .get_axis_default = get_axis_default,
 };
 
 static struct input_absinfo absinfo[] = {
diff --git a/test/test-touchpad.c b/test/test-touchpad.c
index ffb214ec..77618155 100644
--- a/test/test-touchpad.c
+++ b/test/test-touchpad.c
@@ -5273,6 +5273,109 @@ START_TEST(touchpad_pressure_tap_2fg_1fg_light)
 }
 END_TEST
 
+static inline bool
+touchpad_has_touch_size(struct litest_device *dev)
+{
+       struct libevdev *evdev = dev->evdev;
+
+       if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOUCH_MAJOR))
+               return false;
+
+       if (libevdev_get_id_vendor(evdev) == VENDOR_ID_APPLE)
+               return true;
+
+       return false;
+}
+
+START_TEST(touchpad_touch_size)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct axis_replacement axes[] = {
+               { ABS_MT_TOUCH_MAJOR, 0 },
+               { ABS_MT_TOUCH_MINOR, 0 },
+               { ABS_MT_ORIENTATION, 0 },
+               { -1, 0 }
+       };
+
+       if (!touchpad_has_touch_size(dev))
+               return;
+
+       litest_drain_events(li);
+
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 1);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 1);
+       litest_touch_down_extended(dev, 0, 50, 50, axes);
+       litest_touch_move_to_extended(dev, 0,
+                                     50, 50,
+                                     80, 80,
+                                     axes, 10, 1);
+       litest_touch_up(dev, 0);
+       litest_assert_empty_queue(li);
+
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 15);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 15);
+       litest_touch_down_extended(dev, 0, 50, 50, axes);
+       litest_touch_move_to_extended(dev, 0,
+                                     50, 50,
+                                     80, 80,
+                                     axes, 10, 1);
+       litest_touch_up(dev, 0);
+       litest_assert_only_typed_events(li,
+                                       LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
+START_TEST(touchpad_touch_size_2fg)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct axis_replacement axes[] = {
+               { ABS_MT_TOUCH_MAJOR, 0 },
+               { ABS_MT_TOUCH_MINOR, 0 },
+               { ABS_MT_ORIENTATION, 0 },
+               { -1, 0 }
+       };
+
+       if (!touchpad_has_touch_size(dev))
+               return;
+
+       litest_drain_events(li);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 15);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 15);
+       litest_touch_down_extended(dev, 0, 50, 50, axes);
+       litest_touch_move_to_extended(dev, 0,
+                                     50, 50,
+                                     80, 80,
+                                     axes, 10, 1);
+
+       litest_assert_only_typed_events(li,
+                                       LIBINPUT_EVENT_POINTER_MOTION);
+
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 1);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 1);
+       litest_touch_down_extended(dev, 1, 70, 70, axes);
+       litest_touch_move_to_extended(dev, 1,
+                                     70, 70,
+                                     80, 90,
+                                     axes, 10, 1);
+       litest_assert_empty_queue(li);
+
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 15);
+       litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 15);
+       litest_touch_move_to_extended(dev, 0,
+                                     80, 80,
+                                     50, 50,
+                                     axes, 10, 1);
+
+       litest_assert_only_typed_events(li,
+                                       LIBINPUT_EVENT_POINTER_MOTION);
+
+       litest_touch_up(dev, 1);
+       litest_touch_up(dev, 0);
+}
+END_TEST
+
 void
 litest_setup_tests_touchpad(void)
 {
@@ -5434,4 +5537,7 @@ litest_setup_tests_touchpad(void)
        litest_add("touchpad:pressure", touchpad_pressure_tap, LITEST_TOUCHPAD, 
LITEST_ANY);
        litest_add("touchpad:pressure", touchpad_pressure_tap_2fg, 
LITEST_TOUCHPAD, LITEST_ANY);
        litest_add("touchpad:pressure", touchpad_pressure_tap_2fg_1fg_light, 
LITEST_TOUCHPAD, LITEST_ANY);
+
+       litest_add("touchpad:touch-size", touchpad_touch_size, 
LITEST_APPLE_CLICKPAD, LITEST_ANY);
+       litest_add("touchpad:touch-size", touchpad_touch_size_2fg, 
LITEST_APPLE_CLICKPAD, LITEST_ANY);
 }
diff --git a/udev/90-libinput-model-quirks.hwdb 
b/udev/90-libinput-model-quirks.hwdb
index 3ec1ac1f..5d9b2cbc 100644
--- a/udev/90-libinput-model-quirks.hwdb
+++ b/udev/90-libinput-model-quirks.hwdb
@@ -50,6 +50,7 @@ libinput:touchpad:input:b0003v05ACp*
 libinput:touchpad:input:b0005v05ACp*
  LIBINPUT_MODEL_APPLE_TOUCHPAD=1
  LIBINPUT_ATTR_SIZE_HINT=104x75
+ LIBINPUT_ATTR_TOUCH_SIZE_RANGE=150:130
 
 libinput:name:*Apple Inc. Apple Internal Keyboard*:dmi:*
  LIBINPUT_ATTR_KEYBOARD_INTEGRATION=internal
diff --git a/udev/parse_hwdb.py b/udev/parse_hwdb.py
index 5de7eb48..102fb3e7 100755
--- a/udev/parse_hwdb.py
+++ b/udev/parse_hwdb.py
@@ -108,6 +108,7 @@ def property_grammar():
         ('LIBINPUT_ATTR_SIZE_HINT', Group(dimension('SETTINGS*'))),
         ('LIBINPUT_ATTR_RESOLUTION_HINT', Group(dimension('SETTINGS*'))),
         ('LIBINPUT_ATTR_PRESSURE_RANGE', Group(crange('SETTINGS*'))),
+        ('LIBINPUT_ATTR_TOUCH_SIZE_RANGE', Group(crange('SETTINGS*'))),
         ('LIBINPUT_ATTR_TPKBCOMBO_LAYOUT', Or(('below'))),
         ('LIBINPUT_ATTR_LID_SWITCH_RELIABILITY',
          Or(('reliable', 'write_open'))),
-- 
2.13.0

_______________________________________________
wayland-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to