xdg-mime: Mapping Files to Applications taken apart

Date: — Topics: , — Lang: — by Slatian

last time we took apart xdg-open, the freedesktop file-opener and found out that xdg-mime provides the mapping from file to application. So let's take it apart and see what's inside …

Table of Contents

What can xdg-mime do?

xdg-mime is a shell-script that helps with mapping files to the applications that open them. It implements querying a files mime-type, setting and getting the default application for a given mime-type as well as installing and uninstalling mime-type descriptions.

The code is developed in gitlab.freedesktop.org/xdg/xdg-utils.

You can use xdg-mime --help or man xdg-mime yourself to get more details.

xdg-mime query filetype <file>
This sub-command prints the detected mime-type of a given file. (i.e. text/plain)
xdg-mime query default <mimetype>
This prints out the name of the desktop file that is configured as the default opener for the given mime-type. (i.e. slatectx-app-kakoune.desktop)
xdg-mime default <applications> <mimetype> ...
Attempts to set the default opener for the one or more given mime-types to the given application which is identified by the name of its desktop file.
xdg-mime install mimetypes-file <file>
Installs a mime-type description file that follows the freedesktop mime associations specification where it can be used to identify files. Can be put in user or system mode using the --mode flag.
xdg-mime uninstall mimetypes-file <file>
As it says on the tin: It uninstalls a previously installed mime-type file.

Note: This article ended up being quite long, since it should still be understandable when read in a non-linear manner feel free to skip sections that don't seem interesting.

What is in the Script?

Like the other xdg-utils xdg-mime - in addition to a lot of custom logic - imports functionality from the xdg-utils-common.in.

Noteworthy imports from the common file are:

Unique to xdg-mime is:

So this script seems to mostly do the work of parsing and writing the mime to application mapping database, which is to be expected, at first glance mime-type detection seems to be delegated to another command and is in here to provide a stable interface that applications can rely on while xdg-mime does the figuring out how to derive the mime-type.

xdg-mime integrates with the KDE and Gnome mime databases, because those have been there before freedesktop.org and the xdg-utils were a thing. More recently Lxqt made their own tool called qtxdg-mat that has been integrated into xdg-utils.

Actions and CLI Plumbing

Since calling xdg-mime is based on sub-commands, some with their own custom options, this results the parser being a lot of nested case statements and loops for parsing the extra options and arguments.

To keep the logic from becoming an indentation mess xdg-mime has a concept of actions. That is, that the command gets translated into an action-name that is written to the action variable. Which is then reused for deciding what to do.

Potential values for the action variable are:

info
for the filename to mime-type query. with the xdg-mime query filetype sub-command.
makedefault
for setting default mime-handlers with the xdg-mime default sub-command.
defapp
for querying the default application with the xdg-mime query default sub-command.
install
for installing mime descriptions with the xdg-mime install sub-command.
uninstall
for uninstalling mime descriptions with the xdg-mime uninstall sub-command.

Getting the MimeType of a File

The info action triggers a desktop detection and then decides which mime-type detection method to use. On success the output must be the mime-type of the file followed by a newline without any extra information.

Lxqt MimeType Query

For Lxqt qtxdg-mat mimetype <file> is used to query the mime information of a given file.

KDE MimeType Query

The info_kde checks the KDE_SESSION_VERSION environment variable.

For KDE 4 and 6 it uses the kmimetypefinder command, for KDE 5 kmimetypefinder5 and for older versions it gets the mime-type from kfile.

Extra information is cut away using POSIX tools.

Gnome MimeType Query

The info_gnome uses a mechanism similar to file opening where it uses a fallback cascade of multiple methods for detecting the mime type, using the first one that is available.

  1. gio info <file>
  2. gvfs-info <file>
  3. gnomevfs-info --slow-mime <file>

Like with the KDE method any extra information is discarded using standard POSIX tools.

Generic MimeType query

The info_generic also uses a little fallback cascade.

  1. mimetype --brief --dereference <file>
  2. /usr/bin/file --brief --dereference --mime-type <file>

For both commands cutting away extra information is not needed.

