search for resources dir relative to executing binary on Linux (#245)

* search for resources dir relative to executing binary on Linux

From Andrew in #241:

This works in all 3 of the possible scenarios I can think of on Linux:
 * `zig build` with default prefix into `zig-out`
 * `zig build -p ~/local` (my choice when compiling from source)
 * ghostty packaged for a linux distribution into /usr/bin and /share

* update README
This commit is contained in:
Mitchell Hashimoto
2023-08-07 16:08:27 -07:00
committed by GitHub
parent c139279d47
commit ebbf42eec6
2 changed files with 46 additions and 23 deletions

View File

@ -134,16 +134,20 @@ The currently support shell integration features in Ghostty:
#### Shell Integration Installation and Verification #### Shell Integration Installation and Verification
**On macOS,** Ghostty will automatically inject the shell integration code for `zsh` and Ghostty will automatically inject the shell integration code for `zsh` and
`fish`. Other shells are not supported. You can also manually load them `fish`. Other shells are not supported. You can also manually load them
in many cases (see `src/shell-integration`). **If you want to disable this feature,** in many cases (see `src/shell-integration`). **If you want to disable this feature,**
set `shell-integration = none` in your configuration file. set `shell-integration = none` in your configuration file.
**On Linux,** automatic shell integration requires that you set the **For the automatic shell integration to work,** Ghostty must either be run
`GHOSTTY_RESOURCES_DIR` environment variable to point to the from the macOS app bundle or be installed in a location where the contents of
`zig-out/share` directory after building Ghostty from source. `zig-out/share` are available somewhere above the directory where Ghostty
To validate this directory the file `$GHOSTTY_RESOURCES_DIR/terminfo/ghostty.terminfo` is running from. On Linux, this should automatically work if you run from
should exist. the `zig-out` directory tree structure (a standard FHS-style tree).
You may also manually set the `GHOSTTY_RESOURCES_DIR` to point to the
`zig-out/share` contents. To validate this directory the file
`$GHOSTTY_RESOURCES_DIR/terminfo/ghostty.terminfo` should exist.
To verify shell integration is working, look for the following log lines: To verify shell integration is working, look for the following log lines:

View File

@ -534,14 +534,20 @@ const Subprocess = struct {
// Get our bundled resources directory, if it exists. We use this // Get our bundled resources directory, if it exists. We use this
// for terminfo, shell-integration, etc. // for terminfo, shell-integration, etc.
const resources_key = "GHOSTTY_RESOURCES_DIR"; const resources_key = "GHOSTTY_RESOURCES_DIR";
const resources_dir = if (env.get(resources_key)) |dir| dir: { const resources_dir = dir: {
log.info("using Ghostty resources dir from env var: {s}", .{dir}); if (env.get(resources_key)) |dir| {
break :dir dir; if (dir.len > 0) {
} else if (try resourcesDir(alloc)) |dir| dir: { log.info("using Ghostty resources dir from env var: {s}", .{dir});
log.info("found Ghostty resources dir: {s}", .{dir}); break :dir dir;
try env.put(resources_key, dir); }
break :dir dir; }
} else dir: {
if (try resourcesDir(alloc)) |dir| {
log.info("found Ghostty resources dir: {s}", .{dir});
try env.put(resources_key, dir);
break :dir dir;
}
log.warn("Ghostty resources dir not found, some features disabled", .{}); log.warn("Ghostty resources dir not found, some features disabled", .{});
break :dir null; break :dir null;
}; };
@ -853,7 +859,7 @@ const Subprocess = struct {
/// an arena or something similar. /// an arena or something similar.
fn terminfoDir(alloc: Allocator, base: ?[]const u8) !?[]const u8 { fn terminfoDir(alloc: Allocator, base: ?[]const u8) !?[]const u8 {
const dir = base orelse return null; const dir = base orelse return null;
return try tryDir(alloc, dir, "terminfo"); return try tryDir(alloc, dir, "terminfo", "");
} }
/// Gets the directory to the bundled resources directory, if it /// Gets the directory to the bundled resources directory, if it
@ -862,9 +868,9 @@ const Subprocess = struct {
/// The memory returned can't be easily freed so the alloc should be /// The memory returned can't be easily freed so the alloc should be
/// an arena or something similar. /// an arena or something similar.
fn resourcesDir(alloc: Allocator) !?[]const u8 { fn resourcesDir(alloc: Allocator) !?[]const u8 {
// We only support Mac lookups right now because the terminfo // This is the sentinel value we look for in the path to know
// DB can be embedded directly in the App bundle. // we've found the resources directory.
if (comptime !builtin.target.isDarwin()) return null; const sentinel = "terminfo/67/ghostty";
// Get the path to our running binary // Get the path to our running binary
var exe_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var exe_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
@ -874,7 +880,18 @@ const Subprocess = struct {
// bundle as we expect it. // bundle as we expect it.
while (std.fs.path.dirname(exe)) |dir| { while (std.fs.path.dirname(exe)) |dir| {
exe = dir; exe = dir;
if (try tryDir(alloc, dir, "Contents/Resources")) |v| {
// On MacOS, we look for the app bundle path.
if (comptime builtin.target.isDarwin()) {
if (try tryDir(alloc, dir, "Contents/Resources", sentinel)) |v| {
return v;
}
}
// On all platforms, we look for a /usr/share style path. This
// is valid even on Mac since there is nothing that requires
// Ghostty to be in an app bundle.
if (try tryDir(alloc, dir, "share", sentinel)) |v| {
return v; return v;
} }
} }
@ -882,18 +899,20 @@ const Subprocess = struct {
return null; return null;
} }
/// Little helper to check if the "base/sub" directory exists and /// Little helper to check if the "base/sub/suffix" directory exists and
/// if so, duplicate the path and return it. /// if so, duplicate the "base/sub" path and return it.
fn tryDir( fn tryDir(
alloc: Allocator, alloc: Allocator,
base: []const u8, base: []const u8,
sub: []const u8, sub: []const u8,
suffix: []const u8,
) !?[]const u8 { ) !?[]const u8 {
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "{s}/{s}", .{ base, sub }); const path = try std.fmt.bufPrint(&buf, "{s}/{s}/{s}", .{ base, sub, suffix });
if (std.fs.accessAbsolute(path, .{})) { if (std.fs.accessAbsolute(path, .{})) {
return try alloc.dupe(u8, path); const len = path.len - suffix.len - 1;
return try alloc.dupe(u8, path[0..len]);
} else |_| { } else |_| {
// Folder doesn't exist. If a different error happens its okay // Folder doesn't exist. If a different error happens its okay
// we just ignore it and move on. // we just ignore it and move on.