mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-31 20:59:02 +00:00
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>
84 lines
3.3 KiB
Zig
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 {};
|
|
}
|