mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
Allow for default or inherited CWD in new window, tab and split surfaces (redone for GTK-NG) (#9158)
This commit is contained in:
@@ -416,6 +416,12 @@ typedef union {
|
||||
ghostty_platform_ios_s ios;
|
||||
} ghostty_platform_u;
|
||||
|
||||
typedef enum {
|
||||
GHOSTTY_SURFACE_CONTEXT_WINDOW = 0,
|
||||
GHOSTTY_SURFACE_CONTEXT_TAB = 1,
|
||||
GHOSTTY_SURFACE_CONTEXT_SPLIT = 2,
|
||||
} ghostty_surface_context_e;
|
||||
|
||||
typedef struct {
|
||||
ghostty_platform_e platform_tag;
|
||||
ghostty_platform_u platform;
|
||||
@@ -428,6 +434,7 @@ typedef struct {
|
||||
size_t env_var_count;
|
||||
const char* initial_input;
|
||||
bool wait_after_command;
|
||||
ghostty_surface_context_e context;
|
||||
} ghostty_surface_config_s;
|
||||
|
||||
typedef struct {
|
||||
@@ -1035,7 +1042,7 @@ ghostty_surface_t ghostty_surface_new(ghostty_app_t,
|
||||
void ghostty_surface_free(ghostty_surface_t);
|
||||
void* ghostty_surface_userdata(ghostty_surface_t);
|
||||
ghostty_app_t ghostty_surface_app(ghostty_surface_t);
|
||||
ghostty_surface_config_s ghostty_surface_inherited_config(ghostty_surface_t);
|
||||
ghostty_surface_config_s ghostty_surface_inherited_config(ghostty_surface_t, ghostty_surface_context_e);
|
||||
void ghostty_surface_update_config(ghostty_surface_t, ghostty_config_t);
|
||||
bool ghostty_surface_needs_confirm_quit(ghostty_surface_t);
|
||||
bool ghostty_surface_process_exited(ghostty_surface_t);
|
||||
|
||||
@@ -773,7 +773,7 @@ extension Ghostty {
|
||||
name: Notification.ghosttyNewWindow,
|
||||
object: surfaceView,
|
||||
userInfo: [
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface)),
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface, GHOSTTY_SURFACE_CONTEXT_WINDOW)),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -810,7 +810,7 @@ extension Ghostty {
|
||||
name: Notification.ghosttyNewTab,
|
||||
object: surfaceView,
|
||||
userInfo: [
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface)),
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface, GHOSTTY_SURFACE_CONTEXT_TAB)),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -839,7 +839,7 @@ extension Ghostty {
|
||||
object: surfaceView,
|
||||
userInfo: [
|
||||
"direction": direction,
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface)),
|
||||
Notification.NewSurfaceConfigKey: SurfaceConfiguration(from: ghostty_surface_inherited_config(surface, GHOSTTY_SURFACE_CONTEXT_SPLIT)),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -648,6 +648,9 @@ extension Ghostty {
|
||||
/// Wait after the command
|
||||
var waitAfterCommand: Bool = false
|
||||
|
||||
/// Context for surface creation
|
||||
var context: ghostty_surface_context_e = GHOSTTY_SURFACE_CONTEXT_WINDOW
|
||||
|
||||
init() {}
|
||||
|
||||
init(from config: ghostty_surface_config_s) {
|
||||
@@ -669,6 +672,7 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.context = config.context
|
||||
}
|
||||
|
||||
/// Provides a C-compatible ghostty configuration within a closure. The configuration
|
||||
@@ -702,6 +706,9 @@ extension Ghostty {
|
||||
// Set wait after command
|
||||
config.wait_after_command = waitAfterCommand
|
||||
|
||||
// Set context
|
||||
config.context = context
|
||||
|
||||
// Use withCString to ensure strings remain valid for the duration of the closure
|
||||
return try workingDirectory.withCString { cWorkingDir in
|
||||
config.working_directory = cWorkingDir
|
||||
|
||||
@@ -456,6 +456,9 @@ pub const Surface = struct {
|
||||
|
||||
/// Wait after the command exits
|
||||
wait_after_command: bool = false,
|
||||
|
||||
/// Context for the new surface
|
||||
context: apprt.surface.NewSurfaceContext = .window,
|
||||
};
|
||||
|
||||
pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
||||
@@ -477,7 +480,7 @@ pub const Surface = struct {
|
||||
errdefer app.core_app.deleteSurface(self);
|
||||
|
||||
// Shallow copy the config so that we can modify it.
|
||||
var config = try apprt.surface.newConfig(app.core_app, &app.config);
|
||||
var config = try apprt.surface.newConfig(app.core_app, &app.config, opts.context);
|
||||
defer config.deinit();
|
||||
|
||||
// If we have a working directory from the options then we set it.
|
||||
@@ -894,14 +897,23 @@ pub const Surface = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn newSurfaceOptions(self: *const Surface) apprt.Surface.Options {
|
||||
pub fn newSurfaceOptions(self: *const Surface, context: apprt.surface.NewSurfaceContext) apprt.Surface.Options {
|
||||
const font_size: f32 = font_size: {
|
||||
if (!self.app.config.@"window-inherit-font-size") break :font_size 0;
|
||||
break :font_size self.core_surface.font_size.points;
|
||||
};
|
||||
|
||||
const working_directory: ?[*:0]const u8 = wd: {
|
||||
if (!apprt.surface.shouldInheritWorkingDirectory(context, &self.app.config)) break :wd null;
|
||||
const cwd = self.core_surface.pwd(self.app.core_app.alloc) catch null orelse break :wd null;
|
||||
defer self.app.core_app.alloc.free(cwd);
|
||||
break :wd self.app.core_app.alloc.dupeZ(u8, cwd) catch null;
|
||||
};
|
||||
|
||||
return .{
|
||||
.font_size = font_size,
|
||||
.working_directory = working_directory,
|
||||
.context = context,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1523,8 +1535,11 @@ pub const CAPI = struct {
|
||||
}
|
||||
|
||||
/// Returns the config to use for surfaces that inherit from this one.
|
||||
export fn ghostty_surface_inherited_config(surface: *Surface) Surface.Options {
|
||||
return surface.newSurfaceOptions();
|
||||
export fn ghostty_surface_inherited_config(
|
||||
surface: *Surface,
|
||||
source: apprt.surface.NewSurfaceContext,
|
||||
) Surface.Options {
|
||||
return surface.newSurfaceOptions(source);
|
||||
}
|
||||
|
||||
/// Update the configuration to the provided config for only this surface.
|
||||
|
||||
@@ -2238,8 +2238,8 @@ const Action = struct {
|
||||
.{},
|
||||
);
|
||||
|
||||
// Create a new tab
|
||||
win.newTab(parent);
|
||||
// Create a new tab with window context (first tab in new window)
|
||||
win.newTabForWindow(parent);
|
||||
|
||||
// Show the window
|
||||
gtk.Window.present(win.as(gtk.Window));
|
||||
|
||||
@@ -219,7 +219,7 @@ pub const SplitTree = extern struct {
|
||||
// Inherit properly if we were asked to.
|
||||
if (parent_) |p| {
|
||||
if (p.core()) |core| {
|
||||
surface.setParent(core);
|
||||
surface.setParent(core, .split);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -671,6 +671,9 @@ pub const Surface = extern struct {
|
||||
error_page: *adw.StatusPage,
|
||||
terminal_page: *gtk.Overlay,
|
||||
|
||||
/// The context for this surface (window, tab, or split)
|
||||
context: apprt.surface.NewSurfaceContext = .window,
|
||||
|
||||
pub var offset: c_int = 0;
|
||||
};
|
||||
|
||||
@@ -696,6 +699,7 @@ pub const Surface = extern struct {
|
||||
pub fn setParent(
|
||||
self: *Self,
|
||||
parent: *CoreSurface,
|
||||
context: apprt.surface.NewSurfaceContext,
|
||||
) void {
|
||||
const priv = self.private();
|
||||
|
||||
@@ -706,6 +710,9 @@ pub const Surface = extern struct {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the context so initSurface can use it
|
||||
priv.context = context;
|
||||
|
||||
// Setup our font size
|
||||
const font_size_ptr = glib.ext.create(font.face.DesiredSize);
|
||||
errdefer glib.ext.destroy(font_size_ptr);
|
||||
@@ -716,10 +723,8 @@ pub const Surface = extern struct {
|
||||
// Remainder needs a config. If there is no config we just assume
|
||||
// we aren't inheriting any of these values.
|
||||
if (priv.config) |config_obj| {
|
||||
const config = config_obj.get();
|
||||
|
||||
// Setup our pwd if configured to inherit
|
||||
if (config.@"window-inherit-working-directory") {
|
||||
// Setup our cwd if configured to inherit
|
||||
if (apprt.surface.shouldInheritWorkingDirectory(context, config_obj.get())) {
|
||||
if (parent.rt_surface.surface.getPwd()) |pwd| {
|
||||
priv.pwd = glib.ext.dupeZ(u8, pwd);
|
||||
self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec);
|
||||
@@ -3206,6 +3211,7 @@ pub const Surface = extern struct {
|
||||
var config = try apprt.surface.newConfig(
|
||||
app.core(),
|
||||
priv.config.?.get(),
|
||||
priv.context,
|
||||
);
|
||||
defer config.deinit();
|
||||
|
||||
|
||||
@@ -161,8 +161,12 @@ pub const Tab = extern struct {
|
||||
/// ever created for a tab. If a surface was already created this does
|
||||
/// nothing.
|
||||
pub fn setParent(self: *Self, parent: *CoreSurface) void {
|
||||
self.setParentWithContext(parent, .tab);
|
||||
}
|
||||
|
||||
pub fn setParentWithContext(self: *Self, parent: *CoreSurface, context: apprt.surface.NewSurfaceContext) void {
|
||||
if (self.getActiveSurface()) |surface| {
|
||||
surface.setParent(parent);
|
||||
surface.setParent(parent, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -361,10 +361,14 @@ pub const Window = extern struct {
|
||||
/// at the position dictated by the `window-new-tab-position` config.
|
||||
/// The new tab will be selected.
|
||||
pub fn newTab(self: *Self, parent_: ?*CoreSurface) void {
|
||||
_ = self.newTabPage(parent_);
|
||||
_ = self.newTabPage(parent_, .tab);
|
||||
}
|
||||
|
||||
fn newTabPage(self: *Self, parent_: ?*CoreSurface) *adw.TabPage {
|
||||
pub fn newTabForWindow(self: *Self, parent_: ?*CoreSurface) void {
|
||||
_ = self.newTabPage(parent_, .window);
|
||||
}
|
||||
|
||||
fn newTabPage(self: *Self, parent_: ?*CoreSurface, context: apprt.surface.NewSurfaceContext) *adw.TabPage {
|
||||
const priv = self.private();
|
||||
const tab_view = priv.tab_view;
|
||||
|
||||
@@ -372,7 +376,9 @@ pub const Window = extern struct {
|
||||
const tab = gobject.ext.newInstance(Tab, .{
|
||||
.config = priv.config,
|
||||
});
|
||||
if (parent_) |p| tab.setParent(p);
|
||||
if (parent_) |p| {
|
||||
tab.setParentWithContext(p, context);
|
||||
}
|
||||
|
||||
// Get the position that we should insert the new tab at.
|
||||
const config = if (priv.config) |v| v.get() else {
|
||||
@@ -1231,7 +1237,7 @@ pub const Window = extern struct {
|
||||
_: *adw.TabOverview,
|
||||
self: *Self,
|
||||
) callconv(.c) *adw.TabPage {
|
||||
return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null);
|
||||
return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null, .tab);
|
||||
}
|
||||
|
||||
fn tabOverviewOpen(
|
||||
|
||||
@@ -159,12 +159,28 @@ pub const Mailbox = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Context for new surface creation to determine inheritance behavior
|
||||
pub const NewSurfaceContext = enum(c_int) {
|
||||
window = 0,
|
||||
tab = 1,
|
||||
split = 2,
|
||||
};
|
||||
|
||||
pub fn shouldInheritWorkingDirectory(context: NewSurfaceContext, config: *const Config) bool {
|
||||
return switch (context) {
|
||||
.window => config.@"window-inherit-working-directory",
|
||||
.tab => config.@"tab-inherit-working-directory",
|
||||
.split => config.@"split-inherit-working-directory",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns a new config for a surface for the given app that should be
|
||||
/// used for any new surfaces. The resulting config should be deinitialized
|
||||
/// after the surface is initialized.
|
||||
pub fn newConfig(
|
||||
app: *const App,
|
||||
config: *const Config,
|
||||
context: NewSurfaceContext,
|
||||
) Allocator.Error!Config {
|
||||
// Create a shallow clone
|
||||
var copy = config.shallowClone(app.alloc);
|
||||
@@ -175,7 +191,7 @@ pub fn newConfig(
|
||||
// Get our previously focused surface for some inherited values.
|
||||
const prev = app.focusedSurface();
|
||||
if (prev) |p| {
|
||||
if (config.@"window-inherit-working-directory") {
|
||||
if (shouldInheritWorkingDirectory(context, config)) {
|
||||
if (try p.pwd(alloc)) |pwd| {
|
||||
copy.@"working-directory" = pwd;
|
||||
}
|
||||
|
||||
@@ -1845,11 +1845,21 @@ keybind: Keybinds = .{},
|
||||
/// This setting is only supported currently on macOS.
|
||||
@"window-vsync": bool = true,
|
||||
|
||||
/// If true, new windows and tabs will inherit the working directory of the
|
||||
/// If true, new windows will inherit the working directory of the
|
||||
/// previously focused window. If no window was previously focused, the default
|
||||
/// working directory will be used (the `working-directory` option).
|
||||
@"window-inherit-working-directory": bool = true,
|
||||
|
||||
/// If true, new tabs will inherit the working directory of the
|
||||
/// previously focused tab. If no tab was previously focused, the default
|
||||
/// working directory will be used (the `working-directory` option).
|
||||
@"tab-inherit-working-directory": bool = true,
|
||||
|
||||
/// If true, new split panes will inherit the working directory of the
|
||||
/// previously focused split. If no split was previously focused, the default
|
||||
/// working directory will be used (the `working-directory` option).
|
||||
@"split-inherit-working-directory": bool = true,
|
||||
|
||||
/// If true, new windows and tabs will inherit the font size of the previously
|
||||
/// focused window. If no window was previously focused, the default font size
|
||||
/// will be used. If this is false, the default font size specified in the
|
||||
|
||||
Reference in New Issue
Block a user