Files
ghostty/src/os/open.zig
Chris Marchesi e8525c0fd9 Update to Zig 0.16.0
This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.

Key parts:

* In addition to its previous responsibilities, the global state now
  houses state for global I/O implementations and the process
  environment. It is now also utilized in the main application along
  with the C library. Where necessary, global state is isolated from key
  parts of the implementation (e.g., in libghostty subsystems), and it's
  expected that this list will grow.

* We currently manage our own C translation layer where necessary. In
  these cases, cImport has been removed in favor of the new external
  translate-c package. Due to fixes that have needed be made to properly
  translate the dependencies that were swapped out, as mentioned, we
  have had to backport fixes from the current translate-c package (and
  the upstream Arocc dependency). We will host this ourselves until Zig
  0.17.0 is released with these fixes.

* Where necessary (only a small number of cases), some stdlib code from
  0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in
  lib/compat.

Co-authored-by: Leah Amelia Chen <hi@pluie.me>
2026-07-21 12:35:05 -07:00

84 lines
3.3 KiB
Zig

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 global = @import("../global.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(
kind: apprt.action.OpenUrl.Kind,
url: []const u8,
) !void {
var spawn_opts: std.process.SpawnOptions = switch (builtin.os.tag) {
.linux, .freebsd => .{ .argv = &.{ "xdg-open", url } },
.windows => .{ .argv = &.{ "rundll32", "url.dll,FileProtocolHandler", url } },
.macos => switch (kind) {
.text => .{ .argv = &.{ "open", "-t", url } },
.html, .unknown => .{ .argv = &.{ "open", url } },
},
.ios => return error.Unimplemented,
else => @compileError("unsupported OS"),
};
// Ignore anything from stdout. This must be set before spawning the
// process.
spawn_opts.stdout = .ignore;
// Pipe stderr so we can log the stderr from the command. This must be set
// before spawning the process.
spawn_opts.stderr = .pipe;
const exe = if (comptime build_config.snap) local_env: {
// 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.
//
// Note that `spawn` copies the passed in `Environ.Map` into a
// fresh `Environ` block, so this is safe to release immediately
// after spawn.
var environ_map = try global.environMap();
defer environ_map.deinit();
_ = environ_map.orderedRemove("LD_LIBRARY_PATH");
spawn_opts.environ_map = &environ_map;
break :local_env try std.process.spawn(global.io(), spawn_opts);
} else
// Non-snap releases don't need to alter the env.
try std.process.spawn(global.io(), spawn_opts);
const thread = try std.Thread.spawn(.{}, openThread, .{ global.io(), exe });
thread.detach();
}
fn openThread(io: std.Io, 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(io, &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(io) catch {};
}