Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400import AppKitimport QuartzCore
/// Sandbox-friendly key capture: an invisible 1×1 NSPanel that becomes the/// key window after the user clicks the menubar piano. A local NSEvent/// monitor catches keyDown / keyUp while the panel is key — no/// `CGEventTap`, no Accessibility prompt, no global hook.////// Tradeoff: this only captures keys while Menu Band is the active app./// The moment the user clicks any other app the panel resigns key and/// capture ends. That's the whole point — it's what makes this MAS-safe./// The current global-tap mode (Notepat / Ableton in the popover) survives/// any focus change, but requires Accessibility and isn't App-Store-eligible.final class LocalKeyCapture { enum EndReason { case cancelled case resignedKey }
/// Called on every keyDown the panel observes. Return `true` to consume /// (so cmd-q etc. can still fall through if false). Receives the same /// (keyCode, isDown, isRepeat, flags) shape as the global tap so the /// AppDelegate can route both paths through one handler. var onKey: ((UInt16, Bool, Bool, NSEvent.ModifierFlags) -> Bool)? /// Called when the panel resigns key — capture has ended naturally /// because the user clicked another app. var onCaptureEnd: ((EndReason) -> Void)? /// Fires `true` when one or more fingers rest on the trackpad and /// `false` when the last finger lifts. Used by the pitch-bend /// gesture so the bend holds (and survives note changes) for as /// long as a finger is down, instead of springing back on a /// silence timeout or a note release. var onTrackpadTouchActiveChanged: ((Bool) -> Void)? /// Public, sandbox-safe multi-contact frames from NSTouch. Available only /// while Menu Band owns the focused window/responder chain. var onTrackpadFrame: (([TrackpadContact], Double, Double) -> Void)? /// Ordinary registered primary-button state from a physical trackpad click. var onTrackpadPhysicalClick: ((Bool) -> Void)? var cancelShortcut: MenuBandShortcut? /// ⌘⌥R inside focus: the global hotkey never sees keys the capture /// panel owns, and the ⌘ press used to trip the focus gesture instead. /// Matched first and swallowed; the handler runs the same record toggle. var recordShortcut: MenuBandShortcut? var onRecordShortcut: (() -> Void)? /// Answers whether the mouse is currently pressed on Menu Band's own /// status item. On macOS 27 a mouse-down in the menu bar deactivates an /// accessory app even when the press lands on its own piano, so the /// panel resigns key ~70 ms before `statusClicked` runs. That resign is /// not a focus exit: the mouse-up re-arms. Set by the AppDelegate. var isOwnStatusItemPress: (() -> Bool)? /// TrackDrum's permission-free click shield is a non-activating panel in /// a helper process. AppKit can still report our hidden key panel as /// resigned for that swallowed click. While percussion owns the trackpad, /// keep capture armed and reclaim key status after the click finishes. var keepsCaptureArmedOnResign = false { didSet { if keepsCaptureArmedOnResign { protectNextResignForTrackDrumInput(duration: 0.75) } else { protectedResignUntil = -.infinity } } }
private var panel: NSPanel? private var monitor: Any? private var resignKeyObserver: NSObjectProtocol? private(set) var isArmed = false private var protectedResignUntil: CFTimeInterval = -.infinity
static func shouldEndCaptureAfterResign( hasKeyWindow: Bool, keepsCaptureArmed: Bool, hasProtectedTrackDrumInput: Bool ) -> Bool { !hasKeyWindow && (!keepsCaptureArmed || !hasProtectedTrackDrumInput) }
/// TrackDrum contacts arrive just before AppKit synthesizes a click from /// the same physical tap. Protect only that narrow handoff, so an external /// mouse click or keyboard app switch still exits focused performance. func protectNextResignForTrackDrumInput(duration: CFTimeInterval = 0.35) { protectedResignUntil = max( protectedResignUntil, CACurrentMediaTime() + duration ) }
/// Bring up the invisible panel and start listening. Idempotent — if /// already armed, just refreshes the panel as key. func arm() { if panel == nil { buildPanel() } guard let panel = panel else { return } // Activate the app + make panel key so keyDown events route here // instead of the previously-foreground app. Without `activate`, our // panel can't become key and keys go elsewhere. let frontBefore = NSWorkspace.shared.frontmostApplication?.localizedName ?? "nil" let activeBefore = NSApp.isActive NSApp.activate(ignoringOtherApps: true) DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in let key = NSApp.keyWindow.map { "\(type(of: $0))" } ?? "none" debugLog("capture.arm: before active=\(activeBefore) front=\(frontBefore) → active=\(NSApp.isActive) front=\(NSWorkspace.shared.frontmostApplication?.localizedName ?? "nil") key=\(key) panelKey=\(self?.panel?.isKeyWindow ?? false) armed=\(self?.isArmed ?? false)") } // If another window in our app is already key (e.g. the // popover), DON'T steal key — `addLocalMonitorForEvents` // catches keys at the app level regardless of which specific // window is key, so the panel doesn't need to be key for // capture to work. Keeping the popover key prevents its // controls from rendering in the inactive/grey state. let otherKey = NSApp.windows.contains { $0.isKeyWindow && $0 !== panel } if otherKey { panel.orderFront(nil) } else { panel.makeKeyAndOrderFront(nil) } // Indirect (trackpad) touches are delivered down the key // window's responder chain, not by hit-testing — so the // sensor view has to be first responder to see resting // fingers. Harmless when another window is key (the gesture // just falls back to note-release reset in that mode). if let sensor = panel.contentView as? TouchSensorView { panel.makeFirstResponder(sensor) } if monitor == nil { monitor = NSEvent.addLocalMonitorForEvents( matching: [.keyDown, .keyUp, .leftMouseDown, .leftMouseUp] ) { [weak self] event in guard let self = self else { return event } if event.type == .leftMouseDown { self.onTrackpadPhysicalClick?(true) return event } if event.type == .leftMouseUp { self.onTrackpadPhysicalClick?(false) return event } let isDown = (event.type == .keyDown) if isDown, !event.modifierFlags.intersection([.command, .option, .control]).isEmpty { debugLog("capture keyDown code=\(event.keyCode) mods=\(MenuBandShortcut.carbonModifiers(from: event.modifierFlags)) recordShortcut=\(self.recordShortcut.map { "\($0.keyCode)/\($0.modifiers)" } ?? "nil")") } if self.recordShortcut?.matches(event: event) == true { if isDown { self.onRecordShortcut?() } return nil } if isDown, self.cancelShortcut?.matches(event: event) == true { self.disarm(reason: .cancelled) return nil } let consumed = self.onKey?(event.keyCode, isDown, event.isARepeat, event.modifierFlags) ?? false return consumed ? nil : event } } if resignKeyObserver == nil { // Disarm only when our APP loses focus (user clicked another // app). Don't disarm when key passes to another window inside // our own app — opening the popover used to disarm capture // here, killing both the typing-on-piano sound AND the ghost- // letter wave while the popover was visible. resignKeyObserver = NotificationCenter.default.addObserver( forName: NSWindow.didResignKeyNotification, object: panel, queue: .main ) { [weak self] _ in guard let self = self else { return } let stillKeyInApp = NSApp.windows.contains { $0.isKeyWindow } let visible = NSApp.windows.filter { $0.isVisible }.map { "\(type(of: $0))" }.joined(separator: ",") debugLog("capture.panel resigned key: stillKeyInApp=\(stillKeyInApp) active=\(NSApp.isActive) front=\(NSWorkspace.shared.frontmostApplication?.localizedName ?? "nil") visible=[\(visible)]") guard !stillKeyInApp else { return } if self.isOwnStatusItemPress?() == true { // A press on our own piano. Hold capture through the // click; `statusClicked` re-arms after the mouse-up. // If the press ends and nothing re-keyed us, let go. debugLog("local capture: holding across status-item press") self.disarmAfterStatusItemPressIfOrphaned() return } if self.keepsCaptureArmedOnResign { // The helper's non-activating click sink can briefly make // this panel resign. Wait a turn for the matching contact // frame, then distinguish that tap from a real focus exit. DispatchQueue.main.async { [weak self] in guard let self, self.isArmed else { return } let hasKeyWindow = NSApp.windows.contains { $0.isKeyWindow } let protectedInput = CACurrentMediaTime() <= self.protectedResignUntil if Self.shouldEndCaptureAfterResign( hasKeyWindow: hasKeyWindow, keepsCaptureArmed: self.keepsCaptureArmedOnResign, hasProtectedTrackDrumInput: protectedInput ) { self.disarm(reason: .resignedKey) } else if !hasKeyWindow { debugLog("local capture: preserving TrackDrum focus across shield click") self.arm() } } } else { self.disarm(reason: .resignedKey) } } } isArmed = true }
/// After a status-item press held capture across a resign: wait for the /// mouse-up, give the click handler a beat to re-arm, and disarm only if /// no window of ours is key and the app is still inactive. private func disarmAfterStatusItemPressIfOrphaned() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in guard let self, self.isArmed else { return } if NSEvent.pressedMouseButtons != 0 { self.disarmAfterStatusItemPressIfOrphaned() return } let keyInApp = NSApp.windows.contains { $0.isKeyWindow } if !keyInApp && !NSApp.isActive { debugLog("local capture: status-item press left us orphaned") self.disarm(reason: .resignedKey) } } }
/// Tear down the panel + monitor. Called when the user clicks another /// app (panel resigns key) or explicitly when we want to drop capture. func disarm(reason: EndReason = .cancelled) { guard isArmed else { return } isArmed = false if let m = monitor { NSEvent.removeMonitor(m) monitor = nil } if let obs = resignKeyObserver { NotificationCenter.default.removeObserver(obs) resignKeyObserver = nil } panel?.orderOut(nil) // Capture ended → the sensor stops seeing touches; clear any // stale "finger down" so a held bend doesn't strand. onTrackpadTouchActiveChanged?(false) onCaptureEnd?(reason) }
private func buildPanel() { // 1×1 px, off-screen, fully transparent. Borderless NSWindow / // NSPanel return `false` from `canBecomeKey` by default — // makeKeyAndOrderFront silently fails and keyDown events never // route to our local monitor. The KeyCapturePanel subclass below // overrides that so the panel actually becomes key. let p = KeyCapturePanel( contentRect: NSRect(x: -1000, y: -1000, width: 1, height: 1), styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: false ) p.isOpaque = false p.backgroundColor = .clear p.alphaValue = 0 p.hasShadow = false p.level = .statusBar p.hidesOnDeactivate = false p.canHide = false p.acceptsMouseMovedEvents = false // Indirect-touch sensor lives as the panel's content view so it // sits in the key window's responder chain while the user plays. let sensor = TouchSensorView(frame: NSRect(x: 0, y: 0, width: 1, height: 1)) sensor.onActiveChanged = { [weak self] active in self?.onTrackpadTouchActiveChanged?(active) } sensor.onFrame = { [weak self] contacts, timestamp, callbackTime in self?.onTrackpadFrame?(contacts, timestamp, callbackTime) } p.contentView = sensor // Rob ⌘/⌥/⌃+letter combos from the system. The local keyDown monitor // (above) doesn't reliably see modifier key-equivalents, and the // morph path only fires once a bare note is already held. This makes // a SIMULTANEOUS ⌘+c (major) / ⌥+c (minor) chord consistent — and, // for mapped note keys, deliberately shadows Cut/Copy/Paste/Undo/Save // while quiet-focus is armed (returns true → the system shortcut // never fires; ⌘+note plays the chord, ⌘ never reaches the menu). // Non-note combos (⌘-Tab, ⌘-Q) fall through to `super` and behave // normally. p.keyEquivalentHandler = { [weak self] event in guard let self = self else { return false } let flags = event.modifierFlags guard flags.contains(.command) || flags.contains(.control) || flags.contains(.option) else { return false } return self.onKey?(event.keyCode, true, event.isARepeat, flags) ?? false } panel = p }
deinit { disarm(reason: .cancelled) }}
/// Invisible view that reports whether any finger is currently resting/// on the trackpad. Indirect touches (the Magic Trackpad / built-in/// trackpad) only reach a view that opts in via `allowedTouchTypes`/// and is in the key window's responder chain; `wantsRestingTouches`/// keeps a motionless finger counted instead of dropping it the moment/// it stops moving (the exact case that was snapping the bend back).final class TouchSensorView: NSView { var onActiveChanged: ((Bool) -> Void)? /// Primary finger's absolute trackpad position (0…1 each axis, origin /// bottom-left), nil when no finger touches — the clean signal for a /// flat-mapped XY pad (no pointer acceleration). var onTouchXY: ((CGPoint?) -> Void)? /// Complete public NSTouch contact frame, using stable identities so the /// shared transition engine can distinguish held, new, and lifted fingers. var onFrame: (([TrackpadContact], Double, Double) -> Void)? private var lastActive = false private var loggedCount = 0 private var loggedFrameCount = 0
override init(frame frameRect: NSRect) { super.init(frame: frameRect) acceptsTouchEvents = true allowedTouchTypes = [.indirect] wantsRestingTouches = true }
required init?(coder: NSCoder) { super.init(coder: coder) acceptsTouchEvents = true allowedTouchTypes = [.indirect] wantsRestingTouches = true }
override var acceptsFirstResponder: Bool { true }
// Pure touch listener — never intercept the mouse. Returning nil // from hitTest keeps clicks/drag flowing through to whatever // control sits underneath (the popover's keys, buttons, grid), // while indirect (trackpad) touches still arrive via the key // window's responder chain because the view is first responder. override func hitTest(_ point: NSPoint) -> NSView? { nil }
override func touchesBegan(with event: NSEvent) { recompute(event) } override func touchesMoved(with event: NSEvent) { recompute(event) } override func touchesEnded(with event: NSEvent) { recompute(event) } override func touchesCancelled(with event: NSEvent) { recompute(event) }
private func recompute(_ event: NSEvent) { let touches = event.touches(matching: .touching, in: self) let beganIdentities = Set(event.touches(matching: .began, in: self).map { ObjectIdentifier($0.identity as AnyObject) }) let contacts = touches.filter { $0.type == .indirect }.map { touch in let identity = ObjectIdentifier(touch.identity as AnyObject) return TrackpadContact( identifier: Int32(truncatingIfNeeded: identity.hashValue), point: touch.normalizedPosition, state: beganIdentities.contains(identity) ? 3 : 4 ) } if loggedFrameCount < 24 { loggedFrameCount += 1 NSLog("MenuBand NSTouch frame: event=%@ contacts=%d firstResponder=%@ key=%@", String(describing: event.type), contacts.count, (window?.firstResponder === self) ? "yes" : "no", window?.isKeyWindow == true ? "yes" : "no") } onFrame?(contacts, event.timestamp, CACurrentMediaTime()) let active = !touches.isEmpty // [prototype] Report + log the primary finger's absolute position so we // can confirm whether indirect-touch delivery actually reaches this view // during the bend (the bend path warns it was historically unreliable). if let t = touches.first(where: { $0.type == .indirect }) ?? touches.first { let p = t.normalizedPosition onTouchXY?(p) if loggedCount < 60 { loggedCount += 1 NSLog("MenuBand TouchXY: x=%.3f y=%.3f fingers=%d firstResponder=%@", p.x, p.y, touches.count, (window?.firstResponder === self) ? "self" : "OTHER") } } else { onTouchXY?(nil) } guard active != lastActive else { return } lastActive = active onActiveChanged?(active) }}
/// `NSPanel` subclass that overrides `canBecomeKey` to return true. Without/// this, borderless panels silently refuse to become key window — events/// route nowhere and the system beep fires when we expected note playback.private final class KeyCapturePanel: NSPanel { override var canBecomeKey: Bool { true } // canBecomeMain stays default (false). We don't want to look like the // primary window — just receive keyboard events.
/// Routes ⌘/⌥+letter key-equivalents to the note path. Returns true to /// consume (note key → chord, shadowing the system command); false lets /// the combo pass through unchanged. See `buildPanel`. var keyEquivalentHandler: ((NSEvent) -> Bool)? override func performKeyEquivalent(with event: NSEvent) -> Bool { if let handler = keyEquivalentHandler, handler(event) { return true } return super.performKeyEquivalent(with: event) }}