aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/build/GhosttyResources.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/build/GhosttyResources.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/build/GhosttyResources.zig')
-rw-r--r--vendor/libghostty-vt/src/build/GhosttyResources.zig442
1 files changed, 442 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/build/GhosttyResources.zig b/vendor/libghostty-vt/src/build/GhosttyResources.zig
new file mode 100644
index 00000000..6f857655
--- /dev/null
+++ b/vendor/libghostty-vt/src/build/GhosttyResources.zig
@@ -0,0 +1,442 @@
+const GhosttyResources = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const Config = @import("Config.zig");
+const RunStep = std.Build.Step.Run;
+const SharedDeps = @import("SharedDeps.zig");
+
+steps: []*std.Build.Step,
+
+pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !GhosttyResources {
+ var steps: std.ArrayList(*std.Build.Step) = .empty;
+ errdefer steps.deinit(b.allocator);
+
+ // This is the exe used to generate some build data.
+ const build_data_exe = b.addExecutable(.{
+ .name = "ghostty-build-data",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/main_build_data.zig"),
+ .target = b.graph.host,
+ .strip = false,
+ .omit_frame_pointer = false,
+ .unwind_tables = .sync,
+ }),
+ });
+ build_data_exe.linkLibC();
+
+ deps.help_strings.addImport(build_data_exe);
+
+ // Terminfo
+ terminfo: {
+ const os_tag = cfg.target.result.os.tag;
+ const terminfo_share_dir = if (os_tag == .freebsd)
+ "site-terminfo"
+ else
+ "terminfo";
+
+ // Encode our terminfo
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+terminfo");
+ const wf = b.addWriteFiles();
+ const source = wf.addCopyFile(run.captureStdOut(), "ghostty.terminfo");
+
+ if (cfg.emit_terminfo) {
+ const source_install = b.addInstallFile(
+ source,
+ if (os_tag == .freebsd)
+ "share/site-terminfo/ghostty.terminfo"
+ else
+ "share/terminfo/ghostty.terminfo",
+ );
+
+ try steps.append(b.allocator, &source_install.step);
+ }
+
+ // Windows doesn't have the binaries below.
+ if (os_tag == .windows) break :terminfo;
+
+ // Convert to termcap source format if thats helpful to people and
+ // install it. The resulting value here is the termcap source in case
+ // that is used for other commands.
+ if (cfg.emit_termcap) {
+ const run_step = RunStep.create(b, "infotocap");
+ run_step.addArg("infotocap");
+ run_step.addFileArg(source);
+ const out_source = run_step.captureStdOut();
+ _ = run_step.captureStdErr(); // so we don't see stderr
+
+ const cap_install = b.addInstallFile(
+ out_source,
+ if (os_tag == .freebsd)
+ "share/site-terminfo/ghostty.termcap"
+ else
+ "share/terminfo/ghostty.termcap",
+ );
+
+ try steps.append(b.allocator, &cap_install.step);
+ }
+
+ // Compile the terminfo source into a terminfo database
+ {
+ const run_step = RunStep.create(b, "tic");
+ run_step.addArgs(&.{ "tic", "-x", "-o" });
+ const path = run_step.addOutputFileArg(terminfo_share_dir);
+
+ run_step.addFileArg(source);
+ _ = run_step.captureStdErr(); // so we don't see stderr
+
+ // Ensure that `share/terminfo` is a directory, otherwise the `cp
+ // -R` will create a file named `share/terminfo`
+ const mkdir_step = RunStep.create(b, "make share/terminfo directory");
+ switch (cfg.target.result.os.tag) {
+ // windows mkdir shouldn't need "-p"
+ .windows => mkdir_step.addArgs(&.{"mkdir"}),
+ else => mkdir_step.addArgs(&.{ "mkdir", "-p" }),
+ }
+
+ mkdir_step.addArg(b.fmt(
+ "{s}/share/{s}",
+ .{ b.install_path, terminfo_share_dir },
+ ));
+
+ try steps.append(b.allocator, &mkdir_step.step);
+
+ // Use cp -R instead of Step.InstallDir because we need to preserve
+ // symlinks in the terminfo database. Zig's InstallDir step doesn't
+ // handle symlinks correctly yet.
+ const copy_step = RunStep.create(b, "copy terminfo db");
+ copy_step.addArgs(&.{ "cp", "-R" });
+ copy_step.addFileArg(path);
+ copy_step.addArg(b.fmt("{s}/share", .{b.install_path}));
+ copy_step.step.dependOn(&mkdir_step.step);
+ try steps.append(b.allocator, &copy_step.step);
+ }
+ }
+
+ // Shell-integration
+ {
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = b.path("src/shell-integration"),
+ .install_dir = .{ .custom = "share" },
+ .install_subdir = b.pathJoin(&.{ "ghostty", "shell-integration" }),
+ .exclude_extensions = &.{".md"},
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+
+ // Themes
+ if (cfg.emit_themes) {
+ if (b.lazyDependency("iterm2_themes", .{})) |upstream| {
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = upstream.path(""),
+ .install_dir = .{ .custom = "share" },
+ .install_subdir = b.pathJoin(&.{ "ghostty", "themes" }),
+ .exclude_extensions = &.{".md"},
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+ }
+
+ // Fish shell completions
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+fish");
+ const wf = b.addWriteFiles();
+ _ = wf.addCopyFile(run.captureStdOut(), "ghostty.fish");
+
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/fish/vendor_completions.d",
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+
+ // zsh shell completions
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+zsh");
+ const wf = b.addWriteFiles();
+ _ = wf.addCopyFile(run.captureStdOut(), "_ghostty");
+
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/zsh/site-functions",
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+
+ // bash shell completions
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+bash");
+ const wf = b.addWriteFiles();
+ _ = wf.addCopyFile(run.captureStdOut(), "ghostty.bash");
+
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/bash-completion/completions",
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+
+ // Vim and Neovim plugin
+ {
+ const wf = b.addWriteFiles();
+
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+vim-syntax");
+ _ = wf.addCopyFile(run.captureStdOut(), "syntax/ghostty.vim");
+ }
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+vim-ftdetect");
+ _ = wf.addCopyFile(run.captureStdOut(), "ftdetect/ghostty.vim");
+ }
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+vim-ftplugin");
+ _ = wf.addCopyFile(run.captureStdOut(), "ftplugin/ghostty.vim");
+ }
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+vim-compiler");
+ _ = wf.addCopyFile(run.captureStdOut(), "compiler/ghostty.vim");
+ }
+
+ const vim_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/vim/vimfiles",
+ });
+ try steps.append(b.allocator, &vim_step.step);
+
+ const neovim_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/nvim/site",
+ });
+ try steps.append(b.allocator, &neovim_step.step);
+ }
+
+ // Sublime syntax highlighting for bat cli tool
+ // NOTE: The current implementation requires symlinking the generated
+ // 'ghostty.sublime-syntax' file from zig-out to the '~.config/bat/syntaxes'
+ // directory. The syntax then needs to be mapped to the correct language in
+ // the config file within the '~.config/bat' directory
+ // (ex: --map-syntax "/Users/user/.config/ghostty/config.ghostty:Ghostty Config").
+ {
+ const run = b.addRunArtifact(build_data_exe);
+ run.addArg("+sublime");
+ const wf = b.addWriteFiles();
+ _ = wf.addCopyFile(run.captureStdOut(), "ghostty.sublime-syntax");
+
+ const install_step = b.addInstallDirectory(.{
+ .source_dir = wf.getDirectory(),
+ .install_dir = .prefix,
+ .install_subdir = "share/bat/syntaxes",
+ });
+ try steps.append(b.allocator, &install_step.step);
+ }
+
+ // App (Linux)
+ if (cfg.target.result.os.tag == .linux) try addLinuxAppResources(
+ b,
+ cfg,
+ &steps,
+ );
+
+ return .{ .steps = steps.items };
+}
+
+/// Add the resource files needed to make Ghostty a proper
+/// Linux desktop application (for various desktop environments).
+fn addLinuxAppResources(
+ b: *std.Build,
+ cfg: *const Config,
+ steps: *std.ArrayList(*std.Build.Step),
+) !void {
+ assert(cfg.target.result.os.tag == .linux);
+
+ // Background:
+ // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
+
+ const name = b.fmt("Ghostty{s}", .{
+ switch (cfg.optimize) {
+ .Debug, .ReleaseSafe => " (Debug)",
+ .ReleaseFast, .ReleaseSmall => "",
+ },
+ });
+
+ const app_id = b.fmt("com.mitchellh.ghostty{s}", .{
+ switch (cfg.optimize) {
+ .Debug, .ReleaseSafe => "-debug",
+ .ReleaseFast, .ReleaseSmall => "",
+ },
+ });
+
+ const exe_abs_path = b.fmt(
+ "{s}/bin/ghostty",
+ .{b.install_prefix},
+ );
+
+ // The templates that we will process. The templates are in
+ // cmake format and will be processed and saved to the
+ // second element of the tuple.
+ const Template = struct { std.Build.LazyPath, []const u8 };
+ const templates: []const Template = templates: {
+ var ts: std.ArrayList(Template) = .empty;
+ defer ts.deinit(b.allocator);
+
+ // Desktop file so that we have an icon and other metadata
+ try ts.append(b.allocator, .{
+ b.path("dist/linux/app.desktop.in"),
+ b.fmt("share/applications/{s}.desktop", .{app_id}),
+ });
+
+ // Service for DBus activation.
+ try ts.append(b.allocator, .{
+ if (cfg.flatpak)
+ b.path("dist/linux/dbus.service.flatpak.in")
+ else
+ b.path("dist/linux/dbus.service.in"),
+ b.fmt("share/dbus-1/services/{s}.service", .{app_id}),
+ });
+
+ // `systemd` user service. This is kind of nasty but `systemd` looks for
+ // user services in different paths depending on if we are installed
+ // as a system package or not (lib vs. share) so we have to handle that
+ // here. We might be able to get away with always installing to both
+ // because it only ever searches in one... but I don't want to do that
+ // hack until we have to.
+ //
+ // The XDG Freedesktop Portal has a major undocumented requirement
+ // for programs that are launched/controlled by `systemd` to interact
+ // with the Portal. The unit must be named `app-<appid>.service`. The
+ // Portal uses the `systemd` unit name figure out what the program's
+ // application ID is and it will only look at unit names that begin with
+ // `app-`. I can find no place that this is documented other than by
+ // inspecting the code or the issue and PR that introduced this feature.
+ // See the following code:
+ //
+ // https://github.com/flatpak/xdg-desktop-portal/blob/7d4d48cf079147c8887da17ec6c3954acd5a285c/src/xdp-utils.c#L152-L220
+ if (!cfg.flatpak) try ts.append(b.allocator, .{
+ b.path("dist/linux/systemd.service.in"),
+ b.fmt(
+ "{s}/systemd/user/app-{s}.service",
+ .{
+ if (b.graph.system_package_mode) "lib" else "share",
+ app_id,
+ },
+ ),
+ });
+
+ // AppStream metainfo so that application has rich metadata
+ // within app stores
+ try ts.append(b.allocator, .{
+ b.path("dist/linux/com.mitchellh.ghostty.metainfo.xml.in"),
+ b.fmt("share/metainfo/{s}.metainfo.xml", .{app_id}),
+ });
+
+ break :templates try ts.toOwnedSlice(b.allocator);
+ };
+
+ // Process all our templates
+ for (templates) |template| {
+ const tpl = b.addConfigHeader(.{
+ .style = .{ .cmake = template[0] },
+ }, .{
+ .NAME = name,
+ .APPID = app_id,
+ .GHOSTTY = exe_abs_path,
+ });
+
+ // Template output has a single header line we want to remove.
+ // We use `tail` to do it since its part of the POSIX standard.
+ const tail = b.addSystemCommand(&.{ "tail", "-n", "+2" });
+ tail.setStdIn(.{ .lazy_path = tpl.getOutput() });
+
+ const copy = b.addInstallFile(
+ tail.captureStdOut(),
+ template[1],
+ );
+
+ try steps.append(b.allocator, &copy.step);
+ }
+
+ // Right click menu action for Plasma desktop
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("dist/linux/ghostty_dolphin.desktop"),
+ "share/kio/servicemenus/com.mitchellh.ghostty.desktop",
+ ).step);
+
+ // Right click menu action for Nautilus. Note that this _must_ be named
+ // `ghostty.py`. Using the full app id causes problems (see #5468).
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("dist/linux/ghostty_nautilus.py"),
+ "share/nautilus-python/extensions/ghostty.py",
+ ).step);
+
+ // Various icons that our application can use, including the icon
+ // that will be used for the desktop.
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/16.png"),
+ "share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/32.png"),
+ "share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/128.png"),
+ "share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/256.png"),
+ "share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/512.png"),
+ "share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png",
+ ).step);
+ // Flatpaks only support icons up to 512x512.
+ if (!cfg.flatpak) {
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/1024.png"),
+ "share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png",
+ ).step);
+ }
+
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/32.png"),
+ "share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/64.png"),
+ "share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/256.png"),
+ "share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png",
+ ).step);
+ try steps.append(b.allocator, &b.addInstallFile(
+ b.path("images/gnome/512.png"),
+ "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png",
+ ).step);
+}
+
+pub fn install(self: *const GhosttyResources) void {
+ const b = self.steps[0].owner;
+ self.addStepDependencies(b.getInstallStep());
+}
+
+pub fn addStepDependencies(
+ self: *const GhosttyResources,
+ other_step: *std.Build.Step,
+) void {
+ for (self.steps) |step| other_step.dependOn(step);
+}