From df50aacff162146c54ad8ec8486072cec92283a2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 1 Nov 2022 18:10:30 -0700 Subject: [PATCH] macos: Default working directory to home dir if launched from app This also introduces a `--working-directory` config flag. --- src/Window.zig | 1 + src/config.zig | 49 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index ff97babe2..d89a85986 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -399,6 +399,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .path = path, .args = &[_][]const u8{path}, .env = &env, + .cwd = config.@"working-directory", .pre_exec = (struct { fn callback(c: *Command) void { const p = c.getData(Pty) orelse unreachable; diff --git a/src/config.zig b/src/config.zig index 859e21d8b..c1dd56c1f 100644 --- a/src/config.zig +++ b/src/config.zig @@ -7,6 +7,11 @@ const passwd = @import("passwd.zig"); const log = std.log.scoped(.config); +/// Used on Unixes for some defaults. +const c = @cImport({ + @cInclude("unistd.h"); +}); + /// Config is the main config struct. These fields map directly to the /// CLI flag names hence we use a lot of `@""` syntax to support hyphens. pub const Config = struct { @@ -34,6 +39,20 @@ pub const Config = struct { /// command: ?[]const u8 = null, + /// The directory to change to after starting the command. + /// + /// The default is "inherit" except in special scenarios listed next. + /// If ghostty can detect it is launched on macOS from launchd + /// (double-clicked), then it defaults to "home". + /// + /// The value of this must be an absolute value or one of the special + /// values below: + /// + /// - "home" - The home directory of the executing user. + /// - "inherit" - The working directory of the launching process. + /// + @"working-directory": ?[]const u8 = null, + /// Key bindings. The format is "trigger=action". Duplicate triggers /// will overwrite previously set values. /// @@ -154,23 +173,41 @@ pub const Config = struct { } } + // The default for the working directory depends on the system. + const wd_default = switch (builtin.os.tag) { + .macos => if (c.getppid() == 1) "home" else "inherit", + else => "inherit", + }; + // If we are missing either a command or home directory, we need // to look up defaults which is kind of expensive. - if (self.command == null) command: { + const wd_home = std.mem.eql(u8, "home", self.@"working-directory" orelse wd_default); + if (self.command == null or wd_home) command: { const alloc = self._arena.?.allocator(); // First look up the command using the SHELL env var. if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| { log.debug("default shell source=env value={s}", .{value}); self.command = value; - break :command; + + // If we don't need the working directory, then we can exit now. + if (!wd_home) break :command; } else |_| {} - // Get the shell from the passwd entry + // We need the passwd entry for the remainder const pw = try passwd.get(alloc); - if (pw.shell) |sh| { - log.debug("default shell src=passwd value={s}", .{sh}); - self.command = sh; + if (self.command == null) { + if (pw.shell) |sh| { + log.debug("default shell src=passwd value={s}", .{sh}); + self.command = sh; + } + } + + if (wd_home) { + if (pw.home) |home| { + log.debug("default working directory src=passwd value={s}", .{home}); + self.@"working-directory" = home; + } } } }