How to dump the icon of a running X program?

This works for me. Might not work with all applications and all window managers. The pam format is at least supported by ImageMagick, so you can view it with display and convert it to other formats if need be with convert:

xprop -notype 32c _NET_WM_ICON |
  perl -0777 -pe '@_=/\d+/g;
    printf "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", splice@_,0,2;
    $_=pack "N*", @_;
    s/(.)(...)/$2$1/gs' > icon.pam

(and click on the window you want to get the icon from, see also xprop's -id option)

The idea is to use xprop to get the property as a list of 32 bit decimal integers and use perl to convert that into a graphics format (pam chosen here for simplicity though I had to reorder the bytes since _NET_WM_ICON is ARGB while pam is RGBA.

(a note about the pam format. It's a netpbm format, netpbm being a venerable Unix image manipulation toolbox. However, due to various issues, mostly licensing ones, newer netpbm versions are not packages by debian and as a result its derivatives like ubuntu as well, which means that most of the pam conversion utilities are not present there. On other systems that have newer netpbm, you can pipe the output above to pamrgbatopng to generate a png image)


Based on @Stéphane Chazelas amazing snippet I created a script to dump not just the first but all icons of a given window to .png images:

#!/bin/bash
if [[ "$1" == '--help' ]]; then
    echo "Show information or extract a window's icons to NAME-WIDTH.png"
    echo "Usage: ${0##*/} [NAME]"
    exit
fi

if [[ "$1" ]]; then
    cmd=(convert -set 'filename:w' '%w' - "${1}-%[filename:w].png")
else
    cmd=(identify -)
fi

split_icons() {
    xprop -notype 32c _NET_WM_ICON |
    awk -v RS=', | = ' '
        NR == 1         { h  = $1; i++; next }
        NR == i + 1     { x  = $1;        printf "%s = %s", h, x; next }
        NR == i + 2     { s  = x * $1 } { printf ", %s", $1 }
        NR == i + 2 + s { i += s + 2;     printf "\n" }
    '
}

to_pam() {
    perl -0777 -pe '@_=/\d+/g;
    printf "P7\nWIDTH %d\nHEIGHT %d\n", splice@_,0,2;
    printf "DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n";
    $_=pack "N*", @_;
    s/(.)(...)/$2$1/gs'
}

while read -r data; do to_pam <<< "$data" | "${cmd[@]}"; done < <(split_icons)

Without any arguments it uses identify to display icon information, no files are created. With a given NAME it extracts the icons and use convert to save them as NAME-<width>.png.

Example usage:

$ extract-icons --help
Show information or extract a window's icons to NAME-WIDTH.png
Usage: extract-icons [NAME]

$ extract-icons  # ... click on firefox ...
-=>/tmp/magick-14339JS01_CgEl3Yi PAM 64x64 64x64+0+0 8-bit TrueColor sRGB 16.5KB 0.000u 0:00.000
-=>/tmp/magick-14342I14cdBFZKQIm PAM 16x16 16x16+0+0 8-bit TrueColor sRGB 1.09KB 0.000u 0:00.000
-=>/tmp/magick-14345gjMYsrl4Jhmh PAM 128x128 128x128+0+0 8-bit TrueColor sRGB 65.6KB 0.000u 0:00.000
-=>/tmp/magick-14348Wdmu9LGUEH0j PAM 32x32 32x32+0+0 8-bit TrueColor sRGB 4.16KB 0.000u 0:00.000
-=>/tmp/magick-14351HICEECbK3LRi PAM 48x48 48x48+0+0 8-bit TrueColor sRGB 9.28KB 0.000u 0:00.000

$ extract-icons firefox
$ ls -l
total 36.864
-rw-r--r-- 1 rodrigo rodrigo 14.882 2020-09-16 18:44 firefox-128.png
-rw-r--r-- 1 rodrigo rodrigo  1.175 2020-09-16 18:44 firefox-16.png
-rw-r--r-- 1 rodrigo rodrigo  2.265 2020-09-16 18:44 firefox-32.png
-rw-r--r-- 1 rodrigo rodrigo  3.870 2020-09-16 18:44 firefox-48.png
-rw-r--r-- 1 rodrigo rodrigo  5.996 2020-09-16 18:44 firefox-64.png

You can get the full script, with a few added options, as xdg-extract-icons in my xdg-tools repository

Tags:

X11

Icons

Qt

Xorg