Note on /usr/bin/file: It seems strange that /usr/bin/file is used here instead of just calling the file command. After finding no information on it and asking the original developers (this quirk has been around since 2006) it turns out that nobody knows why the absolute path was used here, it would most probably not make a difference to change it back.

How xdg-mime figures out the Default Application

This functionality hides behind the defapp action and can be triggered using the xdg-mime query default <mimetype> command. As a reminder this returns the name of the desktop-file describing the application configured as the default opener or an application that seems the best fit according to some simple heuristics in case no default is was explicitly set.

This time there are only three implementations, a generic one, a new one for Lxqt and one for KDE below version 6.

Note: The application returned by xdg-mime is not necessarily the one that will open when calling xdg-open on the same file, mostly because xdg-open delegates to openers which can have custom logic.

KDE Default Application Querying

KDE has a subsystem called Trader which handles delegation of actions between various KDE components (services). To use it xdg-mime invokes ktraderclient, ktraderclient5 or ktradertest (for older than KDE 4) as $KTRADER. Asking it for a matching Application service for the given mime-type, extracting the result from the returned text.

If no ktrader command appropriate to the current KDE version can be found it falls back to the generic implementation.

Lxqt Default Application Querying

Again qtxdg-mat is used:

qtxdg-mat defapp <file>

Generic Default Application Querying

For where there is no desktop specific mechanism, xdg-mime has a generic, specification compliant 🥳 way to get the default application using the defapp_generic function.

Desktop Prefixes

Before getting into querying default applications, first imagine someone switches between multiple desktops, maybe mate and kde, while you ave some applications you want to be the default everywhere (i.e. you preferred web-browser) other preferences might depend on the desktop you are currently using like the image- or PDF-viewer.

Instead of the desktops stepping on each others toes, every desktop that identifies itself via the XDG_CURRENT_DESKTOP environment variable gets its own configuration file, prefixed by the lowercased name that is read from that variable. This way different preferences on different desktops can be kept separate.

This prefix will be referred to as ${prefix} further down. Examples of its contents may be: mate-, kde-, etc. or it may be empty.

Note: The XDG_CURRENT_DESKTOP works completely independent of the desktop detection (in fact: the desktop detection uses it). So anyone can set this to get their own configuration space for their fresh new custom desktop.

Note: XDG_CURRENT_DESKTOP, like many other XDG variables is a colon separated list that is handled with a fallback cascade. i.e. if your custom xfce based rice identifies itself as my-xfce-rice:xfce, then the application choice will be pulled from my-xfce-rice and automatically fall back to the xfce defaults, even when other desktops are installed.

Reading the Association Files

This is a multistage search that …

The files are searched in the order of highest priority to lowest and when the first result is found it is returned.

While the filenames are different those files share the same ini syntax.
<mimtype>=<desktop-file-name>[;<another-desktop-file-name>...][;]

For the mimeapps.list files, the check_mimeapps_list function searches the [Default Applications] section for an entry with the given mime-type. If such an entry exists, all mentioned desktop-file names are extracted and the first one with a valid executable is returned. If not the search continues.

Bug Note: Apparently there is a bug in check_mimeapps_list that discards any names after the first semicolon, making the loop that should handle fallback useless.

The defaults.list and mimeinfo.cache get treated simpler than the mimeapps.list files. The first result that mates the mime-type is returned, done. Sections are ignored for these files.

Fallback Application Finding

In case no desktop application is found using the configuration file xdg-mime will start searching one itself with the defapp_fallback function.

This iterates over all desktop files that match the given mime-type, picking the one with the highest value in InitialPreference (default is 0 when unset). In case multiple applications have the same InitialPreference, the one first found or in the higher priority location wins.

If a file is found this way it gets returned.

Note: For the InitialPreference, the desktop file specification only says that it is reserved for KDE.

Setting Default Applications

Setting a default application for a given mime-type happens in one of two ways:

For Lxqt only the make_default_lxqt function gets called.

