Icon Themes

Posted by Nathan Osman on March 27, 2016

One of the most confusing aspects of packaging applications on Linux continues to be icon themes. Despite the simplicity of the original concept, getting your application’s icon to display correctly in a broad range of desktop environments continues to challenge even the best of programmers.

Before we begin taking a look at how this is correctly implemented, it would be wise to consult the documentation. freedesktop.org provides us with the Icon Theme Specification which describes how the icons should be organized on the filesystem. Let’s take a look at how an icon “name” is resolved to an absolute path on the filesystem.

According to the specification, three “base” directories are searched for icons:

  • $HOME/.icons
  • $XDG_DATA_DIRS/icons *
  • /usr/share/pixmaps

The first of these exists for backwards compatibility and can safely be ignored. $XDG_DATA_DIRS consists of a colon-separated list of directories. For example, on a clean installation of Wily (Ubuntu 15.10), this is set to:

/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/

Each icon theme exists as a subdirectory within the base directories. If an icon does not exist within the current theme, the default theme (named “hicolor”) is searched as a last resort. Each theme directory provides a text file named index.theme that describes where the individual icons can be found within the theme. For example, the hicolor theme’s index.theme looks something like this:

[Icon Theme]
Name=Hicolor
Comment=Fallback icon theme
Hidden=true
Directories=16x16/actions,...

[16x16/actions]
Size=16
Context=Actions
Type=Threshold

Each of these fields is described in more detail on this page. The excerpt above indicates that 16x16 icons for the “actions” context (category) are located in the 16x16/actions subdirectory.

It is worth noting that although hicolor organizes its directories in the format [size]/[context], some other themes use the format [context]/[size]. Don’t rely on a certain directory hierarchy.

Within the directories specified in index.theme are the individual icon files. All implementations support PNG and XPM images, although XPM is deprecated and should no longer be used. Support for SVG images is optional and implementations that do not support them simply ignore them.

Enough theory.

In order to help understand how this all fits together, I created a set of icons for an imaginary application which I named “IconPro”. I began by creating an empty binary in /usr/local/bin:

sudo touch /usr/local/bin/iconpro
sudo chmod 755 /usr/local/bin/iconpro

I also created a desktop file (iconpro.desktop):

[Desktop Entry]
Name=IconPro
Exec=/usr/local/bin/iconpro
Icon=iconpro
Type=Application
Categories=Graphics;RasterGraphics;

The following command needed to be run in order to register the .desktop file:

sudo desktop-file-install iconpro.desktop

Since I didn’t have any icons installed yet, most desktop environments simply displayed a generic icon in their respective menus / launchers. The following was displayed by Unity 7, Gnome, and KDE respectively:

Copying a file named iconpro.png to /usr/share/pixmaps caused the icon to appear correctly in the menus and launchers of most desktop environments. This is by far the most effective way to ensure that an icon is displayed. Beyond this, things start to get weird very quickly.

Copying a 48x48 image to /usr/share/icons/hicolor/48x48/apps should in theory cause desktop environments to display it in place of the one in /usr/share/pixmaps but in practice, this didn’t always happen. Unity and Gnome implement the expected behavior (update-icon-caches can be used to avoid restarting the desktop environment). KDE continued to display the icon from /usr/share/pixmaps, however.

By default Gnome uses the Adwaita theme. Putting an icon in /usr/share/icons/Adwaita/48x48/apps does indeed override the icon in hicolor. This works in Unity as well with the ubuntu-mono-dark theme. Both desktop environments seem to prefer the highest resolution icon when both small and larger ones are available.

For the last part of this article, I decided to do a bit of digging into GTK internals. I compiled this simple C application, ran it through strace, and searched for a non-existent icon at 48x48. (The default theme was set to ubuntu-mono-dark.)

The following directories were searched in order, where [THEME] was set to ubuntu-mono-dark, Humanity-Dark, Humanity, Adwaita, hicolor, and finally left empty:

  • ~/.local/share/icons/[THEME]
  • ~/.icons/[THEME]
  • /usr/share/ubuntu/icons/[THEME]
  • /usr/share/gnome/icons/[THEME]
  • /usr/local/share/icons/[THEME]
  • /usr/share/icons/[THEME]
  • /usr/share/ubuntu/pixmaps/[THEME]
  • /usr/share/gnome/pixmaps/[THEME]
  • /usr/local/share/pixmaps/[THEME]
  • /usr/share/pixmaps/[THEME]