Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Set the Icon hint of an X window based on its WM_CLASS, using GNOME's Icon Theme
# 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
echo "usage: $ME xid"
echo "Assigns an XDG icon (specified by the current theme) to the window given by xid"
exit 0
# Check for options
while getopts h OPT; do
case "${OPT}" in
h) help ;;
shift $(($OPTIND-1))
# If no id is given, then print usage
if [ "$#" == "0" ]; then help; fi
# Determine the xid
# 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
# 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
# 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
# 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
# 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 (
# 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
# 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
# Finally, set the the window icon to $iconfile
xseticon -id "$xid" "$iconfile"
exit 0

SparklePigBang commented Jul 17, 2014

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

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:

Then click on the window, look for the window ID. [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]

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:

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