Per-window panel icon support via raw _NET_WM_ICON#35
Open
nynjapirate wants to merge 1 commit into
Open
Conversation
CassiaWindowList currently always renders the panel button icon from
the .desktop file matched against WM_CLASS. For windows that update
_NET_WM_ICON per-state — e.g. Nemo / Files / Lancelot showing a
custom-iconed folder, a browser showing a per-tab favicon, anything
that calls gtk_window_set_icon during its lifetime — the panel button
is stuck on the app icon and never reflects the live state.
Cinnamon's standard Cinnamon.App.create_icon_texture_for_window
ignores the for_window arg when the window matches a .desktop, and
Meta.Window.icon is a dead WM_HINTS stub (NULL for modern apps), so
the obvious fixes don't work. This commit reads _NET_WM_ICON
directly via an async xprop subprocess, parses the {w,h,ARGB...}
cardinal list, picks the size closest to (but not exceeding) the
panel icon size, swizzles ARGB→RGBA, builds a GdkPixbuf, caches it
per-xid, and renders the result as the panel icon.
Mechanics:
- notify::title is the trigger (since notify::icon never fires for
_NET_WM_ICON-only changes). An 80ms debounce coalesces title
bursts; first paint of each window misses cache and falls through
to the standard app icon, then the async xprop returns and the
panel re-renders with the per-window icon.
- Signature dedup (200-char sample hash of the cardinals string)
skips repaints when _NET_WM_ICON content is unchanged across a
refetch — avoids panel-button flash for apps like xfce4-terminal
that update title without touching the icon.
- Reference-identity skip on the displayed pixbuf avoids
destroy-and-recreate when nothing changed.
- 30s very-stale floor as a safety net.
For windows without a _NET_WM_ICON update during their lifetime
(most apps), behavior is unchanged: the standard app-icon path is
the fallback when the cache has no pixbuf for the xid.
No new permissions or D-Bus dependencies. xprop is part of x11-utils
which is a stock dep of every desktop Linux install.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
CassiaWindowList renders the panel button icon from the
.desktopfilematched against
WM_CLASS. For applications that update_NET_WM_ICONper-state during the window's lifetime — e.g. Nemo orFiles showing a custom-iconed folder, a browser showing the focused
tab's favicon, anything that calls
gtk_window_set_iconafter windowcreation — the panel button is stuck on the app icon and never
reflects the live state.
Confirmed comparable behavior exists in the stock Cinnamon
window-list@cinnamon.organdgrouped-window-list@cinnamon.orgapplets (they listen on
notify::icon), but Cinnamon'sCinnamon.App.create_icon_texture_for_windowignores thefor_windowarg when the window matches a
.desktop, andMeta.Window.iconis adead
WM_HINTSstub for modern apps. So the obviousnotify::iconlistener fires but the resulting property value is useless. Working
around Cinnamon's preference for app icons needs an alternative
icon-loading path that goes straight to the raw
_NET_WM_ICON.Approach
Read
_NET_WM_ICONdirectly via an asyncxpropsubprocess:Parse the
{w0,h0,ARGB...,w1,h1,ARGB...}cardinal list, pick the sizeclosest to (but not exceeding) the panel icon size, swizzle ARGB→RGBA,
build a
GdkPixbufvianew_from_bytes, cache it per-xid, and renderthe result as the panel icon.
notify::titleis the trigger (sincenotify::icondoesn't fire for_NET_WM_ICON-only changes per the dead-stub problem above). An 80msdebounce coalesces title bursts; first paint of each window misses the
cache and falls through to the standard app-icon path, then the async
xpropreturns and the panel re-renders with the per-window icon.Performance / sanity:
skips repaints when
_NET_WM_ICONcontent is unchanged across arefetch. xfce4-terminal and similar apps update title every command
without touching their icon; without this the icon would thrash.
destroy-and-recreate when nothing changed visually.
the title hook.
For windows that don't update
_NET_WM_ICONduring their lifetime(most apps), behavior is unchanged: the cache stays empty for that
xid, the existing app-icon path is the fallback.
Why xprop and not Gdk.property_get
Gdk.property_getis non-introspectable in gjs/cjs (theCARDINALout-arg type) — segfaults under cjs, throws under gjs. GIRepository
inspection of
GdkX11-3.0confirms onlyX11Window.foreign_new_for_displayand
lookup_for_displayare reachable from JS; nothing on the instancereads property data.
xpropis part ofx11-utils, a stock dep ofevery desktop Linux install, so no new dependency.
Scope
Only
5.4/applet.jsis touched.6.0/applet.jsis a symlink to5.4/applet.jsin this layout.4.0/applet.jsis left for a possiblebackport in a follow-up — the API surface used here (
Gio.Subprocess,GdkPixbuf.Pixbuf.new_from_bytes,metaWindow.get_xwindow) isavailable on 4.0 too but I haven't tested.
Verification
Tested on Cinnamon 6.6.7 with Lancelot (a Nemo fork that updates
_NET_WM_ICONper folder viagtk_window_set_iconinnemo_window_slot_update_icon). Panel button reflects thecustom-iconed folder icon on navigation; falls back to the app icon
for ordinary folders and other apps; no panel-button flash on
unchanged-icon title bursts.
🤖 Generated with Claude Code