Skip to content

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

This comment has been minimized.

Copy link
Owner Author

SparklePigBang commented Jul 17, 2014

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


This comment has been minimized.

Copy link

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
You can’t perform that action at this time.