Instantly share code, notes, and snippets.

Embed
What would you like to do?
Set the Icon hint of an X window based on its WM_CLASS, using GNOME's Icon Theme
#!/bin/bash
# Requirements: xseticon, pyxdg, xprop
# The application files to search through
SEARCH_FILES="$HOME/.local/share/applications/*.desktop /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop"
# The name of this script
ME=`basename $0`
# Print help message and exit
help()
{
echo "usage: $ME xid"
echo "Assigns an XDG icon (specified by the current theme) to the window given by xid"
echo
exit 0
}
# Check for options
while getopts h OPT; do
case "${OPT}" in
h) help ;;
esac
done
shift $(($OPTIND-1))
# If no id is given, then print usage
if [ "$#" == "0" ]; then help; fi
# Determine the xid
xid=$1
# Determine the WM_CLASS of the window, if possible
result=`xprop -id "$xid" WM_CLASS`
# If the xprop returned a nonzero exit status, then we need to exit too
if [ "$?" != "0" ]; then
echo "E: Could not get WM_CLASS for the window with xid $xid. Either it doesn't exist, or it doesn't have WM_CLASS set."
exit 1
fi
# Extract the first WM_CLASS
class=`echo "$result" | sed 's/^WM_CLASS(STRING) = "\([a-zA-Z0-9_-]\+\)".*$/\1/'`
# Find a .desktop file with StartupWMClass=$class, if one exists
file=`grep --files-with-matches --no-messages "StartupWMClass[[:space:]]*=[[:space:]]*$class" $SEARCH_FILES`
# If too many files (ie more than one) were found, then exit
if [[ "$file" == *\n*\n ]]; then
echo "E: More than one matching .desktop file for the window with xid $xid (WM_CLASS: '$class')"
exit 1
fi
# If we couldn't find a file, then exit
if [ ! -e "$file" ]; then
echo "E: Could not find a matching .desktop file for the window with xid $xid (WM_CLASS: '$class')"
exit 1
fi
# Extract the icon name from the .desktop file
icon=`grep '^Icon[[:space:]]*=.*$' "$file" | sed 's@^Icon *= *\([a-zA-Z0-9./_ (),-]\+\)$@\1@'`
# If $icon is a PNG that exists then use that, otherwise determine the icon file from the theme
if [ -e $icon ] && [ "${icon##*.}" == "png" ]; then
iconfile=$icon
else
# Determine the current icon theme
theme=`gsettings get org.gnome.desktop.interface icon-theme` # Has quotes
# Determine the icon file using the XDG Icon File Specification (http://standards.freedesktop.org/icon-theme-spec)
# Try various sizes, preferring the larger ones
for i in 256 128 64 32; do
iconfile=`python -c "from xdg import IconTheme; print IconTheme.getIconPath('$icon',$i,$theme,['png'])"`
if [ -e "$iconfile" ]; then break; fi
done
# If this has failed, then $icon probably doesn't have an icon file
if [ ! -e $iconfile ]; then
echo "E: Could not find icon file for icon name '$icon'. Window xid: $xid, WM_CLASS: '$class'"
exit 1
fi
fi
# Finally, set the the window icon to $iconfile
xseticon -id "$xid" "$iconfile"
exit 0
@SparklePigBang

This comment has been minimized.

Owner

SparklePigBang commented Jul 17, 2014

Can be used with Devil's Pie to automatically set the icons of all windows.

@jeffeb3

This comment has been minimized.

jeffeb3 commented Mar 23, 2017

Works like a charm. Some applications don't set the StartupWMClass in their .desktop file, so I had to add that to a few. To debug this, I would do this:

xwininfo
Then click on the window, look for the window ID.

set-xdg-icon.sh [window id]

That would tell me the name of the class of the app I was looking for WM_CLASS: '$class'

Then I would either create a .desktop file or edit the appropriate .desktop file, and add in:
StartupWMClass=[class name from set-xdg-icon.sh]

I also had a few that couldn't properly figure out where the icon was from the short name (one was "utilties-terminal"), so I replaced that with a full path.

Lastly, I'm using ubuntu, and xseticon isn't available, but I found a script that worked to get the binary out of the AUR and install it for ubuntu, YMMV, but it's pretty easy to read:
https://gist.github.com/unhammer/cfa9cd907adcdf391290

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment