UPDATE: I’ve created a Python implementation of this algorithm. It’s available here.
I was curious how docks seem able to determine the correct icon (ie find the correct .desktop
file) for applications even when their .desktop
files lack the StartupWMClass
key. So I had a look at the source code for ‘Docky’. After a little figuring out of the, almost entirely uncommented :(, code, I determined that the algorithm goes like this, stopping on the first successful step:
- Check if there is a
.desktop
file with the appropriateStartupWMClass
. - See if there is a
.desktop
file whose name is the same as that of theWM_CLASS
of the window. - Then check if the window belongs to ‘OpenOffice’, ‘LibreOffice’ or ‘Wine’.
- Look for a
.desktop
file whose name is the same as that of theWM_CLASS
of the window with all.
characters removed. - Check if the process name matches the
Exec
field of any.desktop
file. - Finally, check if the window command matches the
Exec
field of any.desktop
file.
Here is the relevant method starting a line 165 of Docky.Services/Docky.Services/WindowMatcherService.cs
in Docky-2.2.0
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
IEnumerable DesktopItemsForWindow (Window window) { // use the StartupWMClass as the definitive match if (window.ClassGroup != null && !string.IsNullOrEmpty (window.ClassGroup.ResClass) && window.ClassGroup.ResClass != "Wine" && DockServices.DesktopItems.DesktopItemFromClass (window.ClassGroup.ResClass) != null) { yield return DockServices.DesktopItems.DesktopItemFromClass (window.ClassGroup.ResClass); yield break; } int pid = window.Pid; if (pid <= 1) { if (window.ClassGroup != null && !string.IsNullOrEmpty (window.ClassGroup.ResClass)) { IEnumerable matches = DockServices.DesktopItems.DesktopItemsFromID (window.ClassGroup.ResClass); if (matches.Any ()) foreach (DesktopItem s in matches) yield return s; } yield break; } bool matched = false; // get ppid and parents IEnumerable pids = PidAndParents (pid); // this list holds a list of the command line parts from left (0) to right (n) List commandLine = new List (); // if we have a classname that matches a desktopid we have a winner if (window.ClassGroup != null) { if (WindowIsOpenOffice (window)) { string title = window.Name.Trim (); if (title.EndsWith ("Writer")) commandLine.Add ("ooffice-writer"); else if (title.EndsWith ("Draw")) commandLine.Add ("ooffice-draw"); else if (title.EndsWith ("Impress")) commandLine.Add ("ooffice-impress"); else if (title.EndsWith ("Calc")) commandLine.Add ("ooffice-calc"); else if (title.EndsWith ("Math")) commandLine.Add ("ooffice-math"); } else if (WindowIsLibreOffice (window)) { string title = window.Name.Trim (); if (title.EndsWith ("Writer")) commandLine.Add ("libreoffice-writer"); else if (title.EndsWith ("Draw")) commandLine.Add ("libreoffice-draw"); else if (title.EndsWith ("Impress")) commandLine.Add ("libreoffice-impress"); else if (title.EndsWith ("Calc")) commandLine.Add ("libreoffice-calc"); else if (title.EndsWith ("Math")) commandLine.Add ("libreoffice-math"); } else if (window.ClassGroup.ResClass == "Wine") { // we can match Wine apps normally so don't do anything here } else { string className = window.ClassGroup.ResClass.Replace (".", ""); IEnumerable matches = DockServices.DesktopItems.DesktopItemsFromID (className); if (matches.Any ()) { foreach (DesktopItem s in matches) { yield return s; matched = true; } } } } foreach (int currentPid in pids) { // do a match on the process name string name = NameForPid (currentPid); foreach (DesktopItem s in DockServices.DesktopItems.DesktopItemsFromExec (name)) { yield return s; matched = true; } // otherwise do a match on the commandline commandLine.AddRange (CommandLineForPid (currentPid) .Select (cmd => cmd.Replace (@"\", @"\\"))); if (commandLine.Count () == 0) continue; foreach (string cmd in commandLine) { foreach (DesktopItem s in DockServices.DesktopItems.DesktopItemsFromExec (cmd)) { yield return s; matched = true; } if (matched) break; } // if we found a match, bail. if (matched) yield break; } yield break; } |
See the full code here.