aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/global.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/global.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/global.zig')
-rw-r--r--vendor/libghostty-vt/src/global.zig235
1 files changed, 235 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/global.zig b/vendor/libghostty-vt/src/global.zig
new file mode 100644
index 00000000..29eaf5f3
--- /dev/null
+++ b/vendor/libghostty-vt/src/global.zig
@@ -0,0 +1,235 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const build_config = @import("build_config.zig");
+const cli = @import("cli.zig");
+const internal_os = @import("os/main.zig");
+const fontconfig = @import("fontconfig");
+const glslang = @import("glslang");
+const harfbuzz = @import("harfbuzz");
+const oni = @import("oniguruma");
+const crash = @import("crash/main.zig");
+const renderer = @import("renderer.zig");
+const apprt = @import("apprt.zig");
+
+/// We export the xev backend we want to use so that the rest of
+/// Ghostty can import this once and have access to the proper
+/// backend.
+pub const xev = @import("xev").Dynamic;
+
+/// Global process state. This is initialized in main() for exe artifacts
+/// and by ghostty_init() for lib artifacts. This should ONLY be used by
+/// the C API. The Zig API should NOT use any global state and should
+/// rely on allocators being passed in as parameters.
+pub var state: GlobalState = undefined;
+
+/// This represents the global process state. There should only
+/// be one of these at any given moment. This is extracted into a dedicated
+/// struct because it is reused by main and the static C lib.
+pub const GlobalState = struct {
+ const GPA = std.heap.GeneralPurposeAllocator(.{});
+
+ gpa: ?GPA,
+ alloc: std.mem.Allocator,
+ action: ?cli.ghostty.Action,
+ logging: Logging,
+ rlimits: ResourceLimits = .{},
+
+ /// The app resources directory, equivalent to zig-out/share when we build
+ /// from source. This is null if we can't detect it.
+ resources_dir: internal_os.ResourcesDir,
+
+ /// Where logging should go
+ pub const Logging = packed struct {
+ /// Whether to log to stderr. For lib mode we always disable stderr
+ /// logging by default. Otherwise it's enabled by default.
+ stderr: bool = build_config.app_runtime != .none,
+ /// Whether to log to macOS's unified logging. Enabled by default
+ /// on macOS.
+ macos: bool = builtin.os.tag.isDarwin(),
+ };
+
+ /// Initialize the global state.
+ pub fn init(self: *GlobalState) !void {
+ // const start = try std.time.Instant.now();
+ // const start_micro = std.time.microTimestamp();
+ // defer {
+ // const end = std.time.Instant.now() catch unreachable;
+ // // "[updateFrame critical time] <START us>\t<TIME_TAKEN us>"
+ // std.log.err("[global init time] start={}us duration={}ns", .{ start_micro, end.since(start) / std.time.ns_per_us });
+ // }
+
+ // Initialize ourself to nothing so we don't have any extra state.
+ // IMPORTANT: this MUST be initialized before any log output because
+ // the log function uses the global state.
+ self.* = .{
+ .gpa = null,
+ .alloc = undefined,
+ .action = null,
+ .logging = .{},
+ .rlimits = .{},
+ .resources_dir = .{},
+ };
+ errdefer self.deinit();
+
+ self.gpa = gpa: {
+ // Use the libc allocator if it is available because it is WAY
+ // faster than GPA. We only do this in release modes so that we
+ // can get easy memory leak detection in debug modes.
+ if (builtin.link_libc) {
+ if (switch (builtin.mode) {
+ .ReleaseSafe, .ReleaseFast => true,
+
+ // We also use it if we can detect we're running under
+ // Valgrind since Valgrind only instruments the C allocator
+ else => std.valgrind.runningOnValgrind() > 0,
+ }) break :gpa null;
+ }
+
+ break :gpa GPA{};
+ };
+
+ self.alloc = if (self.gpa) |*value|
+ value.allocator()
+ else if (builtin.link_libc)
+ std.heap.c_allocator
+ else
+ unreachable;
+
+ // We first try to parse any action that we may be executing.
+ self.action = try cli.action.detectArgs(
+ cli.ghostty.Action,
+ self.alloc,
+ );
+
+ // If we have an action executing, we disable logging by default
+ // since we write to stderr we don't want logs messing up our
+ // output.
+ if (self.action != null) self.logging.stderr = false;
+
+ // I don't love the env var name but I don't have it in my heart
+ // to parse CLI args 3 times (once for actions, once for config,
+ // maybe once for logging) so for now this is an easy way to do
+ // this. Env vars are useful for logging too because they are
+ // easy to set.
+ if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
+ defer v.deinit(self.alloc);
+ self.logging = cli.args.parsePackedStruct(Logging, v.value) catch .{};
+ }
+
+ // Setup our signal handlers before logging
+ initSignals();
+
+ // Setup our Xev backend if we're dynamic
+ if (comptime xev.dynamic) xev.detect() catch |err| {
+ std.log.warn("failed to detect xev backend, falling back to " ++
+ "most compatible backend err={}", .{err});
+ };
+
+ // Output some debug information right away
+ std.log.info("ghostty version={s}", .{build_config.version_string});
+ std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
+ std.log.info("runtime={}", .{build_config.app_runtime});
+ std.log.info("font_backend={}", .{build_config.font_backend});
+ if (comptime build_config.font_backend.hasHarfbuzz()) {
+ std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
+ }
+ if (comptime build_config.font_backend.hasFontconfig()) {
+ std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
+ }
+ std.log.info("renderer={}", .{renderer.Renderer});
+ std.log.info("libxev default backend={t}", .{xev.backend});
+
+ // As early as possible, initialize our resource limits.
+ self.rlimits = .init();
+
+ // Initialize our crash reporting.
+ crash.init(self.alloc) catch |err| {
+ std.log.warn(
+ "sentry init failed, no crash capture available err={}",
+ .{err},
+ );
+ };
+
+ // const sentrylib = @import("sentry");
+ // if (sentrylib.captureEvent(sentrylib.Value.initMessageEvent(
+ // .info,
+ // null,
+ // "hello, world",
+ // ))) |uuid| {
+ // std.log.warn("uuid={s}", .{uuid.string()});
+ // } else std.log.warn("failed to capture event", .{});
+
+ // We need to make sure the process locale is set properly. Locale
+ // affects a lot of behaviors in a shell.
+ try internal_os.ensureLocale(self.alloc);
+
+ // Initialize glslang for shader compilation
+ try glslang.init();
+
+ // Initialize oniguruma for regex
+ try oni.init(&.{oni.Encoding.utf8});
+
+ // Find our resources directory once for the app so every launch
+ // hereafter can use this cached value.
+ self.resources_dir = try apprt.runtime.resourcesDir(self.alloc);
+ errdefer self.resources_dir.deinit(self.alloc);
+
+ // Setup i18n
+ if (self.resources_dir.app()) |v| internal_os.i18n.init(v) catch |err| {
+ std.log.warn("failed to init i18n, translations will not be available err={}", .{err});
+ };
+ }
+
+ /// Cleans up the global state. This doesn't _need_ to be called but
+ /// doing so in dev modes will check for memory leaks.
+ pub fn deinit(self: *GlobalState) void {
+ self.resources_dir.deinit(self.alloc);
+
+ // Flush our crash logs
+ crash.deinit();
+
+ if (self.gpa) |*value| {
+ // We want to ensure that we deinit the GPA because this is
+ // the point at which it will output if there were safety violations.
+ _ = value.deinit();
+ }
+ }
+
+ fn initSignals() void {
+ // Only posix systems.
+ if (comptime builtin.os.tag == .windows) return;
+
+ const p = std.posix;
+
+ var sa: p.Sigaction = .{
+ .handler = .{ .handler = p.SIG.IGN },
+ .mask = p.sigemptyset(),
+ .flags = 0,
+ };
+
+ // We ignore SIGPIPE because it is a common signal we may get
+ // due to how we implement termio. When a terminal is closed we
+ // often write to a broken pipe to exit the read thread. This should
+ // be fixed one day but for now this helps make this a bit more
+ // robust.
+ p.sigaction(p.SIG.PIPE, &sa, null);
+ }
+};
+
+/// Maintains the Unix resource limits that we set for our process. This
+/// can be used to restore the limits to their original values.
+pub const ResourceLimits = struct {
+ nofile: ?internal_os.rlimit = null,
+
+ pub fn init() ResourceLimits {
+ return .{
+ // Maximize the number of file descriptors we can have open
+ // because we can consume a lot of them if we make many terminals.
+ .nofile = internal_os.fixMaxFiles(),
+ };
+ }
+
+ pub fn restore(self: *const ResourceLimits) void {
+ if (self.nofile) |lim| internal_os.restoreMaxFiles(lim);
+ }
+};