touchpad: after a click, lock the finger to its current position
authorPeter Hutterer <peter.hutterer@who-t.net>
Tue, 15 Apr 2014 12:27:59 +0000 (14:27 +0200)
committerHans de Goede <hdegoede@redhat.com>
Thu, 22 May 2014 12:39:00 +0000 (14:39 +0200)
On clickpads, clicking the pad usually causes some motion events. To avoid
erroneous movements, lock all fingers into position on the click and don't
allow for motion events until a finger moves past a given threshold
(currently 2% of the touchpad diagonal).

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ã…dahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
src/evdev-mt-touchpad.c
src/evdev-mt-touchpad.h

index b4a89e301dc2b52463244dd2065106d366472525..54a95ef33a74079f35c3e13d429926a902592937 100644 (file)
@@ -32,6 +32,7 @@
 #define DEFAULT_MIN_ACCEL_FACTOR 0.16
 #define DEFAULT_MAX_ACCEL_FACTOR 1.0
 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
+#define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* 2% of size */
 
 static inline int
 tp_hysteresis(int in, int center, int margin)
@@ -158,6 +159,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t)
                tp_motion_history_reset(t);
                t->dirty = true;
                t->state = TOUCH_BEGIN;
+               t->pinned.is_pinned = false;
                tp->nfingers_down++;
                assert(tp->nfingers_down >= 1);
                tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -182,6 +184,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t)
        t->dirty = true;
        t->is_pointer = false;
        t->state = TOUCH_END;
+       t->pinned.is_pinned = false;
        assert(tp->nfingers_down >= 1);
        tp->nfingers_down--;
        tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -346,50 +349,43 @@ tp_process_key(struct tp_dispatch *tp,
 }
 
 static void
-tp_unpin_finger(struct tp_dispatch *tp)
+tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       struct tp_touch *t;
-       tp_for_each_touch(tp, t) {
-               if (t->is_pinned) {
-                       t->is_pinned = false;
+       unsigned int xdist, ydist;
+       struct tp_touch *tmp = NULL;
+
+       if (!t->pinned.is_pinned)
+               return;
+
+       xdist = abs(t->x - t->pinned.center_x);
+       ydist = abs(t->y - t->pinned.center_y);
 
-                       if (t->state != TOUCH_END &&
-                           tp->nfingers_down == 1)
-                               t->is_pointer = true;
+       if (xdist * xdist + ydist * ydist <
+                       tp->buttons.motion_dist * tp->buttons.motion_dist)
+               return;
+
+       t->pinned.is_pinned = false;
+
+       tp_for_each_touch(tp, tmp) {
+               if (tmp->is_pointer)
                        break;
-               }
        }
+
+       if (t->state != TOUCH_END && !tmp->is_pointer)
+               t->is_pointer = true;
 }
 
 static void
-tp_pin_finger(struct tp_dispatch *tp)
+tp_pin_fingers(struct tp_dispatch *tp)
 {
-       struct tp_touch *t,
-                       *pinned = NULL;
+       struct tp_touch *t;
 
        tp_for_each_touch(tp, t) {
-               if (t->is_pinned) {
-                       pinned = t;
-                       break;
-               }
-       }
-
-       assert(!pinned);
-
-       pinned = tp_current_touch(tp);
-
-       if (tp->nfingers_down != 1) {
-               tp_for_each_touch(tp, t) {
-                       if (t == pinned)
-                               continue;
-
-                       if (t->y > pinned->y)
-                               pinned = t;
-               }
+               t->is_pointer = false;
+               t->pinned.is_pinned = true;
+               t->pinned.center_x = t->x;
+               t->pinned.center_y = t->y;
        }
-
-       pinned->is_pinned = true;
-       pinned->is_pointer = false;
 }
 
 static void
@@ -409,16 +405,19 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
 
                tp_motion_hysteresis(tp, t);
                tp_motion_history_push(t);
+
+               tp_unpin_finger(tp, t);
        }
 
-       /* We have a physical button down event on a clickpad. For drag and
-          drop, this means we try to identify which finger pressed the
-          physical button and "pin" it, i.e. remove pointer-moving
-          capabilities from it.
+       /*
+        * We have a physical button down event on a clickpad. To avoid
+        * spurious pointer moves by the clicking finger we pin all fingers.
+        * We unpin fingers when they move more then a certain threshold to
+        * to allow drag and drop.
         */
        if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
            !tp->buttons.has_buttons)
-               tp_pin_finger(tp);
+               tp_pin_fingers(tp);
 }
 
 static void
@@ -441,9 +440,6 @@ tp_post_process_state(struct tp_dispatch *tp, uint32_t time)
 
        tp->buttons.old_state = tp->buttons.state;
 
-       if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
-               tp_unpin_finger(tp);
-
        tp->queued = TOUCHPAD_EVENT_NONE;
 }
 
@@ -798,6 +794,8 @@ tp_init(struct tp_dispatch *tp,
        tp->hysteresis.margin_y =
                diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
 
+       tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD;
+
        if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) ||
            libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT))
                tp->buttons.has_buttons = true;
index 2bdb32957f37a2d6f18bcd741bd62bd46c8d0ba9..e5fbd7a9fea417691c847bda086e15c08eba95c6 100644 (file)
@@ -77,7 +77,6 @@ struct tp_touch {
        bool dirty;
        bool fake;                              /* a fake touch */
        bool is_pointer;                        /* the pointer-controlling touch */
-       bool is_pinned;                         /* holds the phys. button */
        int32_t x;
        int32_t y;
        uint32_t millis;
@@ -92,6 +91,16 @@ struct tp_touch {
                int32_t center_x;
                int32_t center_y;
        } hysteresis;
+
+       /* A pinned touchpoint is the one that pressed the physical button
+        * on a clickpad. After the release, it won't move until the center
+        * moves more than a threshold away from the original coordinates
+        */
+       struct {
+               bool is_pinned;
+               int32_t center_x;
+               int32_t center_y;
+       } pinned;
 };
 
 struct tp_dispatch {
@@ -122,6 +131,7 @@ struct tp_dispatch {
                bool has_buttons;               /* true for physical LMR buttons */
                uint32_t state;
                uint32_t old_state;
+               uint32_t motion_dist;           /* for pinned touches */
        } buttons;                              /* physical buttons */
 
        struct {