From c6ed0ebc6d5cf314f86dd67c618cd56e256e9538 Mon Sep 17 00:00:00 2001 From: ImIvanGil Date: Tue, 12 May 2026 18:23:04 -0600 Subject: [PATCH] fix(icons): resolve icon by StartupWMClass in desktop entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `getIconFromDesktopEntry()` matched window class against three .desktop fields — `Exec[0]`, `Name`, and `Keywords` — but ignored `StartupWMClass`, the field designed precisely for this matching. Concrete example: Brave (`/usr/share/applications/brave-browser.desktop`) has: Name=Brave Exec=brave %U StartupWMClass=brave-browser Icon=brave-desktop The window's class on Wayland is `brave-browser`. None of the matched fields equal `brave-browser` (Name=Brave → "brave", Exec[0]="brave"), so icon resolution fell through to `image-missing`, and Brave showed a generic icon in the workspace indicator. Fix: prepend a `app.startupClass === normalizedClassName` check. Quickshell's DesktopEntry exposes `startupClass` as a first-class property, so this is trivial. This is a generic improvement — any .desktop entry that uses StartupWMClass to disambiguate its window class from its Exec/Name now resolves correctly. Other affected apps in the wild: Spotify (`spotify.desktop` → StartupWMClass=Spotify), Steam, Slack, Discord flatpak variants, and most Electron apps with custom WMClass. --- modules/services/AppSearch.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/services/AppSearch.qml b/modules/services/AppSearch.qml index a8df6546..76cb08f4 100644 --- a/modules/services/AppSearch.qml +++ b/modules/services/AppSearch.qml @@ -55,6 +55,12 @@ Singleton { for (let i = 0; i < list.length; i++) { const app = list[i]; + // Match StartupWMClass — the .desktop field designed exactly for this + // (e.g. brave-browser.desktop has StartupWMClass=brave-browser but + // Exec=brave and Name=Brave, so the other matches below miss it). + if (app.startupClass && app.startupClass.toLowerCase() === normalizedClassName) { + return app.icon || "application-x-executable"; + } if (app.command && app.command.length > 0) { const executableLower = app.command[0].toLowerCase(); if (executableLower === normalizedClassName) {