diff --git a/include/ghostty.h b/include/ghostty.h index 5557844ea..7c612e8a0 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -27,6 +27,7 @@ typedef struct { void *userdata; ghostty_runtime_wakeup_cb wakeup_cb; } ghostty_runtime_config_s; +typedef struct {} ghostty_surface_config_s; // Opaque types typedef void *ghostty_app_t; @@ -47,6 +48,9 @@ ghostty_app_t ghostty_app_new(ghostty_runtime_config_s *, ghostty_config_t); void ghostty_app_free(ghostty_app_t); int ghostty_app_tick(ghostty_app_t); +ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*); +void ghostty_surface_free(ghostty_app_t, ghostty_surface_t); + #ifdef __cplusplus } #endif diff --git a/src/App.zig b/src/App.zig index c55f22d67..2c70d399f 100644 --- a/src/App.zig +++ b/src/App.zig @@ -115,13 +115,6 @@ pub fn create( } errdefer if (comptime builtin.target.isDarwin()) app.darwin.deinit(); - // Create the first window if we're an executable. If we're a lib we - // do NOT create the first window because we expect the embedded API - // to do it via surfaces. - if (build_config.artifact == .exe) { - _ = try app.newWindow(.{}); - } - return app; } @@ -174,6 +167,37 @@ pub fn tick(self: *App) !void { if (!self.quit) try self.drainMailbox(); } +/// Create a new window. This can be called only on the main thread. This +/// can be called prior to ever running the app loop. +pub fn newWindow(self: *App, msg: Message.NewWindow) !*Window { + var window = try Window.create(self.alloc, self, self.config, msg.runtime); + errdefer window.destroy(); + + try self.windows.append(self.alloc, window); + errdefer _ = self.windows.pop(); + + // Set initial font size if given + if (msg.font_size) |size| window.setFontSize(size); + + return window; +} + +/// Close a window and free all resources associated with it. This can +/// only be called from the main thread. +pub fn closeWindow(self: *App, window: *Window) void { + var i: usize = 0; + while (i < self.windows.items.len) { + const current = self.windows.items[i]; + if (window == current) { + window.destroy(); + _ = self.windows.swapRemove(i); + return; + } + + i += 1; + } +} + /// Drain the mailbox. fn drainMailbox(self: *App) !void { while (self.mailbox.pop()) |message| { @@ -187,23 +211,6 @@ fn drainMailbox(self: *App) !void { } } -/// Create a new window -fn newWindow(self: *App, msg: Message.NewWindow) !*Window { - if (comptime build_config.artifact != .exe) { - @panic("newWindow is not supported for embedded ghostty"); - } - - var window = try Window.create(self.alloc, self, self.config, .{}); - errdefer window.destroy(); - try self.windows.append(self.alloc, window); - errdefer _ = self.windows.pop(); - - // Set initial font size if given - if (msg.font_size) |size| window.setFontSize(size); - - return window; -} - /// Create a new tab in the parent window fn newTab(self: *App, msg: Message.NewWindow) !void { if (comptime !builtin.target.isDarwin()) { @@ -286,6 +293,9 @@ pub const Message = union(enum) { }, const NewWindow = struct { + /// Runtime-specific window options. + runtime: apprt.runtime.Window.Options = .{}, + /// The parent window, only used for new tabs. parent: ?*Window = null, @@ -361,4 +371,28 @@ pub const CAPI = struct { v.alloc.destroy(v); } } + + /// Create a new surface as part of an app. + export fn ghostty_surface_new( + app: *App, + opts: *const apprt.runtime.Window.Options, + ) ?*Window { + return surface_new_(app, opts) catch |err| { + log.err("error initializing surface err={}", .{err}); + return null; + }; + } + + fn surface_new_( + app: *App, + opts: *const apprt.runtime.Window.Options, + ) !*Window { + _ = opts; + const w = try app.newWindow(.{}); + return w; + } + + export fn ghostty_surface_free(ptr: ?*Window) void { + if (ptr) |v| v.app.closeWindow(v); + } }; diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 3faa445b3..97f1ca58f 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -46,7 +46,7 @@ pub const App = struct { pub const Window = struct { pub const Options = extern struct { - id: usize, + id: usize = 0, }; pub fn init(app: *const CoreApp, core_win: *CoreWindow, opts: Options) !Window { diff --git a/src/main.zig b/src/main.zig index 8ad3186bf..b84322af6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -94,6 +94,7 @@ pub fn main() !void { // Run our app with a single initial window to start. var app = try App.create(alloc, .{}, &config); defer app.destroy(); + _ = try app.newWindow(.{}); try app.run(); }