touchpad: Support finger-pinnnig during physical button presses
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 17 Feb 2014 04:24:20 +0000 (14:24 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Mon, 24 Mar 2014 06:39:12 +0000 (16:39 +1000)
On a clickpad, one finger has be on the trackpad to trigger a physical button
press. For drag and drop, we still want motion events though when a second
finger is down.

This patch adds finger-pinning. If the touchpad is pressed, the pressing
finger is "pinned" and ignored for further motion events. A second finger may
then be used to drag.

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

index e6b386473bb59017307f8f2a194709c0ec183ef9..379207980a29a069d569763d2a1f304969d38e52 100644 (file)
@@ -152,6 +152,8 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
 static inline void
 tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t)
 {
+       struct tp_touch *tmp;
+
        if (t->state != TOUCH_UPDATE) {
                tp_motion_history_reset(t);
                t->dirty = true;
@@ -160,8 +162,14 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t)
                assert(tp->nfingers_down >= 1);
                tp->queued |= TOUCHPAD_EVENT_MOTION;
 
-               if (tp->nfingers_down == 1)
+               tp_for_each_touch(tp, tmp) {
+                       if (tmp->is_pointer)
+                               break;
+               }
+
+               if (!tmp->is_pointer) {
                        t->is_pointer = true;
+               }
        }
 }
 
@@ -336,6 +344,53 @@ tp_process_key(struct tp_dispatch *tp,
        }
 }
 
+static void
+tp_unpin_finger(struct tp_dispatch *tp)
+{
+       struct tp_touch *t;
+       tp_for_each_touch(tp, t) {
+               if (t->is_pinned) {
+                       t->is_pinned = false;
+
+                       if (t->state != TOUCH_END &&
+                           tp->nfingers_down == 1)
+                               t->is_pointer = true;
+                       break;
+               }
+       }
+}
+
+static void
+tp_pin_finger(struct tp_dispatch *tp)
+{
+       struct tp_touch *t,
+                       *pinned = NULL;
+
+       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;
+               }
+       }
+
+       pinned->is_pinned = true;
+       pinned->is_pointer = false;
+}
+
 static void
 tp_process_state(struct tp_dispatch *tp, uint32_t time)
 {
@@ -354,6 +409,15 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
                tp_motion_hysteresis(tp, t);
                tp_motion_history_push(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.
+        */
+       if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
+           !tp->buttons.has_buttons)
+               tp_pin_finger(tp);
 }
 
 static void
@@ -376,6 +440,9 @@ 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;
 }
 
@@ -441,6 +508,11 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint32_t time)
 static int
 tp_post_scroll_events(struct tp_dispatch *tp, uint32_t time)
 {
+       /* don't scroll if a clickpad is held down */
+       if (!tp->buttons.has_buttons &&
+           (tp->buttons.state || tp->buttons.old_state))
+               return 0;
+
        if (tp->nfingers_down != 2) {
                /* terminate scrolling with a zero scroll event to notify
                 * caller that it really ended now */
@@ -561,8 +633,17 @@ tp_post_events(struct tp_dispatch *tp, uint32_t time)
        if (tp_post_scroll_events(tp, time) != 0)
                return;
 
-       if (t->history.count >= TOUCHPAD_MIN_SAMPLES &&
-           tp->nfingers_down == 1) {
+       if (t->history.count >= TOUCHPAD_MIN_SAMPLES) {
+               if (!t->is_pointer) {
+                       tp_for_each_touch(tp, t) {
+                               if (t->is_pointer)
+                                       break;
+                       }
+               }
+
+               if (!t->is_pointer)
+                       return;
+
                tp_get_delta(t, &dx, &dy);
                tp_filter_motion(tp, &dx, &dy, time);
 
index df83b2c503d8b3c08d8659c899e5ffd6420d6783..c30dc9e4b362b868c7c35407da57ed864522ed33 100644 (file)
@@ -77,6 +77,7 @@ 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;