//! Playpen: a terminal with vertical tabs, web panes and saved layouts, built //! on libghostty-vt. //! //! libghostty-vt supplies the terminal emulator core (escape sequence //! parsing, screen and scrollback state, key/mouse encoding). Everything //! else — process management, rendering, and the GTK4 UI — lives here. const std = @import("std"); const adw = @import("adw"); const gdk = @import("gdk"); const gio = @import("gio"); const gtk = @import("gtk"); const Window = @import("Window.zig"); /// libghostty-vt logs unimplemented sequences at debug level, which is very /// chatty against a real shell. Keep the app's own warnings and errors. pub const std_options: std.Options = .{ .log_level = .info, }; const css = @embedFile("style.css"); var gpa: std.heap.DebugAllocator(.{}) = .init; pub fn main() u8 { defer _ = gpa.deinit(); // Non-unique so every launch is its own process. The default GApplication // behavior hands off to an already-running instance over D-Bus, which for // a terminal means a second launch silently does nothing visible here and // opens a window in whatever session owns the first one. const app = adw.Application.new("dev.greyson.playpen", .{ .non_unique = true }); defer app.unref(); _ = gio.Application.signals.activate.connect(app, ?*anyopaque, &onActivate, null, .{}); const status = gio.Application.run(app.as(gio.Application), 0, null); return @intCast(status); } fn onActivate(app: *adw.Application, _: ?*anyopaque) callconv(.c) void { forceDark(); loadCss(); const window = Window.create(gpa.allocator(), app) catch |err| { std.log.err("failed to create window: {s}", .{@errorName(err)}); return; }; window.present(); } /// The window's own chrome is dark by hand in `style.css`, but stock widgets — /// popovers, dialogs, text entries — follow the desktop's colour scheme and /// would come up light against it. Layouts brought the first real dialogs into /// the app, which is where that mismatch became visible. fn forceDark() void { const manager = adw.StyleManager.getDefault(); manager.setColorScheme(.force_dark); } fn loadCss() void { const display = gdk.Display.getDefault() orelse return; const provider = gtk.CssProvider.new(); defer provider.unref(); provider.loadFromString(css); gtk.StyleContext.addProviderForDisplay( display, provider.as(gtk.StyleProvider), gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, ); }