surface no longer has reference to app

This commit is contained in:
Mitchell Hashimoto
2023-02-22 20:08:48 -08:00
parent ac772c2d2d
commit 705d56d18e
5 changed files with 104 additions and 88 deletions

View File

@ -24,9 +24,6 @@ const log = std.log.scoped(.app);
const SurfaceList = std.ArrayListUnmanaged(*apprt.Surface); const SurfaceList = std.ArrayListUnmanaged(*apprt.Surface);
/// The type used for sending messages to the app thread.
pub const Mailbox = BlockingQueue(Message, 64);
/// General purpose allocator /// General purpose allocator
alloc: Allocator, alloc: Allocator,
@ -38,14 +35,11 @@ config: *const Config,
/// The mailbox that can be used to send this thread messages. Note /// The mailbox that can be used to send this thread messages. Note
/// this is a blocking queue so if it is full you will get errors (or block). /// this is a blocking queue so if it is full you will get errors (or block).
mailbox: *Mailbox, mailbox: Mailbox.Queue,
/// Set to true once we're quitting. This never goes false again. /// Set to true once we're quitting. This never goes false again.
quit: bool, quit: bool,
/// App will call this when tick should be called.
wakeup_cb: ?*const fn () void = null,
/// Initialize the main app instance. This creates the main window, sets /// Initialize the main app instance. This creates the main window, sets
/// up the renderer state, compiles the shaders, etc. This is the primary /// up the renderer state, compiles the shaders, etc. This is the primary
/// "startup" logic. /// "startup" logic.
@ -53,10 +47,6 @@ pub fn create(
alloc: Allocator, alloc: Allocator,
config: *const Config, config: *const Config,
) !*App { ) !*App {
// The mailbox for messaging this thread
var mailbox = try Mailbox.create(alloc);
errdefer mailbox.destroy(alloc);
// If we have DevMode on, store the config so we can show it // If we have DevMode on, store the config so we can show it
if (DevMode.enabled) DevMode.instance.config = config; if (DevMode.enabled) DevMode.instance.config = config;
@ -66,7 +56,7 @@ pub fn create(
.alloc = alloc, .alloc = alloc,
.surfaces = .{}, .surfaces = .{},
.config = config, .config = config,
.mailbox = mailbox, .mailbox = .{},
.quit = false, .quit = false,
}; };
errdefer app.surfaces.deinit(alloc); errdefer app.surfaces.deinit(alloc);
@ -78,16 +68,10 @@ pub fn destroy(self: *App) void {
// Clean up all our surfaces // Clean up all our surfaces
for (self.surfaces.items) |surface| surface.deinit(); for (self.surfaces.items) |surface| surface.deinit();
self.surfaces.deinit(self.alloc); self.surfaces.deinit(self.alloc);
self.mailbox.destroy(self.alloc);
self.alloc.destroy(self); self.alloc.destroy(self);
} }
/// Request the app runtime to process app events via tick.
pub fn wakeup(self: App) void {
if (self.wakeup_cb) |cb| cb();
}
/// Tick ticks the app loop. This will drain our mailbox and process those /// 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 /// events. This should be called by the application runtime on every loop
/// tick. /// tick.
@ -127,26 +111,13 @@ pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void {
while (i < self.surfaces.items.len) { while (i < self.surfaces.items.len) {
if (self.surfaces.items[i] == rt_surface) { if (self.surfaces.items[i] == rt_surface) {
_ = self.surfaces.swapRemove(i); _ = self.surfaces.swapRemove(i);
continue;
} }
i += 1;
} }
} }
/// 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.surfaces.items.len) {
// const current = self.surfaces.items[i];
// if (window == current) {
// window.destroy();
// _ = self.surfaces.swapRemove(i);
// return;
// }
//
// i += 1;
// }
// }
/// Drain the mailbox. /// Drain the mailbox.
fn drainMailbox(self: *App, rt_app: *apprt.runtime.App) !void { fn drainMailbox(self: *App, rt_app: *apprt.runtime.App) !void {
while (self.mailbox.pop()) |message| { while (self.mailbox.pop()) |message| {
@ -154,12 +125,18 @@ fn drainMailbox(self: *App, rt_app: *apprt.runtime.App) !void {
switch (message) { switch (message) {
.new_window => |msg| try self.newWindow(rt_app, msg), .new_window => |msg| try self.newWindow(rt_app, msg),
.new_tab => |msg| try self.newTab(rt_app, msg), .new_tab => |msg| try self.newTab(rt_app, msg),
.close => |surface| try self.closeSurface(rt_app, surface),
.quit => try self.setQuit(), .quit => try self.setQuit(),
.surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message), .surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message),
} }
} }
} }
fn closeSurface(self: *App, rt_app: *apprt.App, surface: *Surface) !void {
if (!self.hasSurface(surface)) return;
rt_app.closeSurface(surface.rt_surface);
}
/// Create a new window /// Create a new window
fn newWindow(self: *App, rt_app: *apprt.runtime.App, msg: Message.NewWindow) !void { fn newWindow(self: *App, rt_app: *apprt.runtime.App, msg: Message.NewWindow) !void {
const window = try rt_app.newWindow(); const window = try rt_app.newWindow();
@ -231,6 +208,10 @@ pub const Message = union(enum) {
/// environment that doesn't support tabs. /// environment that doesn't support tabs.
new_tab: NewTab, new_tab: NewTab,
/// Close a surface. This notifies the runtime that a surface
/// should close.
close: *Surface,
/// Quit /// Quit
quit: void, quit: void,
@ -254,6 +235,25 @@ pub const Message = union(enum) {
}; };
}; };
/// Mailbox is the way that other threads send the app thread messages.
pub const Mailbox = struct {
/// The type used for sending messages to the app thread.
pub const Queue = BlockingQueue(Message, 64);
rt_app: *apprt.App,
mailbox: *Queue,
/// Send a message to the surface.
pub fn push(self: Mailbox, msg: Message, timeout: Queue.Timeout) Queue.Size {
const result = self.mailbox.push(msg, timeout);
// Wake up our app loop
self.rt_app.wakeup();
return result;
}
};
// Wasm API. // Wasm API.
pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct { pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
const wasm = @import("os/wasm.zig"); const wasm = @import("os/wasm.zig");
@ -329,7 +329,7 @@ pub const CAPI = struct {
/// Create a new surface as part of an app. /// Create a new surface as part of an app.
export fn ghostty_surface_new( export fn ghostty_surface_new(
app: *App, app: *App,
opts: *const apprt.runtime.Window.Options, opts: *const apprt.Surface.Options,
) ?*Surface { ) ?*Surface {
return surface_new_(app, opts) catch |err| { return surface_new_(app, opts) catch |err| {
log.err("error initializing surface err={}", .{err}); log.err("error initializing surface err={}", .{err});
@ -339,7 +339,7 @@ pub const CAPI = struct {
fn surface_new_( fn surface_new_(
app: *App, app: *App,
opts: *const apprt.runtime.Window.Options, opts: *const apprt.Surface.Options,
) !*Surface { ) !*Surface {
const w = try app.newWindow(.{ const w = try app.newWindow(.{
.runtime = opts.*, .runtime = opts.*,

View File

@ -42,8 +42,8 @@ const Renderer = renderer.Renderer;
/// Allocator /// Allocator
alloc: Allocator, alloc: Allocator,
/// The app that this window is a part of. /// The mailbox for sending messages to the main app thread.
app: *App, app_mailbox: App.Mailbox,
/// The windowing system surface /// The windowing system surface
rt_surface: *apprt.runtime.Surface, rt_surface: *apprt.runtime.Surface,
@ -128,12 +128,11 @@ const Mouse = struct {
/// stable due to interfacing with various callbacks. /// stable due to interfacing with various callbacks.
pub fn init( pub fn init(
self: *Surface, self: *Surface,
app: *App, alloc: Allocator,
config: *const Config, config: *const Config,
app_mailbox: App.Mailbox,
rt_surface: *apprt.runtime.Surface, rt_surface: *apprt.runtime.Surface,
) !void { ) !void {
const alloc = app.alloc;
// Initialize our renderer with our initialized surface. // Initialize our renderer with our initialized surface.
try Renderer.surfaceInit(rt_surface); try Renderer.surfaceInit(rt_surface);
@ -291,7 +290,7 @@ pub fn init(
.explicit = padding, .explicit = padding,
.balance = config.@"window-padding-balance", .balance = config.@"window-padding-balance",
}, },
.surface_mailbox = .{ .surface = self, .app = app.mailbox }, .surface_mailbox = .{ .surface = self, .app = app_mailbox },
}); });
errdefer renderer_impl.deinit(); errdefer renderer_impl.deinit();
@ -328,7 +327,7 @@ pub fn init(
.renderer_state = &self.renderer_state, .renderer_state = &self.renderer_state,
.renderer_wakeup = render_thread.wakeup, .renderer_wakeup = render_thread.wakeup,
.renderer_mailbox = render_thread.mailbox, .renderer_mailbox = render_thread.mailbox,
.surface_mailbox = .{ .surface = self, .app = app.mailbox }, .surface_mailbox = .{ .surface = self, .app = app_mailbox },
}); });
errdefer io.deinit(); errdefer io.deinit();
@ -343,7 +342,7 @@ pub fn init(
self.* = .{ self.* = .{
.alloc = alloc, .alloc = alloc,
.app = app, .app_mailbox = app_mailbox,
.rt_surface = rt_surface, .rt_surface = rt_surface,
.font_lib = font_lib, .font_lib = font_lib,
.font_group = font_group, .font_group = font_group,
@ -902,30 +901,29 @@ pub fn keyCallback(
} else log.warn("dev mode was not compiled into this binary", .{}), } else log.warn("dev mode was not compiled into this binary", .{}),
.new_window => { .new_window => {
_ = self.app.mailbox.push(.{ _ = self.app_mailbox.push(.{
.new_window = .{ .new_window = .{
.parent = self, .parent = self,
}, },
}, .{ .instant = {} }); }, .{ .instant = {} });
self.app.wakeup();
}, },
.new_tab => { .new_tab => {
_ = self.app.mailbox.push(.{ _ = self.app_mailbox.push(.{
.new_tab = .{ .new_tab = .{
.parent = self, .parent = self,
}, },
}, .{ .instant = {} }); }, .{ .instant = {} });
self.app.wakeup();
}, },
.close_window => self.rt_surface.setShouldClose(), .close_window => {
_ = self.app_mailbox.push(.{ .close = self }, .{ .instant = {} });
},
.quit => { .quit => {
_ = self.app.mailbox.push(.{ _ = self.app_mailbox.push(.{
.quit = {}, .quit = {},
}, .{ .instant = {} }); }, .{ .instant = {} });
self.app.wakeup();
}, },
} }

View File

@ -46,10 +46,11 @@ pub const App = struct {
write_clipboard: *const fn (SurfaceUD, [*:0]const u8) callconv(.C) void, write_clipboard: *const fn (SurfaceUD, [*:0]const u8) callconv(.C) void,
}; };
core_app: *CoreApp,
opts: Options, opts: Options,
pub fn init(opts: Options) !App { pub fn init(core_app: *CoreApp, opts: Options) !App {
return .{ .opts = opts }; return .{ .core_app = core_app, .opts = opts };
} }
pub fn terminate(self: App) void { pub fn terminate(self: App) void {
@ -67,7 +68,7 @@ pub const App = struct {
pub const Surface = struct { pub const Surface = struct {
nsview: objc.Object, nsview: objc.Object,
core_win: *CoreSurface, core_surface: *CoreSurface,
content_scale: apprt.ContentScale, content_scale: apprt.ContentScale,
size: apprt.SurfaceSize, size: apprt.SurfaceSize,
cursor_pos: apprt.CursorPos, cursor_pos: apprt.CursorPos,
@ -84,11 +85,9 @@ pub const Surface = struct {
scale_factor: f64 = 1, scale_factor: f64 = 1,
}; };
pub fn init(app: *const CoreApp, core_win: *CoreSurface, opts: Options) !Surface { pub fn init(self: *Surface, app: *App, opts: Options) !void {
_ = app; self.* = .{
.core_surface = undefined,
return .{
.core_win = core_win,
.nsview = objc.Object.fromId(opts.nsview), .nsview = objc.Object.fromId(opts.nsview),
.content_scale = .{ .content_scale = .{
.x = @floatCast(f32, opts.scale_factor), .x = @floatCast(f32, opts.scale_factor),
@ -98,10 +97,23 @@ pub const Surface = struct {
.cursor_pos = .{ .x = 0, .y = 0 }, .cursor_pos = .{ .x = 0, .y = 0 },
.opts = opts, .opts = opts,
}; };
// Add ourselves to the list of surfaces on the app.
try app.app.addSurface(self);
errdefer app.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);
errdefer self.core_surface.deinit();
} }
pub fn deinit(self: *Surface) void { pub fn deinit(self: *Surface) void {
_ = self; // Remove ourselves from the list of known surfaces in the app.
self.core_surface.app.deleteSurface(self);
// Clean up our core surface so that all the rendering and IO stop.
self.core_surface.deinit();
} }
pub fn getContentScale(self: *const Surface) !apprt.ContentScale { pub fn getContentScale(self: *const Surface) !apprt.ContentScale {
@ -119,19 +131,19 @@ pub const Surface = struct {
} }
pub fn setTitle(self: *Surface, slice: [:0]const u8) !void { pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
self.core_win.app.runtime.opts.set_title( self.core_surface.app.runtime.opts.set_title(
self.opts.userdata, self.opts.userdata,
slice.ptr, slice.ptr,
); );
} }
pub fn getClipboardString(self: *const Surface) ![:0]const u8 { pub fn getClipboardString(self: *const Surface) ![:0]const u8 {
const ptr = self.core_win.app.runtime.opts.read_clipboard(self.opts.userdata); const ptr = self.core_surface.app.runtime.opts.read_clipboard(self.opts.userdata);
return std.mem.sliceTo(ptr, 0); return std.mem.sliceTo(ptr, 0);
} }
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void { pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
self.core_win.app.runtime.opts.write_clipboard(self.opts.userdata, val.ptr); self.core_surface.app.runtime.opts.write_clipboard(self.opts.userdata, val.ptr);
} }
pub fn setShouldClose(self: *Surface) void { pub fn setShouldClose(self: *Surface) void {
@ -148,7 +160,7 @@ pub const Surface = struct {
} }
pub fn refresh(self: *Surface) void { pub fn refresh(self: *Surface) void {
self.core_win.refreshCallback() catch |err| { self.core_surface.refreshCallback() catch |err| {
log.err("error in refresh callback err={}", .{err}); log.err("error in refresh callback err={}", .{err});
return; return;
}; };
@ -168,7 +180,7 @@ pub const Surface = struct {
}; };
// Call the primary callback. // Call the primary callback.
self.core_win.sizeCallback(self.size) catch |err| { self.core_surface.sizeCallback(self.size) catch |err| {
log.err("error in size callback err={}", .{err}); log.err("error in size callback err={}", .{err});
return; return;
}; };
@ -180,14 +192,14 @@ pub const Surface = struct {
button: input.MouseButton, button: input.MouseButton,
mods: input.Mods, mods: input.Mods,
) void { ) void {
self.core_win.mouseButtonCallback(action, button, mods) catch |err| { self.core_surface.mouseButtonCallback(action, button, mods) catch |err| {
log.err("error in mouse button callback err={}", .{err}); log.err("error in mouse button callback err={}", .{err});
return; return;
}; };
} }
pub fn scrollCallback(self: *const Surface, xoff: f64, yoff: f64) void { pub fn scrollCallback(self: *const Surface, xoff: f64, yoff: f64) void {
self.core_win.scrollCallback(xoff, yoff) catch |err| { self.core_surface.scrollCallback(xoff, yoff) catch |err| {
log.err("error in scroll callback err={}", .{err}); log.err("error in scroll callback err={}", .{err});
return; return;
}; };
@ -195,7 +207,7 @@ pub const Surface = struct {
pub fn cursorPosCallback(self: *Surface, x: f64, y: f64) void { pub fn cursorPosCallback(self: *Surface, x: f64, y: f64) void {
// Convert our unscaled x/y to scaled. // Convert our unscaled x/y to scaled.
self.cursor_pos = self.core_win.window.cursorPosToPixels(.{ self.cursor_pos = self.core_surface.window.cursorPosToPixels(.{
.x = @floatCast(f32, x), .x = @floatCast(f32, x),
.y = @floatCast(f32, y), .y = @floatCast(f32, y),
}) catch |err| { }) catch |err| {
@ -206,7 +218,7 @@ pub const Surface = struct {
return; return;
}; };
self.core_win.cursorPosCallback(self.cursor_pos) catch |err| { self.core_surface.cursorPosCallback(self.cursor_pos) catch |err| {
log.err("error in cursor pos callback err={}", .{err}); log.err("error in cursor pos callback err={}", .{err});
return; return;
}; };
@ -219,7 +231,7 @@ pub const Surface = struct {
mods: input.Mods, mods: input.Mods,
) void { ) void {
// log.warn("key action={} key={} mods={}", .{ action, key, mods }); // log.warn("key action={} key={} mods={}", .{ action, key, mods });
self.core_win.keyCallback(action, key, mods) catch |err| { self.core_surface.keyCallback(action, key, mods) catch |err| {
log.err("error in key callback err={}", .{err}); log.err("error in key callback err={}", .{err});
return; return;
}; };
@ -227,14 +239,14 @@ pub const Surface = struct {
pub fn charCallback(self: *const Surface, cp_: u32) void { pub fn charCallback(self: *const Surface, cp_: u32) void {
const cp = std.math.cast(u21, cp_) orelse return; const cp = std.math.cast(u21, cp_) orelse return;
self.core_win.charCallback(cp) catch |err| { self.core_surface.charCallback(cp) catch |err| {
log.err("error in char callback err={}", .{err}); log.err("error in char callback err={}", .{err});
return; return;
}; };
} }
pub fn focusCallback(self: *const Surface, focused: bool) void { pub fn focusCallback(self: *const Surface, focused: bool) void {
self.core_win.focusCallback(focused) catch |err| { self.core_surface.focusCallback(focused) catch |err| {
log.err("error in focus callback err={}", .{err}); log.err("error in focus callback err={}", .{err});
return; return;
}; };

View File

@ -43,9 +43,6 @@ pub const App = struct {
var darwin = if (Darwin.enabled) try Darwin.init() else {}; var darwin = if (Darwin.enabled) try Darwin.init() else {};
errdefer if (Darwin.enabled) darwin.deinit(); errdefer if (Darwin.enabled) darwin.deinit();
// Set our callback for being woken up
core_app.wakeup_cb = wakeup;
return .{ return .{
.app = core_app, .app = core_app,
.darwin = darwin, .darwin = darwin,
@ -71,7 +68,8 @@ pub const App = struct {
} }
/// Wakeup the event loop. This should be able to be called from any thread. /// Wakeup the event loop. This should be able to be called from any thread.
pub fn wakeup() void { pub fn wakeup(self: *const App) void {
_ = self;
glfw.postEmptyEvent(); glfw.postEmptyEvent();
} }
@ -114,11 +112,11 @@ pub const App = struct {
// point in the grid. // point in the grid.
const size = parent.rt_surface.getSize() catch |err| { const size = parent.rt_surface.getSize() catch |err| {
log.err("error querying window size for size callback on new tab err={}", .{err}); log.err("error querying window size for size callback on new tab err={}", .{err});
return; return window;
}; };
parent.sizeCallback(size) catch |err| { parent.sizeCallback(size) catch |err| {
log.err("error in size callback from new tab err={}", .{err}); log.err("error in size callback from new tab err={}", .{err});
return; return window;
}; };
return window; return window;
@ -193,6 +191,9 @@ pub const Surface = struct {
/// The glfw mouse cursor handle. /// The glfw mouse cursor handle.
cursor: glfw.Cursor, cursor: glfw.Cursor,
/// The app we're part of
app: *App,
/// A core surface /// A core surface
core_surface: CoreSurface, core_surface: CoreSurface,
@ -265,6 +266,7 @@ pub const Surface = struct {
// Build our result // Build our result
self.* = .{ self.* = .{
.app = app,
.window = win, .window = win,
.cursor = cursor, .cursor = cursor,
.core_surface = undefined, .core_surface = undefined,
@ -276,13 +278,18 @@ pub const Surface = struct {
errdefer app.app.deleteSurface(self); errdefer app.app.deleteSurface(self);
// Initialize our surface now that we have the stable pointer. // Initialize our surface now that we have the stable pointer.
try self.core_surface.init(app.app, app.app.config, self); try self.core_surface.init(
app.app.alloc,
app.app.config,
.{ .rt_app = app, .mailbox = &app.app.mailbox },
self,
);
errdefer self.core_surface.deinit(); errdefer self.core_surface.deinit();
} }
pub fn deinit(self: *Surface) void { pub fn deinit(self: *Surface) void {
// Remove ourselves from the list of known surfaces in the app. // Remove ourselves from the list of known surfaces in the app.
self.core_surface.app.deleteSurface(self); self.app.app.deleteSurface(self);
// Clean up our core surface so that all the rendering and IO stop. // Clean up our core surface so that all the rendering and IO stop.
self.core_surface.deinit(); self.core_surface.deinit();

View File

@ -28,23 +28,22 @@ pub const Message = union(enum) {
/// A surface mailbox. /// A surface mailbox.
pub const Mailbox = struct { pub const Mailbox = struct {
surface: *Surface, surface: *Surface,
app: *App.Mailbox, app: App.Mailbox,
/// Send a message to the surface. /// Send a message to the surface.
pub fn push(self: Mailbox, msg: Message, timeout: App.Mailbox.Timeout) App.Mailbox.Size { pub fn push(
self: Mailbox,
msg: Message,
timeout: App.Mailbox.Queue.Timeout,
) App.Mailbox.Queue.Size {
// Surface message sending is actually implemented on the app // Surface message sending is actually implemented on the app
// thread, so we have to rewrap the message with our surface // thread, so we have to rewrap the message with our surface
// pointer and send it to the app thread. // pointer and send it to the app thread.
const result = self.app.push(.{ return self.app.push(.{
.surface_message = .{ .surface_message = .{
.surface = self.surface, .surface = self.surface,
.message = msg, .message = msg,
}, },
}, timeout); }, timeout);
// Wake up our app loop
self.surface.app.wakeup();
return result;
} }
}; };