mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
215 lines
7.0 KiB
Zig
215 lines
7.0 KiB
Zig
const builtin = @import("builtin");
|
|
const options = @import("build_options");
|
|
const std = @import("std");
|
|
const glfw = @import("glfw");
|
|
const fontconfig = @import("fontconfig");
|
|
const freetype = @import("freetype");
|
|
const harfbuzz = @import("harfbuzz");
|
|
const macos = @import("macos");
|
|
const tracy = @import("tracy");
|
|
const renderer = @import("renderer.zig");
|
|
const xdg = @import("xdg.zig");
|
|
|
|
const App = @import("App.zig");
|
|
const cli_args = @import("cli_args.zig");
|
|
const Config = @import("config.zig").Config;
|
|
|
|
pub fn main() !void {
|
|
// Output some debug information right away
|
|
std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
|
|
if (options.fontconfig) {
|
|
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
|
|
}
|
|
std.log.info("renderer={}", .{renderer.Renderer});
|
|
|
|
const GPA = std.heap.GeneralPurposeAllocator(.{});
|
|
var gpa: ?GPA = gpa: {
|
|
// Use the libc allocator if it is available beacuse 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{};
|
|
};
|
|
defer if (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();
|
|
};
|
|
|
|
const alloc = alloc: {
|
|
const base = if (gpa) |*value|
|
|
value.allocator()
|
|
else if (builtin.link_libc)
|
|
std.heap.c_allocator
|
|
else
|
|
unreachable;
|
|
|
|
// If we're tracing, wrap the allocator
|
|
if (!tracy.enabled) break :alloc base;
|
|
var tracy_alloc = tracy.allocator(base, null);
|
|
break :alloc tracy_alloc.allocator();
|
|
};
|
|
|
|
// Try reading our config
|
|
var config = try Config.default(alloc);
|
|
defer config.deinit();
|
|
|
|
// If we have a configuration file in our home directory, parse that first.
|
|
const cwd = std.fs.cwd();
|
|
{
|
|
const home_config_path = try xdg.config(alloc, .{ .subdir = "ghostty/config" });
|
|
defer alloc.free(home_config_path);
|
|
|
|
if (cwd.openFile(home_config_path, .{})) |file| {
|
|
defer file.close();
|
|
|
|
var buf_reader = std.io.bufferedReader(file.reader());
|
|
var iter = cli_args.lineIterator(buf_reader.reader());
|
|
try cli_args.parse(Config, alloc, &config, &iter);
|
|
} else |err| switch (err) {
|
|
error.FileNotFound => std.log.info(
|
|
"homedir config not found, not loading path={s}",
|
|
.{home_config_path},
|
|
),
|
|
|
|
else => std.log.warn(
|
|
"error reading homedir config file, not loading err={} path={s}",
|
|
.{ err, home_config_path },
|
|
),
|
|
}
|
|
}
|
|
|
|
// Parse the config from the CLI args
|
|
{
|
|
var iter = try std.process.argsWithAllocator(alloc);
|
|
defer iter.deinit();
|
|
try cli_args.parse(Config, alloc, &config, &iter);
|
|
}
|
|
|
|
// Parse the config files that were added from our file and CLI args.
|
|
// TODO(mitchellh): we should parse the files form the homedir first
|
|
// TODO(mitchellh): support nesting (config-file in a config file)
|
|
// TODO(mitchellh): detect cycles when nesting
|
|
if (config.@"config-file".list.items.len > 0) {
|
|
const len = config.@"config-file".list.items.len;
|
|
for (config.@"config-file".list.items) |path| {
|
|
var file = try cwd.openFile(path, .{});
|
|
defer file.close();
|
|
|
|
var buf_reader = std.io.bufferedReader(file.reader());
|
|
var iter = cli_args.lineIterator(buf_reader.reader());
|
|
|
|
try cli_args.parse(Config, alloc, &config, &iter);
|
|
|
|
// We don't currently support adding more config files to load
|
|
// from within a loaded config file. This can be supported
|
|
// later.
|
|
if (config.@"config-file".list.items.len > len)
|
|
return error.ConfigFileInConfigFile;
|
|
}
|
|
}
|
|
try config.finalize();
|
|
std.log.info("config={}", .{config});
|
|
|
|
// We want to log all our errors
|
|
glfw.setErrorCallback(glfwErrorCallback);
|
|
|
|
// Initialize glfw
|
|
try glfw.init(.{});
|
|
defer glfw.terminate();
|
|
|
|
// Run our app
|
|
var app = try App.init(alloc, &config);
|
|
defer app.deinit();
|
|
try app.run();
|
|
}
|
|
|
|
// Required by tracy/tracy.zig to enable/disable tracy support.
|
|
pub fn tracy_enabled() bool {
|
|
return options.tracy_enabled;
|
|
}
|
|
|
|
// Our log level is always at least info in every build mode.
|
|
pub const log_level: std.log.Level = switch (builtin.mode) {
|
|
.Debug => .debug,
|
|
else => .info,
|
|
};
|
|
|
|
// The function std.log will call.
|
|
pub fn log(
|
|
comptime level: std.log.Level,
|
|
comptime scope: @TypeOf(.EnumLiteral),
|
|
comptime format: []const u8,
|
|
args: anytype,
|
|
) void {
|
|
// Stuff we can do before the lock
|
|
const level_txt = comptime level.asText();
|
|
const prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
|
|
|
// Lock so we are thread-safe
|
|
std.debug.getStderrMutex().lock();
|
|
defer std.debug.getStderrMutex().unlock();
|
|
|
|
// On Mac, we use unified logging. To view this:
|
|
//
|
|
// sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'
|
|
//
|
|
if (builtin.os.tag == .macos) {
|
|
// Convert our levels to Mac levels
|
|
const mac_level: macos.os.LogType = switch (level) {
|
|
.debug => .debug,
|
|
.info => .info,
|
|
.warn => .err,
|
|
.err => .fault,
|
|
};
|
|
|
|
// Initialize a logger. This is slow to do on every operation
|
|
// but we shouldn't be logging too much.
|
|
const logger = macos.os.Log.create("com.mitchellh.ghostty", @tagName(scope));
|
|
defer logger.release();
|
|
logger.log(std.heap.c_allocator, mac_level, format, args);
|
|
}
|
|
|
|
// Always try default to send to stderr
|
|
const stderr = std.io.getStdErr().writer();
|
|
nosuspend stderr.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
|
|
}
|
|
|
|
fn glfwErrorCallback(code: glfw.Error, desc: [:0]const u8) void {
|
|
std.log.warn("glfw error={} message={s}", .{ code, desc });
|
|
}
|
|
|
|
test {
|
|
_ = @import("Atlas.zig");
|
|
_ = @import("Pty.zig");
|
|
_ = @import("Command.zig");
|
|
_ = @import("TempDir.zig");
|
|
_ = @import("font/main.zig");
|
|
_ = @import("renderer.zig");
|
|
_ = @import("terminal/Terminal.zig");
|
|
_ = @import("termio.zig");
|
|
_ = @import("input.zig");
|
|
|
|
// Libraries
|
|
_ = @import("segmented_pool.zig");
|
|
_ = @import("terminal/main.zig");
|
|
|
|
// TODO
|
|
_ = @import("blocking_queue.zig");
|
|
_ = @import("config.zig");
|
|
_ = @import("homedir.zig");
|
|
_ = @import("passwd.zig");
|
|
_ = @import("xdg.zig");
|
|
_ = @import("cli_args.zig");
|
|
_ = @import("lru.zig");
|
|
}
|