cyapa - Introduce better three-finger button emulation
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 9 Jan 2014 23:06:39 +0000 (15:06 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 9 Jan 2014 23:08:35 +0000 (15:08 -0800)
* Change the three-finger button emulation such that the third finger that
  you use to tap will emulate the LEFT, MIDDLE, or RIGHT buttons depending
  on its X position relative to the other two fingers that are being held
  down.

  e.g. if you arrange fingers left-to-right A, B, C, and you hold B and C
  on the pad and tap A, that's a LEFT button.  If you hold A and C and
  tap B, that's a middle button, and if you hold A and B and tap C, that's
  a right button.

* This methodology allows all three buttons to be emulated cleanly without
  having to depress the trackpad's physical button.

* Document all jestures.

sys/dev/smbus/cyapa/cyapa.c
sys/kern/sys_generic.c

index 6426639..c75fe30 100644 (file)
  * NOTE: In explorerps/2 mode the slider has only 4 bits of delta resolution
  *      and may not work as smoothly.  Buttons are recognized as button
  *      8 and button 9.
+ *
+ *                                 FEATURES
+ *
+ * Jitter supression   - Implements 2-pixel hysteresis with memory.
+ *
+ * False-finger supression- Two-fingers-down does not emulate anything,
+ *                         on purpose.
+ *
+ * Slider jesture      - Tap right hand side and slide up or down.
+ *
+ *                       (Three finger jestures)
+ * left button jesture - Two fingers down on the left, tap/hold right
+ * middle button jesture- Two fingers down left & right, tap/hold middle
+ * right button jesture - Two fingers down on the right, tap/hold left
+ *
+ * track-pad button     - Tap/push physical button, left, middle, or right
+ *                       side of the trackpad will issue a LEFT, MIDDLE, or
+ *                       RIGHT button event.
+ *
+ * track-pad button     - Any tap/slide of more than 32 pixels and pushing
+ *                       harder to articulate the trackpad physical button
+ *                       always issues a LEFT button event.
+ *
+ * first-finger tracking- The X/Y coordinates always track the first finger
+ *                       down.  If you have multiple fingers down and lift
+ *                       up the designated first finger, a new designated
+ *                       first finger will be selected without causing the
+ *                       mouse to jump (delta's are reset).
  */
 #include <sys/kernel.h>
 #include <sys/param.h>
@@ -121,7 +149,8 @@ struct cyapa_softc {
        short   track_y;
        short   track_z;
        uint16_t track_but;
-       char    track_id;               /* (for movement) */
+       char    track_id1;              /* first finger id */
+       char    track_id2;              /* second finger id */
        short   delta_x;                /* accumulation -> report */
        short   delta_y;
        short   delta_z;
@@ -1153,9 +1182,13 @@ cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs)
 {
        int nfingers;
        int i;
+       int j;
+       int k;
        short x;
        short y;
        short z;
+       short x1;
+       short x2;
        uint16_t but;   /* high bits used for simulated but4/but5 */
 
        nfingers = CYAPA_FNGR_NUMFINGERS(regs->fngr);
@@ -1214,25 +1247,24 @@ cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs)
                sc->touch_x = -1;
                sc->touch_y = -1;
                sc->touch_z = -1;
-               sc->track_id = -1;
+               sc->track_id1 = -1;
+               sc->track_id2 = -1;
                sc->track_but = 0;
                i = 0;
-       } else if (sc->track_id == -1) {
-               /*
-                * Touch(es), if not tracking for mouse-movement, assign
-                * mouse-movement to the first finger in the array.
-                */
-               i = 0;
-               sc->track_id = regs->touch[i].id;
+               j = 0;
+               k = 0;
        } else {
                /*
                 * The id assigned on touch can move around in the array,
                 * find it.  If that finger is lifted up, assign some other
                 * finger for mouse tracking and reset track_x and track_y
                 * to avoid a mouse jump.
+                *
+                * If >= 2 fingers are down be sure not to assign i and
+                * j to the same index.
                 */
                for (i = 0; i < nfingers; ++i) {
-                       if (sc->track_id == regs->touch[i].id)
+                       if (sc->track_id1 == regs->touch[i].id)
                                break;
                }
                if (i == nfingers) {
@@ -1240,8 +1272,52 @@ cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs)
                        sc->track_x = -1;
                        sc->track_y = -1;
                        sc->track_z = -1;
-                       sc->track_id = regs->touch[i].id;
+                       sc->track_id1 = regs->touch[i].id;
+                       if (sc->track_id2 == sc->track_id1)
+                               sc->track_id2 = -1;
+               }
+
+               /*
+                * A second finger.
+                */
+               for (j = 0; j < nfingers; ++j) {
+                       if (sc->track_id2 == regs->touch[j].id)
+                               break;
                }
+               if (j == nfingers) {
+                       if (nfingers >= 2) {
+                               if (i == 0)
+                                       j = 1;
+                               else
+                                       j = 0;
+                               sc->track_id2 = regs->touch[j].id;
+                       } else {
+                               sc->track_id2 = -1;
+                               j = 0;
+                       }
+               }
+
+               /*
+                * The third finger is used to tap or tap-hold to simulate
+                * a button, we don't have to record it persistently.
+                */
+               if (nfingers >= 3) {
+                       k = 0;
+                       if (i == 0 || j == 0)
+                               k = 1;
+                       if (i == 1 || j == 1)
+                               k = 2;
+               } else {
+                       k = 0;
+               }
+               kprintf("ID %3d,%3d,%3d\t%3d,%3d,%3d\n",
+                       sc->track_id1,
+                       sc->track_id2,
+                       ((nfingers >= 3) ? regs->touch[k].id : -1),
+                       CYAPA_TOUCH_X(regs, i),
+                       CYAPA_TOUCH_X(regs, j),
+                       CYAPA_TOUCH_X(regs, k)
+               );
        }
 
        /*
@@ -1306,9 +1382,8 @@ cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs)
                but = SIMULATE_BUT4;
        } else if (nfingers >= 3 && sc->track_z < 0) {
                /*
-                * Simulate the left button with 3+ fingers when not in
-                * slider mode (4 of 5 fingers also works if the trackpad
-                * is not emulating button-4 and button-5).
+                * Simulate the left, middle, or right button with 3
+                * fingers when not in slider mode.
                 *
                 * This makes it ultra easy to hit GUI buttons and move
                 * windows with a light touch, without having to apply the
@@ -1319,13 +1394,36 @@ cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs)
                 * button 4 or button 5.  Leave SIMULATE_LOCK set to
                 * placemark the condition.  We must go down to 2 fingers
                 * to release the lock.
+                *
+                * LEFT BUTTON: Fingers arranged left-to-right 1 2 3,
+                *              move mouse with fingers 2 and 3 and tap
+                *              or hold with finger 1 (to the left of fingers
+                *              2 and 3).
+                *
+                * RIGHT BUTTON: Move mouse with fingers 1 and 2 and tap
+                *               or hold with finger 3.
+                *
+                * MIDDLE BUTTON: Move mouse with fingers 1 and 3 and tap
+                *                or hold with finger 2.
                 */
+               x1 = CYAPA_TOUCH_X(regs, i);    /* 1st finger down */
+               x2 = CYAPA_TOUCH_X(regs, j);    /* 2nd finger down */
+               x = CYAPA_TOUCH_X(regs, k);     /* 3rd finger (button) down */
                if (sc->track_but & (SIMULATE_BUT4 |
-                                    SIMULATE_BUT5 |
-                                    SIMULATE_LOCK))
+                                           SIMULATE_BUT5 |
+                                           SIMULATE_LOCK)) {
                        but = SIMULATE_LOCK;
-               else
+               } else if (sc->track_but & ~SIMULATE_LOCK) {
+                       but = sc->track_but;
+               } else if (x < x1 && x < x2) {
                        but = CYAPA_FNGR_LEFT;
+               } else if (x > x1 && x < x2) {
+                       but = CYAPA_FNGR_MIDDLE;
+               } else if (x > x2 && x < x1) {
+                       but = CYAPA_FNGR_MIDDLE;
+               } else {
+                       but = CYAPA_FNGR_RIGHT;
+               }
        } else if (nfingers == 2 || (nfingers >= 2 && sc->track_z >= 0)) {
                /*
                 * If 2 fingers are held down or 2 or more fingers are held
index 6351dfb..1b94254 100644 (file)
@@ -673,21 +673,20 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map,
                goto done;
        }
 
-       if (size > sizeof (ubuf.stkbuf)) {
+       if ((com & IOC_VOID) == 0 && size > sizeof(ubuf.stkbuf)) {
                memp = kmalloc(size, M_IOCTLOPS, M_WAITOK);
                data = memp;
        } else {
                memp = NULL;
                data = ubuf.stkbuf;
        }
-       if ((com & IOC_IN) != 0) {
+       if (com & IOC_VOID) {
+               *(caddr_t *)data = uspc_data;
+       } else if (com & IOC_IN) {
                if (size != 0) {
                        error = copyin(uspc_data, data, (size_t)size);
-                       if (error) {
-                               if (memp != NULL)
-                                       kfree(memp, M_IOCTLOPS);
+                       if (error)
                                goto done;
-                       }
                } else {
                        *(caddr_t *)data = uspc_data;
                }
@@ -697,8 +696,6 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map,
                 * gets back something deterministic.
                 */
                bzero(data, (size_t)size);
-       } else if ((com & IOC_VOID) != 0) {
-               *(caddr_t *)data = uspc_data;
        }
 
        switch (com) {
@@ -735,9 +732,9 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map,
                        error = copyout(data, uspc_data, (size_t)size);
                break;
        }
+done:
        if (memp != NULL)
                kfree(memp, M_IOCTLOPS);
-done:
        fdrop(fp);
        return(error);
 }