diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CHANGELOG.md | 18 | ||||
| -rw-r--r-- | src/macos/Sources/PardesView.swift | 52 |
2 files changed, 46 insertions, 24 deletions
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 714497b6..39c2057d 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -2,6 +2,24 @@ ## 0.0.2 +- The macOS trackpad's two verbs trade places: a two-finger click is Exec and + a deep press is Look. Exec is what a hand spends a session doing, so it takes + the gesture that costs nothing, and the verb that goes somewhere is worth one + deliberate push past the click. Three fingers stay Exec, and that is the + hardware rather than a gesture left unused: macOS's secondary click is "click + or tap with two or more fingers", so a three-finger click arrives as + `rightMouseDown` exactly like a two-finger one, and the resting count is the + only thing that keeps either of them off Look. `pressureChange` still + releases the click in flight before the new button goes out, which now steps + around acme's 1-3 chord (Paste) where it used to step around 1-2 (Cut). Look + by finger count is gone, so a Mac with "Force Click and haptic feedback" off + — or a trackpad with no force sensor — reaches it through a real mouse's + right button, the `Look` builtin, or Enter on the word. `trackpad.snap` + asserts all of it without a trackpad, because `NSTouch` and the pressure + stages have no public constructors and `Trackpad` is therefore pure policy a + script can name finger counts to; the swap left every golden screen + byte-identical under the other gesture, which is what shows the two paths + were exchanged and nothing else moved. - A filtered terminal costs what an unfiltered one does. `Filter`'s second stage asked `RGB.contrast` for every cell it painted, and that call ends in `std.math.pow` six times over — a libm round trip per cell, per frame, to diff --git a/src/macos/Sources/PardesView.swift b/src/macos/Sources/PardesView.swift index 3f5f1346..f9afa7b1 100644 --- a/src/macos/Sources/PardesView.swift +++ b/src/macos/Sources/PardesView.swift @@ -45,25 +45,27 @@ protocol PardesViewDelegate: AnyObject { /// constructed, so this is the part of the gesture a test can reach. /// /// acme's three buttons are the whole vocabulary — 1 selects, 2 executes, -/// 3 looks — and a trackpad has one surface. Two fingers is the gesture macOS -/// itself spells "secondary", so it is Look; three is the one left over, so it -/// is Exec, the heavier verb, which is also what a deep press means. +/// 3 looks — and a trackpad has one surface. More than one finger is Exec, +/// because running something is what a hand does over and over and it gets +/// the gesture that costs nothing; Look is the deep press, one deliberate +/// push past the click for the verb that goes somewhere. /// -/// The finger count has to win over the stream, and that is not a preference. -/// macOS's secondary click is "click or tap with TWO OR MORE fingers": with it -/// on, a three-finger click is delivered as rightMouseDown exactly like a -/// two-finger one, and a view that trusts the stream cannot tell them apart — -/// three fingers silently did Look. Measured on real hardware, which is the -/// only way this was ever going to be found: `rightMouseDown: resting=3`. +/// Two and three fingers are the same verb here, and that is the hardware +/// talking rather than a shrug. macOS's secondary click is "click or tap with +/// TWO OR MORE fingers": with it on — the default — a three-finger click is +/// delivered as rightMouseDown exactly like a two-finger one, and a view that +/// trusts the stream calls both of them Look. Measured on real hardware, which +/// is the only way this was ever going to be found: +/// `rightMouseDown: resting=3`. So the count is what keeps a multi-finger +/// click off Look, and the stream cannot be trusted to do it. /// -/// So the stream is only the fallback, for when there are no fingers to count: +/// The stream is only the fallback, for when there are no fingers to count: /// a real mouse's right button is Look and its middle button is Exec, and both /// arrive with an empty touch set. enum Trackpad { static func button(stream: pardes_mouse_button_e, fingers: Int) -> pardes_mouse_button_e { switch fingers { - case 2: return PARDES_MOUSE_RIGHT - case 3...: return PARDES_MOUSE_MIDDLE + case 2...: return PARDES_MOUSE_MIDDLE // One finger, or none to count: the stream is the answer. A trackpad // single click comes in on the left stream and stays left; a real // mouse's right and middle buttons keep their acme meanings. @@ -72,9 +74,9 @@ enum Trackpad { } /// A force click is a deliberate second gesture on top of an ordinary one, - /// so it gets the verb that does something rather than the one that - /// navigates. - static let forceClickButton: pardes_mouse_button_e = PARDES_MOUSE_MIDDLE + /// so it gets the verb that goes somewhere rather than the one that runs + /// something: press harder and you Look. + static let forceClickButton: pardes_mouse_button_e = PARDES_MOUSE_RIGHT } // Matches bg_default/fg_default in src/gui/gui.zig and DEFAULT_FG/DEFAULT_BG in @@ -643,7 +645,7 @@ final class PardesView: NSView { // hardware it does not always: AppKit routes NSTouch through the four // touchesXxx callbacks, and the touch set hanging off a *mouse* event can // come back empty depending on how the click was produced. Empty reads as - // one finger, which is a two-finger Look silently degrading into a select + // one finger, which is a two-finger Exec silently degrading into a select // — the exact failure this was supposed to avoid. So the count is // maintained here and the mouse event's own set is preferred only when it // has something in it. @@ -674,7 +676,7 @@ final class PardesView: NSView { registerForDraggedTypes([.fileURL]) // Indirect touches are the trackpad's. Without this the touch set is // always empty and every click looks like one finger, which is exactly - // the bug that would make two-finger Look silently never fire. + // the bug that would make two-finger Exec silently never fire. allowedTouchTypes = [.indirect] // Without a pressure configuration the deep-press stages are the // system's business and stage 2 may never be delivered here. @@ -1737,15 +1739,17 @@ final class PardesView: NSView { /// flight. AppKit keeps sending stage-2 events while the finger stays down, /// so only the transition counts. /// - /// Whatever button is in flight is released before the middle one goes out: - /// a middle press arriving while the core holds a left select-drag is - /// acme's 1-2 chord, which is Cut. Releasing first costs a cursor move at - /// the click point — which is what clicking there would have done anyway. + /// Whatever button is in flight is released before the look one goes out: + /// a right press arriving while the core holds a left select-drag is + /// acme's 1-3 chord, which is Paste. Releasing first costs a cursor move + /// at the click point — which is what clicking there would have done + /// anyway; a multi-finger press released this way fires its own Exec, + /// which is what the fingers already asked for. /// /// Not gated on the press being a LEFT one, which is what stopped this /// working: on a Force Touch trackpad the deep press is just as likely to - /// have arrived on the right stream, and an in-flight Look upgraded by - /// pressing harder is precisely the gesture. Already-Exec is the only case + /// have arrived on the right stream, and an in-flight Exec upgraded by + /// pressing harder is precisely the gesture. Already-Look is the only case /// with nothing to do. override func pressureChange(with event: NSEvent) { trace("pressure: stage=\(event.stage) latched=\(String(describing: latchedButton?.rawValue))") @@ -1829,7 +1833,7 @@ final class PardesView: NSView { /// (secondary click, three-finger drag, force click, "look up"), none of /// which this process can read, and every one of which turns a gesture into /// a different NSEvent or into none at all. When someone reports that - /// two-finger Look does nothing, this is the only thing that can answer + /// two-finger Exec does nothing, this is the only thing that can answer /// whether AppKit saw two fingers, one, or no click at all. private func trace(_ message: @autoclosure () -> String) { guard PardesView.tracing else { return } |