For everything else there are two functions, the make_default_kde one where the KDE database gets updated and make_default_generic which updates the users mimeapps.list file. Weirdly both are always called. (Remember the whole prefix thing?)

Generic Default Application Setting

Surprisingly unsurprising the generic case is the simpler one here.

It derives the path for $XDG_CONFIG_HOME/mimeapps.list and then runs an awk script on it. This script adds or updates the <mimetype>=<desktop-file-name>; association entry in the [Default Applications] section that belongs to the given mime-type.

What is a bit surprising is that the desktop prefix isn't handled here.

For Lxqt

Again qtxdg-mat has a sub-command that is a prefect fit and doesn't need any wrapping:

qtxdg-mat defapp --set <file> <mimetype>

For KDE 3 and 4

The make_default_kde function is a lot of awk code that seems to do patching on two files. Unfortunately it hasn't been updated after KDE 4

KDE 4

The KDE 4 specific one modifies a mimeapps.list file in a directory returned by kde4-config --path xdgdata-apps 2> /dev/null | cut -d ':' -f 1.

It adds the mime-type to application mapping to the [Added Associations] section if it is missing.

My personal guess without doing further research is that this ensures that the application to mime-type mapping exists because something in KDE actually validates that an application can open a given mime-type. I'm also pretty sure that this mechanism relies on the generic setter running immediately after for setting the entry in the [Default Applications] section. Maybe someone can confirm or deny this.

KDE 3

For KDE 3 (the default case if the version isn't 4), the profilerc file in a directory returned by kde-config --path config 2> /dev/null | cut -d ':' -f 1 gets modified.

There with ini-like syntax, a section gets added:

[<mimetype> - 1]
Application=<desktop-file-name>
AllowAsDefault=true
GenericServiceType=Application
Preference=1
ServiceType=<mimetype>

Sections that act as duplicates will get removed.

Installing and Uninstalling MimeType Descriptions

Now that everything from getting the mime-type to the application is covered one may ask:

How does the mime detection tooling know about all the file-types out there?

On a freedesktop system this information resides in the XDG Shared MIME-info Database which is a binary compiled from XML files that describe how to detect a given mime-type, and what its icon, names and descriptions (in different languages) are.

xdg-mime makes it easier to install those XML descriptions. (When packaging follow the target distributions guidelines first!)

For the general case installing puts/deletes the given file in the desired mime/packages directory.

For user mode that is $XDG_DATA_HOME/mime/packages/, defaulting to ~/.local/share/mime/packages/. For the system mode $XDG_DATA_DIRS/mime/packages/, defaulting to /usr/share/mime/packages/ and /usr/local/share/mime/packages/.

There is also a fallback for a KDE3 mechanism called "mimelink" but I will skip that one for now.

Note: Don't confuse the files in the packages directory with other XML files named like the mime-types themselves. The ones in packages really are packages containing the descriptions for multiple mime-types while the other ones are already unpacked for better discovery.

Hooks on the Mime Database

After (un)installing the update_mime_database function takes care of making sure that the mime database and caches get rebuilt.

When in user mode, the currently running desktop is KDE and a display is present kbuildsycoca, kbuildsycoca4 or kbuildsycoca5 is called, which rebuilds KDE configuration caches.

After that every directory in $PATH with the addition of /opt/gnome/bin is searched for a update-mime-database program. All found binaries are ran with the mime database folder that was just updated as the first argument.

Note: update-mime-database is the official program for rebuilding the mime database.

Relevant Specifications

Until Next Time …

On expectations: When starting to write this article I didn't expect it getting this long and technical, but researching as well as finding and fixing bugs ended up producing a lot of information which I didn't want to hide.

A huge thank you goes to Kevin Krammer and Jeremy White for starting the xdg-utils project, to Simon Lees who is currently the de-facto maintainer and everyone who contributed in any way. And while the xdg-utils might be a mess, they are a bloody awesome and quite functional mess!


Recently xdg-utils has received a beta release after a very long time.

You can help the project by testing it, reporting and researching issues, giving context and writing fixes or documentation.


Next up on this mini-series will probably be a dive into xdg-settings, but please don't quote me on that.