From d2dae7a6964b0b7eeba2eeb4b4a786a8c5fff1b7 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Tue, 15 Aug 2023 14:21:18 +0200 Subject: [PATCH 1/8] macOS: inherit font size when creating new tab This is one part of #281. --- include/ghostty.h | 4 ++ macos/Sources/AppDelegate.swift | 38 ++++++++++++++++++- .../Features/Primary Window/PrimaryView.swift | 5 ++- .../Primary Window/PrimaryWindow.swift | 6 ++- .../PrimaryWindowController.swift | 1 + .../Primary Window/PrimaryWindowManager.swift | 8 ++-- macos/Sources/Ghostty/AppState.swift | 18 ++++++++- macos/Sources/Ghostty/Ghostty.SplitView.swift | 15 +++++--- macos/Sources/Ghostty/Package.swift | 4 ++ macos/Sources/Ghostty/SurfaceView.swift | 7 ++-- src/apprt/embedded.zig | 31 +++++++++++++++ src/input/Binding.zig | 1 + 12 files changed, 119 insertions(+), 19 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index af3aa1fa7..dc2f304a0 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -233,6 +233,7 @@ typedef enum { typedef enum { GHOSTTY_BINDING_COPY_TO_CLIPBOARD, GHOSTTY_BINDING_PASTE_FROM_CLIPBOARD, + GHOSTTY_BINDING_NEW_TAB, } ghostty_binding_action_e; // Fully defined types. This MUST be kept in sync with equivalent Zig @@ -242,6 +243,7 @@ typedef struct { void *userdata; void *nsview; double scale_factor; + uint8_t font_size; } ghostty_surface_config_s; typedef void (*ghostty_runtime_wakeup_cb)(void *); @@ -254,6 +256,7 @@ typedef void (*ghostty_runtime_close_surface_cb)(void *, bool); typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e); typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t); typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, bool); +typedef void (*ghostty_runtime_new_tab_cb)(void *, uint8_t); typedef struct { void *userdata; @@ -268,6 +271,7 @@ typedef struct { ghostty_runtime_focus_split_cb focus_split_cb; ghostty_runtime_goto_tab_cb goto_tab_cb; ghostty_runtime_toggle_fullscreen_cb toggle_fullscreen_cb; + ghostty_runtime_new_tab_cb new_tab_cb; } ghostty_runtime_config_s; //------------------------------------------------------------------- diff --git a/macos/Sources/AppDelegate.swift b/macos/Sources/AppDelegate.swift index 066401056..efe494906 100644 --- a/macos/Sources/AppDelegate.swift +++ b/macos/Sources/AppDelegate.swift @@ -1,6 +1,7 @@ import AppKit import OSLog import GhosttyKit +import SwiftUI @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { @@ -24,6 +25,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { super.init() windowManager = PrimaryWindowManager(ghostty: self.ghostty) + + // Register self as observer for the NewTab notification that + // is triggered via callback from Zig code. + NotificationCenter.default.addObserver( + self, + selector: #selector(onNewTab), + name: Ghostty.Notification.ghosttyNewTab, + object: nil) + } + + deinit { + // Clean up the observer. + NotificationCenter.default.removeObserver( + self, + name: Ghostty.Notification.ghosttyNewTab, + object: nil) } func applicationDidFinishLaunching(_ notification: Notification) { @@ -81,13 +98,30 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { } @IBAction func newTab(_ sender: Any?) { - if let existingWindow = windowManager.mainWindow { - windowManager.addNewTab(to: existingWindow) + if windowManager.mainWindow != nil { + guard let surface = focusedSurface() else { return } + ghostty.newTab(surface: surface) } else { windowManager.addNewWindow() } } + @objc private func onNewTab(notification: SwiftUI.Notification) { + guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } + guard let window = surfaceView.window else { return } + + let fontSizeAny = notification.userInfo?[Ghostty.Notification.NewTabKey] + let fontSize = fontSizeAny as? UInt8 + + if fontSize != nil { + // Add the new tab to the window with the given font size. + windowManager.addNewTab(to: window, withFontSize: fontSize) + } else { + // No font size specified, just add new tab. + windowManager.addNewTab(to: window) + } + } + @IBAction func closeWindow(_ sender: Any) { guard let currentWindow = NSApp.keyWindow else { return } currentWindow.close() diff --git a/macos/Sources/Features/Primary Window/PrimaryView.swift b/macos/Sources/Features/Primary Window/PrimaryView.swift index 221c50078..40b0f464c 100644 --- a/macos/Sources/Features/Primary Window/PrimaryView.swift +++ b/macos/Sources/Features/Primary Window/PrimaryView.swift @@ -11,6 +11,9 @@ struct PrimaryView: View { // We need this to report back up the app controller which surface in this view is focused. let focusedSurfaceWrapper: FocusedSurfaceWrapper + // TODO: Document this + let fontSize: UInt8? + // We need access to our window to know if we're the key window to determine // if we show the quit confirmation or not. @State private var window: NSWindow? @@ -71,7 +74,7 @@ struct PrimaryView: View { self.appDelegate.confirmQuit = $0 }) - Ghostty.TerminalSplit(onClose: Self.closeWindow) + Ghostty.TerminalSplit(onClose: Self.closeWindow, fontSize: self.fontSize) .ghosttyApp(ghostty.app!) .background(WindowAccessor(window: $window)) .onReceive(gotoTab) { onGotoTab(notification: $0) } diff --git a/macos/Sources/Features/Primary Window/PrimaryWindow.swift b/macos/Sources/Features/Primary Window/PrimaryWindow.swift index 85ca49601..1762f29f7 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindow.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindow.swift @@ -16,7 +16,7 @@ class FocusedSurfaceWrapper { class PrimaryWindow: NSWindow { var focusedSurfaceWrapper: FocusedSurfaceWrapper = FocusedSurfaceWrapper() - static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate) -> PrimaryWindow { + static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate, fontSize: UInt8? = nil) -> PrimaryWindow { let window = PrimaryWindow( contentRect: NSRect(x: 0, y: 0, width: 800, height: 600), styleMask: [.titled, .closable, .miniaturizable, .resizable], @@ -27,7 +27,9 @@ class PrimaryWindow: NSWindow { window.contentView = NSHostingView(rootView: PrimaryView( ghostty: ghostty, appDelegate: appDelegate, - focusedSurfaceWrapper: window.focusedSurfaceWrapper)) + focusedSurfaceWrapper: window.focusedSurfaceWrapper, + fontSize: fontSize + )) // We do want to cascade when new windows are created window.windowController?.shouldCascadeWindows = true diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift index 180fee960..6d860e641 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift @@ -14,6 +14,7 @@ class PrimaryWindowController: NSWindowController { override func newWindowForTab(_ sender: Any?) { guard let window = self.window else { preconditionFailure("Expected window to be loaded") } guard let manager = self.windowManager else { return } + // TODO: We need to call to Zig code here so we can get the surface manager.addNewTab(to: window) } } diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift index 16b241070..14f93f4e9 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift @@ -61,16 +61,16 @@ class PrimaryWindowManager { newWindow.makeKeyAndOrderFront(nil) } - func addNewTab(to window: NSWindow) { - guard let controller = createWindowController() else { return } + func addNewTab(to window: NSWindow, withFontSize fontSize: UInt8? = nil) { + guard let controller = createWindowController(withFontSize: fontSize) else { return } guard let newWindow = addManagedWindow(windowController: controller)?.window else { return } window.addTabbedWindow(newWindow, ordered: .above) newWindow.makeKeyAndOrderFront(nil) } - private func createWindowController() -> PrimaryWindowController? { + private func createWindowController(withFontSize fontSize: UInt8? = nil) -> PrimaryWindowController? { guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return nil } - let window = PrimaryWindow.create(ghostty: ghostty, appDelegate: appDelegate) + let window = PrimaryWindow.create(ghostty: ghostty, appDelegate: appDelegate, fontSize: fontSize) Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint) let controller = PrimaryWindowController(window: window) controller.windowManager = self diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 68076c224..76f422225 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -64,7 +64,8 @@ extension Ghostty { close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) }, focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) }, goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) }, - toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) } + toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) }, + new_tab_cb: { userdata, fontSize in AppState.newTab(userdata, fontSize: fontSize) } ) // Create the ghostty app. @@ -137,6 +138,10 @@ extension Ghostty { ghostty_surface_request_close(surface) } + func newTab(surface: ghostty_surface_t) { + ghostty_surface_binding_action(surface, GHOSTTY_BINDING_NEW_TAB, nil) + } + func split(surface: ghostty_surface_t, direction: ghostty_split_direction_e) { ghostty_surface_split(surface, direction) } @@ -258,6 +263,17 @@ extension Ghostty { ) } + static func newTab(_ userdata: UnsafeMutableRawPointer?, fontSize: UInt8) { + guard let surface = self.surfaceUserdata(from: userdata) else { return } + NotificationCenter.default.post( + name: Notification.ghosttyNewTab, + object: surface, + userInfo: [ + Notification.NewTabKey: fontSize, + ] + ) + } + /// Returns the GhosttyState from the given userdata value. static private func appState(fromSurface userdata: UnsafeMutableRawPointer?) -> AppState? { let surfaceView = Unmanaged.fromOpaque(userdata!).takeUnretainedValue() diff --git a/macos/Sources/Ghostty/Ghostty.SplitView.swift b/macos/Sources/Ghostty/Ghostty.SplitView.swift index cc0f72487..9142dd037 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitView.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitView.swift @@ -8,10 +8,11 @@ extension Ghostty { struct TerminalSplit: View { @Environment(\.ghosttyApp) private var app let onClose: (() -> Void)? + let fontSize: UInt8? var body: some View { if let app = app { - TerminalSplitRoot(app: app, onClose: onClose) + TerminalSplitRoot(app: app, onClose: onClose, fontSize: fontSize) } } } @@ -67,9 +68,9 @@ extension Ghostty { @Published var surface: SurfaceView /// Initialize a new leaf which creates a new terminal surface. - init(_ app: ghostty_app_t) { + init(_ app: ghostty_app_t, _ fontSize: UInt8?) { self.app = app - self.surface = SurfaceView(app) + self.surface = SurfaceView(app, fontSize) } } @@ -87,7 +88,7 @@ extension Ghostty { // Initially, both topLeft and bottomRight are in the "nosplit" // state since this is a new split. self.topLeft = .noSplit(from) - self.bottomRight = .noSplit(.init(app)) + self.bottomRight = .noSplit(.init(app, nil)) } } @@ -141,12 +142,14 @@ extension Ghostty { @State private var node: SplitNode @State private var requestClose: Bool = false let onClose: (() -> Void)? + let fontSize: UInt8? @FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String? - init(app: ghostty_app_t, onClose: (() ->Void)? = nil) { + init(app: ghostty_app_t, onClose: (() ->Void)? = nil, fontSize: UInt8? = nil) { self.onClose = onClose - _node = State(wrappedValue: SplitNode.noSplit(.init(app))) + self.fontSize = fontSize + _node = State(wrappedValue: SplitNode.noSplit(.init(app, fontSize))) } var body: some View { diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index d4b3a9fa9..25a10b8ce 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -78,6 +78,10 @@ extension Ghostty.Notification { /// Goto tab. Has tab index in the userinfo. static let ghosttyGotoTab = Notification.Name("com.mitchellh.ghostty.gotoTab") static let GotoTabKey = ghosttyGotoTab.rawValue + + /// New tab. Has font size of currently focused surface in the userinfo. + static let ghosttyNewTab = Notification.Name("com.mitchellh.ghostty.newTab") + static let NewTabKey = ghosttyNewTab.rawValue /// Toggle fullscreen of current window static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen") diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index cad0fdbd5..1960edd60 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -24,7 +24,7 @@ extension Ghostty { @StateObject private var surfaceView: SurfaceView init(_ app: ghostty_app_t, @ViewBuilder content: @escaping ((SurfaceView) -> Content)) { - _surfaceView = StateObject(wrappedValue: SurfaceView(app)) + _surfaceView = StateObject(wrappedValue: SurfaceView(app, nil)) self.content = content } @@ -137,7 +137,7 @@ extension Ghostty { // so we'll use that to tell ghostty to refresh. override var wantsUpdateLayer: Bool { return true } - init(_ app: ghostty_app_t) { + init(_ app: ghostty_app_t, _ fontSize: UInt8?) { self.markedText = NSMutableAttributedString() // Initialize with some default frame size. The important thing is that this @@ -149,7 +149,8 @@ extension Ghostty { var surface_cfg = ghostty_surface_config_s( userdata: Unmanaged.passUnretained(self).toOpaque(), nsview: Unmanaged.passUnretained(self).toOpaque(), - scale_factor: NSScreen.main!.backingScaleFactor) + scale_factor: NSScreen.main!.backingScaleFactor, + font_size: fontSize ?? 0) guard let surface = ghostty_surface_new(app, &surface_cfg) else { self.error = AppError.surfaceCreateError return diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 68a3c805a..5315614d9 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -70,6 +70,10 @@ pub const App = struct { /// Toggle fullscreen for current window. toggle_fullscreen: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, + + /// New tab with desired font size in points + /// TODO: u8 should be something that's nullable + new_tab: ?*const fn (SurfaceUD, u8) callconv(.C) void = null, }; core_app: *CoreApp, @@ -166,6 +170,9 @@ pub const Surface = struct { /// The scale factor of the screen. scale_factor: f64 = 1, + + /// The font size to inherit. If 0, default font size will be used. + font_size: u8 = 0, }; pub fn init(self: *Surface, app: *App, opts: Options) !void { @@ -191,6 +198,13 @@ pub const Surface = struct { var config = try apprt.surface.newConfig(app.core_app, app.config); defer config.deinit(); + // Overwrite the config for this new surface if we need to set a font + // size based on parent. + // TODO: Is this super hacky? + if (opts.font_size != 0) { + config.@"font-size" = opts.font_size; + } + // Initialize our surface right away. We're given a view that is // ready to use. try self.core_surface.init( @@ -580,6 +594,22 @@ pub const Surface = struct { func(self.opts.userdata, nonNativeFullscreen); } + pub fn newTab(self: *const Surface) !void { + const func = self.app.opts.new_tab orelse { + log.info("runtime embedder does not support new_tab", .{}); + return; + }; + + // TODO: Do we check this here? Or do we check this in embedder? + // + const font_size: u8 = font_size: { + if (!self.app.config.@"window-inherit-font-size") break :font_size 0; + break :font_size @intCast(self.core_surface.font_size.points); + }; + + func(self.opts.userdata, font_size); + } + /// The cursor position from the host directly is in screen coordinates but /// all our interface works in pixels. fn cursorPosToPixels(self: *const Surface, pos: apprt.CursorPos) !apprt.CursorPos { @@ -804,6 +834,7 @@ pub const CAPI = struct { const action: input.Binding.Action = switch (key) { .copy_to_clipboard => .{ .copy_to_clipboard = {} }, .paste_from_clipboard => .{ .paste_from_clipboard = {} }, + .new_tab => .{ .new_tab = {} }, }; ptr.core_surface.performBindingAction(action) catch |err| { diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 6431853bd..400e9695e 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -276,6 +276,7 @@ pub const Action = union(enum) { pub const Key = enum(c_int) { copy_to_clipboard, paste_from_clipboard, + new_tab, }; /// Trigger is the associated key state that can trigger an action. From 3e7c4475a1fe81b38e9457e262a162b41c9f1271 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Thu, 17 Aug 2023 20:48:16 +0200 Subject: [PATCH 2/8] macOS: move newTab code to PrimaryWindowManager --- macos/Sources/AppDelegate.swift | 40 +-------------- .../Features/Primary Window/PrimaryView.swift | 2 +- .../PrimaryWindowController.swift | 5 +- .../Primary Window/PrimaryWindowManager.swift | 49 ++++++++++++++++++- 4 files changed, 52 insertions(+), 44 deletions(-) diff --git a/macos/Sources/AppDelegate.swift b/macos/Sources/AppDelegate.swift index efe494906..fa468221d 100644 --- a/macos/Sources/AppDelegate.swift +++ b/macos/Sources/AppDelegate.swift @@ -1,7 +1,6 @@ import AppKit import OSLog import GhosttyKit -import SwiftUI @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { @@ -25,22 +24,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { super.init() windowManager = PrimaryWindowManager(ghostty: self.ghostty) - - // Register self as observer for the NewTab notification that - // is triggered via callback from Zig code. - NotificationCenter.default.addObserver( - self, - selector: #selector(onNewTab), - name: Ghostty.Notification.ghosttyNewTab, - object: nil) - } - - deinit { - // Clean up the observer. - NotificationCenter.default.removeObserver( - self, - name: Ghostty.Notification.ghosttyNewTab, - object: nil) } func applicationDidFinishLaunching(_ notification: Notification) { @@ -98,28 +81,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { } @IBAction func newTab(_ sender: Any?) { - if windowManager.mainWindow != nil { - guard let surface = focusedSurface() else { return } - ghostty.newTab(surface: surface) - } else { - windowManager.addNewWindow() - } - } - - @objc private func onNewTab(notification: SwiftUI.Notification) { - guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } - guard let window = surfaceView.window else { return } - - let fontSizeAny = notification.userInfo?[Ghostty.Notification.NewTabKey] - let fontSize = fontSizeAny as? UInt8 - - if fontSize != nil { - // Add the new tab to the window with the given font size. - windowManager.addNewTab(to: window, withFontSize: fontSize) - } else { - // No font size specified, just add new tab. - windowManager.addNewTab(to: window) - } + windowManager.newTab() } @IBAction func closeWindow(_ sender: Any) { diff --git a/macos/Sources/Features/Primary Window/PrimaryView.swift b/macos/Sources/Features/Primary Window/PrimaryView.swift index 40b0f464c..07bbec947 100644 --- a/macos/Sources/Features/Primary Window/PrimaryView.swift +++ b/macos/Sources/Features/Primary Window/PrimaryView.swift @@ -11,7 +11,7 @@ struct PrimaryView: View { // We need this to report back up the app controller which surface in this view is focused. let focusedSurfaceWrapper: FocusedSurfaceWrapper - // TODO: Document this + // If this is set, we inherit the fontSize from the parent tab or window. let fontSize: UInt8? // We need access to our window to know if we're the key window to determine diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift index 6d860e641..257a0bb6f 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift @@ -12,9 +12,8 @@ class PrimaryWindowController: NSWindowController { // This is required for the "+" button to show up in the tab bar to add a // new tab. override func newWindowForTab(_ sender: Any?) { - guard let window = self.window else { preconditionFailure("Expected window to be loaded") } + guard let window = self.window as? PrimaryWindow else { preconditionFailure("Expected window to be loaded") } guard let manager = self.windowManager else { return } - // TODO: We need to call to Zig code here so we can get the surface - manager.addNewTab(to: window) + manager.newTabForWindow(window: window) } } diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift index 14f93f4e9..8b8fb94d2 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift @@ -1,5 +1,6 @@ import Cocoa import Combine +import SwiftUI // PrimaryWindowManager manages the windows and tabs in the primary window // of the application. It keeps references to windows and cleans them up when @@ -43,6 +44,22 @@ class PrimaryWindowManager { init(ghostty: Ghostty.AppState) { self.ghostty = ghostty + + // Register self as observer for the NewTab notification that + // is triggered via callback from Zig code. + NotificationCenter.default.addObserver( + self, + selector: #selector(onNewTab), + name: Ghostty.Notification.ghosttyNewTab, + object: nil) + } + + deinit { + // Clean up the observer. + NotificationCenter.default.removeObserver( + self, + name: Ghostty.Notification.ghosttyNewTab, + object: nil) } /// Add the initial window for the application. This should only be called once from the AppDelegate. @@ -61,7 +78,37 @@ class PrimaryWindowManager { newWindow.makeKeyAndOrderFront(nil) } - func addNewTab(to window: NSWindow, withFontSize fontSize: UInt8? = nil) { + func newTabForWindow(window: PrimaryWindow) { + guard let surface = window.focusedSurfaceWrapper.surface else { return } + ghostty.newTab(surface: surface) + } + + func newTab() { + if mainWindow != nil { + guard let window = mainWindow as? PrimaryWindow else { return } + self.newTabForWindow(window: window) + } else { + self.addNewWindow() + } + } + + @objc private func onNewTab(notification: SwiftUI.Notification) { + guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } + guard let window = surfaceView.window else { return } + + let fontSizeAny = notification.userInfo?[Ghostty.Notification.NewTabKey] + let fontSize = fontSizeAny as? UInt8 + + if fontSize != nil { + // Add the new tab to the window with the given font size. + self.addNewTab(to: window, withFontSize: fontSize) + } else { + // No font size specified, just add new tab. + self.addNewTab(to: window) + } + } + + private func addNewTab(to window: NSWindow, withFontSize fontSize: UInt8? = nil) { guard let controller = createWindowController(withFontSize: fontSize) else { return } guard let newWindow = addManagedWindow(windowController: controller)?.window else { return } window.addTabbedWindow(newWindow, ordered: .above) From 12311e970776e6a93a712b312fc31abc0ae32770 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Fri, 18 Aug 2023 06:19:54 +0200 Subject: [PATCH 3/8] macOS: simplify code for new tab --- .../PrimaryWindowController.swift | 2 +- .../Primary Window/PrimaryWindowManager.swift | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift index 257a0bb6f..daa64012c 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift @@ -14,6 +14,6 @@ class PrimaryWindowController: NSWindowController { override func newWindowForTab(_ sender: Any?) { guard let window = self.window as? PrimaryWindow else { preconditionFailure("Expected window to be loaded") } guard let manager = self.windowManager else { return } - manager.newTabForWindow(window: window) + manager.triggerNewTab(for: window) } } diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift index 8b8fb94d2..4a608704f 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift @@ -78,15 +78,16 @@ class PrimaryWindowManager { newWindow.makeKeyAndOrderFront(nil) } - func newTabForWindow(window: PrimaryWindow) { + // triggerNewTab tells the Zig core code to create a new tab, which then calls + // back into Swift code. + func triggerNewTab(for window: PrimaryWindow) { guard let surface = window.focusedSurfaceWrapper.surface else { return } ghostty.newTab(surface: surface) } func newTab() { - if mainWindow != nil { - guard let window = mainWindow as? PrimaryWindow else { return } - self.newTabForWindow(window: window) + if let window = mainWindow as? PrimaryWindow { + self.triggerNewTab(for: window) } else { self.addNewWindow() } @@ -99,13 +100,7 @@ class PrimaryWindowManager { let fontSizeAny = notification.userInfo?[Ghostty.Notification.NewTabKey] let fontSize = fontSizeAny as? UInt8 - if fontSize != nil { - // Add the new tab to the window with the given font size. - self.addNewTab(to: window, withFontSize: fontSize) - } else { - // No font size specified, just add new tab. - self.addNewTab(to: window) - } + self.addNewTab(to: window, withFontSize: fontSize) } private func addNewTab(to window: NSWindow, withFontSize fontSize: UInt8? = nil) { From cda87a69635a84bb9b3e312478a78f9643fd9676 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Fri, 18 Aug 2023 06:50:47 +0200 Subject: [PATCH 4/8] embedded: use separate struct to pass options to new_tab_cb --- include/ghostty.h | 7 ++++++- macos/Sources/Ghostty/AppState.swift | 14 +++++++++----- src/apprt/embedded.zig | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index dc2f304a0..d3ac633e4 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -256,7 +256,12 @@ typedef void (*ghostty_runtime_close_surface_cb)(void *, bool); typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e); typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t); typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, bool); -typedef void (*ghostty_runtime_new_tab_cb)(void *, uint8_t); + +typedef struct { + uint8_t font_size; +} ghostty_new_tab_config_s; + +typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_new_tab_config_s); typedef struct { void *userdata; diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 76f422225..97f8f4743 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -65,7 +65,7 @@ extension Ghostty { focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) }, goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) }, toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) }, - new_tab_cb: { userdata, fontSize in AppState.newTab(userdata, fontSize: fontSize) } + new_tab_cb: { userdata, newTabConfig in AppState.newTab(userdata, config: newTabConfig) } ) // Create the ghostty app. @@ -263,14 +263,18 @@ extension Ghostty { ) } - static func newTab(_ userdata: UnsafeMutableRawPointer?, fontSize: UInt8) { + static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_new_tab_config_s) { guard let surface = self.surfaceUserdata(from: userdata) else { return } + + var userInfo: [AnyHashable : Any] = [:]; + if config.font_size != 0 { + userInfo[Notification.NewTabKey] = config.font_size as UInt8; + } + NotificationCenter.default.post( name: Notification.ghosttyNewTab, object: surface, - userInfo: [ - Notification.NewTabKey: fontSize, - ] + userInfo: userInfo ) } diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 5315614d9..ea2c32919 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -71,9 +71,13 @@ pub const App = struct { /// Toggle fullscreen for current window. toggle_fullscreen: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, - /// New tab with desired font size in points - /// TODO: u8 should be something that's nullable - new_tab: ?*const fn (SurfaceUD, u8) callconv(.C) void = null, + /// New tab with options. + new_tab: ?*const fn (SurfaceUD, apprt.App.NewTabOptions) callconv(.C) void = null, + }; + + pub const NewTabOptions = extern struct { + /// The font size to inherit. If 0, default font size will be used. + font_size: u8 = 0, }; core_app: *CoreApp, @@ -600,14 +604,16 @@ pub const Surface = struct { return; }; - // TODO: Do we check this here? Or do we check this in embedder? - // const font_size: u8 = font_size: { if (!self.app.config.@"window-inherit-font-size") break :font_size 0; break :font_size @intCast(self.core_surface.font_size.points); }; - func(self.opts.userdata, font_size); + const options = apprt.App.NewTabOptions{ + .font_size = font_size, + }; + + func(self.opts.userdata, options); } /// The cursor position from the host directly is in screen coordinates but From 79971c62a622bc9ac730ae6dee7359cc22914ad8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 18 Aug 2023 09:09:43 -0700 Subject: [PATCH 5/8] macos: pass around a base surface_config_s rather than a new tab --- include/ghostty.h | 11 +++---- .../Features/Primary Window/PrimaryView.swift | 6 ++-- .../Primary Window/PrimaryWindow.swift | 4 +-- .../Primary Window/PrimaryWindowManager.swift | 15 +++++----- macos/Sources/Ghostty/AppState.swift | 8 ++--- macos/Sources/Ghostty/Ghostty.SplitView.swift | 16 +++++----- macos/Sources/Ghostty/Package.swift | 2 +- macos/Sources/Ghostty/SurfaceView.swift | 12 ++++---- src/apprt/embedded.zig | 29 ++++++++++--------- 9 files changed, 51 insertions(+), 52 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index d3ac633e4..31d2c7e5b 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -252,17 +252,12 @@ typedef void (*ghostty_runtime_set_title_cb)(void *, const char *); typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *, ghostty_clipboard_e); typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty_clipboard_e); typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e); +typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_surface_config_s); typedef void (*ghostty_runtime_close_surface_cb)(void *, bool); typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e); typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t); typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, bool); -typedef struct { - uint8_t font_size; -} ghostty_new_tab_config_s; - -typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_new_tab_config_s); - typedef struct { void *userdata; bool supports_selection_clipboard; @@ -272,11 +267,11 @@ typedef struct { ghostty_runtime_read_clipboard_cb read_clipboard_cb; ghostty_runtime_write_clipboard_cb write_clipboard_cb; ghostty_runtime_new_split_cb new_split_cb; + ghostty_runtime_new_tab_cb new_tab_cb; ghostty_runtime_close_surface_cb close_surface_cb; ghostty_runtime_focus_split_cb focus_split_cb; ghostty_runtime_goto_tab_cb goto_tab_cb; ghostty_runtime_toggle_fullscreen_cb toggle_fullscreen_cb; - ghostty_runtime_new_tab_cb new_tab_cb; } ghostty_runtime_config_s; //------------------------------------------------------------------- @@ -298,6 +293,8 @@ bool ghostty_app_tick(ghostty_app_t); void *ghostty_app_userdata(ghostty_app_t); void ghostty_app_keyboard_changed(ghostty_app_t); +ghostty_surface_config_s ghostty_surface_config_new(); + ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*); void ghostty_surface_free(ghostty_surface_t); ghostty_app_t ghostty_surface_app(ghostty_surface_t); diff --git a/macos/Sources/Features/Primary Window/PrimaryView.swift b/macos/Sources/Features/Primary Window/PrimaryView.swift index 07bbec947..b62e630b7 100644 --- a/macos/Sources/Features/Primary Window/PrimaryView.swift +++ b/macos/Sources/Features/Primary Window/PrimaryView.swift @@ -11,8 +11,8 @@ struct PrimaryView: View { // We need this to report back up the app controller which surface in this view is focused. let focusedSurfaceWrapper: FocusedSurfaceWrapper - // If this is set, we inherit the fontSize from the parent tab or window. - let fontSize: UInt8? + // If this is set, this is the base configuration that we build our surface out of. + let baseConfig: ghostty_surface_config_s? // We need access to our window to know if we're the key window to determine // if we show the quit confirmation or not. @@ -74,7 +74,7 @@ struct PrimaryView: View { self.appDelegate.confirmQuit = $0 }) - Ghostty.TerminalSplit(onClose: Self.closeWindow, fontSize: self.fontSize) + Ghostty.TerminalSplit(onClose: Self.closeWindow, baseConfig: self.baseConfig) .ghosttyApp(ghostty.app!) .background(WindowAccessor(window: $window)) .onReceive(gotoTab) { onGotoTab(notification: $0) } diff --git a/macos/Sources/Features/Primary Window/PrimaryWindow.swift b/macos/Sources/Features/Primary Window/PrimaryWindow.swift index 1762f29f7..6f6e0ba73 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindow.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindow.swift @@ -16,7 +16,7 @@ class FocusedSurfaceWrapper { class PrimaryWindow: NSWindow { var focusedSurfaceWrapper: FocusedSurfaceWrapper = FocusedSurfaceWrapper() - static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate, fontSize: UInt8? = nil) -> PrimaryWindow { + static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate, baseConfig: ghostty_surface_config_s? = nil) -> PrimaryWindow { let window = PrimaryWindow( contentRect: NSRect(x: 0, y: 0, width: 800, height: 600), styleMask: [.titled, .closable, .miniaturizable, .resizable], @@ -28,7 +28,7 @@ class PrimaryWindow: NSWindow { ghostty: ghostty, appDelegate: appDelegate, focusedSurfaceWrapper: window.focusedSurfaceWrapper, - fontSize: fontSize + baseConfig: baseConfig )) // We do want to cascade when new windows are created diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift index 4a608704f..987dc86a2 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowManager.swift @@ -1,5 +1,6 @@ import Cocoa import Combine +import GhosttyKit import SwiftUI // PrimaryWindowManager manages the windows and tabs in the primary window @@ -97,22 +98,22 @@ class PrimaryWindowManager { guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } guard let window = surfaceView.window else { return } - let fontSizeAny = notification.userInfo?[Ghostty.Notification.NewTabKey] - let fontSize = fontSizeAny as? UInt8 + let configAny = notification.userInfo?[Ghostty.Notification.NewTabKey] + let config = configAny as? ghostty_surface_config_s - self.addNewTab(to: window, withFontSize: fontSize) + self.addNewTab(to: window, withBaseConfig: config) } - private func addNewTab(to window: NSWindow, withFontSize fontSize: UInt8? = nil) { - guard let controller = createWindowController(withFontSize: fontSize) else { return } + private func addNewTab(to window: NSWindow, withBaseConfig config: ghostty_surface_config_s? = nil) { + guard let controller = createWindowController(withBaseConfig: config) else { return } guard let newWindow = addManagedWindow(windowController: controller)?.window else { return } window.addTabbedWindow(newWindow, ordered: .above) newWindow.makeKeyAndOrderFront(nil) } - private func createWindowController(withFontSize fontSize: UInt8? = nil) -> PrimaryWindowController? { + private func createWindowController(withBaseConfig config: ghostty_surface_config_s? = nil) -> PrimaryWindowController? { guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return nil } - let window = PrimaryWindow.create(ghostty: ghostty, appDelegate: appDelegate, fontSize: fontSize) + let window = PrimaryWindow.create(ghostty: ghostty, appDelegate: appDelegate, baseConfig: config) Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint) let controller = PrimaryWindowController(window: window) controller.windowManager = self diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 97f8f4743..94ba3ad11 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -61,11 +61,11 @@ extension Ghostty { read_clipboard_cb: { userdata, loc in AppState.readClipboard(userdata, location: loc) }, write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) }, new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: direction) }, + new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) }, close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) }, focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) }, goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) }, - toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) }, - new_tab_cb: { userdata, newTabConfig in AppState.newTab(userdata, config: newTabConfig) } + toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) } ) // Create the ghostty app. @@ -263,12 +263,12 @@ extension Ghostty { ) } - static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_new_tab_config_s) { + static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) { guard let surface = self.surfaceUserdata(from: userdata) else { return } var userInfo: [AnyHashable : Any] = [:]; if config.font_size != 0 { - userInfo[Notification.NewTabKey] = config.font_size as UInt8; + userInfo[Notification.NewTabKey] = config; } NotificationCenter.default.post( diff --git a/macos/Sources/Ghostty/Ghostty.SplitView.swift b/macos/Sources/Ghostty/Ghostty.SplitView.swift index 9142dd037..a3e224139 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitView.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitView.swift @@ -8,11 +8,11 @@ extension Ghostty { struct TerminalSplit: View { @Environment(\.ghosttyApp) private var app let onClose: (() -> Void)? - let fontSize: UInt8? + let baseConfig: ghostty_surface_config_s? var body: some View { if let app = app { - TerminalSplitRoot(app: app, onClose: onClose, fontSize: fontSize) + TerminalSplitRoot(app: app, onClose: onClose, baseConfig: baseConfig) } } } @@ -68,9 +68,9 @@ extension Ghostty { @Published var surface: SurfaceView /// Initialize a new leaf which creates a new terminal surface. - init(_ app: ghostty_app_t, _ fontSize: UInt8?) { + init(_ app: ghostty_app_t, _ baseConfig: ghostty_surface_config_s?) { self.app = app - self.surface = SurfaceView(app, fontSize) + self.surface = SurfaceView(app, baseConfig) } } @@ -142,14 +142,14 @@ extension Ghostty { @State private var node: SplitNode @State private var requestClose: Bool = false let onClose: (() -> Void)? - let fontSize: UInt8? + let baseConfig: ghostty_surface_config_s? @FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String? - init(app: ghostty_app_t, onClose: (() ->Void)? = nil, fontSize: UInt8? = nil) { + init(app: ghostty_app_t, onClose: (() ->Void)? = nil, baseConfig: ghostty_surface_config_s? = nil) { self.onClose = onClose - self.fontSize = fontSize - _node = State(wrappedValue: SplitNode.noSplit(.init(app, fontSize))) + self.baseConfig = baseConfig + _node = State(wrappedValue: SplitNode.noSplit(.init(app, baseConfig))) } var body: some View { diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index 25a10b8ce..006433c72 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -79,7 +79,7 @@ extension Ghostty.Notification { static let ghosttyGotoTab = Notification.Name("com.mitchellh.ghostty.gotoTab") static let GotoTabKey = ghosttyGotoTab.rawValue - /// New tab. Has font size of currently focused surface in the userinfo. + /// New tab. Has base surface config requestesd in userinfo. static let ghosttyNewTab = Notification.Name("com.mitchellh.ghostty.newTab") static let NewTabKey = ghosttyNewTab.rawValue diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index 1960edd60..11f809fc9 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -137,7 +137,7 @@ extension Ghostty { // so we'll use that to tell ghostty to refresh. override var wantsUpdateLayer: Bool { return true } - init(_ app: ghostty_app_t, _ fontSize: UInt8?) { + init(_ app: ghostty_app_t, _ baseConfig: ghostty_surface_config_s?) { self.markedText = NSMutableAttributedString() // Initialize with some default frame size. The important thing is that this @@ -146,11 +146,11 @@ extension Ghostty { super.init(frame: NSMakeRect(0, 0, 800, 600)) // Setup our surface. This will also initialize all the terminal IO. - var surface_cfg = ghostty_surface_config_s( - userdata: Unmanaged.passUnretained(self).toOpaque(), - nsview: Unmanaged.passUnretained(self).toOpaque(), - scale_factor: NSScreen.main!.backingScaleFactor, - font_size: fontSize ?? 0) + var surface_cfg = baseConfig ?? ghostty_surface_config_new() + surface_cfg.userdata = Unmanaged.passUnretained(self).toOpaque() + surface_cfg.nsview = Unmanaged.passUnretained(self).toOpaque() + surface_cfg.scale_factor = NSScreen.main!.backingScaleFactor + guard let surface = ghostty_surface_new(app, &surface_cfg) else { self.error = AppError.surfaceCreateError return diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index ea2c32919..3e2eb9323 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -59,6 +59,9 @@ pub const App = struct { /// views then this can be null. new_split: ?*const fn (SurfaceUD, input.SplitDirection) callconv(.C) void = null, + /// New tab with options. + new_tab: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null, + /// Close the current surface given by this function. close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, @@ -70,14 +73,6 @@ pub const App = struct { /// Toggle fullscreen for current window. toggle_fullscreen: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, - - /// New tab with options. - new_tab: ?*const fn (SurfaceUD, apprt.App.NewTabOptions) callconv(.C) void = null, - }; - - pub const NewTabOptions = extern struct { - /// The font size to inherit. If 0, default font size will be used. - font_size: u8 = 0, }; core_app: *CoreApp, @@ -170,7 +165,7 @@ pub const Surface = struct { userdata: ?*anyopaque = null, /// The pointer to the backing NSView for the surface. - nsview: *anyopaque = undefined, + nsview: ?*anyopaque = null, /// The scale factor of the screen. scale_factor: f64 = 1, @@ -180,10 +175,13 @@ pub const Surface = struct { }; pub fn init(self: *Surface, app: *App, opts: Options) !void { + const nsview = objc.Object.fromId(opts.nsview orelse + return error.NSViewMustBeSet); + self.* = .{ .app = app, .core_surface = undefined, - .nsview = objc.Object.fromId(opts.nsview), + .nsview = nsview, .content_scale = .{ .x = @floatCast(opts.scale_factor), .y = @floatCast(opts.scale_factor), @@ -609,11 +607,9 @@ pub const Surface = struct { break :font_size @intCast(self.core_surface.font_size.points); }; - const options = apprt.App.NewTabOptions{ + func(self.opts.userdata, .{ .font_size = font_size, - }; - - func(self.opts.userdata, options); + }); } /// The cursor position from the host directly is in screen coordinates but @@ -685,6 +681,11 @@ pub const CAPI = struct { }; } + /// Returns initial surface options. + export fn ghostty_surface_config_new() apprt.Surface.Options { + return .{}; + } + /// Create a new surface as part of an app. export fn ghostty_surface_new( app: *App, From a960faa2b815b6fc1e2c72a40db918b6aed8a395 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 18 Aug 2023 09:13:59 -0700 Subject: [PATCH 6/8] apprt/embedded: use setfontsize so that the original font size is retained --- src/apprt/embedded.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 3e2eb9323..af0bb96d7 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -171,7 +171,7 @@ pub const Surface = struct { scale_factor: f64 = 1, /// The font size to inherit. If 0, default font size will be used. - font_size: u8 = 0, + font_size: u16 = 0, }; pub fn init(self: *Surface, app: *App, opts: Options) !void { @@ -200,13 +200,6 @@ pub const Surface = struct { var config = try apprt.surface.newConfig(app.core_app, app.config); defer config.deinit(); - // Overwrite the config for this new surface if we need to set a font - // size based on parent. - // TODO: Is this super hacky? - if (opts.font_size != 0) { - config.@"font-size" = opts.font_size; - } - // Initialize our surface right away. We're given a view that is // ready to use. try self.core_surface.init( @@ -217,6 +210,13 @@ pub const Surface = struct { self, ); errdefer self.core_surface.deinit(); + + // If our options requested a specific font-size, set that. + if (opts.font_size != 0) { + var font_size = self.core_surface.font_size; + font_size.points = opts.font_size; + self.core_surface.setFontSize(font_size); + } } pub fn deinit(self: *Surface) void { @@ -602,9 +602,9 @@ pub const Surface = struct { return; }; - const font_size: u8 = font_size: { + const font_size: u16 = font_size: { if (!self.app.config.@"window-inherit-font-size") break :font_size 0; - break :font_size @intCast(self.core_surface.font_size.points); + break :font_size self.core_surface.font_size.points; }; func(self.opts.userdata, .{ From 5eb902592cfb291f3fd9107ddcaf87c1ebc393ca Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 18 Aug 2023 09:19:22 -0700 Subject: [PATCH 7/8] c include: u8 => u16 --- include/ghostty.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ghostty.h b/include/ghostty.h index 31d2c7e5b..a6fc51d9b 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -243,7 +243,7 @@ typedef struct { void *userdata; void *nsview; double scale_factor; - uint8_t font_size; + uint16_t font_size; } ghostty_surface_config_s; typedef void (*ghostty_runtime_wakeup_cb)(void *); From a18503fc018d28181473b5d4abb17bab210344f0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 18 Aug 2023 09:21:47 -0700 Subject: [PATCH 8/8] macos: can always set base config on new tab --- macos/Sources/Ghostty/AppState.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 94ba3ad11..1a0a17f4b 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -267,9 +267,7 @@ extension Ghostty { guard let surface = self.surfaceUserdata(from: userdata) else { return } var userInfo: [AnyHashable : Any] = [:]; - if config.font_size != 0 { - userInfo[Notification.NewTabKey] = config; - } + userInfo[Notification.NewTabKey] = config; NotificationCenter.default.post( name: Notification.ghosttyNewTab,