aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/os/resourcesdir.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/os/resourcesdir.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/os/resourcesdir.zig')
-rw-r--r--vendor/libghostty-vt/src/os/resourcesdir.zig139
1 files changed, 139 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/os/resourcesdir.zig b/vendor/libghostty-vt/src/os/resourcesdir.zig
new file mode 100644
index 00000000..278de44f
--- /dev/null
+++ b/vendor/libghostty-vt/src/os/resourcesdir.zig
@@ -0,0 +1,139 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Allocator = std.mem.Allocator;
+
+pub const ResourcesDir = struct {
+ /// Avoid accessing these directly, use the app() and host() methods instead.
+ app_path: ?[]const u8 = null,
+ host_path: ?[]const u8 = null,
+
+ /// Free resources held. Requires the same allocator as when resourcesDir()
+ /// is called.
+ pub fn deinit(self: *ResourcesDir, alloc: Allocator) void {
+ if (self.app_path) |p| alloc.free(p);
+ if (self.host_path) |p| alloc.free(p);
+ }
+
+ /// Get the directory to the bundled resources directory accessible
+ /// by the application.
+ pub fn app(self: *ResourcesDir) ?[]const u8 {
+ return self.app_path;
+ }
+
+ /// Get the directory to the bundled resources directory accessible
+ /// by the host environment (i.e. for sandboxed applications). The
+ /// returned directory might not be accessible from the application
+ /// itself.
+ ///
+ /// In non-sandboxed environment, this should be the same as app().
+ pub fn host(self: *ResourcesDir) ?[]const u8 {
+ return self.host_path orelse self.app_path;
+ }
+};
+
+/// Gets the directory to the bundled resources directory, if it
+/// exists (not all platforms or packages have it). The output is
+/// owned by the caller.
+///
+/// This is highly Ghostty-specific and can likely be generalized at
+/// some point but we can cross that bridge if we ever need to.
+pub fn resourcesDir(alloc: Allocator) !ResourcesDir {
+ // Use the GHOSTTY_RESOURCES_DIR environment variable in release builds.
+ //
+ // In debug builds we try using terminfo detection first instead, since
+ // if debug Ghostty is launched by an older version of Ghostty, it
+ // would inherit the old, stale resources of older Ghostty instead of the
+ // freshly built ones under zig-out/share/ghostty.
+ //
+ // Note: we ALWAYS want to allocate here because the result is always
+ // freed, do not try to use internal_os.getenv or posix getenv.
+ if (comptime builtin.mode != .Debug) {
+ if (std.process.getEnvVarOwned(alloc, "GHOSTTY_RESOURCES_DIR")) |dir| {
+ if (dir.len > 0) return .{ .app_path = dir };
+ } else |err| switch (err) {
+ error.EnvironmentVariableNotFound => {},
+ else => return err,
+ }
+ }
+
+ // This is the sentinel value we look for in the path to know
+ // we've found the resources directory.
+ const sentinels = switch (comptime builtin.target.os.tag) {
+ .windows => .{"terminfo/ghostty.terminfo"},
+ .macos => .{"terminfo/78/xterm-ghostty"},
+ .freebsd => .{ "site-terminfo/g/ghostty", "site-terminfo/x/xterm-ghostty" },
+ else => .{ "terminfo/g/ghostty", "terminfo/x/xterm-ghostty" },
+ };
+
+ // Get the path to our running binary
+ var exe_buf: [std.fs.max_path_bytes]u8 = undefined;
+ var exe: []const u8 = std.fs.selfExePath(&exe_buf) catch return .{};
+
+ // We have an exe path! Climb the tree looking for the terminfo
+ // bundle as we expect it.
+ var dir_buf: [std.fs.max_path_bytes]u8 = undefined;
+ while (std.fs.path.dirname(exe)) |dir| {
+ exe = dir;
+
+ // On MacOS, we look for the app bundle path.
+ if (comptime builtin.target.os.tag.isDarwin()) {
+ inline for (sentinels) |sentinel| {
+ if (try maybeDir(&dir_buf, dir, "Contents/Resources", sentinel)) |v| {
+ return .{ .app_path = try std.fs.path.join(alloc, &.{ v, "ghostty" }) };
+ }
+ }
+ }
+
+ // On all platforms (except BSD), 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.
+ inline for (sentinels) |sentinel| {
+ if (try maybeDir(
+ &dir_buf,
+ dir,
+ if (builtin.target.os.tag == .freebsd) "local/share" else "share",
+ sentinel,
+ )) |v| {
+ return .{ .app_path = try std.fs.path.join(alloc, &.{ v, "ghostty" }) };
+ }
+ }
+ }
+
+ // If terminfo detection failed in debug builds (somehow),
+ // fallback and use the provided resources dir.
+ if (comptime builtin.mode == .Debug) {
+ if (std.process.getEnvVarOwned(alloc, "GHOSTTY_RESOURCES_DIR")) |dir| {
+ if (dir.len > 0) return .{ .app_path = dir };
+ } else |err| switch (err) {
+ error.EnvironmentVariableNotFound => {},
+ else => return err,
+ }
+ }
+
+ return .{};
+}
+
+/// Little helper to check if the "base/sub/suffix" directory exists and
+/// if so return true. The "suffix" is just used as a way to verify a directory
+/// seems roughly right.
+///
+/// "buf" must be large enough to fit base + sub + suffix. This is generally
+/// max_path_bytes so its not a big deal.
+pub fn maybeDir(
+ buf: []u8,
+ base: []const u8,
+ sub: []const u8,
+ suffix: []const u8,
+) !?[]const u8 {
+ const path = try std.fmt.bufPrint(buf, "{s}/{s}/{s}", .{ base, sub, suffix });
+
+ if (std.fs.accessAbsolute(path, .{})) {
+ const len = path.len - suffix.len - 1;
+ return buf[0..len];
+ } else |_| {
+ // Folder doesn't exist. If a different error happens its okay
+ // we just ignore it and move on.
+ }
+
+ return null;
+}