mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 21:40:29 +00:00
macOS: fix crash when adding tab from tab overview
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// C imports here are exposed to Swift.
|
||||
|
||||
#import "VibrantLayer.h"
|
||||
#import "ObjCExceptionCatcher.h"
|
||||
|
||||
@@ -411,14 +411,14 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
// If we already have a tab group and we want the new tab to open at the end,
|
||||
// then we use the last window in the tab group as the parent.
|
||||
if let last = parent.tabGroup?.windows.last {
|
||||
last.addTabbedWindow(window, ordered: .above)
|
||||
last.addTabbedWindowSafely(window, ordered: .above)
|
||||
} else {
|
||||
fallthrough
|
||||
}
|
||||
|
||||
case "current": fallthrough
|
||||
default:
|
||||
parent.addTabbedWindow(window, ordered: .above)
|
||||
parent.addTabbedWindowSafely(window, ordered: .above)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,7 +863,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
controller.showWindow(nil)
|
||||
if let firstWindow = firstController.window,
|
||||
let newWindow = controller.window {
|
||||
firstWindow.addTabbedWindow(newWindow, ordered: .above)
|
||||
firstWindow.addTabbedWindowSafely(newWindow, ordered: .above)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,9 +952,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
if tabIndex < tabGroup.windows.count {
|
||||
// Find the window that is currently at that index
|
||||
let currentWindow = tabGroup.windows[tabIndex]
|
||||
currentWindow.addTabbedWindow(window, ordered: .below)
|
||||
currentWindow.addTabbedWindowSafely(window, ordered: .below)
|
||||
} else {
|
||||
tabGroup.windows.last?.addTabbedWindow(window, ordered: .above)
|
||||
tabGroup.windows.last?.addTabbedWindowSafely(window, ordered: .above)
|
||||
}
|
||||
|
||||
// Make it the key window
|
||||
@@ -1386,7 +1386,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
if #available(macOS 26, *) {
|
||||
if window is TitlebarTabsTahoeTerminalWindow {
|
||||
tabGroup.removeWindow(selectedWindow)
|
||||
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
targetWindow.addTabbedWindowSafely(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
DispatchQueue.main.async {
|
||||
selectedWindow.makeKey()
|
||||
}
|
||||
@@ -1401,7 +1401,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
|
||||
// Remove and re-add the window in the correct position
|
||||
tabGroup.removeWindow(selectedWindow)
|
||||
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
targetWindow.addTabbedWindowSafely(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
|
||||
// Ensure our window remains selected
|
||||
selectedWindow.makeKey()
|
||||
|
||||
@@ -39,6 +39,24 @@ extension NSWindow {
|
||||
guard let firstWindow = tabGroup?.windows.first else { return true }
|
||||
return firstWindow === self
|
||||
}
|
||||
|
||||
/// Wraps `addTabbedWindow` with an Objective-C exception catcher because AppKit can
|
||||
/// occasionally throw NSExceptions in visual tab picker flows.
|
||||
@discardableResult
|
||||
func addTabbedWindowSafely(
|
||||
_ child: NSWindow,
|
||||
ordered: NSWindow.OrderingMode
|
||||
) -> Bool {
|
||||
var error: NSError?
|
||||
let success = GhosttyAddTabbedWindowSafely(self, child, ordered.rawValue, &error)
|
||||
|
||||
if let error {
|
||||
let reason = error.localizedDescription
|
||||
Ghostty.logger.error("addTabbedWindow failed: \(reason)")
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
/// Native tabbing private API usage. :(
|
||||
|
||||
@@ -296,13 +296,13 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
if tabIndex == 0 {
|
||||
// We were previously the first tab. Add it before ("below")
|
||||
// the first window in the tab group currently.
|
||||
tabGroup.windows.first!.addTabbedWindow(window, ordered: .below)
|
||||
tabGroup.windows.first!.addTabbedWindowSafely(window, ordered: .below)
|
||||
} else if tabIndex <= tabGroup.windows.count {
|
||||
// We were somewhere in the middle
|
||||
tabGroup.windows[tabIndex - 1].addTabbedWindow(window, ordered: .above)
|
||||
tabGroup.windows[tabIndex - 1].addTabbedWindowSafely(window, ordered: .above)
|
||||
} else {
|
||||
// We were at the end
|
||||
tabGroup.windows.last!.addTabbedWindow(window, ordered: .below)
|
||||
tabGroup.windows.last!.addTabbedWindowSafely(window, ordered: .below)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
macos/Sources/Helpers/ObjCExceptionCatcher.h
Normal file
9
macos/Sources/Helpers/ObjCExceptionCatcher.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/// Minimal Objective-C exception bridge for AppKit tabbing APIs.
|
||||
FOUNDATION_EXPORT BOOL GhosttyAddTabbedWindowSafely(
|
||||
id parent,
|
||||
id child,
|
||||
NSInteger ordered,
|
||||
NSError * _Nullable * _Nullable error
|
||||
);
|
||||
41
macos/Sources/Helpers/ObjCExceptionCatcher.m
Normal file
41
macos/Sources/Helpers/ObjCExceptionCatcher.m
Normal file
@@ -0,0 +1,41 @@
|
||||
#import "ObjCExceptionCatcher.h"
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
BOOL GhosttyAddTabbedWindowSafely(
|
||||
id parent,
|
||||
id child,
|
||||
NSInteger ordered,
|
||||
NSError * _Nullable * _Nullable error
|
||||
) {
|
||||
#if TARGET_OS_OSX
|
||||
@try {
|
||||
[((NSWindow *)parent) addTabbedWindow:(NSWindow *)child ordered:(NSWindowOrderingMode)ordered];
|
||||
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,
|
||||
}];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
#else
|
||||
if (error != NULL) {
|
||||
*error = [NSError errorWithDomain:@"Ghostty.ObjCException"
|
||||
code:2
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"GhosttyAddTabbedWindowSafely is unavailable on this platform.",
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user