From c6f23bbb32cac497022f19280c929ee9b2ace3ec Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 1 Feb 2025 15:10:05 -0600 Subject: [PATCH 1/4] core: con't copy App and apprt.App Besides avoiding copying, this allows consumers to choose to allocate these structs on the stack or to allocate on the heap. It also gives the apprt.App a stable pointer sooner in the process. --- src/App.zig | 17 +++++------------ src/apprt/embedded.zig | 17 +++++++++++------ src/apprt/glfw.zig | 4 ++-- src/apprt/gtk/App.zig | 4 ++-- src/main_ghostty.zig | 8 +++++--- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/App.zig b/src/App.zig index 3bbeff2c8..fceab0275 100644 --- a/src/App.zig +++ b/src/App.zig @@ -82,28 +82,23 @@ pub const CreateError = Allocator.Error || font.SharedGridSet.InitError; /// /// After calling this function, well behaved apprts should then call /// `focusEvent` to set the initial focus state of the app. -pub fn create( +pub fn init( + self: *App, alloc: Allocator, -) CreateError!*App { - var app = try alloc.create(App); - errdefer alloc.destroy(app); - +) CreateError!void { var font_grid_set = try font.SharedGridSet.init(alloc); errdefer font_grid_set.deinit(); - app.* = .{ + self.* = .{ .alloc = alloc, .surfaces = .{}, .mailbox = .{}, .font_grid_set = font_grid_set, .config_conditional_state = .{}, }; - errdefer app.surfaces.deinit(alloc); - - return app; } -pub fn destroy(self: *App) void { +pub fn deinit(self: *App) void { // Clean up all our surfaces for (self.surfaces.items) |surface| surface.deinit(); self.surfaces.deinit(self.alloc); @@ -114,8 +109,6 @@ pub fn destroy(self: *App) void { // should gracefully close all surfaces. assert(self.font_grid_set.count() == 0); self.font_grid_set.deinit(); - - self.alloc.destroy(self); } /// Tick ticks the app loop. This will drain our mailbox and process those diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 31dd2f46b..2234fdc2d 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -117,10 +117,11 @@ pub const App = struct { config: Config, pub fn init( + self: *App, core_app: *CoreApp, config: *const Config, opts: Options, - ) !App { + ) !void { // We have to clone the config. const alloc = core_app.alloc; var config_clone = try config.clone(alloc); @@ -129,7 +130,7 @@ pub const App = struct { var keymap = try input.Keymap.init(); errdefer keymap.deinit(); - return .{ + self.* = .{ .core_app = core_app, .config = config_clone, .opts = opts, @@ -1316,13 +1317,16 @@ pub const CAPI = struct { opts: *const apprt.runtime.App.Options, config: *const Config, ) !*App { - var core_app = try CoreApp.create(global.alloc); - errdefer core_app.destroy(); + var core_app = try global.alloc.create(CoreApp); + errdefer { + core_app.deinit(); + global.alloc.destroy(core_app); + } // Create our runtime app var app = try global.alloc.create(App); errdefer global.alloc.destroy(app); - app.* = try .init(core_app, config, opts.*); + try app.init(core_app, config, opts.*); errdefer app.terminate(); return app; @@ -1345,7 +1349,8 @@ pub const CAPI = struct { const core_app = v.core_app; v.terminate(); global.alloc.destroy(v); - core_app.destroy(); + core_app.deinit(); + global.alloc.destroy(core_app); } /// Update the focused state of the app. diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 6e131435d..b82771d75 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -50,7 +50,7 @@ pub const App = struct { pub const Options = struct {}; - pub fn init(core_app: *CoreApp, _: Options) !App { + pub fn init(self: *App, core_app: *CoreApp, _: Options) !void { if (comptime builtin.target.os.tag.isDarwin()) { log.warn("WARNING WARNING WARNING: GLFW ON MAC HAS BUGS.", .{}); log.warn("You should use the AppKit-based app instead. The official download", .{}); @@ -107,7 +107,7 @@ pub const App = struct { // We want the event loop to wake up instantly so we can process our tick. glfw.postEmptyEvent(); - return .{ + self.* = .{ .app = core_app, .config = config, .darwin = darwin, diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 93e069376..7786f976a 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -110,7 +110,7 @@ quit_timer: union(enum) { expired: void, } = .{ .off = {} }, -pub fn init(core_app: *CoreApp, opts: Options) !App { +pub fn init(self: *App, core_app: *CoreApp, opts: Options) !void { _ = opts; // Log our GTK version @@ -424,7 +424,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + 3, ); - return .{ + self.* = .{ .core_app = core_app, .app = adw_app, .config = config, diff --git a/src/main_ghostty.zig b/src/main_ghostty.zig index 985c6c9bd..1eb91b6b2 100644 --- a/src/main_ghostty.zig +++ b/src/main_ghostty.zig @@ -98,11 +98,13 @@ pub fn main() !MainReturn { } // Create our app state - var app = try App.create(alloc); - defer app.destroy(); + var app: App = undefined; + defer app.deinit(); + try app.init(alloc); // Create our runtime app - var app_runtime = try apprt.App.init(app, .{}); + var app_runtime: apprt.App = undefined; + try app_runtime.init(&app, .{}); defer app_runtime.terminate(); // Since - by definition - there are no surfaces when first started, the From 3c49d8775106b2507ad21978c678eac5a1e33669 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 5 Feb 2025 11:30:46 -0600 Subject: [PATCH 2/4] fix order of defer --- src/main_ghostty.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main_ghostty.zig b/src/main_ghostty.zig index 1eb91b6b2..ca63c8195 100644 --- a/src/main_ghostty.zig +++ b/src/main_ghostty.zig @@ -99,8 +99,8 @@ pub fn main() !MainReturn { // Create our app state var app: App = undefined; - defer app.deinit(); try app.init(alloc); + defer app.deinit(); // Create our runtime app var app_runtime: apprt.App = undefined; From 1979fb92f42086271f5d09fc5b4f24a378a611a6 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 5 Feb 2025 15:04:28 -0600 Subject: [PATCH 3/4] embedded: fix core app init --- src/apprt/embedded.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 2234fdc2d..307efd26a 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -1318,6 +1318,7 @@ pub const CAPI = struct { config: *const Config, ) !*App { var core_app = try global.alloc.create(CoreApp); + try core_app.init(global.alloc); errdefer { core_app.deinit(); global.alloc.destroy(core_app); From 83690744b2540d3bc2fc828bfefe53865d499b85 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 27 Jun 2025 09:09:54 -0700 Subject: [PATCH 4/4] reintroduce App.create --- src/App.zig | 17 +++++++++++++++++ src/apprt/embedded.zig | 11 +++-------- src/main_ghostty.zig | 5 ++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/App.zig b/src/App.zig index fceab0275..02089ae5b 100644 --- a/src/App.zig +++ b/src/App.zig @@ -76,6 +76,15 @@ first: bool = true, pub const CreateError = Allocator.Error || font.SharedGridSet.InitError; +/// Create a new app instance. This returns a stable pointer to the app +/// instance which is required for callbacks. +pub fn create(alloc: Allocator) CreateError!*App { + var app = try alloc.create(App); + errdefer alloc.destroy(app); + try app.init(alloc); + return app; +} + /// 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. @@ -111,6 +120,14 @@ pub fn deinit(self: *App) void { self.font_grid_set.deinit(); } +pub fn destroy(self: *App) void { + // Deinitialize the app + self.deinit(); + + // Free the app memory + self.alloc.destroy(self); +} + /// Tick ticks the app loop. This will drain our mailbox and process those /// events. This should be called by the application runtime on every loop /// tick. diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 307efd26a..dec1e4135 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -1317,12 +1317,8 @@ pub const CAPI = struct { opts: *const apprt.runtime.App.Options, config: *const Config, ) !*App { - var core_app = try global.alloc.create(CoreApp); - try core_app.init(global.alloc); - errdefer { - core_app.deinit(); - global.alloc.destroy(core_app); - } + const core_app = try CoreApp.create(global.alloc); + errdefer core_app.destroy(); // Create our runtime app var app = try global.alloc.create(App); @@ -1350,8 +1346,7 @@ pub const CAPI = struct { const core_app = v.core_app; v.terminate(); global.alloc.destroy(v); - core_app.deinit(); - global.alloc.destroy(core_app); + core_app.destroy(); } /// Update the focused state of the app. diff --git a/src/main_ghostty.zig b/src/main_ghostty.zig index ca63c8195..b75d8397c 100644 --- a/src/main_ghostty.zig +++ b/src/main_ghostty.zig @@ -98,9 +98,8 @@ pub fn main() !MainReturn { } // Create our app state - var app: App = undefined; - try app.init(alloc); - defer app.deinit(); + const app: *App = try App.create(alloc); + defer app.destroy(); // Create our runtime app var app_runtime: apprt.App = undefined;