mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
apprt: start embedded implement, make App API available to C
This commit is contained in:
@ -16,14 +16,35 @@ extern "C" {
|
||||
|
||||
#define GHOSTTY_SUCCESS 0
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Types
|
||||
|
||||
// Fully defined types. This MUST be kept in sync with equivalent Zig
|
||||
// structs. To find the Zig struct, grep for this type name. The documentation
|
||||
// for all of these types is available in the Zig source.
|
||||
typedef void (*ghostty_runtime_wakeup_cb)(void *);
|
||||
typedef struct {
|
||||
void *userdata;
|
||||
ghostty_runtime_wakeup_cb wakeup_cb;
|
||||
} ghostty_runtime_config_s;
|
||||
|
||||
// Opaque types
|
||||
typedef void *ghostty_app_t;
|
||||
typedef void *ghostty_config_t;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Published API
|
||||
|
||||
int ghostty_init(void);
|
||||
|
||||
ghostty_config_t ghostty_config_new();
|
||||
void ghostty_config_free(ghostty_config_t);
|
||||
void ghostty_config_load_string(ghostty_config_t, const char *, uintptr_t);
|
||||
void ghostty_config_finalize(ghostty_config_t);
|
||||
|
||||
ghostty_app_t ghostty_app_new(ghostty_runtime_config_s *, ghostty_config_t);
|
||||
void ghostty_app_free(ghostty_app_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
40
src/App.zig
40
src/App.zig
@ -65,9 +65,13 @@ pub const Darwin = struct {
|
||||
/// Initialize the main app instance. This creates the main window, sets
|
||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||
/// "startup" logic.
|
||||
pub fn create(alloc: Allocator, config: *const Config) !*App {
|
||||
pub fn create(
|
||||
alloc: Allocator,
|
||||
rt_opts: apprt.runtime.App.Options,
|
||||
config: *const Config,
|
||||
) !*App {
|
||||
// Initialize app runtime
|
||||
var app_backend = try apprt.runtime.App.init();
|
||||
var app_backend = try apprt.runtime.App.init(rt_opts);
|
||||
errdefer app_backend.terminate();
|
||||
|
||||
// The mailbox for messaging this thread
|
||||
@ -303,3 +307,35 @@ pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
// C API
|
||||
pub const CAPI = struct {
|
||||
const global = &@import("main.zig").state;
|
||||
|
||||
/// Create a new app.
|
||||
export fn ghostty_app_new(
|
||||
opts: *const apprt.runtime.App.Options,
|
||||
config: *const Config,
|
||||
) ?*App {
|
||||
return app_new_(opts, config) catch |err| {
|
||||
log.err("error initializing app err={}", .{err});
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
fn app_new_(
|
||||
opts: *const apprt.runtime.App.Options,
|
||||
config: *const Config,
|
||||
) !*App {
|
||||
const app = try App.create(global.alloc, opts.*, config);
|
||||
errdefer app.destroy();
|
||||
return app;
|
||||
}
|
||||
|
||||
export fn ghostty_app_free(ptr: ?*App) void {
|
||||
if (ptr) |v| {
|
||||
v.destroy();
|
||||
v.alloc.destroy(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ const DevMode = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("build_config.zig");
|
||||
const imgui = @import("imgui");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
@ -15,7 +16,8 @@ const Config = @import("config.zig").Config;
|
||||
|
||||
/// If this is false, the rest of the terminal will be compiled without
|
||||
/// dev mode support at all.
|
||||
pub const enabled = !builtin.target.isWasm();
|
||||
/// TODO: remove this and use build_config everywhere
|
||||
pub const enabled = build_config.devmode_enabled;
|
||||
|
||||
/// The global DevMode instance that can be used app-wide. Assume all functions
|
||||
/// are NOT thread-safe unless otherwise noted.
|
||||
|
@ -9,20 +9,22 @@
|
||||
//! logic as possible, and to only reach out to platform-specific implementation
|
||||
//! code when absolutely necessary.
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("build_config.zig");
|
||||
|
||||
pub usingnamespace @import("apprt/structs.zig");
|
||||
pub const glfw = @import("apprt/glfw.zig");
|
||||
pub const browser = @import("apprt/browser.zig");
|
||||
pub const embedded = @import("apprt/embedded.zig");
|
||||
pub const Window = @import("apprt/Window.zig");
|
||||
|
||||
/// The implementation to use for the app runtime. This is comptime chosen
|
||||
/// so that every build has exactly one application runtime implementation.
|
||||
/// Note: it is very rare to use Runtime directly; most usage will use
|
||||
/// Window or something.
|
||||
pub const runtime = if (builtin.target.isWasm())
|
||||
browser
|
||||
else switch (builtin.os.tag) {
|
||||
else => glfw,
|
||||
pub const runtime = switch (build_config.artifact) {
|
||||
.exe => glfw,
|
||||
.lib => embedded,
|
||||
.wasm_module => browser,
|
||||
};
|
||||
|
||||
test {
|
||||
|
43
src/apprt/embedded.zig
Normal file
43
src/apprt/embedded.zig
Normal file
@ -0,0 +1,43 @@
|
||||
//! Application runtime for the embedded version of Ghostty. The embedded
|
||||
//! version is when Ghostty is embedded within a parent host application,
|
||||
//! rather than owning the application lifecycle itself. This is used for
|
||||
//! example for the macOS build of Ghostty so that we can use a native
|
||||
//! Swift+XCode-based application.
|
||||
|
||||
pub const App = struct {
|
||||
/// Because we only expect the embedding API to be used in embedded
|
||||
/// environments, the options are extern so that we can expose it
|
||||
/// directly to a C callconv and not pay for any translation costs.
|
||||
///
|
||||
/// C type: ghostty_runtime_config_s
|
||||
pub const Options = extern struct {
|
||||
/// Userdata that is passed to all the callbacks.
|
||||
userdata: ?*anyopaque = null,
|
||||
|
||||
/// Callback called to wakeup the event loop. This should trigger
|
||||
/// a full tick of the app loop.
|
||||
wakeup: *const fn (?*anyopaque) callconv(.C) void,
|
||||
};
|
||||
|
||||
pub fn init(_: Options) !App {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn terminate(self: App) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn wakeup(self: App) !void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn wait(self: App) !void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Window = struct {
|
||||
pub fn deinit(self: *Window) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
@ -26,7 +26,9 @@ const glfwNative = glfw.Native(.{
|
||||
const log = std.log.scoped(.glfw);
|
||||
|
||||
pub const App = struct {
|
||||
pub fn init() !App {
|
||||
pub const Options = struct {};
|
||||
|
||||
pub fn init(_: Options) !App {
|
||||
if (!glfw.init(.{})) return error.GlfwInitFailed;
|
||||
return .{};
|
||||
}
|
||||
|
@ -10,6 +10,10 @@ const assert = std.debug.assert;
|
||||
/// building a standalone exe, an embedded lib, etc.
|
||||
pub const artifact = Artifact.detect();
|
||||
|
||||
/// Whether our devmode UI is enabled or not. This requires imgui to be
|
||||
/// compiled.
|
||||
pub const devmode_enabled = artifact == .exe;
|
||||
|
||||
pub const Artifact = enum {
|
||||
/// Standalone executable
|
||||
exe,
|
||||
@ -29,8 +33,11 @@ pub const Artifact = enum {
|
||||
|
||||
return switch (builtin.output_mode) {
|
||||
.Exe => .exe,
|
||||
.Obj => .lib,
|
||||
else => unreachable,
|
||||
.Lib => .lib,
|
||||
else => {
|
||||
@compileLog(builtin.output_mode);
|
||||
@compileError("unsupported artifact output mode");
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ pub fn main() !void {
|
||||
glfw.setErrorCallback(glfwErrorCallback);
|
||||
|
||||
// Run our app with a single initial window to start.
|
||||
var app = try App.create(alloc, &config);
|
||||
var app = try App.create(alloc, .{}, &config);
|
||||
defer app.destroy();
|
||||
try app.run();
|
||||
}
|
||||
|
@ -11,10 +11,17 @@ const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
const main = @import("main.zig");
|
||||
|
||||
// Some comptime assertions that our C API depends on.
|
||||
comptime {
|
||||
const apprt = @import("apprt.zig");
|
||||
assert(apprt.runtime == apprt.embedded);
|
||||
}
|
||||
|
||||
/// Global options so we can log. This is identical to main.
|
||||
pub const std_options = main.std_options;
|
||||
|
||||
pub usingnamespace @import("config.zig").CAPI;
|
||||
pub usingnamespace @import("App.zig").CAPI;
|
||||
|
||||
/// Initialize ghostty global state. It is possible to have more than
|
||||
/// one global state but it has zero practical benefit.
|
||||
|
Reference in New Issue
Block a user