move config loading into apprt to prep for reloading

This commit is contained in:
Mitchell Hashimoto
2023-03-13 21:44:45 -07:00
parent 8cb9ee5d59
commit 3e1f975551
6 changed files with 76 additions and 22 deletions

View File

@ -30,11 +30,6 @@ alloc: Allocator,
/// The list of surfaces that are currently active. /// The list of surfaces that are currently active.
surfaces: SurfaceList, surfaces: SurfaceList,
// The configuration for the app. This may change (app runtimes are notified
// via the callback), but the change will only ever happen during tick()
// so app runtimes can ensure there are no data races in reading this.
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.Queue, mailbox: Mailbox.Queue,
@ -47,17 +42,15 @@ quit: bool,
/// "startup" logic. /// "startup" logic.
pub fn create( pub fn create(
alloc: Allocator, alloc: Allocator,
config: *const Config,
) !*App { ) !*App {
// 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;
var app = try alloc.create(App); var app = try alloc.create(App);
errdefer alloc.destroy(app); errdefer alloc.destroy(app);
app.* = .{ app.* = .{
.alloc = alloc, .alloc = alloc,
.surfaces = .{}, .surfaces = .{},
.config = config,
.mailbox = .{}, .mailbox = .{},
.quit = false, .quit = false,
}; };
@ -99,6 +92,21 @@ pub fn tick(self: *App, rt_app: *apprt.App) !bool {
return self.quit or self.surfaces.items.len == 0; return self.quit or self.surfaces.items.len == 0;
} }
/// Update the configuration associated with the app. This can only be
/// called from the main thread.
///
/// The caller owns the config memory. The prior config must not be freed
/// until this function returns successfully.
pub fn updateConfig(self: *App, config: *const Config) !void {
// Update our config
self.config = config;
// Go through and update all of the surface configurations.
for (self.surfaces.items) |surface| {
try surface.handleMessage(.{ .change_config = config });
}
}
/// Add an initialized surface. This is really only for the runtime /// Add an initialized surface. This is really only for the runtime
/// implementations to call and should NOT be called by general app users. /// implementations to call and should NOT be called by general app users.
/// The surface must be from the pool. /// The surface must be from the pool.
@ -125,6 +133,7 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
while (self.mailbox.pop()) |message| { while (self.mailbox.pop()) |message| {
log.debug("mailbox message={s}", .{@tagName(message)}); log.debug("mailbox message={s}", .{@tagName(message)});
switch (message) { switch (message) {
.reload_config => try self.reloadConfig(rt_app),
.new_window => |msg| try self.newWindow(rt_app, msg), .new_window => |msg| try self.newWindow(rt_app, msg),
.close => |surface| try self.closeSurface(rt_app, surface), .close => |surface| try self.closeSurface(rt_app, surface),
.quit => try self.setQuit(), .quit => try self.setQuit(),
@ -134,6 +143,12 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
} }
} }
fn reloadConfig(self: *App, rt_app: *apprt.App) !void {
_ = rt_app;
_ = self;
//try rt_app.reloadConfig();
}
fn closeSurface(self: *App, rt_app: *apprt.App, surface: *Surface) !void { fn closeSurface(self: *App, rt_app: *apprt.App, surface: *Surface) !void {
if (!self.hasSurface(surface)) return; if (!self.hasSurface(surface)) return;
rt_app.closeSurface(surface.rt_surface); rt_app.closeSurface(surface.rt_surface);
@ -195,6 +210,10 @@ fn hasSurface(self: *App, surface: *Surface) bool {
/// The message types that can be sent to the app thread. /// The message types that can be sent to the app thread.
pub const Message = union(enum) { pub const Message = union(enum) {
/// Reload the configuration for the entire app and propagate it to
/// all the active surfaces.
reload_config: void,
/// Create a new terminal window. /// Create a new terminal window.
new_window: NewWindow, new_window: NewWindow,

View File

@ -528,6 +528,8 @@ pub fn close(self: *Surface) void {
/// surface. /// surface.
pub fn handleMessage(self: *Surface, msg: Message) !void { pub fn handleMessage(self: *Surface, msg: Message) !void {
switch (msg) { switch (msg) {
.change_config => |config| try self.changeConfig(config),
.set_title => |*v| { .set_title => |*v| {
// The ptrCast just gets sliceTo to return the proper type. // The ptrCast just gets sliceTo to return the proper type.
// We know that our title should end in 0. // We know that our title should end in 0.
@ -553,6 +555,24 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
} }
} }
/// Update our configuration at runtime.
fn changeConfig(self: *Surface, config: *const configpkg.Config) !void {
// Update our new derived config immediately
const derived = DerivedConfig.init(self.alloc, config) catch |err| {
// If the derivation fails then we just log and return. We don't
// hard fail in this case because we don't want to error the surface
// when config fails we just want to keep using the old config.
log.err("error updating configuration err={}", .{err});
return;
};
self.config.deinit();
self.config = derived;
// Update our derived configurations for the renderer and termio,
// then send them a message to update.
// TODO
}
/// Returns the x/y coordinate of where the IME (Input Method Editor) /// Returns the x/y coordinate of where the IME (Input Method Editor)
/// keyboard should be rendered. /// keyboard should be rendered.
pub fn imePoint(self: *const Surface) apprt.IMEPos { pub fn imePoint(self: *const Surface) apprt.IMEPos {

View File

@ -19,6 +19,7 @@ const Renderer = renderer.Renderer;
const apprt = @import("../apprt.zig"); const apprt = @import("../apprt.zig");
const CoreApp = @import("../App.zig"); const CoreApp = @import("../App.zig");
const CoreSurface = @import("../Surface.zig"); const CoreSurface = @import("../Surface.zig");
const Config = @import("../config.zig").Config;
// Get native API access on certain platforms so we can do more customization. // Get native API access on certain platforms so we can do more customization.
const glfwNative = glfw.Native(.{ const glfwNative = glfw.Native(.{
@ -29,6 +30,7 @@ const log = std.log.scoped(.glfw);
pub const App = struct { pub const App = struct {
app: *CoreApp, app: *CoreApp,
config: Config,
/// Mac-specific state. /// Mac-specific state.
darwin: if (Darwin.enabled) Darwin else void, darwin: if (Darwin.enabled) Darwin else void,
@ -53,14 +55,19 @@ 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();
// Load our configuration
var config = try Config.load(core_app.alloc);
errdefer config.deinit();
return .{ return .{
.app = core_app, .app = core_app,
.config = config,
.darwin = darwin, .darwin = darwin,
}; };
} }
pub fn terminate(self: App) void { pub fn terminate(self: *App) void {
_ = self; self.config.deinit();
glfw.terminate(); glfw.terminate();
} }
@ -139,7 +146,7 @@ pub const App = struct {
errdefer surface.deinit(); errdefer surface.deinit();
// If we have a parent, inherit some properties // If we have a parent, inherit some properties
if (self.app.config.@"window-inherit-font-size") { if (self.config.@"window-inherit-font-size") {
if (parent_) |parent| { if (parent_) |parent| {
surface.core_surface.setFontSize(parent.font_size); surface.core_surface.setFontSize(parent.font_size);
} }
@ -313,7 +320,7 @@ pub const Surface = struct {
// 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( try self.core_surface.init(
app.app.alloc, app.app.alloc,
app.app.config, &app.config,
.{ .rt_app = app, .mailbox = &app.app.mailbox }, .{ .rt_app = app, .mailbox = &app.app.mailbox },
self, self,
); );

View File

@ -9,6 +9,7 @@ const apprt = @import("../apprt.zig");
const input = @import("../input.zig"); const input = @import("../input.zig");
const CoreApp = @import("../App.zig"); const CoreApp = @import("../App.zig");
const CoreSurface = @import("../Surface.zig"); const CoreSurface = @import("../Surface.zig");
const Config = @import("../config.zig").Config;
pub const c = @cImport({ pub const c = @cImport({
@cInclude("gtk/gtk.h"); @cInclude("gtk/gtk.h");
@ -37,6 +38,7 @@ pub const App = struct {
}; };
core_app: *CoreApp, core_app: *CoreApp,
config: Config,
app: *c.GtkApplication, app: *c.GtkApplication,
ctx: *c.GMainContext, ctx: *c.GMainContext,
@ -53,6 +55,10 @@ pub const App = struct {
// rid of this dep. // rid of this dep.
if (!glfw.init(.{})) return error.GlfwInitFailed; if (!glfw.init(.{})) return error.GlfwInitFailed;
// Load our configuration
var config = try Config.load(core_app.alloc);
errdefer config.deinit();
// Create our GTK Application which encapsulates our process. // Create our GTK Application which encapsulates our process.
const app = @ptrCast(?*c.GtkApplication, c.gtk_application_new( const app = @ptrCast(?*c.GtkApplication, c.gtk_application_new(
null, null,
@ -108,6 +114,7 @@ pub const App = struct {
return .{ return .{
.core_app = core_app, .core_app = core_app,
.app = app, .app = app,
.config = config,
.ctx = ctx, .ctx = ctx,
.cursor_default = cursor_default, .cursor_default = cursor_default,
.cursor_ibeam = cursor_ibeam, .cursor_ibeam = cursor_ibeam,
@ -116,7 +123,7 @@ pub const App = struct {
// Terminate the application. The application will not be restarted after // Terminate the application. The application will not be restarted after
// this so all global state can be cleaned up. // this so all global state can be cleaned up.
pub fn terminate(self: App) void { pub fn terminate(self: *App) void {
c.g_settings_sync(); c.g_settings_sync();
while (c.g_main_context_iteration(self.ctx, 0) != 0) {} while (c.g_main_context_iteration(self.ctx, 0) != 0) {}
c.g_main_context_release(self.ctx); c.g_main_context_release(self.ctx);
@ -125,6 +132,8 @@ pub const App = struct {
c.g_object_unref(self.cursor_ibeam); c.g_object_unref(self.cursor_ibeam);
c.g_object_unref(self.cursor_default); c.g_object_unref(self.cursor_default);
self.config.deinit();
glfw.terminate(); glfw.terminate();
} }
@ -575,7 +584,7 @@ pub const Surface = struct {
// 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( try self.core_surface.init(
self.app.core_app.alloc, self.app.core_app.alloc,
self.app.core_app.config, &self.app.config,
.{ .rt_app = self.app, .mailbox = &self.app.core_app.mailbox }, .{ .rt_app = self.app, .mailbox = &self.app.core_app.mailbox },
self, self,
); );

View File

@ -2,6 +2,7 @@ const App = @import("../App.zig");
const Surface = @import("../Surface.zig"); const Surface = @import("../Surface.zig");
const renderer = @import("../renderer.zig"); const renderer = @import("../renderer.zig");
const termio = @import("../termio.zig"); const termio = @import("../termio.zig");
const Config = @import("../config.zig").Config;
/// The message types that can be sent to a single surface. /// The message types that can be sent to a single surface.
pub const Message = union(enum) { pub const Message = union(enum) {
@ -24,6 +25,11 @@ pub const Message = union(enum) {
/// Write the clipboard contents. /// Write the clipboard contents.
clipboard_write: WriteReq, clipboard_write: WriteReq,
/// Change the configuration to the given configuration. The pointer is
/// not valid after receiving this message so any config must be used
/// and derived immediately.
change_config: *const Config,
/// Close the surface. This will only close the current surface that /// Close the surface. This will only close the current surface that
/// receives this, not the full application. /// receives this, not the full application.
close: void, close: void,

View File

@ -14,8 +14,6 @@ const xdg = @import("xdg.zig");
const apprt = @import("apprt.zig"); const apprt = @import("apprt.zig");
const App = @import("App.zig"); const App = @import("App.zig");
const cli_args = @import("cli_args.zig");
const Config = @import("config.zig").Config;
const Ghostty = @import("main_c.zig").Ghostty; const Ghostty = @import("main_c.zig").Ghostty;
/// Global process state. This is initialized in main() for exe artifacts /// Global process state. This is initialized in main() for exe artifacts
@ -29,13 +27,8 @@ pub fn main() !void {
defer state.deinit(); defer state.deinit();
const alloc = state.alloc; const alloc = state.alloc;
// Try reading our config
var config = try Config.load(alloc);
defer config.deinit();
//std.log.debug("config={}", .{config});
// Create our app state // Create our app state
var app = try App.create(alloc, &config); var app = try App.create(alloc);
defer app.destroy(); defer app.destroy();
// Create our runtime app // Create our runtime app