Skip to content

Instantly share code, notes, and snippets.

@mdmayfield
Last active November 5, 2019 22:35
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdmayfield/7720a0cd1e8b84a61e1543f801dc8245 to your computer and use it in GitHub Desktop.
Save mdmayfield/7720a0cd1e8b84a61e1543f801dc8245 to your computer and use it in GitHub Desktop.

MacOS Touchpad Behavior Analysis

Single Touch

On a MacBook Air with 104mm x 76mm touchpad, macOS appears to put its equivalent of upper_thumb_line 20mm from the bottom, and lower_thumb_line 10mm from the bottom. On Magic Trackpad, 130mm x 108mm active area, these seem to be the same (20mm/10mm from bottom).

In AREA, i.e. above upper_thumb_line

  • Finger: Live touch, no speed threshold, free movement
  • Thumb: Live touch, no speed threshold, free movement

Below upper_thumb_line but above lower_thumb_line

  • Finger: Live touch, no speed threshold, free movement
  • Thumb: "Mostly dead" touch until (speed threshold exceeded OR crosses north of upper_thumb_line), then live with free movement

Below lower_thumb_line

  • Finger: "Mostly dead" touch until (speed threshold exceeded OR crosses north of lower_thumb_line), then live with free movement
  • Thumb: "Mostly dead" touch until (speed threshold exceeded OR crosses north of upper_thumb_line), then live with free movement

Recommendation: Use existing logic to degrade performance gracefully on semi-MT TPs. On TPs with plenty of slots but no presure/size, give all first touches the same behavior as HW-detected thumbs. On TPs with pressure/size, treat all first touches south of lower_thumb_line as thumbs; follow HW-detected thumb status for touches that move or start north of lower_thumb_line.

Existing resting touch, then add second touch above

In general, a higher-positioned touch will immediately turn a lower resting touch into a temporary thumb ("mostly dead"). When the higher touch is lifted, the resting touch once in its lifetime will immediately revert to the status of a new single touch (making a new shape + position check for whether a speed threshold applies). After this, if the lower touch is turned into a thumb again by a second higher touch, the lower touch becomes permanently and unconditionally a thumb (dead).

A lower touch always becomes a thumb for life unconditionally when it is turned into a temporary thumb for the second time, regardless of whether it actually moved or broke a speed threshold in between the two upper touches.

This could be considered a "two strikes and you're out" policy for thumbhood.

Handedness does not appear to be a factor; all tests were observed to work symmetrically.

Examples

1

  • Rest thumb south of upper_thumb_line.
  • Add a finger, move cursor. Lift finger.
    • Move thumb slowly - touch stays dead; nothing happens.
    • Move thumb quickly - touch becomes live and cursor moves.
  • Add a finger again, move cursor. Lift finger.
    • Move thumb slowly - touch stays dead; nothing happens.
    • Move thumb quickly - touch stays dead; nothing happens.

2

  • Rest thumb south of upper_thumb_line.
  • Add a finger, move cursor. Lift finger. (Do not move thumb)
  • Add a finger again, move cursor. Lift finger.
    • Move thumb slowly - touch stays dead; nothing happens.
    • Move thumb quickly - touch stays dead; nothing happens.

3

  • Rest index finger #1 anywhere above lower_thumb_line.
  • Add opposite hand index finger #2 30-50mm above and to one side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch becomes live and cursor moves.
    • Move finger #1 quickly - touch becomes live and cursor moves.
  • Add opposite hand index finger #2 again 30-50mm above and to one side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch stays dead; nothing happens.
    • Move finger #1 quickly - touch stays dead; nothing happens.

4

  • Rest index finger #1 anywhere above lower_thumb_line.
  • Add opposite hand index finger #2 30-50mm above and to one side, move cursor. Lift finger #2. (Do not move finger #1)
  • Add opposite hand index finger #2 30-50mm above and to one side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch stays dead; nothing happens.
    • Move finger #1 quickly - touch stays dead; nothing happens.

5

  • Rest index finger #1 anywhere below lower_thumb_line.
  • Add opposite hand index finger #2 30-50mm above and to either side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch stays dead; nothing happens.
    • Move finger #1 quickly - touch becomes live and cursor moves.
  • Add opposite hand index finger #2 again 30-50mm above and to either side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch stays dead; nothing happens.
    • Move finger #1 quickly - touch stays dead; nothing happens.

6

  • Rest index finger #1 anywhere below lower_thumb_line.
  • Add opposite hand index finger #2 30-50mm above and to either side, move cursor. Lift finger #2. (Do not move finger #1)
  • Add opposite hand index finger #2 again 30-50mm above and to either side, move cursor. Lift finger #2.
    • Move finger #1 slowly - touch stays dead; nothing happens.
    • Move finger #1 quickly - touch stays dead; nothing happens.

Existing resting touch, then add second touch below

If there is an existing live touch, and a second touch appears below it, the second touch begins its life as a "mostly dead" temporary thumb. However this status does not count against its "two strikes" before becoming a permanent thumb. All speed threshold rules based on position and HW thumb detection apply as above.

