diff options
| author | Adam Malczewski <[email protected]> | 2026-06-13 21:07:34 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-13 21:07:34 +0900 |
| commit | d5a6edf7c58f9646676d53c32009d6b884deffca (patch) | |
| tree | 8b1b20a4026df652fe95ed99f7543ce9cbdcc2b0 | |
| parent | 3ff6cd370f400450e236f70c2949d5f19fb62313 (diff) | |
| download | unbox-d5a6edf7c58f9646676d53c32009d6b884deffca.tar.gz unbox-d5a6edf7c58f9646676d53c32009d6b884deffca.zip | |
notes: spec dock favicons (XDG icon lookup + lunasvg/stb_image decode); defer in plan §7
| -rw-r--r-- | notes/favicon-spec.md | 97 | ||||
| -rw-r--r-- | notes/plan.md | 1 |
2 files changed, 98 insertions, 0 deletions
diff --git a/notes/favicon-spec.md b/notes/favicon-spec.md new file mode 100644 index 0000000..b7ff407 --- /dev/null +++ b/notes/favicon-spec.md @@ -0,0 +1,97 @@ +# Feature spec — dock favicons (app icons on preview cards) + +> STATUS: **SPEC ONLY — not scheduled, no code yet.** Written so it can be picked +> up cold. Vocabulary is canonical: **favicon** = "the application icon shown on a +> preview, resolved from the toplevel's `app_id` via the XDG icon theme" +> (GLOSSARY:67). No new terms needed. + +## Goal +Each minimized window's dock preview card shows its application icon (favicon), +resolved from the toplevel `app_id` through the freedesktop XDG icon theme. Today +a card shows preview + title; the b2 list-binding design already reserves a +per-row `favicon` string field — it is just unwired. + +## Architecture (mechanism/policy split, per the constitution) +Three disjoint pieces across two units: + +1. **Pure lookup core — ext-stage-dock (or a shared pure core).** Input + `(app_id, size, scale, theme, [inherited…])` → ordered candidate file paths, + per the freedesktop Icon Theme Specification. Pure input→output, doctest-hard, + ZERO I/O: the parsed theme index + a "does this file exist" predicate are + INJECTED, so it tests against a fake filesystem. Real `index.theme` parsing and + `stat`s live in a thin edge adapter. + +2. **Decode + texture load — KERNEL / ui substrate (the GATING work).** The + substrate's RmlUi render interface must turn an icon FILE PATH into a sampled + GL texture. Today it only registers the custom `unbox-preview://N` texture; it + (VERIFY) does not implement RmlUi `LoadTexture` for on-disk images. Add a + decoder feeding RmlUi's texture loader so an `<img>` referencing an icon file + resolves. Needs PNG **and** SVG (see formats below). + +3. **Dock wiring — ext-stage-dock.** Per slot: resolve `app_id` → favicon path + (piece 1) → bind the favicon `<img src>` via the existing b2 + `bind_list_string("slots","favicon", …)`. Show a generic fallback icon on miss. + +## Dependencies to approve (AGENTS.md: no new deps without sign-off) +- **Themes (data, NOT a build dep):** `hicolor-icon-theme` + `adwaita-icon-theme` + are already installed and sufficient. Optional for coverage: `papirus-icon-theme` + (extra). No build wiring. +- **Decoders (the real new deps — vendored Meson subprojects):** + - **stb_image** — PNG (single header, trivial). App icons in hicolor are + commonly PNG (`foot.png`, `firefox.png` present). + - **lunasvg** — SVG. NON-OPTIONAL: modern themes are SVG-first (adwaita ships + **715 .svg vs 51 .png**; papirus/breeze all SVG), and the generic fallbacks + are SVG. lunasvg is light, vendorable, and is RmlUi's own blessed SVG plugin + backend. **Reject `librsvg`** (heavy GNOME/Rust dep). + +## New contract surface (sketch — finalize when built) +- **Kernel `ui.hpp`:** prefer the minimal route — once `LoadTexture` decodes + `file://` image paths, `<img src="file:///usr/share/icons/…/foot.svg">` just + works with NO new public API. Only add a typed + `UiSubstrate::create_icon(path,size) -> source_uri` (mirroring `create_preview`) + if texture caching/lifetime forces kernel ownership. Decide at build time. +- **ext-stage-dock:** no public-header change — favicon is internal policy; the + b2 `favicon` list field already exists in the contract. + +## Lookup core detail (freedesktop Icon Theme Spec, minimal) +- Resolve order: requested theme's size-matched dirs → inherited themes → `hicolor` + → `/usr/share/pixmaps/<app_id>.{png,svg,xpm}` → none. +- `app_id` normalization rules (pure): as-is; lowercased; reverse-DNS reduced to + last component (`org.foo.Bar` → `bar`); known aliases. Try in order. +- Size selection: exact size dir, else `scalable`, else nearest. Prefer full-color + over `-symbolic` unless only symbolic exists. + +## Image-format reality (measured on this box) +- hicolor app icons: mostly PNG, some SVG. adwaita/papirus/breeze: SVG-first. +- ⇒ both decoders required; SVG is the one that can't be skipped. + +## Caching / lifetime +Favicons are few and immutable per app (unlike refreshable per-window previews). +Decode on first use, cache by `(path,size)` for the session. Owner (substrate vs +dock) decided when built — leans substrate if route (a) above is taken. + +## Test plan +- **Lookup core (doctest, fake fs, no I/O):** exact hit; reverse-DNS last-segment; + size 48 vs scalable; theme→inherited→hicolor fallback; pixmaps fallback; miss→none. +- **Substrate decode (headless+gles2):** load a known PNG and a known SVG → sampled + texture; position-aware known-color pixel readback (mirror the a1 preview color + test); decode failure degrades to none, never throws. +- **Dock glue:** resolvable `app_id` binds a non-empty favicon src; unresolvable + binds the fallback. + +## Implementation order (when scheduled — two briefs, kernel FIRST) +1. **KERNEL:** vendor lunasvg + stb_image (Meson wraps), wire RmlUi `LoadTexture` + to decode PNG/SVG, cache, headless decode test, `ui.hpp` doc. (Gating.) +2. **ext-stage-dock:** pure lookup core + edge adapter; resolve per slot; bind the + favicon `<img>`; fallback icon; tests. Config (theme name + size) via + `unbox.toml` — coordinate with the ext-keybindings config pattern or a + `[dock]` block. + +## Open questions +- Theme selection: hardcoded default vs `unbox.toml [dock] icon_theme = "…"` + (lean: config with a sane default — adwaita/hicolor). +- Does RmlUi 6.2 `<img>` accept `file://` once `LoadTexture` is wired, or do we + need a custom `unbox-icon://` scheme like the preview? Verify against the + vendored source. +- SVG rasterization size: render at the card's favicon box (dp==px at 1.0 ratio); + re-decode on the (rare) size change. diff --git a/notes/plan.md b/notes/plan.md index f7a9db0..7e6ca01 100644 --- a/notes/plan.md +++ b/notes/plan.md @@ -144,6 +144,7 @@ trusted. | Catch2 vs doctest revisit | doctest | doctest blocks something real | | dmabuf render-format negotiation (`wlr_renderer_get_render_formats` is private in wlroots 0.20) | hardcoded ARGB8888/LINEAR (verified on crocus) | wlroots bump slice or a GPU that rejects it | | window placement policy (new toplevels overlap at origin) | tinywl parity: no placement | slice 7 tiling (or earlier if it blocks testing) | +| dock favicon decoders (vendor lunasvg+stb_image; wire RmlUi LoadTexture for PNG/SVG) | no favicon — preview+title only | favicon feature scheduled — full design in `notes/favicon-spec.md` | ## 8. References |
