diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index a9faa94a..345d4271 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -78,6 +78,32 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent pasteboard.setString(payload, forType: .string) } +#if DEBUG + @objc func openDebugScrollbackTab(_ sender: Any?) { + guard let tabManager else { return } + let tab = tabManager.addTab() + let config = GhosttyConfig.load() + let lineCount = min(max(config.scrollbackLimit * 2, 2000), 60000) + let command = "for i in {1..\(lineCount)}; do printf \"scrollback %06d\\n\" $i; done\n" + sendTextWhenReady(command, to: tab) + } + + private func sendTextWhenReady(_ text: String, to tab: Tab, attempt: Int = 0) { + let maxAttempts = 60 + if let surface = tab.focusedSurface, surface.surface != nil { + surface.sendText(text) + return + } + guard attempt < maxAttempts else { + NSLog("Debug scrollback: surface not ready after \(maxAttempts) attempts") + return + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in + self?.sendTextWhenReady(text, to: tab, attempt: attempt + 1) + } + } +#endif + func attachUpdateAccessory(to window: NSWindow) { titlebarAccessoryController.start() titlebarAccessoryController.attach(to: window) diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 90832a4f..b994eae5 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -703,6 +703,15 @@ class TerminalSurface: Identifiable { return ghostty_surface_needs_confirm_quit(surface) } + func sendText(_ text: String) { + guard let surface = surface else { return } + guard let data = text.data(using: .utf8), !data.isEmpty else { return } + data.withUnsafeBytes { rawBuffer in + guard let baseAddress = rawBuffer.baseAddress?.assumingMemoryBound(to: CChar.self) else { return } + ghostty_surface_text(surface, baseAddress, UInt(rawBuffer.count)) + } + } + deinit { if ownsDisplayLink { GhosttyApp.shared.releaseDisplayLink() @@ -1480,14 +1489,21 @@ private final class GhosttyScrollView: NSScrollView { weak var surfaceView: GhosttyNSView? override func scrollWheel(with event: NSEvent) { - if let surfaceView { - if window?.firstResponder !== surfaceView { - window?.makeFirstResponder(surfaceView) - } - surfaceView.scrollWheel(with: event) + guard let surfaceView else { + super.scrollWheel(with: event) return } - super.scrollWheel(with: event) + + if window?.firstResponder !== surfaceView { + window?.makeFirstResponder(surfaceView) + } + + if let surface = surfaceView.terminalSurface?.surface, + ghostty_surface_mouse_captured(surface) { + surfaceView.scrollWheel(with: event) + } else { + super.scrollWheel(with: event) + } } } diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 11ee642f..8f9d4466 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -248,7 +248,8 @@ class TabManager: ObservableObject { } } - func addTab() { + @discardableResult + func addTab() -> Tab { let newTab = Tab(title: "Terminal \(tabs.count + 1)") tabs.append(newTab) selectedTabId = newTab.id @@ -257,6 +258,7 @@ class TabManager: ObservableObject { object: nil, userInfo: [GhosttyNotificationKey.tabId: newTab.id] ) + return newTab } func moveTabToTop(_ tabId: UUID) { diff --git a/Sources/cmuxApp.swift b/Sources/cmuxApp.swift index 91e45f9e..fa386d28 100644 --- a/Sources/cmuxApp.swift +++ b/Sources/cmuxApp.swift @@ -56,6 +56,14 @@ struct cmuxApp: App { } } +#if DEBUG + CommandMenu("Debug") { + Button("New Tab With Large Scrollback") { + appDelegate.openDebugScrollbackTab(nil) + } + } +#endif + // New tab commands CommandGroup(replacing: .newItem) { Button("New Tab") {