November 30, 2021

2021-11-30 Tuesday.

  • Mail & catch-up with Kendy. Lunch.
  • Pleased to see the work we have done together released announced as Nextcloud Office which will be the default Office in Nextcloud from now on; looking very slick. A few points:
    • Lots of that coding touches Collabora Online and is already included and published in CODE 21.11, and of course we build on LibreOffice Technology so that work is also already contributed and will go into the next LibreOffice releases.
    • From a customer and support perspective we continue to work together as before to deliver excellent support to all of our customers and users as today.
    • Naturally we are thrilled that Nextcloud joins a long list of other partners and OEMs that choose Collabora Online as their default option, very often white-labelled.
    • Nextcloud Office built on COOL and LibreOffice Technology
    • Interested by Richard Moore (head of MI6)'s take on data traps
      "The data-trap is this: that if you allow another country to gain access to really critical data about your society, over time that will erode your sovereignty - you no longer have control over that data."

November 29, 2021

2021-11-29 Monday.

  • Mail chew, planning call, TDF board candidate questions, recording for Jos; lunch, worked through E-mail, audit, partner bits. Out for a run with J.

Firmware “Best Known Configuration” in fwupd

I’ve just deployed some new functionality to the LVFS adding support for component <tag>s. These are used by server vendors to identify a known-working (or commercially supported) set of firmware on the machine. This is currently opt-in for each vendor to avoid the UI clutter on the components view, and so if you’re a vendor reading this post and realize you want this feature, let me know and it’s two clicks on the admin panel.

The idea is that when provisioning the machine, we can set HostBkc=vendor-2021q1 in /etc/fwupd/daemon.conf and then any invocation of fwupdmgr sync-bkc will install or downgrade firmware on all compatible devices (UEFI, RAID, network adapter, & SAS HBA etc.) to make the system match a compatible set. This allows two things:

  • Factory recovery where a system in the field has been upgraded
  • Ensuring a consistent set of vendor-tested firmware for a specific workload

The tags are either assigned in the archive firmware.metainfo.xml file or added post-upload on the LVFS and are then included in the public AppStream metadata. A single firmware can be marked with multiple tags, and tags can be duplicated for different firmwares. This would allow a server vendor to say “this set of firmware has been tested as a set for workload A, and this other set of firmware has been tested for workload B” which is somewhat odd for us consumer-types, but seems to be pretty normal for enterprise deployments.

As a bonus feature, updating or downgrading firmware away from the “Best Known Configuration” is allowed, but we’ll show a semi-scary warning. Using fwupdmgr sync-bkc will undo any manual changes and bring the machine back to the BKC. Needless to say fwupd will not ship with a configured BKC.

We’ll include this somewhat-niche-but-required feature with fwupd 1.7.3 which will hopefully be released before Christmas. Questions and comments welcome.

November 26, 2021

#20 Colorful Characters

Update on what happened across the GNOME project in the week from November 19 to November 26.

Core Apps and Libraries


A simple utility application to find and insert unusual characters.

Bilal Elmoussaoui reports

I have landed a GTK 4 / libadwaita port of Characters which includes a refactoring/cleanup of the whole codebase. Along with that Alexander Mikhaylenko ported the accompanying C library from using libunistring which allowed us to update the unicode database to the latest release.


An object-oriented programming language with a self-hosting compiler that generates C code and uses the GObject system

lwildberg announces

With much help from Rico Tzschichholz I fixed multiline errors. Before, the location of errors which stretch over multiple lines were just not printed out. On the way, I also made the output a bit prettier and more useful by adding the line, where the error is located in the source file. Instead of

int foo = bar();

it outputs now

    2 | int foo = bar();
      |           ^~~

As you can see, the underline consists also now out of “~"’s and starts with a “^”. This makes it easier to find the beginning of the error especially in multiline errors.


Building blocks for modern GNOME apps using GTK4.

Manuel Genovés says

The work we’ve been doing these past months for the Timed Animation API has landed on libadwaita. I’m working to land the Spring Animation API soon


Core system user interface for things like launching apps, switching windows, system search, and more.

Ivan Molodetskikh reports

The work-in-progress screenshot UI got a bit of polish. I added hotkeys to switch between area, screen and window selection. The window selection now picks the currently focused window by default. The window border radius was increased to remove gaps for windows with rounded corners. Additionally, I made the “Screenshot captured” notification display the thumbnail with the correct aspect ratio to avoid ugly scaling. Finally, the window selection button is now disabled in screencast mode since it’s not implemented yet.


Georges Stavracas (feaneron) says

This week, a fascinating new feature landed in xdg-desktop-portal: restoring screencast sessions. We already implement this feature in the GNOME portal frontend, and together with that, we’ve done a round of polish and cleanups to these portal dialogs.


Emmanuele Bassi announces

Libgnome-desktop has been split into three separate shared libraries, and two of them (GnomeRR and GnomeBG) have been ported from GTK3 to GTK4. This will unblock the port to GTK4 of various system components.


Emmanuele Bassi announces

I released the first developers snapshot for GWeather-4.0, which is meant to be used by GTK4 applications showing weather or time zone information. The new API is not yet stable, but it is already useful for porting existing applications. You can find what changed in the migration guide.

Circle Apps and Libraries


A browser for your pinned tabs.

sonnyp says

Tangram 1.4.0 was released. It comes with

  • per-tab notifications priority
  • middle-click / Ctrl+Click to open links in the default browser
  • a fix for popular websites with external sign-in

See Tangram on GNOME Apps.


Easy to use BitTorrent client.

Felix says

I added authentication support to Fragments V2. It’s possible now to connect to a remote username/password protected Transmission/Fragments session. The credentials are then automatically stored in the keyring so you don’t have to enter them again the next time.

Third Party Projects


A solitaire (one player) version of the classic Eastern tile game. The objective is to select pairs of similar tiles.

gwagner says

