xdg-mime: Mapping Files to Applications taken apart
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
orsystem
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:
- a way to detect which desktop one is currently using
- mapping the desktop file to the binary it calls and vice versa
- helpers for command-line and UI
Unique to xdg-mime
is:
- a callback to update desktop specific mime database caches.
- querying and setting of default applications from gnome, KDE and generic desktops
- a function for getting desktop files matching a given mime-type.
- functionality for figuring out an application when no explicit default has been set
- functionality for opening the default application for KDE, gnome and generic desktops
- command line parsing and validating
- XML parsing in awk … okay
- lots of parsing and updating files with awk
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.
- If the desktop is KDE and
/usr/bin/file
is not an executable useinfo_kde
. - If the desktop belongs to the gnome gio family (Gnome,Cinnamon,Lxde,Mate,Xfce) use
info_gnome
- In case of Lxqt use
info_lxqt
(new in the beta 1.2.0 release) - Otherwise use
info_generic
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.
gio info <file>
gvfs-info <file>
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.
mimetype --brief --dereference <file>
/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:
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 …
- checks the users
$XDG_CONFIG_HOME/${prefix}mimeapps.list
- checks the system wide
$XDG_CONFIG_DIRS/${prefix}mimeapps.list
- checks again for an
applications/${prefix}mimeapps.list
in both system and user data directories (usually~/.local/share/
and/usr/share/
) - searches both user and system data directories for a prefixed with the current desktops name
applications/${prefix}defaults.list
andapplications/${prefix}mimeinfo.cache
. - falls back to searching the application files for the one that sees itself most fit for opening a file with the given mime-type using the
defapp_fallback
function.
The files are searched in the order of highest priority to lowest and when the first result is found it is returned.
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:
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]
<desktop-file-name>
true
Application
1
<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
- The XDG Shared MIME-info Database which allows storing information about mime-types themselves.
- The XDG MIME Applications Associations which are responsible for mapping applications to mime-types.
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.