From ebbf42eec6c42c3e842ea06fe945cbf0aa485533 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 7 Aug 2023 16:08:27 -0700 Subject: [PATCH] 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 --- README.md | 16 +++++++++----- src/termio/Exec.zig | 53 ++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 15970c278..f573bf242 100644 --- a/README.md +++ b/README.md @@ -134,16 +134,20 @@ The currently support shell integration features in Ghostty: #### 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 in many cases (see `src/shell-integration`). **If you want to disable this feature,** set `shell-integration = none` in your configuration file. -**On Linux,** automatic shell integration requires that you set the -`GHOSTTY_RESOURCES_DIR` environment variable to point to the -`zig-out/share` directory after building Ghostty from source. -To validate this directory the file `$GHOSTTY_RESOURCES_DIR/terminfo/ghostty.terminfo` -should exist. +**For the automatic shell integration to work,** Ghostty must either be run +from the macOS app bundle or be installed in a location where the contents of +`zig-out/share` are available somewhere above the directory where Ghostty +is running from. On Linux, this should automatically work if you run from +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: diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index f4fade723..a8004b2d1 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -534,14 +534,20 @@ const Subprocess = struct { // Get our bundled resources directory, if it exists. We use this // for terminfo, shell-integration, etc. const resources_key = "GHOSTTY_RESOURCES_DIR"; - const resources_dir = if (env.get(resources_key)) |dir| dir: { - log.info("using Ghostty resources dir from env var: {s}", .{dir}); - break :dir dir; - } else if (try resourcesDir(alloc)) |dir| dir: { - log.info("found Ghostty resources dir: {s}", .{dir}); - try env.put(resources_key, dir); - break :dir dir; - } else dir: { + const resources_dir = dir: { + if (env.get(resources_key)) |dir| { + if (dir.len > 0) { + log.info("using Ghostty resources dir from env var: {s}", .{dir}); + break :dir 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", .{}); break :dir null; }; @@ -853,7 +859,7 @@ const Subprocess = struct { /// an arena or something similar. fn terminfoDir(alloc: Allocator, base: ?[]const u8) !?[]const u8 { 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 @@ -862,9 +868,9 @@ const Subprocess = struct { /// The memory returned can't be easily freed so the alloc should be /// an arena or something similar. fn resourcesDir(alloc: Allocator) !?[]const u8 { - // We only support Mac lookups right now because the terminfo - // DB can be embedded directly in the App bundle. - if (comptime !builtin.target.isDarwin()) return null; + // This is the sentinel value we look for in the path to know + // we've found the resources directory. + const sentinel = "terminfo/67/ghostty"; // Get the path to our running binary var exe_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; @@ -874,7 +880,18 @@ const Subprocess = struct { // bundle as we expect it. while (std.fs.path.dirname(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; } } @@ -882,18 +899,20 @@ const Subprocess = struct { return null; } - /// Little helper to check if the "base/sub" directory exists and - /// if so, duplicate the path and return it. + /// Little helper to check if the "base/sub/suffix" directory exists and + /// if so, duplicate the "base/sub" path and return it. fn tryDir( alloc: Allocator, base: []const u8, sub: []const u8, + suffix: []const u8, ) !?[]const u8 { 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, .{})) { - return try alloc.dupe(u8, path); + const len = path.len - suffix.len - 1; + return try alloc.dupe(u8, path[0..len]); } else |_| { // Folder doesn't exist. If a different error happens its okay // we just ignore it and move on.