aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/os/open.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/open.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/os/open.zig')
-rw-r--r--vendor/libghostty-vt/src/os/open.zig97
1 files changed, 97 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/os/open.zig b/vendor/libghostty-vt/src/os/open.zig
new file mode 100644
index 00000000..0cead555
--- /dev/null
+++ b/vendor/libghostty-vt/src/os/open.zig
@@ -0,0 +1,97 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Allocator = std.mem.Allocator;
+const build_config = @import("../build_config.zig");
+const apprt = @import("../apprt.zig");
+
+const log = std.log.scoped(.@"os-open");
+
+/// Open a URL in the default handling application.
+///
+/// Any output on stderr is logged as a warning in the application logs.
+/// Output on stdout is ignored. The allocator is used to buffer the
+/// log output and may allocate from another thread.
+///
+/// This function is purposely simple for the sake of providing
+/// some portable way to open URLs. If you are implementing an
+/// apprt for Ghostty, you should consider doing something special-cased
+/// for your platform.
+pub fn open(
+ alloc: Allocator,
+ kind: apprt.action.OpenUrl.Kind,
+ url: []const u8,
+) !void {
+ var exe: std.process.Child = switch (builtin.os.tag) {
+ .linux, .freebsd => .init(
+ &.{ "xdg-open", url },
+ alloc,
+ ),
+
+ .windows => .init(
+ &.{ "rundll32", "url.dll,FileProtocolHandler", url },
+ alloc,
+ ),
+
+ .macos => .init(
+ switch (kind) {
+ .text => &.{ "open", "-t", url },
+ .html, .unknown => &.{ "open", url },
+ },
+ alloc,
+ ),
+
+ .ios => return error.Unimplemented,
+ else => @compileError("unsupported OS"),
+ };
+
+ // Pipe stdout/stderr so we can collect output from the command.
+ // This must be set before spawning the process.
+ exe.stdout_behavior = .Pipe;
+ exe.stderr_behavior = .Pipe;
+
+ // In the snap on Linux the launcher exports LD_LIBRARY_PATH pointing at
+ // the snap's bundled libraries. Leaking this into child process can
+ // can be problematic, so let's drop it from the env
+ var snap_env: std.process.EnvMap = if (comptime build_config.snap) blk: {
+ var env = try std.process.getEnvMap(alloc);
+ env.remove("LD_LIBRARY_PATH");
+ break :blk env;
+ } else undefined;
+ defer if (comptime build_config.snap) snap_env.deinit();
+ if (comptime build_config.snap) exe.env_map = &snap_env;
+
+ // Spawn the process on our same thread so we can detect failure
+ // quickly.
+ try exe.spawn();
+
+ // Create a thread that handles collecting output and reaping
+ // the process. This is done in a separate thread because SOME
+ // open implementations block and some do not. It's easier to just
+ // spawn a thread to handle this so that we never block.
+ const thread = try std.Thread.spawn(.{}, openThread, .{ alloc, exe });
+ thread.detach();
+}
+
+fn openThread(alloc: Allocator, exe_: std.process.Child) !void {
+ // 50 KiB is the default value used by std.process.Child.run and should
+ // be enough to get the output we care about.
+ const output_max_size = 50 * 1024;
+
+ var stdout: std.ArrayListUnmanaged(u8) = .{};
+ var stderr: std.ArrayListUnmanaged(u8) = .{};
+ defer {
+ stdout.deinit(alloc);
+ stderr.deinit(alloc);
+ }
+
+ // Copy the exe so it is non-const. This is necessary because wait()
+ // requires a mutable reference and we can't have one as a thread
+ // param.
+ var exe = exe_;
+ try exe.collectOutput(alloc, &stdout, &stderr, output_max_size);
+ _ = try exe.wait();
+
+ // If we have any stderr output we log it. This makes it easier for
+ // users to debug why some open commands may not work as expected.
+ if (stderr.items.len > 0) log.warn("wait stderr={s}", .{stderr.items});
+}