Skip to content

Commit 65a6448

Browse files
lklimekclaude
andcommitted
fix(macos): disable TouchBar to prevent KVO crash on shutdown
Fixes a SIGILL crash on macOS during app shutdown caused by AppKit's TouchBar KVO observers. macOS registers observers on the responder chain to track Touch Bar availability; during eframe/winit teardown, the observed objects are deallocated before the observers are cleaned up, triggering an exception in _NSTouchBarFinderObservation invalidate. Calling [NSApplication setAutomaticCustomizeTouchBarMenuItemEnabled: NO] before the app window is created prevents macOS from installing those observers in the first place. The app does not use the Touch Bar. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5aaa3c9 commit 65a6448

3 files changed

Lines changed: 45 additions & 0 deletions

File tree

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ async fn start(app_data_dir: &std::path::Path) -> Result<(), eframe::Error> {
6161
..Default::default()
6262
};
6363

64+
crate::platform::disable_touchbar();
65+
6466
eframe::run_native(
6567
&format!("Dash Evo Tool v{}", VERSION),
6668
native_options,

src/platform/macos.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@ use objc2::MainThreadMarker;
22
use objc2::msg_send;
33
use objc2::runtime::{AnyClass, AnyObject};
44

5+
/// Disables automatic Touch Bar customization to prevent a KVO crash on
6+
/// shutdown. macOS registers KVO observers on the responder chain to track
7+
/// Touch Bar availability; during eframe/winit teardown the observed objects
8+
/// can be deallocated before the observers are removed, causing a SIGILL in
9+
/// `_NSTouchBarFinderObservation invalidate`.
10+
///
11+
/// Calling `[NSApplication setAutomaticCustomizeTouchBarMenuItemEnabled: NO]`
12+
/// before the app window is created prevents macOS from installing those
13+
/// observers in the first place.
14+
///
15+
/// # Thread safety
16+
///
17+
/// This function calls AppKit APIs that must run on the main thread.
18+
/// The `MainThreadMarker` parameter enforces this at compile time.
19+
pub fn disable_touchbar(_mtm: MainThreadMarker) {
20+
// SAFETY: `setAutomaticCustomizeTouchBarMenuItemEnabled:` is a class
21+
// method on NSApplication, stable since macOS 10.12.1. Passing a BOOL
22+
// `false` is ABI-compatible with the Objective-C declaration.
23+
unsafe {
24+
let Some(cls) = AnyClass::get(c"NSApplication") else {
25+
tracing::debug!("NSApplication class not found, skipping TouchBar disable");
26+
return;
27+
};
28+
let _: () = msg_send![cls, setAutomaticCustomizeTouchBarMenuItemEnabled: false];
29+
}
30+
tracing::debug!("Disabled automatic Touch Bar customization");
31+
}
32+
533
/// Queries `accessibilityChildren` on the key window's content view to force
634
/// macOS to call the AccessKit adapter's subclassed method, which transitions
735
/// the adapter from `Inactive` to `Active`. Without this, tools like Peekaboo

src/platform/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,18 @@ pub fn force_accessibility_activation() -> bool {
2929
true
3030
}
3131
}
32+
33+
/// Disable automatic Touch Bar customization to prevent a KVO crash during
34+
/// shutdown on macOS. No-op on other platforms.
35+
///
36+
/// Must be called before `eframe::run_native()`, from the main thread.
37+
pub fn disable_touchbar() {
38+
#[cfg(target_os = "macos")]
39+
{
40+
let Some(mtm) = objc2::MainThreadMarker::new() else {
41+
tracing::error!("disable_touchbar called from non-main thread");
42+
return;
43+
};
44+
macos::disable_touchbar(mtm);
45+
}
46+
}

0 commit comments

Comments
 (0)