Bundle all the icons together.

This commit is contained in:
Greyson Parrelli
2026-08-20 14:19:30 -04:00
parent a742b51d3b
commit db69dc41b2
25 changed files with 204 additions and 0 deletions
+51
View File
@@ -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");
}
}
+5
View File
@@ -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;