Something went wrong. Try again.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798const 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.////// 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"), };
// Ignore anything from stdout. This must be set before spawning the // process. exe.stdout_behavior = .Ignore; // Pipe stderr so we can log the stderr from the command. This must be set // before spawning the process. 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, .{exe}); thread.detach();}
fn openThread(exe_: std.process.Child) void { // 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_; if (exe.stderr) |stderr| { var buffer: [256]u8 = undefined; var stream = stderr.readerStreaming(&buffer); const reader = &stream.interface; while (true) { const line = reader.takeDelimiterExclusive('\n') catch |outer| switch (outer) { error.EndOfStream => break, error.ReadFailed => break, error.StreamTooLong => reader.take(buffer.len) catch |inner| switch (inner) { error.ReadFailed => break, error.EndOfStream => break, }, }; log.warn("open stderr={s}", .{line}); } } _ = exe.wait() catch {};}