Example

  • Place finger #1 on the upper half of the touchpad
    • Observe touch #1 is live and can move the cursor
  • Place finger #2 30-50mm below and to the side, but above the lower_thumb_line
    • Observe touch #1 is still live, but #2 is now (temporarily) dead
  • Lift finger #1
    • Finger #2 immediately becomes live again (subject to speed threshold rules based on size/shape and Y position, as above)
  • Place finger #1 again
    • Observe #1 is live and #2 is now (permanently) dead
  • Lift finger #1
    • Observe touch #2 remains dead

Speed-based thumb detection

Seems fairly similar to current implementation in libinput 1.11.9, except with "mostly dead" temporary thumb instead of permanent.

  • Place one finger on TP and move it around moderately quickly

  • Rest second finger anywhere >25mm from it while it is still moving

    • Second finger detected apparently by speed
    • Becomes a temporary thumb as above; if detected again, becomes permanent thumb
  • Place one finger on TP and move it around moderately quickly

  • Rest second finger <25mm near it while it is still moving

    • Some very hard-to-analyze complexity/inconsistency exists around this
      • Scroll gesture usually begins
      • Sometimes only the moving finger would continue to be live
      • Sometimes a scroll gesture would start but then be cancelled, returning mouse cursor control to one of the fingers

Recommendation: Simplify this slightly; use current logic for detecting thumbs by speed, adapted to use new thumb rules.

2+ Finger Gestures

With a few rare exceptions, experimentation didn't show evidence of timeouts used to determine gesture mode; the interpretation of gestures is primarily based on positional and speed thresholds.

Permanently "dead" thumbs never count toward gesture detection. A temporary thumb usually doesn't count toward gesture detection, but does in certain circumstances; see below.

Surprisingly, HW size/shape thumb detection doesn't seem enter into this, except for a few edge cases involving touches below the lower_thumb_line.

Recommendation: Use existing logic to degrade performance gracefully on semi-MT TPs. On full-MT TPs with enough slots, but no size/pressure, give the lowest touch the same behavior as a HW-detected thumb. Otherwise follow HW-detected thumb status.

The gesture detection continues to allow cursor movement/2-finger scrolling (from the non-lowest touch(es), regardless of HW thumb size/pressure) until both touches move enough to reveal the type of gesture, OR a touch exceeds a speed threshold and the gesture detection is abandoned. Implementing this would require some added logic to tp_gesture_handle_state_unknown and the circumstances in which tp_gesture_post_events calls tp_gesture_post_pointer_motion.

Thumb + >=3-finger gestures have more complex behavior shown below.

Examples

Live thumb 2-finger pinch/scroll vs. dead thumb cursor movement

  • Place thumb and finger both on TP, at any angle, separated by 5 - 50 mm
    • If thumb is kept still (within 2mm) while finger moves (>2mm)
      • Finger continues to control cursor, uninterrupted, as if it were the only touch, until one of these happens:
      • If finger OR thumb exceeds a speed threshold, thumb becomes "mostly dead" temporary thumb as above, gesture cancelled
      • If finger does not exceed the speed treshold, AND thumb begins moving >2mm, pinch or scroll begins (based on directions)
    • If finger is kept still (within 2mm) while thumb moves (>2mm)
      • Finger (not thumb!) controls cursor as if it were the only touch, until one of these happens:
      • If finger OR thumb exceeds a speed threshold, thumb becomes "mostly dead" temporary thumb as above, gesture cancelled
      • If thumb does not exceed the speed treshold, AND finger begins moving >2mm, pinch or scroll begins (based on directions)
  • Place thumb and finger both on TP > 50mm apart
    • Only pinch gesture is possible
    • Other rules from above apply

Note that the finger and thumb can begin their touches at any time and in either order; there is no timeout where any particular gesture is assumed. As long as both touches don't exceed the speed or distance thresholds, they are both gesture-eligible even though the lower becomes a (provisional) temporary thumb for cursor movement purposes.

Ideally, resting a thumb anywhere on the touchpad still allows for both gestures AND cursor movement; while moving the cursor with one finger is not interrupted by dropping a resting thumb (even if the movement is too slow for speed-based detection).

