macos: config API

This commit is contained in:
Mitchell Hashimoto
2023-02-14 15:53:28 -08:00
parent 6fc4b144a2
commit 9bd527fe00
7 changed files with 229 additions and 115 deletions

View File

@@ -297,13 +297,5 @@ pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
};
pub const CAPI = struct {
const ProcessState = @import("main.zig").ProcessState;
var state: ?ProcessState = null;
export fn ghostty_init() c_int {
assert(state == null);
state = undefined;
ProcessState.init(&state.?);
return 0;
}
const Ghostty = @import("main_c.zig").Ghostty;
};

View File

@@ -583,6 +583,59 @@ pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
}
};
// Wasm API.
pub const CAPI = struct {
const Ghostty = @import("main_c.zig").Ghostty;
const cli_args = @import("cli_args.zig");
/// Create a new configuration filled with the initial default values.
export fn ghostty_config_new(g: *Ghostty) ?*Config {
const result = g.alloc.create(Config) catch |err| {
log.err("error allocating config err={}", .{err});
return null;
};
result.* = Config.default(g.alloc) catch |err| {
log.err("error creating config err={}", .{err});
return null;
};
return result;
}
export fn ghostty_config_free(g: *Ghostty, ptr: ?*Config) void {
if (ptr) |v| {
v.deinit();
g.alloc.destroy(v);
}
}
/// Load the configuration from a string in the same format as
/// the file-based syntax for the desktop version of the terminal.
export fn ghostty_config_load_string(
g: *Ghostty,
self: *Config,
str: [*]const u8,
len: usize,
) void {
config_load_string_(g, self, str[0..len]) catch |err| {
log.err("error loading config err={}", .{err});
};
}
fn config_load_string_(g: *Ghostty, self: *Config, str: []const u8) !void {
var fbs = std.io.fixedBufferStream(str);
var iter = cli_args.lineIterator(fbs.reader());
try cli_args.parse(Config, g.alloc, self, &iter);
}
export fn ghostty_config_finalize(self: *Config) void {
self.finalize() catch |err| {
log.err("error finalizing config err={}", .{err});
};
}
};
test {
std.testing.refAllDecls(@This());
}

View File

@@ -2,100 +2,17 @@ const std = @import("std");
const builtin = @import("builtin");
const options = @import("build_options");
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 xev = @import("xev");
const renderer = @import("renderer.zig");
const xdg = @import("xdg.zig");
const internal_os = @import("os/main.zig");
const App = @import("App.zig");
const cli_args = @import("cli_args.zig");
const Config = @import("config.zig").Config;
/// ProcessState 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.
///
/// ProcessState.init should be one of the first things ever called
/// when using Ghostty. Ghostty calls this for you so this is more of a note
/// for maintainers.
pub const ProcessState = struct {
const GPA = std.heap.GeneralPurposeAllocator(.{});
gpa: ?GPA,
alloc: std.mem.Allocator,
pub fn init(self: *ProcessState) 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});
std.log.info("libxev backend={}", .{xev.backend});
// First things first, we fix our file descriptors
internal_os.fixMaxFiles();
// We need to make sure the process locale is set properly. Locale
// affects a lot of behaviors in a shell.
internal_os.ensureLocale();
// Initialize ourself to nothing so we don't have any extra state.
self.* = .{
.gpa = null,
.alloc = undefined,
};
errdefer self.deinit();
self.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{};
};
self.alloc = alloc: {
const base = if (self.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();
};
}
pub fn deinit(self: *ProcessState) void {
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();
}
}
};
const Ghostty = @import("main_c.zig").Ghostty;
pub fn main() !void {
var state: ProcessState = undefined;
ProcessState.init(&state);
var state: Ghostty = undefined;
Ghostty.init(&state);
defer state.deinit();
const alloc = state.alloc;

View File

@@ -2,10 +2,109 @@
// within other applications. Depending on the build settings some APIs
// may not be available (i.e. embedding into macOS exposes various Metal
// support).
//
// This currently isn't supported as a general purpose embedding API.
// This is currently used only to embed ghostty within a macOS app. However,
// it could be expanded to be general purpose in the future.
const std = @import("std");
const assert = std.debug.assert;
const builtin = @import("builtin");
const options = @import("build_options");
const fontconfig = @import("fontconfig");
const harfbuzz = @import("harfbuzz");
const renderer = @import("renderer.zig");
const tracy = @import("tracy");
const xev = @import("xev");
const internal_os = @import("os/main.zig");
const main = @import("main.zig");
pub usingnamespace @import("App.zig").CAPI;
/// Global options so we can log. This is identical to main.
pub const std_options = main.std_options;
pub usingnamespace @import("App.zig").CAPI;
pub usingnamespace @import("config.zig").CAPI;
/// Initialize ghostty global state. It is possible to have more than
/// one global state but it has zero practical benefit.
export fn ghostty_init() ?*Ghostty {
assert(builtin.link_libc);
const alloc = std.heap.c_allocator;
const g = alloc.create(Ghostty) catch return null;
Ghostty.init(g);
return g;
}
/// 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.
///
/// init should be one of the first things ever called when using Ghostty.
pub const Ghostty = struct {
const GPA = std.heap.GeneralPurposeAllocator(.{});
gpa: ?GPA,
alloc: std.mem.Allocator,
pub fn init(self: *Ghostty) 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});
std.log.info("libxev backend={}", .{xev.backend});
// First things first, we fix our file descriptors
internal_os.fixMaxFiles();
// We need to make sure the process locale is set properly. Locale
// affects a lot of behaviors in a shell.
internal_os.ensureLocale();
// Initialize ourself to nothing so we don't have any extra state.
self.* = .{
.gpa = null,
.alloc = undefined,
};
errdefer self.deinit();
self.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{};
};
self.alloc = alloc: {
const base = if (self.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();
};
}
pub fn deinit(self: *Ghostty) void {
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();
}
}
};