mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-25 00:21:46 +00:00
macos: guard fullscreen tab presentation with objc catcher (#13636)
#13611 Route new-tab window presentation through an Objective-C exception catcher. AppKit can raise an NSInternalInconsistencyException while selecting a new tab in native fullscreen. Catch the presentation exception, report it through the existing error logging path, and leave Ghostty running when AppKit’s fullscreen window stack is inconsistent. This was pretty hard to reproduce but I was able to reproduce it about 1/3rd of the time via AppleScript automation...
This commit is contained in:
@@ -480,8 +480,10 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
Self.applyCascade(to: window, hasFixedPos: hasFixedPos)
|
||||
}
|
||||
|
||||
controller.showWindow(self)
|
||||
window.makeKeyAndOrderFront(self)
|
||||
// showWindow makes regular windows key and ordered front. AppKit can
|
||||
// throw while selecting a tab if its fullscreen stack is inconsistent,
|
||||
// so this must cross the Objective-C exception bridge.
|
||||
controller.showWindowSafely(self)
|
||||
|
||||
// We also activate our app so that it becomes front. This may be
|
||||
// necessary for the dock menu.
|
||||
|
||||
@@ -58,6 +58,21 @@ extension NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
extension NSWindowController {
|
||||
/// Wraps `showWindow` with an Objective-C exception catcher because selecting
|
||||
/// a tab can raise an AppKit fullscreen window-stack exception.
|
||||
@discardableResult
|
||||
func showWindowSafely(_ sender: Any?) -> Bool {
|
||||
var error: NSError?
|
||||
let success = GhosttyShowWindowSafely(self, sender, &error)
|
||||
if let error {
|
||||
Ghostty.logger.error("showWindow failed: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
/// Native tabbing private API usage. :(
|
||||
extension NSWindow {
|
||||
var titlebarView: NSView? {
|
||||
|
||||
@@ -11,3 +11,10 @@ FOUNDATION_EXPORT BOOL GhosttyAddTabbedWindowSafely(
|
||||
NSInteger ordered,
|
||||
NSError * _Nullable * _Nullable error
|
||||
);
|
||||
|
||||
/// NSWindowController.showWindow wrapper
|
||||
FOUNDATION_EXPORT BOOL GhosttyShowWindowSafely(
|
||||
id _Nonnull controller,
|
||||
id _Nullable sender,
|
||||
NSError * _Nullable * _Nullable error
|
||||
);
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
static NSError *GhosttyErrorFromException(NSException *exception, NSInteger code) {
|
||||
NSString *reason = exception.reason ?: @"Unknown Objective-C exception";
|
||||
return [NSError errorWithDomain:@"Ghostty.ObjCException"
|
||||
code:code
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: reason,
|
||||
@"exception_name": exception.name,
|
||||
}];
|
||||
}
|
||||
|
||||
BOOL GhosttyAddTabbedWindowSafely(
|
||||
id parent,
|
||||
id child,
|
||||
@@ -18,13 +28,27 @@ BOOL GhosttyAddTabbedWindowSafely(
|
||||
return YES;
|
||||
} @catch (NSException *exception) {
|
||||
if (error != NULL) {
|
||||
NSString *reason = exception.reason ?: @"Unknown Objective-C exception";
|
||||
*error = [NSError errorWithDomain:@"Ghostty.ObjCException"
|
||||
code:1
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: reason,
|
||||
@"exception_name": exception.name,
|
||||
}];
|
||||
*error = GhosttyErrorFromException(exception, 1);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL GhosttyShowWindowSafely(
|
||||
id controller,
|
||||
id _Nullable sender,
|
||||
NSError * _Nullable * _Nullable error
|
||||
) {
|
||||
// Selecting a newly added tab can throw from NSWindowStackController when
|
||||
// the tab group contains windows in inconsistent native fullscreen states.
|
||||
// Catch the exception here so the AppKit assertion doesn't abort the app.
|
||||
@try {
|
||||
[((NSWindowController *)controller) showWindow:sender];
|
||||
return YES;
|
||||
} @catch (NSException *exception) {
|
||||
if (error != NULL) {
|
||||
*error = GhosttyErrorFromException(exception, 2);
|
||||
}
|
||||
|
||||
return NO;
|
||||
|
||||
Reference in New Issue
Block a user