Recommendation: Explore using tp_get_combined_touches_delta to move the cursor while the gesture state is unknown AND neither touch has exceeded the speed threshold and triggered thumb detection. (I've occasionally witnessed a brief slowdown of cursor speed when dropping a resting thumb; that suggests Apple may be doing something like this.)

Two fingers vs. Finger + Thumb

  • A HW-detected thumb below the upper_thumb_line, but above the lower_thumb_line, is NOT required to exceed the normal thumb speed threshold. Instead, the gesture speed threshold applies.
  • If a HW-detected thumb is below the lower_thumb_line, it must rise above the lower_thumb_line before it counts toward a pinch.
  • If a HW-detected finger is below the lower_thumb_line, the normal gesture speed threshold applies.
  • Otherwise, thumb/finger vs. 2-finger behavior is identical; higher touch counts as "finger" and lower as "thumb"
  • HW thumb detection does not seem to be a factor except regarding the lower_thumb_line
  • A HW-detected thumb may count as a finger in a two-finger scroll if it is inline with the finger and moves appropriately

Recommendation: Simplify thumb logic by treating all gesture touches below the lower_thumb_line like Apple treats size-based thumbs there (since pressure-based is unreliable at the edge, and it seems not worth implementing just for size-based).

Live thumb 3-finger pinch vs. dead thumb 2-finger scroll

  • Same as 2-finger pinch above, except scrolling instead of cursor movement
  • Once a pinch OR scroll begins, the opposite is disqualified
  • There is no timeout to default to one or the other
  • If ONLY thumb moves, it becomes dead (much like 2-finger situation above)
  • If ONLY fingers move, thumb becomes dead (much like 2-finger situation above)

Live thumb 4-finger pinch vs. dead thumb 3-finger swipe

  • Place thumb and 3 fingers near-simultaneously
    • 3-finger swipe is impossible; moving fingers alone does nothing
    • Moving thumb + fingers toward/away from each other begins 4-finger pinch
  • Place thumb, wait about 200ms, place 3 fingers
    • In this state either 3fg-swipe or 4fg-pinch is possible
    • Follow similar positional threshold logic as 2fg pinch above
  • A HW-detected thumb may count as a finger in a swipe if it is inline with fingers and moves appropriately

Live thumb 5-finger pinch vs. dead thumb 4-finger swipe

  • Behavior is identical to above (4-finger pinch)
  • macOS doesn't seem to differentiate between 4 and 5 finger pinches

Scrolling Behavior

On macOS, two-finger scrolling is constrained to 90° angles - the scroll is rounded off to the nearest cardinal direction. As a side effect, scrolling very near a 45° diagonal results in a "stairstep" movement if the scroll wanders back and forth between the two directions.

(Strangely, I never noticed this before, despite using Mac touchpads for over a decade.)

Physical clicks

MacOS behavior when the clickpad is physically pressed follows logically from the thumb detection described above:

  • If one or more touches exist above the clicking thumb, the thumb becomes a "temporary thumb" and does not count toward clickfinger.
  • If two or three fingers click side-by-side (anywhere on the touchpad), no thumb is detected and all touches count toward clickfinger.
  • Two fingers must be within 50mm horizontally to count as a right-click.
  • Two fingers must be within 35mm vertically to count as a right-click.
  • HW thumb detection prevents a finger + a thumb from counting as a right-click (even if the touches are very close); it counts as a left-click instead.
  • In the event of a physical right-click-drag with two fingers but without a thumb, upon clickpad physical button press, both fingers control the cursor together, apparently without changing thumb state (like tp_get_combined_touches_delta?) but without triggering a scroll gesture.
  • MacOS does not support a 3-finger middle click. IF a physical middle-click with three fingers is attempted, a left-click occurs. If a physical middle-click-drag with 3 fingers and no thumb is attempted, a 3-finger swipe or pinch gesture initiates.

Recommendation: Don't follow MacOS on the 3-finger clicks; keep the ability to middle-click. On a physical click with 2-3 fingers but no thumb, temporarily disable scroll, swipe, and pinch gestures.

Gestures with physical button down

  • 2+ finger gestures are allowed, and behave the same, with the clickpad button down
  • The other thumb-detection logic keeps the behavior predictable in this case:
    • A moving thumb can be used to drag things at first
    • If the thumb is below the upper_thumb_line, it's hard to move accidentally because of the speed threshold
    • If moving fingers become involved, the thumb will become (temporarily, then permanently) a thumb
    • Two or three fingers above the thumb will affect the thumb's state in the same way as a single finger

Tap-to-click

  • Two fingers must be within 50mm horizontally to count as a right-click.
  • Two fingers must be within 35mm vertically to count as a right-click.
  • HW thumb detection causes a thumb + finger tap-to-click to be completely ignored when next to each other (no click).
    • If the thumb is lower than the other touch, it counts as a left-click.
  • MacOS does not support three-finger middle-clicks, but can enable a "look up" feature when tapping with three fingers.
  • Three fingers can count as a special click even when spaced across the entire touchpad horizontally (approx. 100mm).
  • Three fingers can count as a special click even when spaced across the entire touchpad vertically (approx. 70mm).
  • HW thumb detection causes a thumb + two fingers tap-to-click to be completely ignored when in a horizontal row (no click).
    • If the thumb is lower than the other touches, it counts as a right-click.
  • Tapping with a thumb works only when the thumb is the only active touch, AND there is no other "permanently dead" thumb on the TP.
  • Attempting to steer the cursor with a thumb, and tap with a finger, leads to predictably counterproductive results as expected from "Two Strikes" thumb detection.

Configurable Options

  • On macOS right click can be set to:
    • Click or tap with two fingers
    • Click in bottom right corner
    • Click in bottom left corner
  • When one of the latter two options is chosen, thumb detection is the same as the first option.
  • The secondary-click area on the lower left or right of the touchpad is less than 50% of the touchpad width - more like 30% of the width, or 30mm, whichever is smaller.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment