xdg-settings: Setting a default browser isn't that simple

Date: — by Slatian

Table of Contents

What is xdg-settings?

xdg-settings is part of the xdg-utils and according to its manpage it can query, check for and set different properties of an XDG-desktop.

Running xdg-settings --list reveals that it only ever got implementations for setting the default web browser and for setting URL-scheme handlers (which is the more generic case of setting a default web browser).

Slatian Notes

The reason I started looking into this is because someone opened an issue about xdg-open ignoring the BROWSER variable, which turned out to be xdg-settings giving out misinformation about the order of variables.

This is good by the way! if you notice something broken with a tool open an issue, even if you are wrong about the part that is broken.

Someone will help figuring out the actual issue, especially in systems with multiple components where a weirdness in one place can be the symptom of an error somewhere else.

I'll take a look at the current git HEAD with the commit hash 0f6385262417f1c0c4d13bc05d95c32578272b64, though xdg-settings hasn't been touched for years, so this also applies to the 1.2.1 version you probably still have. (except for Lxqt, that one is new)

xdg-settings makes use of some common functionality I already described when taking apart xdg-open.

Overview over xdg-settings

Inside xdg-settings, it is split up into multiple sections. That is helper functions for browser setting, MIME related functionality, desktop specific functionality.

Desktops that have their own sections are:

While browsing the sections one will also notice common function prefixes:

There is also a big dispatch function (dispatch_specific) that calls the appropriate function based on the given handler and the requested command. The handler is derived based on the detected desktop environment (see the xdg-open article on desktops to learn how that works) in a switch statement right at the end of the file.

The desktops are translated as follows:

Desktop Handler
kde kde
deepin deepin
gnome gnome
gnome3 gnome3
cinnamon gnome3
lxde lxde
mate mate
lxqt lxqt
xfce xfce
generic generic
enlightenment generic

wsl, cygwin, darwin and flatpak error with "unknown desktop environment".

Listing the supported settings

Listing the supported settings is implemented in an interesting way.

The dispatch function contains multiple switch-case statements for the supported parameters, one of each line is annotated with comment, #PROP: {description goes here}. The list function greps the file for line containing that comment and then uses some regex magic to format that into a human readable list.

The helper functions

get_browser_mime <mimetype>
A wrapper around xdg-mime query default $1 that defaults to text/html.
fix_local_desktop_file <desktop-file-name> <mimetype>
If a desktop file exists locally for the user make sure it has a given mime-type in its list of supported mime types.
set_browser_mime <desktop-file-name> <mimetype>
This attempts to undo any desktop changes using fix_local_desktop_file and then uses xdg-mime default to set the default browser, checks if the change happened and sets it back if the change didn't have the desired effect.

Generic Browser configuration

The generic browser configuration makes use of the already mentioned helper functions.

Getting the default Browser

This is implemented in get_browser_generic.

If the BROWSER environment variable is set, the script tries resolving the command to a desktop file, and if successful outputs that as the default browser. If not the script continues.

If the mimetype x-scheme-handler/http resolves to a desktop file that is returned as the default browser. If not the script continues.

Bug Note: The order of first checking the BROWSER variable and then checking the mimetype is reversed from how xdg-open reads them. It also puts a less granular configuration mechanism over a more granular one. I consider this a bug.

If the binary x-www-browser resolves to a desktop file that is returned.

Otherwise the script exits wit exit code 4 (Action failed)

Checking the default Browser

Checking the default Browser is distinct from getting and comparing the default browser in that instead of checking the first setting that gives a sensible value it checks all relevant settings and only returns true if all of those settings apply.

This is implemented in check_browser_generic.

There are three functions that have to resolve to the given desktop file before the check function says yes, that is the default browser:

Setting the default Browser

This is implemented in set_browser_generic.

Bug Note: This function errors if the BROWSER environment variable is set, this is related to xdg-settings considering BROWSER to be higher priority than the more granular x-scheme-handler/ mimetypes.

After testing for the BROWSER environment variable the set function tests if the desktop file resolves to a binary, this makes sure that the requested file exists and has a non-empty Exec key which is the minimum needed to actually open URLs.

It the n uses the set_browser_mime to set the following mimetypes to be handled by the requested desktop file:

Generic URL scheme configuration

Generic URL scheme configuration is mostly wrapping around the already described helpers with some special treatment for the http and https schemes when the BROWSER environment variables are set.

Getting the scheme handler

This is implemented in get_url_scheme_handler_generic.

Passes the call through to the get_browser_mime helper with the mimetype x-scheme-handler/{scheme}.

Except when BROWSER is set and the scheme is either http or https, then it calls get_browser_generic.

Checking the scheme handler

This is implemented in check_url_scheme_handler_generic.

Tests if the desktop file resolves to a binary and if so it tests if it is returned by get_url_scheme_handler_generic.

Setting the URLs scheme handler

This is implemented in set_url_scheme_handler_generic.

Bug Note: This function again has special handling for BROWSER being set the scheme being http or https that it refuses to work with an error message that it can't change the BROWSER variable.

If the desktop file resolves to a command it uses set_browser_mime with x-scheme-handler/{scheme} as the mimetype.

To be continued …

This post won't cover the special cases for each desktop. I'm taking a slow start to 2025 and will describe those in a followup post.

Reading through xdg-settings really shows that it hasn't been maintained for a while (which isn't surprising given that the xdg-utils were effectively abandoned for multiple years).

This is a call to action and for help: See if your favourite desktop is implemented correctly in xdg-settings and if not open an issue or merge request. I'll help you if you want.

Thanks for reading, I hope you had an incident free calendar rollover!