mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
apprt: C API for embedded updated to new style
This commit is contained in:
162
src/App.zig
162
src/App.zig
@ -77,7 +77,7 @@ pub fn destroy(self: *App) void {
|
||||
/// tick.
|
||||
///
|
||||
/// This returns whether the app should quit or not.
|
||||
pub fn tick(self: *App, rt_app: *apprt.runtime.App) !bool {
|
||||
pub fn tick(self: *App, rt_app: *apprt.App) !bool {
|
||||
// If any surfaces are closing, destroy them
|
||||
var i: usize = 0;
|
||||
while (i < self.surfaces.items.len) {
|
||||
@ -119,7 +119,7 @@ pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void {
|
||||
}
|
||||
|
||||
/// Drain the mailbox.
|
||||
fn drainMailbox(self: *App, rt_app: *apprt.runtime.App) !void {
|
||||
fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
|
||||
while (self.mailbox.pop()) |message| {
|
||||
log.debug("mailbox message={s}", .{@tagName(message)});
|
||||
switch (message) {
|
||||
@ -138,7 +138,12 @@ fn closeSurface(self: *App, rt_app: *apprt.App, surface: *Surface) !void {
|
||||
}
|
||||
|
||||
/// Create a new window
|
||||
fn newWindow(self: *App, rt_app: *apprt.runtime.App, msg: Message.NewWindow) !void {
|
||||
fn newWindow(self: *App, rt_app: *apprt.App, msg: Message.NewWindow) !void {
|
||||
if (!@hasDecl(apprt.App, "newWindow")) {
|
||||
log.warn("newWindow is not supported by this runtime", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
const window = try rt_app.newWindow();
|
||||
if (self.config.@"window-inherit-font-size") {
|
||||
if (msg.parent) |parent| {
|
||||
@ -150,7 +155,12 @@ fn newWindow(self: *App, rt_app: *apprt.runtime.App, msg: Message.NewWindow) !vo
|
||||
}
|
||||
|
||||
/// Create a new tab in the parent window
|
||||
fn newTab(self: *App, rt_app: *apprt.runtime.App, msg: Message.NewTab) !void {
|
||||
fn newTab(self: *App, rt_app: *apprt.App, msg: Message.NewTab) !void {
|
||||
if (!@hasDecl(apprt.App, "newTab")) {
|
||||
log.warn("newTab is not supported by this runtime", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = msg.parent orelse {
|
||||
log.warn("parent must be set in new_tab message", .{});
|
||||
return;
|
||||
@ -281,147 +291,3 @@ 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;
|
||||
}
|
||||
|
||||
/// Tick the event loop. This should be called whenever the "wakeup"
|
||||
/// callback is invoked for the runtime.
|
||||
export fn ghostty_app_tick(v: *App) void {
|
||||
v.tick() catch |err| {
|
||||
log.err("error app tick err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
/// Return the userdata associated with the app.
|
||||
export fn ghostty_app_userdata(v: *App) ?*anyopaque {
|
||||
return v.runtime.opts.userdata;
|
||||
}
|
||||
|
||||
export fn ghostty_app_free(ptr: ?*App) void {
|
||||
if (ptr) |v| {
|
||||
v.destroy();
|
||||
v.alloc.destroy(v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new surface as part of an app.
|
||||
export fn ghostty_surface_new(
|
||||
app: *App,
|
||||
opts: *const apprt.Surface.Options,
|
||||
) ?*Surface {
|
||||
return surface_new_(app, opts) catch |err| {
|
||||
log.err("error initializing surface err={}", .{err});
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
fn surface_new_(
|
||||
app: *App,
|
||||
opts: *const apprt.Surface.Options,
|
||||
) !*Surface {
|
||||
const w = try app.newWindow(.{
|
||||
.runtime = opts.*,
|
||||
});
|
||||
return w;
|
||||
}
|
||||
|
||||
export fn ghostty_surface_free(ptr: ?*Surface) void {
|
||||
if (ptr) |v| v.app.closeWindow(v);
|
||||
}
|
||||
|
||||
/// Returns the app associated with a surface.
|
||||
export fn ghostty_surface_app(win: *Surface) *App {
|
||||
return win.app;
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_refresh(win: *Surface) void {
|
||||
win.window.refresh();
|
||||
}
|
||||
|
||||
/// Update the size of a surface. This will trigger resize notifications
|
||||
/// to the pty and the renderer.
|
||||
export fn ghostty_surface_set_size(win: *Surface, w: u32, h: u32) void {
|
||||
win.window.updateSize(w, h);
|
||||
}
|
||||
|
||||
/// Update the content scale of the surface.
|
||||
export fn ghostty_surface_set_content_scale(win: *Surface, x: f64, y: f64) void {
|
||||
win.window.updateContentScale(x, y);
|
||||
}
|
||||
|
||||
/// Update the focused state of a surface.
|
||||
export fn ghostty_surface_set_focus(win: *Surface, focused: bool) void {
|
||||
win.window.focusCallback(focused);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_key(
|
||||
win: *Surface,
|
||||
action: input.Action,
|
||||
key: input.Key,
|
||||
mods: c_int,
|
||||
) void {
|
||||
win.window.keyCallback(
|
||||
action,
|
||||
key,
|
||||
@bitCast(input.Mods, @truncate(u8, @bitCast(c_uint, mods))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_char(win: *Surface, codepoint: u32) void {
|
||||
win.window.charCallback(codepoint);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_mouse_button(
|
||||
win: *Surface,
|
||||
action: input.MouseButtonState,
|
||||
button: input.MouseButton,
|
||||
mods: c_int,
|
||||
) void {
|
||||
win.window.mouseButtonCallback(
|
||||
action,
|
||||
button,
|
||||
@bitCast(input.Mods, @truncate(u8, @bitCast(c_uint, mods))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Update the mouse position within the view.
|
||||
export fn ghostty_surface_mouse_pos(win: *Surface, x: f64, y: f64) void {
|
||||
win.window.cursorPosCallback(x, y);
|
||||
}
|
||||
|
||||
export fn ghostty_surface_mouse_scroll(win: *Surface, x: f64, y: f64) void {
|
||||
win.window.scrollCallback(x, y);
|
||||
}
|
||||
|
||||
export fn ghostty_surface_ime_point(win: *Surface, x: *f64, y: *f64) void {
|
||||
const pos = win.imePoint();
|
||||
x.* = pos.x;
|
||||
y.* = pos.y;
|
||||
}
|
||||
};
|
||||
|
@ -57,18 +57,38 @@ pub const App = struct {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn wakeup(self: App) !void {
|
||||
pub fn wakeup(self: App) void {
|
||||
self.opts.wakeup(self.opts.userdata);
|
||||
}
|
||||
|
||||
pub fn wait(self: App) !void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
/// Create a new surface for the app.
|
||||
fn newSurface(self: *App, opts: Surface.Options) !*Surface {
|
||||
// Grab a surface allocation because we're going to need it.
|
||||
var surface = try self.core_app.alloc.create(Surface);
|
||||
errdefer self.core_app.alloc.destroy(surface);
|
||||
|
||||
// Create the surface -- because windows are surfaces for glfw.
|
||||
try surface.init(self, opts);
|
||||
errdefer surface.deinit();
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/// Close the given surface.
|
||||
pub fn closeSurface(self: *App, surface: *Surface) void {
|
||||
surface.deinit();
|
||||
self.core_app.alloc.destroy(surface);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Surface = struct {
|
||||
app: *App,
|
||||
nsview: objc.Object,
|
||||
core_surface: *CoreSurface,
|
||||
core_surface: CoreSurface,
|
||||
content_scale: apprt.ContentScale,
|
||||
size: apprt.SurfaceSize,
|
||||
cursor_pos: apprt.CursorPos,
|
||||
@ -87,6 +107,7 @@ pub const Surface = struct {
|
||||
|
||||
pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
||||
self.* = .{
|
||||
.app = app,
|
||||
.core_surface = undefined,
|
||||
.nsview = objc.Object.fromId(opts.nsview),
|
||||
.content_scale = .{
|
||||
@ -99,18 +120,23 @@ pub const Surface = struct {
|
||||
};
|
||||
|
||||
// Add ourselves to the list of surfaces on the app.
|
||||
try app.app.addSurface(self);
|
||||
errdefer app.app.deleteSurface(self);
|
||||
try app.core_app.addSurface(self);
|
||||
errdefer app.core_app.deleteSurface(self);
|
||||
|
||||
// Initialize our surface right away. We're given a view that is
|
||||
// ready to use.
|
||||
try self.core_surface.init(app.app, app.app.config, self);
|
||||
try self.core_surface.init(
|
||||
app.core_app.alloc,
|
||||
app.core_app.config,
|
||||
.{ .rt_app = app, .mailbox = &app.core_app.mailbox },
|
||||
self,
|
||||
);
|
||||
errdefer self.core_surface.deinit();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Surface) void {
|
||||
// Remove ourselves from the list of known surfaces in the app.
|
||||
self.core_surface.app.deleteSurface(self);
|
||||
self.app.core_app.deleteSurface(self);
|
||||
|
||||
// Clean up our core surface so that all the rendering and IO stop.
|
||||
self.core_surface.deinit();
|
||||
@ -131,19 +157,19 @@ pub const Surface = struct {
|
||||
}
|
||||
|
||||
pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
|
||||
self.core_surface.app.runtime.opts.set_title(
|
||||
self.app.opts.set_title(
|
||||
self.opts.userdata,
|
||||
slice.ptr,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getClipboardString(self: *const Surface) ![:0]const u8 {
|
||||
const ptr = self.core_surface.app.runtime.opts.read_clipboard(self.opts.userdata);
|
||||
const ptr = self.app.opts.read_clipboard(self.opts.userdata);
|
||||
return std.mem.sliceTo(ptr, 0);
|
||||
}
|
||||
|
||||
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
|
||||
self.core_surface.app.runtime.opts.write_clipboard(self.opts.userdata, val.ptr);
|
||||
self.app.opts.write_clipboard(self.opts.userdata, val.ptr);
|
||||
}
|
||||
|
||||
pub fn setShouldClose(self: *Surface) void {
|
||||
@ -187,7 +213,7 @@ pub const Surface = struct {
|
||||
}
|
||||
|
||||
pub fn mouseButtonCallback(
|
||||
self: *const Surface,
|
||||
self: *Surface,
|
||||
action: input.MouseButtonState,
|
||||
button: input.MouseButton,
|
||||
mods: input.Mods,
|
||||
@ -198,7 +224,7 @@ pub const Surface = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scrollCallback(self: *const Surface, xoff: f64, yoff: f64) void {
|
||||
pub fn scrollCallback(self: *Surface, xoff: f64, yoff: f64) void {
|
||||
self.core_surface.scrollCallback(xoff, yoff) catch |err| {
|
||||
log.err("error in scroll callback err={}", .{err});
|
||||
return;
|
||||
@ -207,7 +233,7 @@ pub const Surface = struct {
|
||||
|
||||
pub fn cursorPosCallback(self: *Surface, x: f64, y: f64) void {
|
||||
// Convert our unscaled x/y to scaled.
|
||||
self.cursor_pos = self.core_surface.window.cursorPosToPixels(.{
|
||||
self.cursor_pos = self.cursorPosToPixels(.{
|
||||
.x = @floatCast(f32, x),
|
||||
.y = @floatCast(f32, y),
|
||||
}) catch |err| {
|
||||
@ -225,7 +251,7 @@ pub const Surface = struct {
|
||||
}
|
||||
|
||||
pub fn keyCallback(
|
||||
self: *const Surface,
|
||||
self: *Surface,
|
||||
action: input.Action,
|
||||
key: input.Key,
|
||||
mods: input.Mods,
|
||||
@ -237,7 +263,7 @@ pub const Surface = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn charCallback(self: *const Surface, cp_: u32) void {
|
||||
pub fn charCallback(self: *Surface, cp_: u32) void {
|
||||
const cp = std.math.cast(u21, cp_) orelse return;
|
||||
self.core_surface.charCallback(cp) catch |err| {
|
||||
log.err("error in char callback err={}", .{err});
|
||||
@ -245,7 +271,7 @@ pub const Surface = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn focusCallback(self: *const Surface, focused: bool) void {
|
||||
pub fn focusCallback(self: *Surface, focused: bool) void {
|
||||
self.core_surface.focusCallback(focused) catch |err| {
|
||||
log.err("error in focus callback err={}", .{err});
|
||||
return;
|
||||
@ -259,3 +285,152 @@ pub const Surface = struct {
|
||||
return .{ .x = pos.x * scale.x, .y = pos.y * scale.y };
|
||||
}
|
||||
};
|
||||
|
||||
// C API
|
||||
pub const CAPI = struct {
|
||||
const global = &@import("../main.zig").state;
|
||||
const Config = @import("../config.zig").Config;
|
||||
|
||||
/// 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 {
|
||||
var core_app = try CoreApp.create(global.alloc, config);
|
||||
errdefer core_app.destroy();
|
||||
|
||||
// Create our runtime app
|
||||
var app = try global.alloc.create(App);
|
||||
errdefer global.alloc.destroy(app);
|
||||
app.* = try App.init(core_app, opts.*);
|
||||
errdefer app.terminate();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/// Tick the event loop. This should be called whenever the "wakeup"
|
||||
/// callback is invoked for the runtime.
|
||||
export fn ghostty_app_tick(v: *App) void {
|
||||
_ = v.core_app.tick(v) catch |err| {
|
||||
log.err("error app tick err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
/// Return the userdata associated with the app.
|
||||
export fn ghostty_app_userdata(v: *App) ?*anyopaque {
|
||||
return v.opts.userdata;
|
||||
}
|
||||
|
||||
export fn ghostty_app_free(v: *App) void {
|
||||
const core_app = v.core_app;
|
||||
v.terminate();
|
||||
global.alloc.destroy(v);
|
||||
core_app.destroy();
|
||||
}
|
||||
|
||||
/// Create a new surface as part of an app.
|
||||
export fn ghostty_surface_new(
|
||||
app: *App,
|
||||
opts: *const apprt.Surface.Options,
|
||||
) ?*Surface {
|
||||
return surface_new_(app, opts) catch |err| {
|
||||
log.err("error initializing surface err={}", .{err});
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
fn surface_new_(
|
||||
app: *App,
|
||||
opts: *const apprt.Surface.Options,
|
||||
) !*Surface {
|
||||
return try app.newSurface(opts.*);
|
||||
}
|
||||
|
||||
export fn ghostty_surface_free(ptr: *Surface) void {
|
||||
ptr.app.closeSurface(ptr);
|
||||
}
|
||||
|
||||
/// Returns the app associated with a surface.
|
||||
export fn ghostty_surface_app(surface: *Surface) *App {
|
||||
return surface.app;
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_refresh(surface: *Surface) void {
|
||||
surface.refresh();
|
||||
}
|
||||
|
||||
/// Update the size of a surface. This will trigger resize notifications
|
||||
/// to the pty and the renderer.
|
||||
export fn ghostty_surface_set_size(surface: *Surface, w: u32, h: u32) void {
|
||||
surface.updateSize(w, h);
|
||||
}
|
||||
|
||||
/// Update the content scale of the surface.
|
||||
export fn ghostty_surface_set_content_scale(surface: *Surface, x: f64, y: f64) void {
|
||||
surface.updateContentScale(x, y);
|
||||
}
|
||||
|
||||
/// Update the focused state of a surface.
|
||||
export fn ghostty_surface_set_focus(surface: *Surface, focused: bool) void {
|
||||
surface.focusCallback(focused);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_key(
|
||||
surface: *Surface,
|
||||
action: input.Action,
|
||||
key: input.Key,
|
||||
mods: c_int,
|
||||
) void {
|
||||
surface.keyCallback(
|
||||
action,
|
||||
key,
|
||||
@bitCast(input.Mods, @truncate(u8, @bitCast(c_uint, mods))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_char(surface: *Surface, codepoint: u32) void {
|
||||
surface.charCallback(codepoint);
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
export fn ghostty_surface_mouse_button(
|
||||
surface: *Surface,
|
||||
action: input.MouseButtonState,
|
||||
button: input.MouseButton,
|
||||
mods: c_int,
|
||||
) void {
|
||||
surface.mouseButtonCallback(
|
||||
action,
|
||||
button,
|
||||
@bitCast(input.Mods, @truncate(u8, @bitCast(c_uint, mods))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Update the mouse position within the view.
|
||||
export fn ghostty_surface_mouse_pos(surface: *Surface, x: f64, y: f64) void {
|
||||
surface.cursorPosCallback(x, y);
|
||||
}
|
||||
|
||||
export fn ghostty_surface_mouse_scroll(surface: *Surface, x: f64, y: f64) void {
|
||||
surface.scrollCallback(x, y);
|
||||
}
|
||||
|
||||
export fn ghostty_surface_ime_point(surface: *Surface, x: *f64, y: *f64) void {
|
||||
const pos = surface.core_surface.imePoint();
|
||||
x.* = pos.x;
|
||||
y.* = pos.y;
|
||||
}
|
||||
};
|
||||
|
@ -10,10 +10,10 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
const main = @import("main.zig");
|
||||
const apprt = @import("apprt.zig");
|
||||
|
||||
// Some comptime assertions that our C API depends on.
|
||||
comptime {
|
||||
const apprt = @import("apprt.zig");
|
||||
assert(apprt.runtime == apprt.embedded);
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ comptime {
|
||||
pub const std_options = main.std_options;
|
||||
|
||||
pub usingnamespace @import("config.zig").CAPI;
|
||||
pub usingnamespace @import("App.zig").CAPI;
|
||||
pub usingnamespace apprt.runtime.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