diff --git a/include/ghostty.h b/include/ghostty.h index 4a4d84e7f..f630ab0d0 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -26,10 +26,12 @@ extern "C" { // structs. To find the Zig struct, grep for this type name. The documentation // for all of these types is available in the Zig source. typedef void (*ghostty_runtime_wakeup_cb)(void *); +typedef void (*ghostty_runtime_set_title_cb)(void *, const char *); typedef struct { void *userdata; ghostty_runtime_wakeup_cb wakeup_cb; + ghostty_runtime_set_title_cb set_title_cb; } ghostty_runtime_config_s; typedef struct { diff --git a/macos/Sources/GhosttyApp.swift b/macos/Sources/GhosttyApp.swift index b6a7066c9..50baf2ea8 100644 --- a/macos/Sources/GhosttyApp.swift +++ b/macos/Sources/GhosttyApp.swift @@ -22,6 +22,7 @@ struct GhosttyApp: App { case .ready: TerminalView(app: ghostty.app!) .modifier(WindowObservationModifier()) + .navigationTitle(ghostty.title) } } } @@ -35,6 +36,9 @@ class GhosttyState: ObservableObject { /// The readiness value of the state. @Published var readiness: Readiness = .loading + /// The title of the window as requested by the underlying terminal. + @Published var title: String = "Ghostty"; + /// The ghostty global configuration. var config: ghostty_config_t? = nil @@ -69,7 +73,8 @@ class GhosttyState: ObservableObject { // uses to interface with the application runtime environment. var runtime_cfg = ghostty_runtime_config_s( userdata: Unmanaged.passUnretained(self).toOpaque(), - wakeup_cb: { userdata in GhosttyState.wakeup(userdata) }) + wakeup_cb: { userdata in GhosttyState.wakeup(userdata) }, + set_title_cb: { userdata, title in GhosttyState.setTitle(userdata, title: title) }) // Create the ghostty app. guard let app = ghostty_app_new(&runtime_cfg, cfg) else { @@ -97,6 +102,14 @@ class GhosttyState: ObservableObject { DispatchQueue.main.async { state.appTick() } } + static func setTitle(_ userdata: UnsafeMutableRawPointer?, title: UnsafePointer?) { + let state = Unmanaged.fromOpaque(userdata!).takeUnretainedValue() + guard let titleStr = String(cString: title!, encoding: .utf8) else { return } + DispatchQueue.main.async { + state.title = titleStr + } + } + deinit { ghostty_app_free(app) ghostty_config_free(config) diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index a4ddb90a7..8d1fa25e7 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -29,6 +29,9 @@ pub const App = struct { /// Callback called to wakeup the event loop. This should trigger /// a full tick of the app loop. wakeup: *const fn (?*anyopaque) callconv(.C) void, + + /// Called to set the title of the window. + set_title: *const fn (?*anyopaque, [*]const u8) callconv(.C) void, }; opts: Options, @@ -97,8 +100,10 @@ pub const Window = struct { } pub fn setTitle(self: *Window, slice: [:0]const u8) !void { - _ = self; - _ = slice; + self.core_win.app.runtime.opts.set_title( + self.core_win.app.runtime.opts.userdata, + slice.ptr, + ); } pub fn getClipboardString(self: *const Window) ![:0]const u8 {