From df82dbde853b82cd6cd86decae5d21d5e809bf6c Mon Sep 17 00:00:00 2001 From: paravozz Date: Fri, 24 Apr 2026 19:47:10 +0100 Subject: [PATCH] fix(macos): don't panic on unexpected event types in keyboard callbacks `KeyboardState::process_native_event` is called by the macro-generated `keyDown:` / `keyUp:` / `flagsChanged:` selectors in `view.rs`. The state-matching on `event.eventType()` only covered NSKeyDown, NSKeyUp, and NSFlagsChanged, falling through to `_ => unreachable!()` for anything else. AppKit occasionally dispatches non-key events into those selectors in the wild (observed: `NSAppKitDefined` and `NSSystemDefined` events during Cmd-Tab, input-source switches, modifier-repeat sequences, and sleep/wake transitions). Because the selectors are generated as `extern "C"`, hitting `unreachable!()` crosses the FFI boundary as `panic_cannot_unwind` and aborts the hosting process. For plug-ins, that crashes the DAW. Evidence (Bitform 1.0.4 panic log, Ableton Live 12.3.6 on macOS, 2026-04-24): info: panicked at library/core/src/panicking.rs:225:5: panic in a function that cannot unwind backtrace: ... 12: core::panicking::panic_nounwind 13: core::panicking::panic_cannot_unwind 14: flagsChanged at src/macos/view.rs:81:9 Replace the unreachable with `return None` so the handler silently drops events it can't interpret. There's nothing meaningful to synthesize from an event that isn't a key or flags-changed, and panicking from an `extern "C"` callback is never the right move. The `NSFlagsChanged` arm already has a `return None` branch for the non-modifier-key subcase (line 311), so the return-None pattern is consistent with existing handling of uninteresting events. --- src/macos/keyboard.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs index 18f3bfe5..2b161b38 100644 --- a/src/macos/keyboard.rs +++ b/src/macos/keyboard.rs @@ -311,7 +311,18 @@ impl KeyboardState { return None; } } - _ => unreachable!(), + // AppKit occasionally dispatches non-key events into + // `keyDown:` / `keyUp:` / `flagsChanged:` selectors + // (e.g. `NSAppKitDefined`, `NSSystemDefined`, or sync + // events around Cmd-Tab and input-source switches). + // Panicking here crosses the `extern "C"` boundary as + // `panic_cannot_unwind` and aborts the hosting process, + // which has been observed crashing plug-in hosts when + // the focused view is a baseview `NSView`. Drop the + // event silently instead; the handler has nothing + // meaningful to synthesize from an event that isn't + // a key or flags-changed. + _ => return None, }; let is_composing = false; let repeat: bool = event_type == NSEventType::NSKeyDown && msg_send![event, isARepeat];