diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index f7d244f7d..ea2043cc6 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -46,6 +46,9 @@ pub const App = struct { cursor_default: *c.GdkCursor, cursor_ibeam: *c.GdkCursor, + /// This is set to false when the main loop should exit. + running: bool = true, + pub fn init(core_app: *CoreApp, opts: Options) !App { _ = opts; @@ -161,12 +164,12 @@ pub const App = struct { /// Run the event loop. This doesn't return until the app exits. pub fn run(self: *App) !void { - while (true) { + while (self.running) { _ = c.g_main_context_iteration(self.ctx, 1); // Tick the terminal app const should_quit = try self.core_app.tick(self); - if (should_quit) return; + if (should_quit) self.quit(); } } @@ -192,6 +195,29 @@ pub const App = struct { try window.init(self); } + fn quit(self: *App) void { + const list = c.gtk_window_list_toplevels(); + + // If we have no toplevel windows, then we're done. + if (list == null) { + self.running = false; + return; + } + + // We have toplevel windows, so we want to go through each and + // request that they exit. + defer c.g_list_free(list); + c.g_list_foreach(list, struct { + fn callback(data: c.gpointer, ud: c.gpointer) callconv(.C) void { + _ = ud; + const ptr = data orelse return; + const widget = @ptrCast(*c.GtkWidget, @alignCast(@alignOf(c.GtkWidget), ptr)); + const window = @ptrCast(*c.GtkWindow, widget); + c.gtk_window_destroy(window); + } + }.callback, null); + } + fn activate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void { _ = app; _ = ud; diff --git a/src/config.zig b/src/config.zig index 527ac54cb..c7b55060c 100644 --- a/src/config.zig +++ b/src/config.zig @@ -346,6 +346,11 @@ pub const Config = struct { .{ .key = .w, .mods = .{ .ctrl = true, .shift = true } }, .{ .close_surface = {} }, ); + try result.keybind.set.put( + alloc, + .{ .key = .q, .mods = .{ .ctrl = true, .shift = true } }, + .{ .quit = {} }, + ); try result.keybind.set.put( alloc, .{ .key = .f4, .mods = .{ .alt = true } },