mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-01-01 11:12:16 +00:00
86 lines
2.8 KiB
Zig
86 lines
2.8 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const Allocator = std.mem.Allocator;
|
|
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 },
|
|
.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;
|
|
|
|
// 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});
|
|
}
|