Reworked GNOME Mahjongg. Initial plan was to help out with the Dark Style Initiative ( but i ended up porting to Gtk4 and libadwaita. I also made some refactorings to make the codebase more modern (Composite Templates, more subclasses)


A graphical utility to review and modify permissions of Flatpak applications.

Martín Abente Lahaye announces

A new Flatseal release is out 🥳🎉, and it comes with subtle visual improvements, a few bugs fixes, one more permission and a big quality of life improvement. Check it out!

GNOME Shell Extensions

Just Perfection

A tweak tool to customize the GNOME Shell and to disable UI elements.

Just Perfection says

Just Perfection extension is celebrating one year of active development with version 16: The extension added profile switcher to its prefs window and users can select between default, minimal and super minimal profiles. This version added ripple effects visibility, disabling double super key to app grid, panel in overview and more.

That’s all for this week!

See you next week, and be sure to stop by with updates on your own projects!

November 25, 2021

CMSes & static site generators: why I (still) chose WordPress for my business websites

For many years, until 2021, the idéemarque* website was my own static HTML hand-written codebase, which had the advantage of performance and flexibility (vs “what a theme dictates”), but was also impossible to scale, because it had a bus factor of 1 and a pain level over 9000. I even had it version-controlled in Git all the way back to 2014 (back when I finally joined the Git masochists sect). I was the only person in the world who could maintain it or contribute to it, because, quite frankly, you need to reach geek level 30+ to enter that dungeon, while most people, including new generations, don’t know how to use computers.

Pictured: How I felt whenever I had to make changes to my static website.

I spent time evaluating various alternatives to “coding everything by hand”, including Hugo and Publii (I’ll spare you all the framework-style templating systems turbonerd crap like Bootstrap, Smarty, Django, etc.). Hugo and Publii are very cool conceptually, and would work wonders for a casual geek’s blog, but there are a number of problems they cannot address conceptually, in my view:

  • Not advanced and flexible enough for me to do “anything” easily (oh you want to integrate XYZ dynamic features? Yeah, let’s see how well you paint yourself into that corner)
  • Relies on their own themes that I can’t be bothered to learn hacking (and I don’t want to be hiring a dev specialized in those technologies to do it for me) just to be able to accomplish my vision (“I want it to look just like that!“). It’s easy to make a nice-looking hello-world website if you fit within the theme’s planned usecases, but as soon as you start saying “I want every core page to have a unique layout depending on the contents” and “I want the front page to look & behave differently from all the rest”, you run into limitations pretty quickly and say “Screw this, if I’m going to start hacking this thing, I’m no better off than writing my own custom website codebase myself.”
  • They are arguably designed and best suited for the “one user” usecase. Collaborative editing and permissions management? Not happening.
  • Nobody but turbonerds has the skills to understand and manage a static site generator. Most people can’t be bothered to handle files and folders. They live in the browser, and simply giving them a log-in to your site is the only way I can see to lower the barriers to entry among your team.

As much as we geeks love to hate WordPress and the bloat it represents, it is pretty much the standard website building platform that is visibly thriving, and that we know will still be Free & Open-Source and available ten years from now; and for all its warts, with enough experience and geekdom you can tweak and harden it into something fairly reliable and somewhat secure (the fact that automatic updates are now available for both the core and extensions helps; yes, it makes me nervous to think that things may change and possibly break on their own, but you can’t afford to have an un-patched website nowadays, and I think the security benefits of automatic updates outweigh the theoretical compatibility worries).

Combined with the new Gutenberg editing experience, some themes out there are also flexible enough to easily lay out complex pages without feeling like I’m running into limitations all the time.

Pictured: me deploying WordPress for my mostly-static sites and trying to make it fast.

Not everything is perfect of course. As of 2021, on the performance front, getting consistent and reliable caching working with SuperCache is a mindboggling experience, full of “mandelbugs” (like this one); in my case, each of my websites has at least some (or all) of the caching behavior not working (whether it is some pages never being able to generate cache files, or the cached files not being retained, no matter what you do and what combination of voodoo incantation and settings you use), but maybe someday someone will complete a heavy round of refactoring to improve the situation (maybe you can help there?) and things will Just Work™. But for now, I guess I’ll live with that.

All in all, it is only from 2019 onwards, after much research (and much technological progress in general), that I found myself with enough tooling to make this work in a way that would meet my expectations of design & workflow flexibility, and therefore feel confident enough that this will be my long-term solution for a particular type/segment of my websites. My personal website (of which this blog is only a subset) still is hand-coded, however, because it “does the job.”

Years ago, someone once told me that whenever someone in your team decides to write your company’s website from scratch (or using some templating system), they “inevitably end up reimplementing WordPress… poorly.”

So yeah. We’re using WordPress.

*: idéemarque is the first and only Free & Open-Source branding agency that contributes to the desktop Linux landscape on a daily basis, because that's what I do and I'm already in too deep. Or, as an academic would say, I'm just "unreasonably persistent."

New LVFS redirect behavior

tl;dr: if you’re using libfwupd to download firmware, nothing changes and everything continues as before. If you’re using something like wget that doesn’t follow redirects by default you might need to add a command line argument to download firmware from the LVFS.

Just a quick note to explain something that some people might have noticed; if you’re using fwupd >= 1.6.1 or >= 1.5.10 when you connect to the LVFS to download a firmware file you actually get redirected to the same file on the CDN. e.g. downloading https://fwupd/download/ gets a redirect to https://cdn.fwupd/download/ which is then streamed to the user. Why this insanity?

As some of you know, egress charges from AWS are insanely high. The Linux Foundation are the kind people that kindly pay the LVFS bill every month, and a 4 years ago that was just a few hundred dollars and that was a rounding error to them. Last year we again grew at more than 100% and the projection for next year is going to surpass even that; the average size of firmware files has gone from ~30MB to ~50MB with much, much, larger server firmware in the pipeline. We certainly can’t watch the egress bill scale linearly with the LVFS popularity, else some accountant at the Linux Foundation is going to start asking questions – especially when Fastly provides the LF a geo-replicated CDN – which we’re not using.

So why don’t we put the CDN URL in the XML metadata directly, and then avoid all this redirect complexity altogether? This time the lawyers get us, as we’re required by US law to restrict distribution of some firmware to some countries on an embargo list. It’s very complicated, and it varies by vendor, but it’s not something we can avoid. So for this reason, the LVFS does a GeoIP lookup on the client IP, and if it’s all okay we then redirect the client to the CDN-cached version. It also lets us tell the vendor how many times the firmware has been downloaded without importing the CDN logs every 24 hours – which would be even harder as we only keep them for a short time for privacy reasons.

November 24, 2021

On Flatpak disk usage and deduplication

There is a blog post doing the rounds asserting that Flatpak Is Not The Future. The post is really long, and it seems unlikely that I and the author will ever agree on this topic, so I’m only going to talk about a couple of paragraphs about disk usage and sharing of runtimes between apps which caught my eye. This is highly relevant to my day job because all apps on Endless OS are Flatpaks—for example, the English downloadable version has 58 Flatpak apps pre-installed, and 13 runtimes—and I’ve had and answered some of the same questions discussed in the post.

They claim that they deduplicate runtimes. I question how much can really be shared between different branches when everything is recompiled.

This question is really easy to answer using du, which does not double-count files which are hardlinked together. Let’s compare the 20.08 and 21.08 versions of the freedesktop runtime:

wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/20.08
674M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/20.08
wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
498M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/20.08 /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
674M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/20.08
385M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
wjt@camille:~$ echo $(( 498 - 385 ))                                                                                                                                                

113 MB (out of 498 MB for the smaller, more up-to-date 21.08 runtime) is shared between these two runtimes.

How about the GNOME 41 runtime, which is derived from the 21.08 freedesktop runtime?

wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08                                                                                                
498M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.gnome.Platform/x86_64/41                                                                                                
715M	/var/lib/flatpak/runtime/org.gnome.Platform/x86_64/41
wjt@camille:~$ du -sh /var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08 /var/lib/flatpak/runtime/org.gnome.Platform/x86_64/41
498M	/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/21.08
327M	/var/lib/flatpak/runtime/org.gnome.Platform/x86_64/41
wjt@camille:~$ echo $(( 715 - 327 ))                                                                                                                                                

388 MB (out of 715 MB) of the GNOME 41 runtime is shared with the 21.08 runtime.

I can’t imagine what system updates will be like in the future when you have a few dozen apps storing tens of gigabytes of runtimes that all want to be kept up to date.

There is no need to imagine! I have 163 Flatpak apps on my Endless OS system. Let’s see how many runtimes I have, how big they are, and how many apps use each one:

wjt@camille:~$ flatpak list --app --columns=runtime | sort | uniq -c | wc -l
wjt@camille:~$ flatpak list --app --columns=runtime | sort | uniq -c | sort -n
      1 com.endlessm.apps.Platform/x86_64/6
      1 org.freedesktop.Platform/x86_64/18.08
      1 org.gnome.Platform/x86_64/3.34
      1 org.gnome.Sdk/x86_64/41
      1 org.kde.Platform/x86_64/5.14
      2 com.endlessm.Platform/x86_64/eos3.2
      2 org.gnome.Platform/x86_64/3.28
      3 org.freedesktop.Platform/x86_64/19.08
      3 org.freedesktop.Sdk/x86_64/21.08
      3 org.gnome.Platform/x86_64/3.38
      3 org.kde.Platform/x86_64/5.15-21.08
      5 org.gnome.Platform/x86_64/3.36
      9 org.kde.Platform/x86_64/5.15
     10 org.freedesktop.Platform/x86_64/20.08
     24 com.endlessm.apps.Platform/x86_64/5
     28 org.freedesktop.Platform/x86_64/21.08
     30 org.gnome.Platform/x86_64/40
     36 org.gnome.Platform/x86_64/41
wjt@camille:~$ cd /var/lib/flatpak/runtime; flatpak list --app --columns=runtime | sort | uniq | xargs du -sh --total
918M	com.endlessm.apps.Platform/x86_64/5
907M	com.endlessm.apps.Platform/x86_64/6
2.0G	com.endlessm.Platform/x86_64/eos3.2
632M	org.freedesktop.Platform/x86_64/18.08
211M	org.freedesktop.Platform/x86_64/19.08
569M	org.freedesktop.Platform/x86_64/20.08
385M	org.freedesktop.Platform/x86_64/21.08
782M	org.freedesktop.Sdk/x86_64/21.08
26M	org.gnome.Platform/x86_64/3.28
329M	org.gnome.Platform/x86_64/3.34
264M	org.gnome.Platform/x86_64/3.36
263M	org.gnome.Platform/x86_64/3.38
198M	org.gnome.Platform/x86_64/40
277M	org.gnome.Platform/x86_64/41
264M	org.gnome.Sdk/x86_64/41
436M	org.kde.Platform/x86_64/5.14
231M	org.kde.Platform/x86_64/5.15
215M	org.kde.Platform/x86_64/5.15-21.08
8.7G	total

I have 18 runtimes, totalling 8.7 GB of storage (deduplicated), not “tens of gigabytes”. The top 5 most-used runtimes on my system cover 128 of the 163 apps. (I am ignoring the .Locale extensions of each runtime: the English and French translations of the 21.08 runtime total 17 MB, compared to 498 MB for the runtime itself, so I think this is a reasonable simplification for rough numbers.) As for updates? GNOME Software applies them automatically and silently. I don’t think about them at all.

People may disagree about whether the numbers above are large or small, compared to the upsides that Flatpak does or does not bring. But the numbers themselves are readily accessible, as is much of the past and ongoing work that has gone into making them as small/large as they are.

Personally, I think the trade-off is absolutely worth it for me and for Endless OS users, particularly since going all-in on Flatpak means that the base, immutable Endless OS install is just 4.2 GB. Of course there is room for improvement, and years ago I wrote a quick hack to help study exactly which files would ideally be shared between two runtimes but are not. At the time, the primary cause was non-reproducible builds. Since then, the Flatpak ecosystem has moved over to Buildstream which should help a lot, though I haven’t rerun the experiments except for what you see above. Automated statistics about apps using obsolete runtimes might be useful for the Flathub community, as might automated runtime updates, and some further work on understanding if and how the derived runtimes (GNOME & KDE) could share more with the freedesktop runtime version they are based on. And, would widespread use of filesystems that support block-level deduplication (like btrfs) help?

Toolbx is now on Matrix

Toolbx now has its own room on Point your Matrix clients to and join the conversation.

We are working on setting up an IRC bridge with Libera.Chat but that will take a few more months as we go through the process to register our project channel.

November 21, 2021

Pixel Sites

I’ve created a couple of minisites for key OS components, built using no frameworks, but plain CSS. Just having CSS grid and variables made it viable for me to avoid using frameworks recently. Having includes/imports one wouldn’t even need Jekyll.

The founding stone on all of these is the pixel art, which is now becoming my favorite art form.

Flatpak Fleet Comander Toolbox Zbus

If you maintain an upstream OS component and are looking to replace a wiki or a markdown readme with a simple site, I’ve created a template to get you started quickly.

November 20, 2021

Flatseal 1.7.5

A new Flatseal release is out 🥳🎉, and it comes with subtle visual improvements, a few bugs fixes, one more permission and a big quality of life improvement.

Starting with the visuals, @BrainBlasted replaced the custom widgets, used in the applications list, for proper libhandy’s widgets. Plus, he fixed a small styling detail to make applications icons look sharper!

This change results in more consistent visuals and less custom code for me to maintain 😊

On the permissions side of things, @Arxcis added support for  the per-app-dev-shm permission, which was added to Flatpak a few releases ago.  Also, I extended the Other files subsection to allow negated filesystem overrides, e.g. !home.

Moving on to quality of life improvements, I took the time to make the UI fully usable with keyboard input. For some of us, who prefer the keyboard, this makes the UI faster to navigate. For other people, it makes Flatseal accessible for the first time. Thanks to @sophie-h for reminding me of this 🙌

With a good combination of shortcuts and mnemonics Flatseal has finally become keyboard friendly. Still far from perfect, but it’s the perfect start.

On the bug fixing front, @Arxcis fixed an issue that prevented users from overriding originally-negated permissions, and I fixed a couple more that caused undefined values to appear in override files. Although these were very rare, both issues were present since the original version of Flatseal.

Additionally, by popular demand, applications are now sorted by the application’s name 😅

Last but never least, thanks to @AsciiWolf, @ovari, @cho2, @Vistaus, @BigmenPixel0 and @eson57 for keeping their translations up to date, and to @TheEvilSkeleton and @usnotv for new French and Turkish translations, respectively.

November 19, 2021

Fair Weather Friends

Today I released libgweather-3.90.0, the first developers snapshot of GWeather 4:

Behold! A project logo

This release is mostly meant to be used as a target for porting existing code to the new API, and verifying that everything works as it should.

The major changes from GWeather-3.0 are:

  • the GTK3 widgets have gone to a farm up state, so you’ll have to write your own UI for searching locations
  • GWeatherLocation is a GObject type, so you can use it with GListModel and friends, which should help with the point above
  • the deprecated API has been removed
  • the API that will be part of GWeather 4.0 will be stable, and regular API/ABI stability guarantees will apply

If you are using libgweather in your application, you should head over to the migration guide and check out what changed.

Ideally, there are still things that need to be cleaned up in the GWeather API, for instance:

  • GWeatherTimezone parses the tzdata file directly, which comes with its own set of issues, like having to track whether the time zone database has changed or not; we should use GTimeZone instead, but the API provided by the two types do not match entirely. I need to check the current users of that API, and if possible, just drop the whole type.
  • GWeatherInfo has a bunch of getter functions that return the bare values and that can fail, and additional getter functions that always return a formatted string, and cannot fail; it’s not a great API.
  • GWeatherLocation returns the localised names by default, and has additional getters for the “English” (really: POSIX C locale) names.

If you encounter issues when porting, please: file an issue on GitLab.

#19 Updated Calculations

Update on what happened across the GNOME project in the week from November 12 to November 19.

Core Apps and Libraries


Perform arithmetic, scientific or financial calculations.

Chris 🌱️ reports

Robert Roth and I have ported Calculator to GTK4+libadwaita. If you’d like to test it early, you can now install it via the GNOME nightly flatpak repository.


Building blocks for modern GNOME apps using GTK4.

Alexander Mikhaylenko says

AdwLeaflet now supports shortcuts and mouse back/forward buttons in addition to touch and touchpad swipes for navigating back/forward. The corresponding properties has been renamed from can-swipe-back/forward to can-navigate-back/forward to reflect that.


Lets you install and update applications and system extensions.

Philip Withnall announces

Milan Crha added support for libsoup3 to gnome-software. Many components are currently being ported to libsoup3 by a variety of contributors across the desktop.


Core system user interface for things like launching apps, switching windows, system search, and more.

Ivan Molodetskikh announces

This week I improved the way windows are laid out in the window selection mode of the work-in-progress screenshot UI. Like in the regular overview, client-side window shadows are no longer included into the window size. Selection is now indicated with a nice rounded outline similar to the one in GNOME 3.38. Finally, I removed the background wallpaper to reduce confusion between the screenshot UI and the overview.


A phone dialer and call handler.

Evangelos announces

I worked with Guido on showing a contact’s avatars in Calls using libhandy’s HdyAvatar. Avatar information is exposed over DBus allowing Phosh to show avatars on the lockscreen.


The low-level core library that forms the basis for projects such as GTK and GNOME.

Philip Withnall announces

Emmanuel Fleury has almost finished his campaign of fixing warnings in the Windows code in GLib, which will allow us to keep code quality up more easily in future.


Use the GNOME platform libraries in your JavaScript programs. GJS powers GNOME Shell, Polari, GNOME Documents, and many other apps.

Philip Withnall announces

In GJS, Evan Welsh fixed issue #1 after a lot of careful work. It should make the performance of promises in JS more predictable under higher load.

Circle Apps and Libraries


Easy to use BitTorrent client.

Felix says

Fragments V2 has made good progress in the past few days. Maximiliano and I implemented the necessary bits in transmission-client and transmission-gobject to allow changing the settings of the underlying transmission-daemon. The new redesigned preferences window added support for many requested settings, like selecting an own folder for incomplete torrents. Maximiliano also ported the in-app notifications to the new AdwToast API. Chris 🌱️ added support for opening downloaded torrents.

Third Party Projects


A simple user-friendly terminal emulator

Alexander Mikhaylenko reports

Chris 🌱️ has added a light/dark switcher and the app now always uses “Terminal” branding instead of “King’s Cross”


Lets you choose the application to open files and links.

sonnyp says

Junction 1.2.0 has been released with better compatibility, new features and a better design thanks to libadwaita and Tobias Bernard’s help.

Sound Recorder

A simple, modern sound recorder.

Bilal Elmoussaoui announces

Chris 🌱️'s port of Sound Recorder to GTK 4 / libadwaita has been cleaned up and merged today.


A simple Crossword player and Editor.

Jonathan Blandford reports

An initial version of GNOME Crosswords was released and is looking for feedback. This is a simple crossword game for GNOME, with a planned Crossword Editor in the works. See the release announcement for more details.

That’s all for this week!

See you next week, and be sure to stop by with updates on your own projects!

November 18, 2021

FCC unlock procedure updates in ModemManager 1.18.4

If you own a laptop (Dell, HP, Lenovo) with a WWAN module, it is very likely that the modules are FCC-locked on every boot, and the special FCC unlock procedure needs to be run before they can be used.

Until ModemManager 1.18.2, the procedure was automatically run for the FCC unlock procedures we knew about, but this will no longer happen. Once 1.18.4 is out, the procedure will need to be explicitly enabled by each user, under their own responsibility, or otherwise implicitly enabled after installing an official FCC unlock tool provided by the manufacturer itself.

See a full description of the rationale behind this change in the ModemManager documentation site and the suggested code changes in the gitlab merge request.

If you want to enable the ModemManager provided unofficial FCC unlock tools once you have installed 1.18.4, run (assuming sysconfdir=/etc and datadir=/usr/share) this command (*):

sudo ln -sft /etc/ModemManager/fcc-unlock.d /usr/share/ModemManager/fcc-unlock.available.d/*

The user-enabled tools in /etc should not be removed during package upgrades, so this should be a one-time setup.

(*) Updated to have one single command instead of a for loop; thanks heftig!

Introducing GNOME Crosswords

GNOME Crosswords

Howdy folks! I want to announce a game for GNOME that I’ve been working on for a few months.

I’ve always enjoyed solving Crossword puzzles. It’s something I grew up doing as a kid, and we continue to do them as a family at the dinner table at night. I’ve wanted to try my hand at writing crosswords for a while, but there isn’t really a good tool available for doing so, and certainly no free software ones that work well with a recent GNOME release. I recently bought myself a lovely new Fedora-loaded Lenovo, and after it arrived, I thought I’d take a shot at writing such a tool.

Over the past four months or so I managed to get something worth releasing. The code is available here. It should build on relatively recent Linux distributions, though it does need libadwaita from git (toasts!). I also put together a flatpak file for testing here (no repo yet, as getting that set up defeated me). Once I’m more confident that the puzzles are solvable and fun I plan to publish it to flathub.

A dog's day
Non-traditional grid
Guardian cryptic No 28,605
The Guardian Daily Cryptic with reveal answers enabled


It’s still early, but it already has some fun features:

  • Puzzle Sets. The heart of the game is the Puzzle Set. It’s a collection of crossword puzzles that are tied together by a theme. Solving a puzzle unlocks more puzzles. I currently have one puzzle set (“Cats and Dogs”) with nine puzzles in it, but I have a few more puzzle sets planned. It contains mostly traditional puzzles, but I threw in a cryptic to keep people on their toes.
  • Nontraditional shapes and styles: I wanted to make something a bit little more whimsical and fun, as well as the more traditional puzzle grids. So I added support for colors and shapes as well. My son had fun doing pixel art to create some of the grids.
  • Reveal mistakes: For when you get stuck! It also supports checksums for puzzles that don’t include the solution.
  • Scalable grid: Currently the UI only exposes four sizes, but we have all the pieces to scale crosswords to different sizes.
  • Support for the .ipuz spec: This spec supports a ton of things, and I don’t support it fully yet, but most of the crossword part of the spec is included. There aren’t a ton of .ipuz files floating around, but you can use puzzlepull to download the Guardian Daily puzzle if you want to try some other examples.
Puzzle Set
The first Puzzle Set

Crossword Editor

GNOME Crosswords Editor

As part of building this app, I realized that creating grids was as big a part of the app as writing the actual game itself. To facilitate that, I started writing a crossword editor as well. It’s in the early stages, but it already has one of the most important features: a tool to create the initial grid. Making puzzles that fit well together is surprisingly hard. To make it easier, I wrote a crossword solver that quickly suggests words to fill in the grid. I’m proud of the design – it’s able to efficiently suggest options out of a list of 500K words really quickly (<1 μs on my machine). I was able to use it to build an autofill dialog that can recursively fill in a section of the puzzle when making a grid.

I still have more work to do on the editor and it’s clear that the autofill dialog isn’t a panacea, but it helped me figure out out some tricky corners. Here’s a video of the autofill dialog in action:


I especially want to thank Rosanna and my kids for play-testing this and suggesting clues, as well as their patience while I was writing it. Thanks also to Federico for giving great advice, great code, and for being a star. Matthias for helping me relearn GTK and explaining GtkIMContext. Also, the example code in GNOME Builder was immensely helpful for getting this started.

What’s next?

There are a ton of features I’d like to add to this game. It really needs printing support, which should be relatively easy. I’d also love to see it get internationalized (and not just translated) – are crosswords in non-Latin languages a thing? And I’ve seen enough of Benjamin‘s GUADEC presentations over the years to know GTK can do something cooler than popup a dialog when you finish a crossword.

But the most important thing is that the game needs to be fun! For that, we need more puzzles and the existing puzzles need to be better. If you’re interested in joining me in creating a good set of puzzles for Linux, try the game out and let me know.

November 17, 2021

Status update, November 2021

I am impressed with the well-deserved rise of Sourcehut, a minimalist and open source alternative to Github and Gitlab. I like their unbiased performance comparison with other JavaScript-heavy Git forges. I am impressed by their substantial contributions to Free Software. And I like that the main developers, Drew DeVault and Simon Ser, both post monthly status update blog posts on their respective blogs.

I miss blog posts.

So I am unashamedly copying the format. I am mostly not paid to work on Free Software but sometimes I am so the length of the report will vary wildly.

This month I got a little more Codethink time to work on (shout out Javier Jardón for getting me that time). Status report here.


I spoke at the first ever PackagingCon to spread the good word about Freedesktop SDK and BuildStream.

As always, I did a little review and issue triage for Tracker and Tracker Miners.

And I have been spending time working on an audio effect. More about that in another post.

open source is flexible

I had as main objective when I started my Coding Experience(CE) to get to grips with C or C++ since I am convinced that understanding one or both languages will help me become a better developer. Cog is developed in C which explained my excitement when I was introduced to the project. The first couple of tasks assigned to me were challenging but quite beginner-friendly.

Like it usually happens to many developers, I got stuck on an issue. After weeks of working on it, I couldn’t complete it. My mentor and I had a couple of meetings/coding sessions which helped me move ahead though not to the point of finishing the work. I could feel that there was a knowledge gap I had to bridge in C which studies and practice hadn’t given me that ability yet. Cutting the long story short I got really exhausted and anxious and suggested to my mentor that we move to something else and revisit this issue later.

After a couple of days, I was presented with a new program that can help me make the most of the CE. It turns out I will be moving back to contributing actively on GJS since there was good progress when I previously contributed to it. The only difference is most of my contributions will be in C++ and will probably include more core stuff.

Most if not all open source communities work to help contributors stay motivated and to keep making progress. For this reason they have very flexible methods of working. It’s a more free culture than you’ll find when building proprietary software. It’s very easy to find situations where a couple of contributors sit down to take their time and come up with a plan that makes the environment more conducive for new contributors. GNOME does this, Igalia and Mozilla too based on my interactions. It makes sense to mention that not everyone gets the opportunity of venturing into open source through Outreachy hence I will be talking later about other opportunities in open source for beginners and a handful of success stories I have come across.

This blog post can be considered a reason why you can start your software development career in open source. You just need to find an organization that meets your needs. By the way Cog 0.12 is soon be released. Release notes will be available here.

Thanks for reading and I will love to hear how flexible you think open source is in the comments and how this has helped you make progress. Please do not forget to like, share and subscribe and let’s connect on twitter and linkedIn.

November 16, 2021

Endless Orange Week: GNOME on WSL

The week of 8th–12th November was Endless Orange Week, a program where the entire Endless OS Foundation team engaged in projects designed to grow our collective learning related to our skills, work and mission. My project was to explore running a complete GNOME desktop in a window on Windows, via Windows Subsystem for Linux.

Screenshot of Windows Remote Desktop Connection window, containing a GNOME desktop running Hack and GNOME Setttings, in turn showing Virtualization: wsl


We’ve long faced the challenge of getting Endless OS into the hands of existing PC users, whether to use it themselves or to try it out with a view to a larger deployment. Most people don’t know what an OS is, and even if they have a spare PC find the process of replacing the OS technically challenging. Over the years, we’ve tried various approaches: live USBs, an ultra-simple standalone installer (as seen in GNOME OS), dual-booting with Windows (with a 3-click installer app), virtual machine images, and so on. These have been modestly successful – 5% of our download users are using a dual-boot system, for example – but there’s still room for improvement. (I have a personal interest in this problem space because it’s what I joined Endless to work on in 2016!)

In the last few years, it’s become possible to run Linux executables on Windows, using Windows Subsystem for Linux (WSL). Installing the Debian app from the Windows Store gives you a command-line environment which works pretty much like a normal Debian system, running atop a Microsoft-supplied Linux kernel. Most recently, unmodified applications can present X11 or Wayland windows and play audio through PulseAudio; behind the scenes, a Microsoft-supplied distribution with their branches of Weston and PulseAudio exports each Wayland or X11 window & its audio over RDP to the host system, where it appears as a free-standing window like any other. There is also support upstream in Mesa for using the host system’s GPU, with DirectX commands forwarded to the host via a miraculous kernel interface.

This raises an interesting question: rather than individual apps installed and launched from a command line, could the whole desktop be run as a window, packaged up into an easy-to-use launcher, and published in the Windows Store? If so, this would be a nice improvement on the other installation methods we’ve tried to date!

Proofs of concept

I spent last week researching this. Yes, you can indeed run a complete GNOME desktop under WSL. I tried two approaches, which each have strengths and weaknesses. I worked with Debian Bookworm, which has GNOME 41 and an up-to-date Mesa with the Direct3D backend. Imagine telling someone 20 years ago that Debian would one day include development headers for DirectX in the main repository!

I packaged up my collection of scripts and hacks into something which can build a suitable rootfs to import into WSL and launch either demo with a single command. There may be things that I got working in my “pet” container that don’t quite work in this replicable “cattle” container, but I did my best.

GNOME desktop as X11 app

GNOME Shell can be run as a so-called “nested session”, with the entire desktop appearing as a window inside your existing session. Thanks to my team-mate Georges Stavracas for his help understanding this mode, and particularly the surprising (to me) detail that the nested session can only be run as an X11 window, not a Wayland window, which explained some baffling errors I saw when I first tried to get this going.

Once you’ve got enough of the environment GNOME expects running (more on this below), you can indeed just run it with a carefully-crafted set of environment variables and command-line arguments, and it appears on your Windows system, resplendent with Weston’s window decorations:

GNOME desktop running a game, in a window on Windows

Apps can even emit sound over PulseAudio as normal, or at least they could once I fixed an edge case in Flatpak’s handling of the PulseAudio socket. It’s hard to show audio in a screenshot, but you can see the sounds being emitted by Sidetrack (the WebKitWebViewProcess) as well as Hack somewhere in the background (the python3 process), both of which are Flatpak apps, and the RDP sink as output.

So on the face of it this seems quite promising! But Shell’s nested mode is primarily intended for development, with the window size fixed at launch by an environment variable with DEBUG in its name.

I found WSLg to be quite fragile. WSLg’s Xwayland often fell over for unknown reasons. I had to go out of my way to install a newer Intel graphics driver than would automatically be used, to get the vGPU support needed for Mesa’s Direct3D backend to work. But having done this, on one of my machines, the driver would just crash with SIGILL – apparently the driver unconditionally uses AVX instructions even if the CPU doesn’t support them. On my higher-end machine, most apps worked fine, but Shell on this stack would display just a few frames and then hang. In both cases, I could work around the problem by forcing Mesa to use software rendering, but this means losing one of the key advantages of WSLg!

Another weird anecdote: using the D3D12 backend, the GTK demo app’s shadertoy demo works fine, but its gears demo doesn’t render anything – and nor does glxgears! Apparently D3D12 just doesn’t like gears⁈

Thanks to Daniel Stone at Collabora for his patient help navigating Mesa and D3D12 passthrough.

GNOME desktop exported over RDP

In the past few GNOME releases, it’s become possible to access the desktop remotely using RDP. This is Windows’ native remote-access protocol, and is also the mechanism used by WSLg to export windows and audio to the host.

So another approach is to launch GNOME Shell in its headless mode, and then explicitly connect to it with Windows’ RDP client. A nice touch in WSL is that, by the magic of binfmt_misc and an automatic mount of the host system’s drive, you can invoke Windows executables on the host from within the WSL environment, so a single launch script can bring up GNOME, then spawn the Windows RDP client on the host with the correct parameters. (This is how WSLg works too.)

Not pictured below are the ugly authentication and certificate warning dialogs during the connection flow:

GNOME desktop, accessed over RDP, showing GTK 4's Shadertoy GL demo

Here, GNOME Shell is (AFAICT) rendering using Mesa’s accelerated D3D12 backend, as are GL applications running on it (the GTK 4 Shadertoy demo). But in this model we lose WSLg’s PulseAudio forwarding, and its use of shared memory to send the pixel contents of the desktop to the client. Both of these are solvable problems, though. GNOME Remote Desktop uses the same RDP library, FreeRDP, as WSLg, and all the other supporting code on the Linux side is open-source. GNOME Remote Desktop uses Pipewire rather than PulseAudio, and Mutter rather than Weston, so WSLg’s RDP plugins for PulseAudio and Weston could not be used directly, but audio forwarding over RDP seems a desirable feature to support for normal remoting use-cases. Supporting the shared-memory transport for RDP in GNOME Remote Desktop is perhaps a harder sell, but in principle it could be done.

Just like the nested session, the dimensions of a headless GNOME Shell session are currently fixed on startup. But again I think this would be desirable to solve anyway: this already works well for regular virtual machines, and when connecting to Windows RDP servers.

Rough edges

When you start a WSL shell, PID 1 is an init process provided by WSL, and that’s pretty much all you have: no systemd, no D-Bus system or session bus, nothing. GNOME requires, at least, a functioning system and session bus with various services on them. So for this prototype I used genie, which launches systemd in its own PID namespace and gives you shells within. This works OK, once you change the default target to not try to bring up a full graphical session, disable features not supported by the WSL kernel, and deal with something trampling on WSLg’s X11 sockets. (I thought it is systemd-tmpfiles, but I tried masking the x11.conf file with no success, so I hacked around it for now.) It may be easier to manually launch the D-Bus system bus and session bus without systemd, and run gnome-session in its non-systemd mode, but I expect over time that running GNOME without a systemd user instance will be an increasingly obscure configuration.

Speaking of X11 sockets: both my demos launch GNOME Shell as a pure Wayland compositor, without X11 support. This is because Mutter requires the X11 socket directory to have the sticky bit set, for security reasons, and refuses to start Xwayland if this is not true. But on WSLg /tmp/.X11-unix is a symlink; it is not possible to set the sticky bits on symlinks, and Mutter uses lstat() to explicitly check the symlink’s permissions rather than its target. I think it would be safe to check the symlink’s target instead, provided that Mutter also checked that /tmp had the sticky bit (preventing the symlink from being replaced), but I haven’t fully thought this through.

WSL is non-trivial to set up. The first time you try to run a WSL distro installed from the Windows Store, you have to follow a link to a support article which tells you how to install WSL, which involves a trip deep into the Settings app followed by a download and reboot. As mentioned above, I also had issues with the vGPU support in Intel’s driver on both my systems, which I had to go out of my way to install in the first place, and WSLg’s Xwayland session was somewhat unstable. So I fear it may not be much easier for a non-technical user than our existing installation methods. Perhaps this will change over time.

GNOME Remote Desktop’s RDP backend needs some manual set-up at present. You have to manually generate a TLS key and certificate, set up a new username & password combo, and set the session to be read-write. You also have to arrange for the GNOME Keyring to be unlocked in your headless session, which is a nice chicken-and-egg problem. Once you’ve done all this (and remembered to install a PipeWire session manager in your minimal container) it works rather nicely, and I know that design and engineering work is ongoing to make the set-up easier.


Although I got the desktop running, and there are some obvious bits of follow-up work that could be done to make the experience better, I don’t think this is currently a viable approach for making it easier to try GNOME or Endless OS. There is too much manual set-up required; while it might be possible to bundle some of the Linux side of this into the WSL wrapper app, installing WSL itself is still quite cumbersome, if not exactly rocket science. The many moving parts are still rather new, and I hit various crashes and strange behaviour. Even with software rendering, the performance was fine on my relatively high-end developer laptop, but performance was pretty bad on my normal Windows machine, a lower-spec device that might be more representative of computers in general.

I do still think this general approach of running the desktop windowed in a container on a foreign OS is an interesting one to keep an eye on and re-evaluate periodically. Chrome OS is another potential target, since it also supports running arbitrary Linux containers with Wayland forwarding, though my understanding is that it also involves rather a lot of manual set-up and is not supported on managed devices or when parental controls are in use…

I was happy to experience first-hand the progress GNOME has made in supporting RDP. This kind of functionality may not be important to most GNOME developers and enthusiasts but it’s really important in some contexts. I used to work in an environment where I needed remote access to my desktop, and RDP was the only permitted protocol. Back in 2014, the tools to do this on my Linux system were truly dire, particularly if you want to access a normal desktop remotely rather than a virtualised desktop; by contrast, accessing my Windows system from a Windows, Linux or macOS client worked really well. GNOME Remote Desktop has made some big strides in the right direction, with better integration with the desktop and fewer fragile hacks. I’ll keep watching this space with interest.

WSL itself is also an impressive technical achievement, and the entire Linux side of it is free software. There is nice integration with the host system – for example, you can run Visual Studio Code on the host by running code in WSL, and everything works transparently.

On a personal level, I learnt many new things during the course of Endless Orange Week. Besides learning about WSL, I also learnt how to break on a syscall in gdb (catch syscall 16 for ioctl() on x86_64) and inspect the parameter registers; how Mesa chooses its backend (fun fact: most of the modules in /usr/lib/x86_64-linux-gnu/dri/ are hardlinks of one another); the importance of a PipeWire session manager; more about how PID and mount namespaces work; and so on. It was a nice change from my usual day-to-day work, and I think the research is valuable, even if it doesn’t immediately translate into a production project.

Calculator and GTK4

It was a long time since I last wrote, but important news coming up, so I thought it's time to post again.

The kind Christopher Davis has spent some time on porting Calculator to GTK4, a process which looks like complete to me know, with the merge into master happening fairly soon. This was a lot of work, given the framework changes, and would be nice to have some testing mileage on it, so I'm asking the curious people to check it out, and report any issues you may find.

Currently it is only available on the merge request branch, but it is fairly easy to try out using Builder. Thanks go out to the developers behind Builder+Flatpak for building up the whole ecosystem which makes development fairly easy without breaking the local environment, and without having to maintain a full JHBuild environment.

Looking at the changes in a bit more detail:

libadwaita instead of libhandy

With the introduction of libadwaita there were quite a few steps to migrate from libhandy, but fortunately the migration steps are well-described in libadwaita docs, so it was not really hard, except for a couple of trial and errors to get the preferences comboboxes getting to work due to model class changes.

gtk4 instead of gtk3

The steps are well-described in the case of GTK too, but the amount of changes to be done was overwhelming, so after attempting twice to do the full migration by going step by step (replacing deprecated APIs, migrating to APIs already existing in GTK3, etc) but failing badly, thankfully Christopher showed up with a rough port (creating several tasks to be fixed on the MR, which I could act upon). I helped wherever I could by testing and fixing here and there:

  • ported financial dialogs to fit the Human Interface Guidelines, resizing them to proper sizes, moving action buttons into headerbars, and other minor tweaks

  • given that GTK4 combobox doesn't support long models (and our currency selection is fairly long) as in "there is no scrolling for comboboxes", and GtkDropDown only supports plain models, not tree models (which we used for representing UnitCategory -> Unit selection) something had to be done, and I decided to split the conversion comboboxes into conversion category and conversion unit selectors, adding an additional GtkDropDown to select the unit category, reducing technical debt (keeping the units of both comboboxes in sync added some complexity) , and allowing us to use GtkDropDown for unit selection, which does make search in the unit selectors possible (of course search bar is only shown in case there are more than 10 elements to search from, there is no need to search in less than 10 units)


gtksourceview5 instead of gtksourceview4

Now, even this might not be visible for everyone, Calculator uses gtksourceview component for the calculator entry due to some convenience functions (like function name autocompletion, variable name autocompletion) the code had to be updated there too, and even though the migration steps are short here, it contains completion code changes, which is exactly what we are using.


Important thing to note is that this is only a development version for now. libadwaita and gtksourceview are still moving targets (as we are fairly early in the GNOME 42 development cycle APIs might change, e.g. there was a change over the weekend in libadwaita breaking the build, fixed now), so breakage might happen.

Further plans

Search in the selectors works, but currently only direct text search for the full name, I absolutely want to support case-insensitive, match-anywhere, search-symbol-too, plus it would be great to add support for favorite currencies/units too, but none of these is blocking the GTK4 merge, but would be nice to have.

Another important development recently was the refactoring of currency conversion backend, which now should make the addition of new currency conversion sources (currently two hard-coded ones available) and enable/disable for any of them via code fairly easy. Taking this idea further adding a plugin system for enable/disable/implement new providers via libpeas would also be a nice thing, and would help in lots of areas.

If you would like to help in any of the above areas, or have other ideas, just drop me a note, report issues, comment on existing ones, let's improve this small utility a bit.

November 15, 2021

Toolbx: Red Hat is hiring a software engineer

The Desktop Team at Red Hat wants to hire a software engineer to work full-time on Toolbx (formerly known as Toolbox) with me, and hopefully go on to maintain it in the near future. You will be working upstream and downstream (Fedora and RHEL) to improve the developer and troubleshooting experience on OSTree-based Linux operating systems like Fedora Silverblue and CoreOS, and extend some of the benefits to even traditional package-based OSes like Fedora Workstation.

If you are excited to work across the different layers of a modern Linux operating system, with a focus on container and desktop technologies, and aren’t afraid of getting your hands dirty with C and Go, then please go ahead and apply. Toolbx is a relatively young project with a rapidly growing community, so you are sure to have a fun ride.

November 14, 2021

Endless Orange Week: Hack content creators platform (2)

The past Friday was the last day of the Endless Orange Week. It was a nice and fun experience, and even if I was not able to do as much as I wanted, we were able to make something that "works" in the Hack project.

The Hack Quest editor

The first step to have custom quests on the Hack app was to complete the Ink language support. We started to work on this some time ago, but never completed the functionality.

I worked on that the first three days, updating the ink library and building the missing pieces to be able to load quests from random paths. I've implemented that in a way that the Hack application is able to receive a path to a .ink file, and it's able to build and run the quets.

The Quests are not just the script, but they have some metadata, like title, subtitle, description, difficulty and the card image to show on the interface. To solve that I defined a "custom quest bundle format", that's bassically a folder with:

  • questId
  • quest.jpg
  • metadata.json

So I also added the functionality to import a bundle zip file and export with the quest information.

I created some command line options to use this new functionality:

  --play-ink=FULL_INK_FILE_PATH                 Start a custom ink quest.
  --import-quest=PATH_TO_BUNDLE_OR_INK_FILE     Import a custom ink quest.
  --export-quest=CUSTOM_QUEST_ID                Export a custom quest bundle.

Quest creation interface and the Inky Editor

The first idea was to try to provide a full quest creation experience in the app, but that was too much, so we decided to simplify the way to create quests and depend on the Inky editor external tool. Manuel Quiñones took some time to update the flatpak application with the latests ink version, so we can use to create custom quests.

The Inky editor provides help about the language, syntax highlighting and a simple way to test the script, so it's a nice tool. The main problem with this tool is that it doesn't provide a way to launch it with a file path so it's not possible to integrate with the Hack app.

So at the end, the Quest creation dialog is just a way to define the metadata and to select the Quest ink files from your filesystem. How the ink script is created is a decision to make for the content creator.

The future

We've no time to complete all we wanted to do, and I didn't create a new release, so this new functionality is still not there. But we'll try to do a release soon.

Simon is working on some interface improvements and also on a new tutorial Quest, so we can introduce the Custom Quest creation tool in the same app.

The Character editor

The other part of this week planning was the character editor. Joana did a really nice work designing the application, the initial assets and the user experience, but I had not too much time to work on the implementation. So I spent just one day working on this.

The main idea was to create a new independent app, and then provide a way to integrate with the Hack application and the custom Quest creation dialog. And it'll be a simple application so maybe it could be useful or interesting for other people, it's a fun way to play around and create random faces.

We just created the application Avatar Creator. I created a simple python Gtk4 application and worked a bit on the basic functionality. So right now it loads a list of svg assets and provide the 3x3 grid. You can click on a grid cell and then choose what basic image should go there.

I added the initial set of basic images, created by Joana, to create this funny robot faces, but the format is simple enough to extend with different "avatar libraries" in the future.

Right now it's also possible to export to png, so the app is functional, but it needs a bit more work.

My idea is to work a bit more in the following weeks, when I have some time, on weekends or holidays and at some point, publish it in flathub. And lets see if there are more developers interested on this app so it can grow.

The application is simple enough to be a good place for GNOME newcomers and it's also a fun project to work on. A simple toy app to create faces that could have some potential, some future ideas:

  • "Smart" random faces generator
  • Configurable grid: Maybe is interesting to make it bigger or smaller to play around
  • Programmed simple base image manipulations, like rotation, mirror, color
  • Animation creation, maybe be able to export to gif

The Endless Orange Week experience

This week was a really nice experience, because we were working in a "personal" chosen project, that we liked and without the day to day meetings, times schedules and other related work stuff.

But that was not all. In Endless we've different teams that work mostly isolated, because we're working on different fields, we've some overlapping, but we work day to day as small teams, and this week we were all using the same slack channel to show our progress, and it was nice.

Maybe now that we're not a big organization with a lot of workers, we can do something like this more often, it's always good to know more about other coworkers and to learn something that maybe it's not related with your main project, but it could be interesting.

I'm really happy that we did this Endless Orange Week, it's sad that it ended too soon, I'm waiting to learn from my coworkers what amazing things they do during this week and I'm looking forward the next year Orange Week!

November 13, 2021

Adventures with portals

This week (November 8th – 12th) is the Endless Orange Week, a program where the entire Endless team engages in projects designed to grow our collective learning related to our skills, work and mission.

My project for this program was improving XDG portals. I set myself out to work on the following problems:

  • Improve the ScreenCast portal by introducing a new feature to restore previous screencasts.
  • Add an portal-based audio access mechanism
  • Modernize libportal

Let’s have a look at what these features are, and what’s the progress.

Portal-based Screen Casting

One of the portals introduced for applications to capture the contents of a monitor or a window was the ScreenCast portal. This portal has gained some relevance, since it is the primary – and in most cases, the only – way to capture windows and monitors on Wayland. This is the mechanism used by OBS Studio, Chromium, Firefox, etc.

This portal has a well defined set of steps centered around “sessions”:

  1. Applications initiate the process by asking the ScreenCast portal to create a screencast session. The portal replies with a handler for this session
  2. Applications then configure this session. They set if they want monitors or windows or both; the way they’d like to receive cursor (metadata, embedded in each frame, or hidden); and whether they allow multiple sources or not. The portal replies acknowledging this configuration.
  3. Finally, applications start the screencast session. This is when a dialog shows up asking you to select a window or a monitor. The portal replies with a list of streams. Each stream has a corresponding PipeWire node, width and height, and position.

This process is repeated every time an application wants to screencast. It’s a robust series of steps, and has served us well so far, but having to select a monitor or window every time can be a frustrating experience.

For some use cases, this process is problematic. Take Steam’s recent introduction of PipeWire-based Remote Play: the whole purpose of this feature is to allow playing remotely, potentially without physical access to your computer. Evidently, in this case, showing a dialog to select a monitor is not going to work if the person is probably not in front of the machine.

This is where my new proposal to the ScreenCast portal comes in.

The mechanism proposed there is composed of two new properties: (i) a persist mode, where applications can tell the portal that they want to restore this screencast session later; and (ii) a restore token to restore a previous screencast session.

In summary, when configuring (step 2) a screencast session, applications can tell the portal “hey, I’d like to restore this session later”; in this case, after you select a monitor or window and start the stream (step 3), the portal will give the app what I called a restore token. Applications should store this token however they want (ideally using the platform’s preferred preferences systems, such as GSettings for GNOME).

Applications that have a restore token should use them when configuring the screencast session (step 2). The portal will receive this token, and try to restore the previous session’s windows and monitors. If that fails, e.g. when you changed monitors or the windows is not open, the selection dialog is presented again. From the application’s perpective, it doesn’t know (nor does it matter) if the previous session is restored or not, as the application will receive a list of streams and PipeWire nodes regardless of what it happens.

If you look closely, it is possible to endlessly restore the same session by passing both the token to restore a previous session, and asking the portal to restore it again later.

I was not confident of this mechanism when proposing it, but after implementing it on the GNOME portals, I realized this mechanism is actually really robust. It allows for very sophisticated features to be implemented by portal implementations without any additional property.

My initial implementation is merely a “Remember this decision” checkbox, purely to test this feature, so this will change soon. After discussing with peers both in the design and development fronts, a few ideas on how to manage these permissions came up:

  • Revoking these permissions after a reasonably long period of time – 6 months, or more, or less. That can help preventing applications to have access to a certain window or monitor forever.
  • Improve the indication of ongoing screencasts. GNOME currently shows an orange icon in the titlebar, but we’re considering other alternatives such as notifications when starting and stopping streams, adding a minimum visibility time to the indicator, etc.
  • Add a way to manage these permissions in GNOME Settings, either with new panel under the Privacy group, or a new section inside the Applications panel.

The goal behind these ideas is allow fine control over what is being shared with who, and when, without being annoying. There is a lot to do in this front, but I’m hopeful that most use cases will be covered.

And here it is, a proof-of-concept that this idea works:

Audio access

Another area that is a bit lacking in the portals front is device access. We currently have the Device portal, with which applications can ask for permission to access specific devices such as cameras, speakers, and microphones.

PipeWire has some support for this type of device access in place, but currently only for cameras. We need the same kind of access control for microphones and speakers, specially microphones, and that’s what I’ve been working on during the week.

I initially approached this by proposing a new Audio portal, which was basically a copy-pasted version of the Camera portal. The basics of these portals are:

  1. Applications request audio access, which queries permissions for using audio devices; applications should say whether they want to use speakers, or microphones, or both.
  2. Portals potentially ask you if you would allow that application to access these devices.
  3. Application gets a PipeWire connection with only the allowed devices exposed.

This is exactly how the Camera portal operates. Peer reviews, however, raised one very important point: there are situations where we can’t have separate PipeWire connections for cameras and microphones. So we had to come up with another solution that could satisfy these requirements.

In the end, after some rounds of discussions, we agreed on making this live in the Device portal. This is still under review, and things may change, but it seems to me that we found the best course of action with this last round of discussions.

What does that mean in terms of UX? Not much, I’m afraid, except that people would be able to have greater control over what sandboxed applications can access. You could, for example, prevent a proprietary app you don’t trust from accessing your cameras and microphones.

Modernizing libportal

libportal is a small library written to help application developers interact with portals without having to manually call the D-Bus APIs. Some of these D-Bus APIs can be quite verbose and complicated to deal with, and libportal abstracts that away in a nice and simple way.

libportal, however, currently relies on some tricky dance with headers to work with GTK3, GTK4, and Qt 5 without actually linking to these libraries. Sadly, this means libportal is not introspectable, which prevents it from being used in other languages such as JavaScript or Python.

I took part of this week to work on that. I started by cleaning up the build system (#52); then the next step was to split each toolkit-specific header into its own library that effectively links against the toolkit (#53). This finally allowed adding introspection to libportal and the GTK3 and GTK4 variants (#54). I also used this chance to rewrite libportal’s GTK4 demo app in JavaScript to exercise the language bindings.

At last, libportal’s CI now produces Flatpak bundles for the GTK3, GTK4, and Qt 5 test apps (#55). This should make it easier to ask for people to test new changes, since they can just download a Flatpak bundle and test it.


I think this was a successful week, there was significant progress in these fronts and half of these pull requests were merged already. I was hoping that the other ones could land before the week ends; sadly they didn’t, but we’re very close to that. In special, the screencast session restore work is just in a stone’s throw from landing!

Thanks Endless OS Foundation for allowing me to work on portals as part of the Endless Orange Week program!

Buy Me a Coffee at

November 11, 2021

Record Live Multiple-Location Audio immediately in GNOME Gingerblue 2.0.1

GNOME Gingerblue 2.0.1 is available and builds/runs on GNOME 41 systems such as Fedora Core 35.

It supports immediate, live audio recording in compressed Ogg Vorbis encoded audio files stored in the private $HOME/Music/ directory from the microphone/input line on a computer or remote audio cards through USB connection through PipeWire ( with GStreamer ( on Fedora Core 34 ( as well as XSPF 1.0 playlist stored in the private $HOME/Music/GNOME.xspf playlist of the previous, latest recording.

See the GNOME Gingerblue project ( for screenshots, Fedora Core 35 x86_64 RPM package and GNU autoconf installation package ( for GNOME 41 systems and for the GPLv3 source code in my GNOME Git repository.

Gingerblue music recording session screen. Click “Next” to begin session.

The default name of the musician is extracted from g_get_real_name(). You can edit the name of the musician and then click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip the details).

Type the name of the musical song name. Click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip any of the details).

Type the name of the musical instrument. The default instrument is “Guitar”. Click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip any of the details).

Type the name of the audio line input. The default audio line input is “Mic” ( gst_pipeline_new("record_pipe") in GStreamer). Click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip the details).

Enter the recording label. The default recording label is “GNOME” (Free label). Click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip the details).

Enter the Computer. The default station label is a Fully-Qualified Domain Name (g_get_host_name()) for the local computer. Click “Next” to continue ((or “Back” to start all over again) or “Finish” to skip the details).

Notice the immediate, live recording file. The default immediate, live recording file name falls back to the result of g_strconcat(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC), "/", gtk_entry_get_text(GTK_ENTRY(musician_entry)), "_-_", gtk_entry_get_text(GTK_ENTRY(song_entry)), "_[",g_date_time_format_iso8601 (datestamp),"]",".ogg", NULL) in gingerblue/src/gingerblue-main.c

Click on “Cancel” once in GNOME Gingerblue to stop immediate recording and click on “Cancel” once again to exit the application (or Ctrl-c in the terminal).

The following Multiple-Location Audio Recording XML file [.gingerblue] is created in G_USER_DIRECTORY_MUSIC (usually $HOME/Music/ on American English systems):

<?xml version='1.0' encoding='UTF-8'?>
<gingerblue version='2.0.1'>
<song>Gingerblue Track 0001</song>
<label>GNOME Music</label>

You’ll find the recorded Ogg Vorbis audio files along with the Multiple-Location Audio Recording XML files in g_get_user_special_dir(G_USER_DIRECTORY_MUSIC) (usually $HOME/Music/) on GNOME 41 systems configured in the American English language and can launch the latest recording by opening $HOME/Music/GNOME.xspf:

<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="">

November 10, 2021

Pango Updates

Here is another update on what will appear in Pango 1.50 soon. Quite a few things, as it turns out. (This is a continuation of my last Pango update)

Bidi Improvements

Pango has long claimed to have good support for bidirectional text rendering and editing. But until recently, there were some annoying bugs: you could end up in loops when moving the cursor through mixed-direction text.

The relevant code has been rewritten, closing a very old bug (#157).

Useful Information

Modern fonts can contain a lot of different information – there can be colormaps, and glyphs can be represented not just as splines, but also as pngs or svgs.

For GTK, an important piece of information for each glyph is whether it is colored or monochrome. With the new PangoGlyphVisAttr.is_color field,  GTK no longer needs to poke directly at the font data to find out.

Another case where GTK needed data that wasn’t available through Pango APIs has been closed with pango_font_get_languages(). This data is used in the font chooser filter popup:

Superscripts and subscripts

Superscripts and subscripts now use font metrics information for size changes and baseline shift.  They can also be properly nested, so markup like


yields the expected rendering:

Customizing segmentation

Pango determines word and sentence boundaries according to the Unicode Text Segmentation Spec (TR29). The specification can’t deal with all the complexities of formatted text, so sometimes its results need tailoring, which can now be done with the new word and sentence attributes:

$ pango-segmentation --kind=word --text=\
    "<span segment='word'>1-based</span> index"

|1-based| |index|

Better Markup

Besides the new attributes that have been mentioned, Pango markup now lets you specify many attributes in more natural units.

For example,  you can now say

<span font_size='12.5pt'>

instead of

<span font_size='12800'>

Small Caps

Pango has had the PangoVariant enumeration with its PANGO_VARIANT_SMALL_CAPS value since forever, but it has never done anything. Since we’ve added support for OpenType features (in Pango 1.37), it has been possible to produce Small Caps by using the smcp=1 feature, if the font supports it. Sadly, most fonts don’t.

With the text transformation and font scaling infrastructure now in place, it was easy to emulate Small Caps for fonts that don’t  support this natively:

While at it, we’ve expanded the PangoVariant enumeration to cover all the CSS casing variants, and made the GTK CSS engine use them instead of OpenType features.

Better debugging

Pango ships with a versatile test utility called pango-view, which has options to test many of Pangos layout features. It has recently learned to show more auxiliary information in visual form, like glyph extents, caret positions and slopes:

$ pango-view --text Boxes \ --annotate=glyph,caret,slope


System76: A Case Study on How Not To Collaborate With Upstream

Preface: the following post was written in the context of the events that happened in September. Some time has passed, and I held off on publishing in the hopes we could reach a happy ending with System76. As time has passed, that hope has faded. Attempts to reach out to System76 have not been productive, and I feel we’ve let the impression they’ve given the wider tech community about GNOME sit for far too long.  Some things have changed since I originally wrote the post, so some bits have been removed.

Recently there’s been some heated discussion regarding GNOME’s future. This has led to a lot of fear, uncertainty, and doubt being spread about GNOME, as well as attacks and hostility toward GNOME as a whole and toward individual contributors. This largely started due to the actions of one company’s employees in particular: System76.

This is not the first time System76 has been at the center of a public conflict with the GNOME community, nor is it the first time it was handled poorly. At this point, I no longer feel comfortable working with System76 without some sort of acknowledgment and apology for their poor behavior, and a promise that this won’t happen again.

You might be thinking: what sort of behavior are you talking about? What has System76 done to deserve this treatment? Well, it’s not any one incident – it’s a pattern of behavior that’s repeated multiple times over the past few years. I’ll share incidents I know of from the past, what their behavior has been like in the present, and my own thoughts on the future.

Disclaimer: The following content is my opinion and my opinion alone. I do not speak for GNOME as a whole, only for myself using the observations I’ve made. These observations may be incorrect.

System76 & the LVFS

This is the first major issue that I’m aware of. It will also showcase some of the behaviors that will repeat. Back in 2017 and 2018, Richard Hughes was communicating with System76 to get their firmware working on the LVFS and fwupd. Then in a sudden move, System76 announced their own infrastructure and software for firmware updates. They also told Richard that he should use their infrastructure instead of his. Richard expressed his surprise and disappointment at this move on his blog.

Initially this seems like a case of a communication breakdown alongside a technical disagreement. However, the real unacceptable behavior comes with System76’s response to Richard’s post. They start by sharing part of the correspondence between both parties. I believe the communication breakdown occurs in this section, where Richard explains why their current methods will not work for the LVFS. Then they go further and imply that the LVFS is handing away private data:

Be wary

We had intended to use LVFS in the future, but that is no longer the case; there are too many problems with the project.
If you want to use LVFS, disable the data collection. There’s no need for it. Understand that the first instinct of the project leaders was to unnecessarily opt-in data collection from user installations.
Understand that you’re electing for your distribution to communicate with third party servers. The more servers your distribution communicates with out of the box (especially as root), the more surface area there is for vulnerabilities. Heavily scrutinize any addition of a third-party server for updates.
Understand that if you are a company specializing in Linux-only devices and considering using LVFS, you are handing your private sales data to LVFS. We suggest hosting LVFS on your own servers.

System76 employees continued to spread this idea into early 2019 online, likely prompting a post from Richard to refute their claims. Later on the LVFS team put in work to support the use case of System76 so that vendors could use the LVFS without any sort of reporting. Soon after System76 began using the LVFS and fwupd without any fanfare or retraction of their prior statements.

Now, before we move on to the next incident there are a few key takeaways that should be kept in mind. These will be repeating behaviors:

  • System76 blindsided upstream with criticism and advertised their own in-house solutions.
  • System76 spread misinformation for months after not getting their way.

System76, Ubuntu, & Bug Fixes

In 2019 Sebastien Bacher from Canonical shared a post about System76’s treatment of upstreams – in this case, both Ubuntu and GNOME:

I saw a few cases of those situations happening recently

  1. System76 / Pop! OS finds a bug (where ‘find’ often means that they confirm an existing upstream bug is impacting their OS version)

  2. They write a patch or workaround, include it in their package but don’t upstream the change/fix (or just drop a .patch labelled as workaround in a comment rather than submitting it for proper review)

  3. Later-on they start commenting on the upstream (Ubuntu, GNOME, …) bugs trackers, pointing out to users that the issue has been addressed in Pop! OS, advertising how they care about users and that’s why they got the problem solved in their OSSystem76 / Pop! OS team, while you should be proud of the work you do for you users I think you are going the wrong way there. Working on fixes and including them early in your product is one thing, not upstreaming those fixes and using that for marketing you as better than your upstreams is a risky game.

At this point in time System76 had already established a pattern of behavior where instead of working with upstreams, they take the opportunity to market themselves and their solutions.

System76, GNOME Shell, & Tiling

This is a fairly straightforward incident, but it’s one that’s concerning as it involves a misleading narrative that System76 has been running for two years. In late 2019 System76 began work on an extension that allowed for i3-like tiling in GNOME. When an upstream designer reached out for collaboration on tiling upstream Jeremy Soller, principal engineer at System76, refused to work with him. Now, the true problematic part is that System76 has continued to repeat that upstream is not interested in tiling for the past few years. A few examples:

I’m afraid GNOME does not want to support quarter tiling windows.

It would be nice if GNOME wanted to have tiling window functionalities integrated into their desktop. But the truth is that they don’t.

– Michael Murphy

This is untrue, and the linked tweet shows that. Yet System76 employees keep repeating the idea that we don’t want tiling.

System76 & GNOME 40

Soon after we announced the direction for GNOME 40, Jeremy Soller surprised us with the statement that System76 did not “consent” to the new GNOME Shell design. He also claimed that feedback from their designer was dismissed. GNOME 40’s design was largely discussed on design calls that were not recorded, which makes this claim hard to verify. It’s also hard to address because while there may not have been any intent of harm, their designer may still have felt unheard or hurt – causing pain doesn’t require intending to cause pain. So, I will focus on what I can address.

Jeremy claims and has continued to claim that their proposals were rejected. That is not the case. The reality is that System76 had a designer involved for roughly one year of the three-year design process. Their designer participated in design calls and discussions with the design team, and her role was focused on the UX research itself (defining research questions, running interviews, parsing the results, etc.). There were never any mockups or concrete proposals from System76 during the design process. I’ve also heard that System76 conducted a survey and did not share the results with the team working on the 40 design.

To my understanding, the one time System76 made a proposal was at the very end of the design process during a meeting the design team had with stakeholders. There they pitched COSMIC and didn’t receive the feedback they wanted at the time. Perhaps this is what they were referring to, but at this point it was too late for any proposed changes to be made.

The behavior of the company repeated the initial pattern. When System76 did not get their way with their design, they started sharing misinformation. This particular instance of misinformation has been incredibly harmful for GNOME’s reputation. I’ve often seen the first two tweets brought up as “proof” that GNOME doesn’t want to work with downstreams or listen to users. In reality they are an example of how System76 acts toward others when they don’t get what they want.

System76, libadwaita, & GTK Themes

Now we’ve arrived at the latest incident. I’ve been involved in this one more than the others, and I have watched it unfold from the beginning to the present. I’ve decided to go into further depth, breaking down the most important parts by the day.

September 1

This all started at the beginning of the month, when Jeremy noticed that we override gtk-theme-name and set the Adwaita stylesheet for apps using libadwaita. He shared this on his Twitter. Jeremy then posted misinformation about dark style support. Why is this misinformation?

Jeremy then very publicly called out GNOME as a whole – something that a few people interpreted as a threatening ultimatum. Jeremy was soon made aware that libadwaita is a WIP library and that there is still time to work with us regarding vendor theming. Yet, this was not the end for him posting about this on Twitter. Federico wrote and linked a blog post detailing our goals and what can be done to help resolve the situation, with help from Alexander.

September 2

Jeremy replied to Federico’s blog post with a quote from Alexander. This quote was taken out of context and assumed a different meaning, a meaning that is convenient to the narrative that was building up until this point. In reality Alexander was only explaining the status of things as they are in the present, and what the current goals are. Jeremy also quoted an old blog post from Adrien from before libadwaita existed. Do note that in said blog post Adrien went over the possibility for vendors and users to style within this framework despite his personal opinion. When asked to work with us upstream instead of going to Twitter, Jeremy refused – he would not work with us unless he got exactly what he wanted.

September 3

Jeremy stated that System76 was excited to use libadwaita. The very next day Michael Murphy, one of their core team members, contradicted this saying:

I wasn’t even aware that libadwaita even existed when Jeremy started tweeting about it. I don’t think any of us knew about libadwaita and it’s goals.

Michael also made the claim that we don’t want to collaborate when at this point we’d asked them to collaborate instead of pointing fingers multiple times, as shown in the prior archives.

Finally after three days an opportunity appeared to talk and I took it. Below is a summary of the conversation, of which I also kept screenshots of:

  • Jeremy sent the proposal he mentioned
  • I read through the proposal, then gave my feedback. I said I thought that it was interesting, but I also gave a detailed breakdown on why I didn’t think it would be implemented, including the following points:
    • The GTK team wants to put platform libraries in charge of platform styling
    • Most app developers I know (even those who hadn’t signed the well-known open letter at the time) are not happy with the state of arbitrarily applied stylesheet changes
    • Many app developers don’t want to take on the burden of supporting multiple stylesheets
    • Other stylesheets may not be up-to-date implementing Adwaita’s API, re-creating the existing problem
    • “Hard coding” Adwaita is part of our work toward things like a recoloring API for apps and a properly supported dark style preference
  • I expressed there was room to discuss distro branding of default apps, but not a will to bring back arbitrary stylesheet support.
  • I explained the concerns of app developers regarding custom widgets
  • Jeremy expressed his fear about replacing default apps because of these changes
  • I explained that it would be better to work with us on a non-stylesheet method for vendor branding
  • As the conversation wrapped up, I asked that in the future they take issues to GitLab before going to Twitter. I also expressed the pain they had caused and subtly tried to tell them that some developers would want an apology before working with them.
  • Jeremy did not acknowledge the first part and refused to make an apology.

Jeremy then opened the GTK issue, and was redirected to libadwaita. After some discussion on the libadwaita issue he opened a merge request.

September 6

After a lot of discussion on the issue and the MR, the merge request was closed once was made clear that:

  • Third-party and core application developers largely did not want the proposed solution.
  • Multiple developers, including myself, were not comfortable working with System76 without an acknowledgment of their behavior and an apology.

The Aftermath

Once again, System76 employees decided to spread misinformation and complaints on Twitter instead of making an attempt to work with us upstream on their issues. It’s reasonable to assume that they learned about libadwaita and began posting ultimatums on Twitter on the same day. Once again, they have spread misinformation about the project and our goals.

In the weeks since the MR was closed, a few different employees have continued spreading misinformation on Twitter about the situation. Their existing misinformation has already caused ripples within the community, getting many people stirred up and making claims like GTK4 being only for GNOME, or GNOME wanting to be the “only” desktop on Linux because of the approach to themes we have. We’ve had a lot of vitriol thrown our way.

System76’s words also have reach beyond the Linux ecosystem, and large content creators are only hearing their side of the story.

There have been additional attempts by others to reach out diplomatically, but those seem to have fallen through. Just the other day the CEO claimed that there were no technical or quality reasons for the change, heavily implying that it was done just to hurt downstreams.

With all of the misinformation and the consequences of said misinformation, and with the refusal to listen or engage with our own needs, I do not feel like it is worth my time to engage with System76. Until they recognize their behavior for what it is and make a commitment to stop it, I will not be doing free labor for them. If they can do that, I would be happy to work with them on resolving their issues.

Epilogue: What’s Really Going On With Theming?

Now that we’ve addressed System76’s hand in stirring up controversy, let me clarify what the situation actually is, and what vendors and other desktop environments can do moving forward.

Terms To Know

This is a high-level overview, but since we’re talking about technical details there will be some terms to know:

  • Stylesheet: Compilations of CSS styles for widgets. “Themes” as we know them are stylesheets, e.g. Adwaita, Arc, Pop, or Yaru.
  • Platform library: A platform library is a collection of widgets, useful functions, constants, and styles for apps on a platform to use. Granite and libadwaita are examples of platform libraries.
  • Visual API: An API or “contract” for the visuals of an application.

GTK & Theming

Let me clear this up immediately: nothing about the theming infrastructure has changed in GTK itself between GTK3 and GTK4. The only changes are listed in the migration guide, and are mostly removed or replaced GTK-specific CSS functions. All the changes that have caused controversy have to do with libadwaita.

You might have seen some people bring up two particular issues as counterpoints to this:

What do those two issues mean? Well, it means that the GTK team thinks that these particular issues are better handled in platform libraries. This does not mean only libadwaita or only libgranite, nor does it mean that GTK-based applications would no longer be able to have a dark theme or different themes. The platform library would be responsible for handling those items in accordance with their goals.

Desktops X, Y, and Z could work together and create a libxyz that loads different stylesheets just like GTK does now, or they could each make their own platform library that handles their specific needs. The proposed GTK changes would simply put the choice in the hands of each platform.

GNOME, libadwaita, & Theming

In April when Adwaita was moved to libadwaita, the controversial change to override gtk-theme-name was made. The commit explains the reasoning for this particular change:

Ensure Adwaita is loaded instead of the default, take care to load Adwaita-hc when HighContrast is selected system-wide.

Why do we want to ensure libadwaita is loaded at all instead of other stylesheets? It comes down to two key things:

  • Adwaita for platform integration
  • Adwaita as a visual API

In GNOME, we want to have a stable platform for app developers to use to build new, useful, and well-integrated apps. Part of having a stable platform is having a stable visual API. The GNOME Human Interface Guidelines lay out the ideas and goals for apps targeting GNOME to follow, and libadwaita helps developers implement those ideas. The stylesheet itself is part of keeping interfaces in line with the HIG.

With libadwaita the widgets, the stylesheet, and the HIG are all tied together in one easy to use package. With each new version of GNOME we can iterate on them all at once so it will be easier for developers to keep up with different changes. This is in contrast to the current state of development where app developers need to implement their own versions of common patterns or copy-paste them from other apps.

Adwaita being used this way means that apps also need to rely on it being there, and apps will make assumptions based on that. For example, we want it to be very simple for app developers to recolor widgets. Before if apps wanted to do this, they needed to depend on sassc and libsass and occasionally regenerate the themes on-the-fly. Now all they need to do is set the background-color on a widget.

This new method works perfectly with the new Adwaita stylesheet. Here we have a simple Color Picker app:

Color Picker using the Adwaita Stylesheet
Color Picker using the Adwaita Stylesheet

Recoloring still works well with stylesheets that are based on Adwaita, like Yaru:

Color Picker using the Yaru Stylesheet
Color Picker using the Yaru Stylesheet

Things begin breaking down with stylesheets that deviate too far from Adwaita:

Color Picker using the Pop stylesheet
Color Picker using the Pop stylesheet

Without enforcing Adwaita as the stylesheet apps may not look correct or in some cases may not function at all. For a more in-depth breakdown of the problem with more examples, see Tobias Bernard’s post “Restyling apps at scale.”

November 09, 2021

Typesetting a whole book part III, the analog edition

In earlier editions (part 1, part 2) we looked at typesetting a full book to a PDF file. This is fun and all, but until you actually hold a physical copy in your hands you don't really know how good the end result is. Puddings, eatings and all that.

So I decided to examine how would you go about printing and binding an entire book. For text I used P. G. Wodehouse's The Inimitable Jeeves. It has roughly 220 pages which is a good amount for perfect binding. Typesetting it in LibreOffice only took a few hours. To make things even simpler I used only one font, the Palatino lookalike P052 that comes packaged with Ghostscript. As the Jeeves stories take place in the 1920s something like Century would have been more period accurate but we'll have to work with what we got.

The only printer I had access to was an A4 laser printer that could only print on one side of the page. Thus to keep things as simple as possible the page size became A5, which is easy to obtain by folding A4 paper in half. None of the printer dialogs seemed to do the imposition I needed (single page saddle fold, basically) so I had to convert the A5 originals to A4 printable sheets with a custom Python script (using PyPDF2)

Printing turned out to have its complications as it always does. The printer did not have Cups drivers so I had to print using Windows. I used Acrobat Reader as it should be the gold standard of PDF manipulation. The original documents were in A4 and the printer only had A4 paper capability. And yet, Acrobat insisted on compositing the pages to 8.5x11 and then printing them distorted because AMERICA! After going through every menu (especially the hidden ones) I managed to make it stop doing that. Then I printed the pages by first printing the odd pages, taking the output, adding it back in the paper tray and then printing the even pages. If you want to try the same, note that for the latter batch you need to print the pages in reverse order.

Do this in small batches. I did and was happy that I did, because every now and then I botched it up and had to redo the pages. But eventually I did prevail.

Then you fold each page in half and stack them to get the text block.

Fasten them together using clips and cut out a piece of cardboard for the cover.

Then glue the spine together, first the pages and then bookbinder's mull on top of that. This step is a bit involved, but Youtube has several videos that show you how to do it.

Then glue on the cover and place the whole thing into a press. Low tech solutions work surprisingly well. (Trivia: this box contains 25 Meson mugs.)

Leave like this overnight. In the morning the book is ready for trimming.

Sadly I don't have a heavy duty paper guillotine so the edges remained a bit rough. Other than that the end result looks pretty nice.

All in all this was a surprisingly simple and fun project. The binding process was so simple that even a craft-noob like myself could get it done without too much trouble. I did make a practice run with some empty pages, though. The clips were a bit fiddly, having a proper press or a woodworking bench would have made the end result smoother.

November 08, 2021

A good next step

Some months back (in March to be precise), I completed an amazing outreachy internship at GNOME. The months after that have been full of activity. In July I had the opportunity of presenting at the first open source conference I ever attended- GNOME’s Annual Developer Conference (GUADEC). Here I shared the contributions I made to the community by working on GNOME’s JavaScript Debugger where I wrote JavaScript code, improved on some areas of the documentation and helped new contributors find their way when I could.

Fast-foward to a couple of days later, I was given the opportunity to be part of another enriching open source internship (Coding Experience) at Igalia. I first came across this opportunity through one of my outreachy blog posts “Seeking for career opportunities“. Special thanks to Paul Wise for leaving the link to FOSSjobs website in his/her/their comment ( and, Philip (my outreachy mentor) and those on the FOSSjobs gnome matrix channel for sharing this opportunity.

At Igalia I work on improving on the user experience of Cog and other WPE-Webkit related tasks. I must and have to say that working at Igalia is very challenging and also interesting. Work at Igalia is exciting to me because of the team and the work culture(there is socialization).I work with amazing people at Igalia-my mentor Chris Lord (who is always very helpful), Adrian(on Cog), just to mention a few. If you agree with me on the need to work on your communication skills to easily contribute to open source, then those skills come in handy at Igalia since we work on open source projects. I am glad to have been able to find time after this long to blog again and I will continue sharing my experiences consistently to help others facing challenges in open source and struggling with learning how to code and staying in the field.

I like to end here so this blog post doesn’t become lengthy. In the weeks that follow, I will be sharing information about my experience at Igalia(work, challenges, improvements since my last internship). This series is called “My Open Source Journey”. Thanks for reading and please keep reading, liking and sharing.

November 07, 2021

Endless Orange Week: Hack content creators platform

This week (Nov 8 – 12) I am participating in Endless Orange Week, a program where the entire Endless team engages in projects designed to grow our collective learning related to our skills, work and mission.

We propose a project, that could be anything, and then work during the whole week, without distraction. I've choosed to work on the Hack project, that's a really nice project that needs some love, because since the past year, we have other priorities, so there's no time to improve the Hack app.

The project: Hack content creators platform

The Hack application is a "Quest" launcher, and each Quest is an interactive learning experience, a guided introduction to some technology or topic.

Quests are just python scripts, with a simple library to expose messages to the user, ask questions, or wait for desktop events, like launch an application, focus an application, etc. And all these Quests are inside the application, and are created by the Hack team and released with a new Hack app flatpak.

The main idea of the project is to provide a simple Quest editor to allow any Hack user to create and share their our Quests.

To have this Hack content creators platform we'll need:

  1. To simplify the way we create Quests, instead of a python script, we'll uses a Domain Specific Language, called Ink. We started to work on this, but we never ended the support.
  2. To create the interface to be able to import and export custom Quests, that could be zip bundles, with the Ink script and some images.
  3. To create the interface to write the actual Quests and save or bundle.
  4. Create some introductory Quest to explain "how to create your own Quests!".
  5. Create an character editor, to be able to "design" new characters for Quests.

The Quest editor

The Quests will be written using the Ink language, and there's something done before. The first idea is to just provide a text editor and some helpful information about the format, and maybe a button to validate. But if there's time we can use something advanced or even integrate the Inky editor.

The Character editor

Each Quest has a main character, and we've five in the Hack app right now, but it could be great to be able to define new ones for custom Quests. That's the idea of this part of the project.

The initial idea is to have a library of character parts to combine, and the editor will allow the user to combine this parts and maybe change colors, to be able to create unique characters for your Quests.

The Team

I'm not working alone in this "side" project during the Endless Orange Week, Simon Schampijer and Joana Filizola will be working on this too, so this is a big task but we've a great team. Let's see how far we are able to go during just one week.

The impact

This is just a project to try to keep alive more time the Hack application, without a lot of effort or a whole team behind it. We are not able to put more content there periodically, so if there's a way to create new content easily and (maybe in the future) a way to publish, it'll be possible to create a community around the project.

And we have also new possibilities, in the near future, we can add some Hack content to the Endless Key, and using the Ink language, so this editor could help to bring more content there easily.

And the final piece, the Character editor, could be an independent application, a nice simple application that could be used to create your character for your profile photo, or to generate random character pics.

November 05, 2021

Editing a background check policy

We’re implementing a background check policy for some roles at Tidelift, because we see security-critical information from our customers. Our background check provider (who I won’t name, because this isn’t about them) has a template policy that is pretty good! But the rest of their product has a very high standard for UX—their designers generally do not settle for pretty good.

The red pen bleeds during term paper season... (11/52)
“The red pen bleeds during term paper season… (11/52)” by Rodger_Evans is licensed under CC BY-ND 2.0

Since I know they care, and since employment documents (especially ones that relate to bias) are an important way Tidelift communicate about our company values, I took the time to send them my changes. Having done that work I figured I’d make it a blog post too :) Some notes:

Consistency between software and legal documents

A legal document is, in many cases, a part of a company’s user experience. As such, it needs to be vetted for consistency with the rest of the software, just as you’d vet any other part of the UX.

This is hard, and easy to screw up, because let’s face it—who likes reading and re-reading legal documents? Not even lawyers, if we’re being honest. This particular document screws this up in two ways.

First, the tool (very correctly!) encourages companies not to do a background check for every position, since that introduces a significant bias against people who may have been rehabilitated and should have a fair chance at employment. But the legal document says very plainly that “all offers of employment are contingent on … a background check” (emphasis mine). The legal terms must be brought into alignment with the software’s reality.

Similarly, one of the benefits of this tool is that it takes care of the paperwork for you—without pens and paper. And yet the legal document says that a “signed, written consent will be obtained … in compliance with … law”. Now, good American lawyers know that under the E-SIGN Act of 2000, lots of digital things are “signatures” for the purposes of American law, but most people don’t know that. Good drafting will avoid confusing these non-lawyers by simply saying the consent will be “explicit” and recorded by the service.

Multi-clause sentences and checklists

This is not always the case, but many sentences in legal documents would benefit from being broken up into bullet points, so that each criteria is easier to follow or even treat as a checklist. Consider the difference between:

A decision to withdraw an offer of employment will be made only after conducting an individualized inquiry into whether the information obtained is job-related and a decision to withdraw an offer is consistent with business necessity.  

Original policy


A decision to withdraw an offer of employment will be made only after conducting an individualized inquiry into whether:
– the information obtained through the reference check is relevant to the nature and performance of the role; and
– withdrawal is consistent with the business’s needs.

My revisions

This sounds sort of trivial, but clear writing really can help you spot errors. In this case, breaking up a sentence into bullet points made me notice that the document was inconsistent—an important anti-bias process step was listed in another section, but silently dropped in this list. So someone skimming just the one section of the policy might get it very, very wrong. (Programmers reading this will be nodding along right now—this is debugging by using better formatting to ease code inspection.)

Similarly, where a process is spread across multiple paragraphs, consider using numbered bullet points to emphasize the checklist-like nature of the process and improve the ability to discuss. Much easier to ask “did you do step 4” than “did you do the second clause of the third sentence”.

Q&A style

I continue to believe that many legal documents should at least be edited (not necessarily finalized) in a Q&A style—in other words, changing each section header to a question, and making sure the section actually answers the question. I talked a bit more about that in this post about doing it for a draft of the Mozilla Public License.

In all cases, doing this forces you to make sure that each section has a focused, clear purpose. For example, in the original draft of this policy, there was a section titled “Evaluating Background Check Results”. Revising that into a question (“How will we evaluate the results of the background check?”) helped me realize that one sentence was about a related, but different topic—privacy. Breaking that out into its own privacy section both clarifying the original section and allowed the new section to respond more forcefully to employee concerns about confidentiality.

In the best cases the Q&A framing can really help understanding for non-legal (and even legal) readers. In this case, I found that a well-placed question helped differentiate “why the company does it” from “how the company does it”—which was muddled in the original draft, and important to aid understanding of a tricky anti-bias concept.

Be careful about definitions

Last but not least—you can often tell when a document has suffered from copy/paste when it uses a defined term exactly once, rather than multiple times. Not only does this give you the opportunity to remove the defined term (in this case “Company”) but it also reminds you to give extra focus on the ways that term is used, since it is likely to have copy/paste problems. (In this particular case the stray “Company” thankfully didn’t point to anything worse than jarring word choice—but at least it was easy to eliminate!)

Interested in learning more? Come work with me!

I can’t promise we approach everything with this level of detail; we’re still a small startup and one of the ways we balance the tension between pragmatism and idealism is to Just Get Things Done. But I’m also proud of the way so many of us think through the things some other companies get wrong. If that sounds like fun, we’re hiring!

November 02, 2021

Announcing Solanum

A little while ago I released version 3.0.0 of my app Solanum. When doing the release, I realized something: I never announced the app here. Woops. So, here is the announcement.

Screenshot of Solanum's Initial State
Screenshot of Solanum’s initial state

Solanum is an app that lets you work in timed sessions with regular breaks, based on the Pomodoro Technique. By default work sessions are 25 minutes, short breaks are 5 minutes, and long breaks are 15 minutes, and you work 4 sessions before a long break. These defaults can be configured in the Preferences window from Solanum 3.0.0 onward.

Screenshot of Solanum's Preferences window
Screenshot of Solanum’s Preferences window

When GNOME 42 releases with the new dark style preference, Solanum will support it out of the box.

If you’re looking for a pomodoro app to help you with time management, you can find Solanum on Flathub.

Download Solanum from Flathub

If you find Solanum useful, please consider sponsoring me on GitHub or Patreon 🙂

November 01, 2021

GtkSourceView is getting a VIM emulator

One of the things we spent some time on early with Builder was creating built-in VIM emulation. Originally it was written in C, but then got converted to this nasty CSS for a couple of reasons. First, that keybindings in GTK 3 were largely done with CSS due to how we moved away from gtkrc in the 2.x to 3.x migration. And beyond that, CSS was the one way that’d let us share a lot of keybinding code between shortcut themes so we could support emacs, vim, sublime, etc.

One thing that didn’t work well was VIM’s “modes”. People often think of VIM as a single state (normal, insert, replace, visual, etc). In reality it’s a stack of them. Just look at the following state diagram if you’re going to actually to match things somewhat accurately. Doing this in a manner that would allow sharing between shortcut themes in Builder was simply beyond the amount of time we had to spend on it given all the other features that needed to land.

As we start to port Builder to GTK 4 this is one area that worried me the most. GTK 4 has improved so much in this regard, but we also have this giant legacy ball of tar. So recently I started looking at a new approach to providing VIM emulation in a way that would let us improve the quality and extend it’s reach.

The experiment I’ve come up with so far is to build upon GtkIMContext. This allows us to filter key presses and releases from the widget and do so in a way that allows layered input-methods (ibus, emoji, unicode, etc) to continue working.

One question that will always pop up is “why not use $some_form_of_vim” and bridge that. The answer is pretty simple, it’s a huge dependency to take on as a library. It also requires synchronizing text and across boundaries which may not even be capable in places where GtkSourceView is used. Beyond that there are a large number of impedance mismatches in how things work from a generic standpoint.

But fear not, we can actually do pretty good for the most commonly used things. We can probably even implement registers (some of it is there already) and marks for people that want it. Some day I imagine we’ll land code folding upstream, so even supporting that might be possible.

My goal is to get this into 5.4 so that we have it well tested by time Builder lands a GTK 4 port. The exposed API is very small and just the single object GtkSourceVimIMContext. By keeping the implementation private we allow ourselves quite a bit of flexibility to adapt how we implement things if and when that is necessary.

I haven’t decided yet if we would want to have a property like GtkSourceView:enable-vim to simply enable it. I’m hesitant to do that because supporting it correctly can still be somewhat involved. You need presentation of a command bar and active command state for example. To do that requires application coordination anyway, so perhaps requiring the application to add a GtkEventControllerKey with the GtkSourceVimIMContext is the better option.

If you’d like to help on any of this, feel free to send me MRs. Everyone has slightly different usage of VIM and there are lots of missing spots you can fill in.

git clone
cd gtksourceview
git checkout wip/chergert/vim
meson build
cd build
hack and repeat

As always, I usually leave screenshots and videos for people here if you can handle my other mindless dribble.

October 31, 2021

Pixel Inktober

For the first time ever I actually finished a monthly challenge.

0. Pixel Inktober 1. Crystal 2. Suit 3. Vessel 4. Knot 5. Raven 6. Spirit 7. Fan 8. Watch 9. Pressure 10. Pick 11. Sour 12. Stuck 13. Roof 14. Tick 15. Helmet 16. Compass 17. Collide 18. Moon 19. Loop 20. Sprout 21. Fuzzy 22. Open 23. Leak 24. Extinct 25. Splat 26. Connect 27. Spark 28. Crispy 29. Patch 30. Slither 31. Risk

Off to the Nodevember!

October 30, 2021

scikit-survival 0.16 released

I am proud to announce the release if version 0.16.0 of scikit-survival, The biggest improvement in this release is that you can now change the evaluation metric that is used in estimators’ score method. This is particular useful for hyper-parameter optimization using scikit-learn’s GridSearchCV. You can now use as_concordance_index_ipcw_scorer, as_cumulative_dynamic_auc_scorer, or as_integrated_brier_score_scorer to adjust the score method to your needs. The example below illustrates how to use these in practice.

For a full list of changes in scikit-survival 0.16.0, please see the release notes.


Pre-built conda packages are available for Linux, macOS, and Windows via

 conda install -c sebp scikit-survival

Alternatively, scikit-survival can be installed from source following these instructions.

Hyper-Parameter Optimization with Alternative Metrics

The code below is also available as a notebook and can directly be executed by clicking

In this example, we are going to use the German Breast Cancer Study Group 2 dataset. We want to fit a Random Survival Forest and optimize it’s max_depth hyper-parameter using scikit-learn’s GridSearchCV.

Let’s begin by loading the data.

import numpy as np
from sksurv.datasets import load_gbsg2
from sksurv.preprocessing import encode_categorical
gbsg_X, gbsg_y = load_gbsg2()
gbsg_X = encode_categorical(gbsg_X)
lower, upper = np.percentile(gbsg_y["time"], [10, 90])
gbsg_times = np.arange(lower, upper + 1)

Next, we create an instance of Random Survival Forest.

from sksurv.ensemble import RandomSurvivalForest
rsf_gbsg = RandomSurvivalForest(random_state=1)

We define that we want to evaluate the performance of each hyper-parameter configuration by 3-fold cross-validation.

from sklearn.model_selection import KFold
cv = KFold(n_splits=3, shuffle=True, random_state=1)

Next, we define the set of hyper-parameters to evaluate. Here, we search for the best value for max_depth between 1 and 10 (excluding). Note that we have to prefix max_depth with estimator__, because we are going to wrap the actual RandomSurvivalForest instance with one of the classes above.

cv_param_grid = {
"estimator__max_depth": np.arange(1, 10, dtype=int),

Now, we can put all the pieces together and start searching for the best hyper-parameters that maximize concordance_index_ipcw.

from sklearn.model_selection import GridSearchCV
from sksurv.metrics import as_concordance_index_ipcw_scorer
gcv_cindex = GridSearchCV(
as_concordance_index_ipcw_scorer(rsf_gbsg, tau=gbsg_times[-1]),
).fit(gbsg_X, gbsg_y)

The same process applies when optimizing hyper-parameters to maximize cumulative_dynamic_auc.

from sksurv.metrics import as_cumulative_dynamic_auc_scorer
gcv_iauc = GridSearchCV(
as_cumulative_dynamic_auc_scorer(rsf_gbsg, times=gbsg_times),
).fit(gbsg_X, gbsg_y)

While as_concordance_index_ipcw_scorer and as_cumulative_dynamic_auc_scorer can be used with any estimator, as_integrated_brier_score_scorer is only available for estimators that provide the predict_survival_function method, which includes RandomSurvivalForest. If available, hyper-parameters that maximize the negative intergrated time-dependent Brier score will be selected, because a lower Brier score indicates better performance.

from sksurv.metrics import as_integrated_brier_score_scorer
gcv_ibs = GridSearchCV(
as_integrated_brier_score_scorer(rsf_gbsg, times=gbsg_times),
).fit(gbsg_X, gbsg_y)

Finally, we can visualize the results of the grid search and compare the best performing hyper-parameter configurations (marked with a red dot).

import matplotlib.pyplot as plt
def plot_grid_search_results(gcv, ax, name):
_, axs = plt.subplots(3, 1, figsize=(6, 6), sharex=True)
plot_grid_search_results(gcv_cindex, axs[0], "c-index")
plot_grid_search_results(gcv_iauc, axs[1], "iAUC")
plot_grid_search_results(gcv_ibs, axs[2], "$-$IBS")
Results of hyper-parameter optimization.

Results of hyper-parameter optimization.

When optimizing for the concordance index, a high maximum depth works best, whereas the other metrics are best when choosing a maximum depth of 5 and 6, respectively.

October 26, 2021

GNOME Gingerblue 2.0.1 Recording Software supports XSPF 1.0

GNOME Gingerblue 2.0.1 is Free Recording Software for GNOME.

In the 2.0.1 release I have added support for XSPF 1.0 from

If you launch the software, a immediate recording is stored into the
$HOME/Music folder along with the playlist in $HOME/Music/GNOME.xspf

You can download the source code from for the software and read more about the free playlist standard at

Distribution packages are available from
Fedora Core 34
Fedora Core 35
Ubuntu 21.04 LTS

October 25, 2021

2.5 years of Oculus Rift

Once again time has passed, and another update on Oculus Rift support feels due! As always, it feels like I’ve been busy with work and not found enough time for Rift CV1 hacking. Nevertheless, looking back over the history since I last wrote, there’s quite a lot to tell!

In general, the controller tracking is now really good most of the time. Like, wildly-swing-your-arms-and-not-lose-track levels (most of the time). The problems I’m hunting now are intermittent and hard to identify in the moment while using the headset – hence my enthusiasm over the last updates for implementing stream recording and a simulation setup. I’ll get back to that.

Outlier Detection

Since I last wrote, the tracking improvements have mostly come from identifying and rejecting incorrect measurements. That is, if I have 2 sensors active and 1 sensor says the left controller is in one place, but the 2nd sensor says it’s somewhere else, we’ll reject one of those – choosing the pose that best matches what we already know about the controller. The last known position, the gravity direction the IMU is detecting, and the last known orientation. The tracker will now also reject observations for a time if (for example) the reported orientation is outside the range we expect. The IMU gyroscope can track the orientation of a device for quite a while, so can be relied on to identify strong pose priors once we’ve integrated a few camera observations to get the yaw correct.

It works really well, but I think improving this area is still where most future refinements will come. That and avoiding incorrect pose extractions in the first place.

Plot of headset tracking – orientation and position

The above plot is a sample of headset tracking, showing the extracted poses from the computer vision vs the pose priors / tracking from the Kalman filter. As you can see, there are excursions in both position and orientation detected from the video, but these are largely ignored by the filter, producing a steadier result.

Left Touch controller tracking – orientation and position

This plot shows the left controller being tracked during a Beat Saber session. The controller tracking plot is quite different, because controllers move a lot more than the headset, and have fewer LEDs to track against. There are larger gaps here in the timeline while the vision re-acquires the device – and in those gaps you can see the Kalman filter interpolating using IMU input only (sometimes well, sometimes less so).

Improved Pose Priors

Another nice thing I did is changes in the way the search for a tracked device is made in a video frame. Before starting looking for a particular device it always now gets the latest estimate of the previous device position from the fusion filter. Previously, it would use the estimate of the device pose as it was when the camera exposure happened – but between then and the moment we start analysis more IMU observations and other camera observations might arrive and be integrated into the filter, which will have updated the estimate of where the device was in the frame.

This is the bit where I think the Kalman filter is particularly clever: Estimates of the device position at an earlier or later exposure can improve and refine the filter’s estimate of where the device was when the camera captured the frame we’re currently analysing! So clever. That mechanism (lagged state tracking) is what allows the filter to integrate past tracking observations once the analysis is done – so even if the video frame search take 150ms (for example), it will correct the filter’s estimate of where the device was 150ms in the past, which ripples through and corrects the estimate of where the device is now.

LED visibility model

To improve the identification of devices better, I measured the actual angle from which LEDs are visible (about 75 degrees off axis) and measured the size. The pose matching now has a better idea of which LEDs should be visible for a proposed orientation and what pixel size we expect them to have at a particular distance.

Better Smoothing

I fixed a bug in the output pose smoothing filter where it would glitch as you turned completely around and crossed the point where the angle jumps from +pi to -pi or vice versa.

Improved Display Distortion Correction

I got a wide-angle hi-res webcam and took photos of a checkerboard pattern through the lens of my headset, then used OpenCV and panotools to calculate new distortion and chromatic aberration parameters for the display. For me, this has greatly improved. I’m waiting to hear if that’s true for everyone, or if I’ve just fixed it for my headset.

Persistent Config Cache

Config blocks! A long time ago, I prototyped code to create a persistent OpenHMD configuration file store in ~/.config/openhmd. The rift-kalman-filter branch now uses that to store the configuration blocks that it reads from the controllers. The first time a controller is seen, it will load the JSON calibration block as before, but it will now store it in that directory – removing a multiple second radio read process on every subsequent startup.

Persistent Room Configuration

To go along with that, I have an experimental rift-room-config branch that creates a rift-room-config.json file and stores the camera positions after the first startup. I haven’t pushed that to the rift-kalman-filter branch yet, because I’m a bit worried it’ll cause surprising problems for people. If the initial estimate of the headset pose is wrong, the code will back-project the wrong positions for the cameras, which will get written to the file and cause every subsequent run of OpenHMD to generate bad tracking until the file is removed. The goal is to have a loop that monitors whether the camera positions seem stable based on the tracking reports, and to use averaging and resetting to correct them if not – or at least to warn the user that they should re-run some (non-existent) setup utility.

Video Capture + Processing

The final big ticket item was a rewrite of how the USB video frame capture thread collects pixels and passes them to the analysis threads. This now does less work in the USB thread, so misses fewer frames, and also I made it so that every frame is now searched for LEDs and blob identities tracked with motion vectors, even when no further analysis will be done on that frame. That means that when we’re running late, it better preserves LED blob identities until the analysis threads can catch up – increasing the chances of having known LEDs to directly find device positions and avoid searching. This rewrite also opened up a path to easily support JPEG decode – which is needed to support Rift Sensors connected on USB 2.0 ports.

Session Simulator

I mentioned the recording simulator continues to progress. Since the tracking problems are now getting really tricky to figure out, this tool is becoming increasingly important. So far, I have code in OpenHMD to record all video and tracking data to a .mkv file. Then, there’s a simulator tool that loads those recordings. Currently it is capable of extracting the data back out of the recording, parsing the JSON and decoding the video, and presenting it to a partially implemented simulator that then runs the same blob analysis and tracking OpenHMD does. The end goal is a Godot based visualiser for this simulation, and to be able to step back and forth through time examining what happened at critical moments so I can improve the tracking for those situations.

To make recordings, there’s the rift-debug-gstreamer-record branch of OpenHMD. If you have GStreamer and the right plugins (gst-plugins-good) installed, and you set env vars like this, each run of OpenHMD will generate a recording in the target directory (make sure the target dir exists):

export OHMD_TRACE_DIR=/home/user/openhmd-traces/

Up Next

The next things that are calling to me are to improve the room configuration estimation and storage as mentioned above – to detect when the poses a camera is reporting don’t make sense because it’s been bumped or moved.

I’d also like to add back in tracking of the LEDS on the back of the headset headband, to support 360 tracking. I disabled those because they cause me trouble – the headband is adjustable relative to the headset, so the LEDs don’t appear where the 3D model says they should be and that causes jitter and pose mismatches. They need special handling.

One last thing I’m finding exciting is a new person taking an interest in Rift S and starting to look at inside-out tracking for that. That’s just happened in the last few days, so not much to report yet – but I’ll be happy to have someone looking at that while I’m still busy over here in CV1 land!

As always, if you have any questions, comments or testing feedback – hit me up at or on @thaytan Twitter/IRC.

Thank you to the kind people signed up as Github Sponsors for this project!

A call for more downstream testing of Meson

As Meson gets more and more popular, the number of regressions also grows. This is an unvoidable fact of life. To minimize this effort we publish release candidates before the actual releases. Unfortunately not many people use these so many issues are not found until after the release (as happened with 0.60.0).

For this reason we'd like to ask more people to test these rcs on their systems. It's fairly straightforward.

Testing individual projects

If you have a CI that installs Meson using pip, this is easy. You can tell Pip to use prerelease versions with the --pre flag.

pip install --pre meson

If you use prebuilt images rather than reinstalling on every build, do update your images once a week. Meson releases happen traditionally on Sunday evenings European time.

Testing if you are a distro or similar

The release candidates are packaged and uploaded to Debian experimental, so if you can use those, it is the simplest solution. They are not uploaded to unstable as I was instructed not to do so because of breakage potential. If you are a Debian person and know that the above explanation is incorrect and that I should be doing something else, let me know so I can change how that is done.

If you have some different setup that has a full CI run (hopefully something smaller than a full Debian archive rebuild) then doing that with the rc version would be the best test.

If you don't have such a test suite, you'll probably want to set one up for other reasons as well. :)

Registrations for GNOME.Asia Summit 2021 are open!

Registrations for GNOME.Asia Summit 2021 are open!

GNOME.Asia Summit will take place virtually from November 20th – 21st, 2021. It is the featured annual GNOME conference in Asia, focusing primarily on the GNOME desktop, but also covering applications and platform development tools. The Summit brings together the GNOME community in Asia to provide a forum for users, developers, foundation leaders, governments, and businesses to discuss the present technology and future developments.

This year’s summit will feature two speaking tracks on both event days as well as two Keynote Speakers.  

Please check out the event website to register for GNOME.Asia Summit 2021! 

October 21, 2021

My teleconf setup

Several friends have asked about my camera/videoconferencing setup, so some notes on that.

Picture from my desktop camera. Lighting isn’t quite as even as I’d like (and as always in stills, my smile is goofy) but you can see the background blur clearly.


I’ve joked that for lawyers, a good videoconferencing setup is now like a good suit—sort of pointless but nevertheless can help make a good impression in a field where impressions, for better and for worse, matter.

I picked up the new book “Presenting Virtually” from Duarte and it starts with something that’s pretty basic, but also not always obvious—you can’t control networks, and often don’t control what presentation software you’re using. What you do control is your hardware, so make that the best you can.


I bought a Canon 77D to take baby pictures and… it was in a closet when the pandemic hit. I use it with a 24mm pancake lens. Canon provides a driver that lets you use the camera as a webcam.

Given the cost, I’m not sure this makes sense for most people to do unless they already have a compatible Canon laying around. But if you do have a supported one it works great!

As an alternative, friends speak very highly of this new Dell camera.


I cheat by having good natural light in my office and then supplementing it, rather than having to blast light all over to make up for the gap. This means my light was cheap; the primary criteria was being able to change the color (from a bright white to yellow-ish) so that things looked right.

The exact model I got is no longer available, but is basically similar to this one.

Pro tip for new-ish home workers: if you have two rooms, one dark and one bright, make your bedroom dark and cramped and your office big and light. The previous residents of our place made the reverse choice and I don’t understand it at all.


I have a Blue Yeti mic. I’m not sure I’d recommend it for most people. The audio quality is very good, but positioning it over a desk is finicky. (I use these for both my camera and mic, and they work once you get them set up, but they’re a pain.) In addition, it has a headphone jack—which is fine except it insists on reporting to the operating system that it is live even when it has nothing plugged in, so I frequently have to say “no, bad zoom, use the speakers that are actually speakers”.

If I were doing it over again, I’d get something designed more specifically for the home office use case. A friend swears by their Jabra 510, and this new thing from Logitech looks pretty interesting.

What I’m not doing (at least not yet)

I’m sorely tempted to get a teleprompter, but Stephen has mostly convinced me not to. In my experience, at this time, the bar is pretty low—having a good camera and light really does make things noticeably better for people on the other end, even if your eye contact isn’t perfect while doing a slide deck. So you can get a lot of bang for a lot less effort than Stephen spent. Still, tempting some days :)

Hope this is helpful!

Text in librsvg starts to get better

Up to now, text support in librsvg has been fairly limited. The text chapter in the SVG spec is pretty big and it contains features that are very much outside of my experience (right-to-left languages, vertical text). But now I think I have a plan for how to improve the text features.

Bidirectional text

Bidirectional text, or "bidi" for short, happens when one has a right-to-left language like Arabic or Hebrew intermixed with things like Arabic numerals, or Latin text.

This is an interesting little example from the SVG spec:

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns=""
     width="100%" height="100%" viewBox="0 0 600 72"
     direction="rtl" xml:lang="he">

  <title direction="ltr" xml:lang="en">Right-to-left Text</title>
  <desc direction="ltr" xml:lang="en">
    An example for using the 'direction' and 'unicode-bidi' properties
    in documents that predominantly use right-to-left languages.

  <text x="300" y="50" text-anchor="middle" font-size="36"> כתובת MAC:&#x200F;
    <tspan direction="ltr" unicode-bidi="embed">00-24-AF-2A-55-FC</tspan> 


It is supposed to render like this; it says "MAC address: xxx-yyy-zzz":

Text that says "MAC address: numbers" in Hebrew with an English acronym

However, until librsvg 2.52.2 this did not work correctly. You see, librsvg takes each piece of text inside a <text> and creates a Span for it. If there is a <tspan> element, it also gets a Span created. However, librsvg did not handle the case where an inner <tspan> changes the direction property from its parent <text> element.

Another bug:

<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="" xml:lang="fa" direction="rtl" width="200" height="100">
    <g font-family="Dana-FaNum" font-size="16">
        <text x="50%" y="50%"><tspan font-weight="bold">نام: </tspan><tspan>مهدی</tspan></text>

It is supposed to render more or less like this (the line says, "Name: Mehdi"):

Right-to-left spans in text

This was broken, too!. I did not realize that within <text>, the spans need to be laid out exactly in the text direction, in this case rtl. I thought that it was sufficient to let Pango deal with each group of characters within a span.

Together with that, I fixed text-anchor for right-to-left text. This is what one uses to align a text block with respect to its start, middle, or end. It was also broken for RTL languages before.

One remaining bug is that there should be a space to the left of the :, but librsvg is eating it. I think it needs to implement the SVG2 white-space behavior, which replaces the ambiguous definition of xml:space from SVG1.1.

A test that makes me exceedingly happy

This family of images from Wikimedia Commons has been broken for many years. Someone has been painstakingly making SVG2 maps of German rail by writing SVG code by hand; they use features that became available only after Madds Holland's Outreachy internship from last Summer. Also, the maps use text offsetting features that were just broken.

S-Bahn map for the Rhein Neckar region

The little arrowheads were broken because librsvg didn't support orient="auto-start-reverse" in markers (arrowheads get done with SVG's "marker" feature). Madds added support for that during their Outreachy internship.

Then, most of the the multi-line text objects in there were laid out incorrectly. Librsvg was laying out spans inside text chunks incorrectly when they had dx/dy offsets and the text also had text-anchor different from the default.


Broken rendering of the S-Bahn map; arrows and multi-line text are wrong


Good rendering of the S-Bahn map

One last detail which I haven't figured out is that the positioning of glyphs is a bit off. If you look at the slanted label for the LU-Rheingönheim station, the "heim" at the end looks a bit misaligned with the rest of the label. Firefox renders it correctly. Advice is appreciated!

Future plans

All those fixes will appear in librsvg 2.52.3, due in a few days.

I want to add more tests for right-to-left and bidi text; they can be affected by many properties for which there are no tests right now.

After bidi text works reasonably well, I want to add support for positioning individual glyphs with the x/y/dx/dy properties. People from Wikimedia Commons really want this, to be able to lay out equations and such.

Once individual glyphs can be positioned independently, maybe textPath support, which cartographers really like for curved labels.

A handy tool

I recently discovered the Ahem font. It is part of the CSS fonts tests, and consequently part of the Web Platform Tests. Quoting from its description:

The font’s em-square is exactly square. Its ascent and descent combined is exactly the size of the em square; this means that the font’s extent is exactly the same as its line-height, meaning that it can be exactly aligned with padding, borders, margins, and so forth.

That is, you can typeset something with the Ahem font and it will appear as contiguous squares. If you render the string ABCD, it will appear as a rectangle four-ems wide by one-em tall. This is wonderful for reproducible tests!

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="" width="600" height="300">
  <rect x="0" y="0" width="100%" height="100%" fill="white"/>
  <line x1="50%" y1="0" x2="50%" y2="100%" stroke-width="2" stroke="blue"/>
  <line x1="0" y1="100" x2="100%" y2="100" stroke-width="2" stroke="blue"/>

  <text x="50%" y="100" font-family="Ahem" font-size="20" text-anchor="end"><tspan dy="-1em">A</tspan><tspan dx="-40" dy="1em">BC</tspan></text>

That renders like this:

Three black squares on a white background

Text rendering clearly means spending days trying to get three black squares to draw in the correct position.

October 20, 2021

PSA: gnome-settings-daemon's MediaKeys API is going away

 In 2007, Jan Arne Petersen added a D-Bus API to what was still pretty much an import into gnome-control-center of the "acme" utility I wrote to have all the keys on my iBook working.

It switched the code away from remapping keyboard keys to "XF86Audio*", to expecting players to contact the D-Bus daemon and ask to be forwarded key events.


Multimedia keys circa 2003

In 2013, we added support for controlling media players using MPRIS, as another interface. Fast-forward to 2021, and MPRIS support is ubiquitous, whether in free software, proprietary applications or even browsers. So we'll be parting with the "org.gnome.SettingsDaemon.MediaKeys" D-Bus API. If your application still wants to work with older versions of GNOME, it is recommended to at least quiet the MediaKeys API's unavailability.


Multimedia keys in 2021

TL;DR: Remove code that relies on gnome-settings-daemon's MediaKeys API, make sure to add MPRIS support to your app.

October 15, 2021

GWeather next

tl;dr Libgweather, the small GNOME library that queries weather services, is getting a major version bump to allow applications using it to be ported to GTK4.

In the beginning, there was a weather applet in the GNOME panel. It had a bunch of code that poked at a couple of websites to get the weather information for a given airport or weather observation stations, and shipped with a list of locations and their nearest METAR code.

In 2007, the relevant code was moved to its own separate repository, so that other applications and system settings could reuse the same code as the panel applet: the libgweather library was born. Aside from the basic weather information and location objects, libgweather also had a couple of widgets: one for selecting a location (with autocompletion), and one for selecting a timezone using a location.

Since libgweather was still very much an ad hoc library for a handful of applications, there was no explicit API and ABI stability guarantee made by its maintainers; in fact, in order to use it, you had to “opt in” with a specific C pre-processor symbol.

Time passed, and a few more applications appeared during the initial GNOME 3 cycles—like Weather, followed by Clocks a month later. Most of the consumers of libgweather were actually going through a language binding, which meant they were not really “opting into” the API through the explicit pre-processor symbol; it also meant that changes in the API and ABI could end up being found only after a libgweather release, instead of during a development cycle. Of course, back then, we only had a single CI/CD pipeline for the whole project, with far too little granularity and far too wide scope. Still, the GWeather consumers were few and far between, and the API was not stabilised.

Fast forward to now.

The core GNOME applications using GWeather are in the process of being ported to GTK4, but GWeather still ships with two GTK3 widgets. Since you cannot have GTK3 and GTK4 types in the same process, this requires either porting GWeather to GTK4 or dropping the widgets. As it turns out, the widgets are not really shared across applications using libgweather, and all of them have also been redesigned or are using the libadwaita/GTK4 port as a chance to refresh their overall appearences. This makes our life a little bit easier, as we can drop the widgets without really losing any actual functionality that people do care about.

For GNOME 42, the plan for libgweather is:

  • bump up the API version to 4.0, and ensure parallel installability with the older libgweather-3; this requires renaming things like the pkg-config file and the settings schema, alongside the shared library
  • drop the GTK widgets, and some old API that hasn’t been working in years, like getting the radar image animation
  • stabilise the API, and turn libgweather into a proper library, with the usual API and ABI stability guarantees (deprecations and new symbols added only during development cycles, no changes/removals until the following major API bump)
  • make it easier to use libgweather objects with GListModel-based API
  • document the API properly
  • clean up the internals from various years of inconsistent coding style and practices

I’m also going through the issues imported from Bugzilla and closing the ones that have long since been fixed.

In the meantime, the old libgweather-3 API is going to be frozen, for the tools that still use it and won’t be ported to GTK4 any time soon.

For more information, you can read:

If you’re using libgweather, I strongly recommend you to use the 40.0 release or build from the libgweather-3 branch until you are planning to port to GTK4.

If you’re distributing libgweather, I recommend you package the new libgweather under a new name, given that it’s parallel installable with the old one; my recommendation is to use libgweather4 or libgweather-4 as the name of the package.