From db69dc41b28168dd0ec765348a687ede3d70a13b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 20 Aug 2026 14:19:30 -0400 Subject: [PATCH] Bundle all the icons together. --- assets/icons/README.md | 15 ++++++ assets/icons/icons.gresource.xml | 31 +++++++++++ .../actions/document-edit-symbolic.svg | 4 ++ .../scalable/actions/edit-undo-symbolic.svg | 4 ++ .../actions/emblem-system-symbolic.svg | 4 ++ .../scalable/actions/go-down-symbolic.svg | 4 ++ .../scalable/actions/go-next-symbolic.svg | 4 ++ .../scalable/actions/go-previous-symbolic.svg | 4 ++ .../icons/scalable/actions/go-up-symbolic.svg | 4 ++ .../scalable/actions/list-add-symbolic.svg | 4 ++ .../scalable/actions/list-remove-symbolic.svg | 4 ++ .../actions/media-record-symbolic.svg | 4 ++ .../actions/process-stop-symbolic.svg | 4 ++ .../scalable/actions/tab-new-symbolic.svg | 7 +++ .../scalable/actions/user-trash-symbolic.svg | 8 +++ .../actions/utilities-terminal-symbolic.svg | 1 + .../actions/view-fullscreen-symbolic.svg | 7 +++ .../scalable/actions/view-grid-symbolic.svg | 4 ++ .../actions/view-refresh-symbolic.svg | 4 ++ .../actions/view-restore-symbolic.svg | 7 +++ .../scalable/actions/web-browser-symbolic.svg | 1 + .../actions/window-close-symbolic.svg | 4 ++ build.zig | 15 ++++++ src/icons.zig | 51 +++++++++++++++++++ src/main.zig | 5 ++ 25 files changed, 204 insertions(+) create mode 100644 assets/icons/README.md create mode 100644 assets/icons/icons.gresource.xml create mode 100644 assets/icons/scalable/actions/document-edit-symbolic.svg create mode 100644 assets/icons/scalable/actions/edit-undo-symbolic.svg create mode 100644 assets/icons/scalable/actions/emblem-system-symbolic.svg create mode 100644 assets/icons/scalable/actions/go-down-symbolic.svg create mode 100644 assets/icons/scalable/actions/go-next-symbolic.svg create mode 100644 assets/icons/scalable/actions/go-previous-symbolic.svg create mode 100644 assets/icons/scalable/actions/go-up-symbolic.svg create mode 100644 assets/icons/scalable/actions/list-add-symbolic.svg create mode 100644 assets/icons/scalable/actions/list-remove-symbolic.svg create mode 100644 assets/icons/scalable/actions/media-record-symbolic.svg create mode 100644 assets/icons/scalable/actions/process-stop-symbolic.svg create mode 100644 assets/icons/scalable/actions/tab-new-symbolic.svg create mode 100644 assets/icons/scalable/actions/user-trash-symbolic.svg create mode 100644 assets/icons/scalable/actions/utilities-terminal-symbolic.svg create mode 100644 assets/icons/scalable/actions/view-fullscreen-symbolic.svg create mode 100644 assets/icons/scalable/actions/view-grid-symbolic.svg create mode 100644 assets/icons/scalable/actions/view-refresh-symbolic.svg create mode 100644 assets/icons/scalable/actions/view-restore-symbolic.svg create mode 100644 assets/icons/scalable/actions/web-browser-symbolic.svg create mode 100644 assets/icons/scalable/actions/window-close-symbolic.svg create mode 100644 src/icons.zig diff --git a/assets/icons/README.md b/assets/icons/README.md new file mode 100644 index 0000000..e579e53 --- /dev/null +++ b/assets/icons/README.md @@ -0,0 +1,15 @@ +# Bundled icons + +Symbolic icons copied from [adwaita-icon-theme](https://gitlab.gnome.org/GNOME/adwaita-icon-theme) +50.0 (the version pinned by `flake.nix`), licensed under CC BY-SA 3.0 or +LGPL v3, at the theme's option. + +They are compiled into the binary (see `icons.gresource.xml`, `build.zig`, +and `src/icons.zig`) because the app refers to them by freedesktop name, and +recent icon-theme releases dropped many of these legacy names — whether a +host machine still resolves them is luck. GNOME's own guidance is that apps +ship the icons they use. + +Everything lives under `scalable/actions/` regardless of the icon's original +category: GTK's resource lookup goes by name, not category, and one flat +directory keeps the manifest honest. diff --git a/assets/icons/icons.gresource.xml b/assets/icons/icons.gresource.xml new file mode 100644 index 0000000..f54bcf4 --- /dev/null +++ b/assets/icons/icons.gresource.xml @@ -0,0 +1,31 @@ + + + + + scalable/actions/document-edit-symbolic.svg + scalable/actions/edit-undo-symbolic.svg + scalable/actions/emblem-system-symbolic.svg + scalable/actions/go-down-symbolic.svg + scalable/actions/go-next-symbolic.svg + scalable/actions/go-previous-symbolic.svg + scalable/actions/go-up-symbolic.svg + scalable/actions/list-add-symbolic.svg + scalable/actions/list-remove-symbolic.svg + scalable/actions/media-record-symbolic.svg + scalable/actions/process-stop-symbolic.svg + scalable/actions/tab-new-symbolic.svg + scalable/actions/user-trash-symbolic.svg + scalable/actions/utilities-terminal-symbolic.svg + scalable/actions/view-fullscreen-symbolic.svg + scalable/actions/view-grid-symbolic.svg + scalable/actions/view-refresh-symbolic.svg + scalable/actions/view-restore-symbolic.svg + scalable/actions/web-browser-symbolic.svg + scalable/actions/window-close-symbolic.svg + + diff --git a/assets/icons/scalable/actions/document-edit-symbolic.svg b/assets/icons/scalable/actions/document-edit-symbolic.svg new file mode 100644 index 0000000..d5d54f6 --- /dev/null +++ b/assets/icons/scalable/actions/document-edit-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/edit-undo-symbolic.svg b/assets/icons/scalable/actions/edit-undo-symbolic.svg new file mode 100644 index 0000000..bd8f05c --- /dev/null +++ b/assets/icons/scalable/actions/edit-undo-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/emblem-system-symbolic.svg b/assets/icons/scalable/actions/emblem-system-symbolic.svg new file mode 100644 index 0000000..c01314d --- /dev/null +++ b/assets/icons/scalable/actions/emblem-system-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/go-down-symbolic.svg b/assets/icons/scalable/actions/go-down-symbolic.svg new file mode 100644 index 0000000..0159fbd --- /dev/null +++ b/assets/icons/scalable/actions/go-down-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/go-next-symbolic.svg b/assets/icons/scalable/actions/go-next-symbolic.svg new file mode 100644 index 0000000..0ff48be --- /dev/null +++ b/assets/icons/scalable/actions/go-next-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/go-previous-symbolic.svg b/assets/icons/scalable/actions/go-previous-symbolic.svg new file mode 100644 index 0000000..1b7901f --- /dev/null +++ b/assets/icons/scalable/actions/go-previous-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/go-up-symbolic.svg b/assets/icons/scalable/actions/go-up-symbolic.svg new file mode 100644 index 0000000..a32393d --- /dev/null +++ b/assets/icons/scalable/actions/go-up-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/list-add-symbolic.svg b/assets/icons/scalable/actions/list-add-symbolic.svg new file mode 100644 index 0000000..cf68622 --- /dev/null +++ b/assets/icons/scalable/actions/list-add-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/list-remove-symbolic.svg b/assets/icons/scalable/actions/list-remove-symbolic.svg new file mode 100644 index 0000000..b20ba0b --- /dev/null +++ b/assets/icons/scalable/actions/list-remove-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/media-record-symbolic.svg b/assets/icons/scalable/actions/media-record-symbolic.svg new file mode 100644 index 0000000..a2fbb44 --- /dev/null +++ b/assets/icons/scalable/actions/media-record-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/process-stop-symbolic.svg b/assets/icons/scalable/actions/process-stop-symbolic.svg new file mode 100644 index 0000000..19b9537 --- /dev/null +++ b/assets/icons/scalable/actions/process-stop-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/tab-new-symbolic.svg b/assets/icons/scalable/actions/tab-new-symbolic.svg new file mode 100644 index 0000000..38e455b --- /dev/null +++ b/assets/icons/scalable/actions/tab-new-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/scalable/actions/user-trash-symbolic.svg b/assets/icons/scalable/actions/user-trash-symbolic.svg new file mode 100644 index 0000000..2e20f9c --- /dev/null +++ b/assets/icons/scalable/actions/user-trash-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/scalable/actions/utilities-terminal-symbolic.svg b/assets/icons/scalable/actions/utilities-terminal-symbolic.svg new file mode 100644 index 0000000..eba46d6 --- /dev/null +++ b/assets/icons/scalable/actions/utilities-terminal-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/scalable/actions/view-fullscreen-symbolic.svg b/assets/icons/scalable/actions/view-fullscreen-symbolic.svg new file mode 100644 index 0000000..e65d6d6 --- /dev/null +++ b/assets/icons/scalable/actions/view-fullscreen-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/scalable/actions/view-grid-symbolic.svg b/assets/icons/scalable/actions/view-grid-symbolic.svg new file mode 100644 index 0000000..1ab7b14 --- /dev/null +++ b/assets/icons/scalable/actions/view-grid-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/view-refresh-symbolic.svg b/assets/icons/scalable/actions/view-refresh-symbolic.svg new file mode 100644 index 0000000..3248fe7 --- /dev/null +++ b/assets/icons/scalable/actions/view-refresh-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/scalable/actions/view-restore-symbolic.svg b/assets/icons/scalable/actions/view-restore-symbolic.svg new file mode 100644 index 0000000..3bd41d8 --- /dev/null +++ b/assets/icons/scalable/actions/view-restore-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/scalable/actions/web-browser-symbolic.svg b/assets/icons/scalable/actions/web-browser-symbolic.svg new file mode 100644 index 0000000..c808611 --- /dev/null +++ b/assets/icons/scalable/actions/web-browser-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/scalable/actions/window-close-symbolic.svg b/assets/icons/scalable/actions/window-close-symbolic.svg new file mode 100644 index 0000000..f68a861 --- /dev/null +++ b/assets/icons/scalable/actions/window-close-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/build.zig b/build.zig index 2dbfd64..17edd67 100644 --- a/build.zig +++ b/build.zig @@ -32,6 +32,21 @@ pub fn build(b: *std.Build) void { exe.root_module.addImport("ghostty-vt", ghostty.module("ghostty-vt")); + // The symbolic icons the app names are compiled into the binary, so that + // showing a terminal or web-browser glyph doesn't depend on which icon + // theme the host has installed. glib-compile-resources comes from the same + // Nix shell as GTK itself; the compiled bundle is embedded by src/icons.zig + // and registered at startup. + const gresource = b.addSystemCommand(&.{"glib-compile-resources"}); + gresource.addArg("--sourcedir"); + gresource.addDirectoryArg(b.path("assets/icons")); + gresource.addArg("--target"); + const icons_gresource = gresource.addOutputFileArg("icons.gresource"); + gresource.addFileArg(b.path("assets/icons/icons.gresource.xml")); + exe.root_module.addAnonymousImport("icons.gresource", .{ + .root_source_file = icons_gresource, + }); + const gobject_imports = .{ .{ "adw", "adw1" }, .{ "cairo", "cairo1" }, diff --git a/src/icons.zig b/src/icons.zig new file mode 100644 index 0000000..75740d4 --- /dev/null +++ b/src/icons.zig @@ -0,0 +1,51 @@ +//! The symbolic icons the app names, carried inside the binary. +//! +//! Widgets here ask for icons by freedesktop name — `utilities-terminal- +//! symbolic`, `web-browser-symbolic`, and so on — and by default GTK resolves +//! those against whatever icon theme the host has installed. Recent +//! adwaita-icon-theme releases dropped many of these legacy names, so whether +//! a given machine still shows them is luck. This module removes the luck: +//! the icons are compiled into the binary as a GResource (see +//! `assets/icons/icons.gresource.xml` and the glib-compile-resources step in +//! `build.zig`) and registered with the icon theme at startup. +//! +//! Registration is deliberately as a *fallback*: `addResourcePath` folds the +//! bundle into hicolor, the theme of last resort, so a host theme that does +//! provide a name keeps winning and the app stays native-looking. The bundle +//! only answers for names the host can't. + +const std = @import("std"); +const gdk = @import("gdk"); +const gio = @import("gio"); +const glib = @import("glib"); +const gtk = @import("gtk"); + +const data = @embedFile("icons.gresource"); + +/// Register the bundled icons. Called once at activation, before the first +/// window is built, so nothing is ever looked up ahead of it. +/// +/// Failure is logged and swallowed: a missing icon renders as the +/// broken-image glyph, which is exactly the state this module exists to +/// avoid, but not a reason to refuse to open a window. +pub fn init() void { + var err: ?*glib.Error = null; + + // newFromData copies if the embedded bytes aren't suitably aligned, so + // static is safe here regardless of where @embedFile landed them. + const bytes = glib.Bytes.newStatic(data, data.len); + defer bytes.unref(); + + const resource = gio.Resource.newFromData(bytes, &err) orelse { + if (err) |e| { + std.log.warn("failed to load bundled icons: {s}", .{e.f_message orelse "unknown"}); + e.free(); + } + return; + }; + gio.resourcesRegister(resource); + + if (gdk.Display.getDefault()) |display| { + gtk.IconTheme.getForDisplay(display).addResourcePath("/dev/greyson/playpen/icons"); + } +} diff --git a/src/main.zig b/src/main.zig index ae37714..2373a9d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,6 +12,7 @@ const gio = @import("gio"); const Settings = @import("Settings.zig"); const Window = @import("Window.zig"); const appearance = @import("appearance.zig"); +const icons = @import("icons.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. @@ -50,6 +51,10 @@ fn onActivate(app: *adw.Application, _: ?*anyopaque) callconv(.c) void { // user chose rather than repainted into it a moment later. appearance.init(); + // Same reasoning: the bundled icons are registered before anything asks + // for one by name. + icons.init(); + const window = Window.create(gpa.allocator(), app) catch |err| { std.log.err("failed to create window: {s}", .{@errorName(err)}); return;