August 21, 2024

Best Intentions

This is going to be a bit of a sporadic blog post covering XDG Intents, GSoC and few other updates from GNOME goings on.

# XDG Intents

Most end-user platforms have something they call an intent system or something approximating the idea. Implementations vary somewhat, but these often amount to a high-level desktop or application action coupled to a URI or mime-type. There examples of fancy URIs like sms:555-1234?body=on my way that can do intent-like things, but intents are higher-level, more purposeful and certainly not restricted to metadata shoehorned into a URI.

I'm going to approach this like the original proposal by David Faure and the discussions that followed, by contrasting it with mime-types and then demonstrating what the files for some real-world use cases might look like.

# The Landscape

Let's start with the mime-apps Specification. For desktop environments mime-types are, most of all, useful for associating content with applications that can consume it. Once you can do that, the very next thing you want is defaults and fallback priorities. Now can you double-click stuff to have your favourite application open it, or right-click to open it with another of your choice. Hooray.

We've also done something kind of clever, by supporting URI handlers with the special x-scheme-handler/* mime-type. It is clever, it does work and it was good enough for a long time. It's not very impressive when you see what other platforms are doing with URIs, though.

Moving on to the Implements key in the Desktop Entry Specification, where applications can define "interfaces" they support. A .desktop file for an application that supports a search interface might look like this:

[Desktop Entry]
Name=Contacts
Icon=org.gnome.Contacts
Exec=gnome-contacts %U
Terminal=false
Type=Application
DBusActivatable=true
Implements=org.gnome.Shell.SearchProvider2

The last line is a list of interfaces, which in this case is the D-Bus interface used for the overview search in GNOME Shell. In the case of the org.freedesktop.FileManager1 interface we could infer a default from the preferred inode/directory mime-type handler, but there is no support for defining a default or fallback priority for these interfaces.

While researching URI handlers as part of the work funded by the STF, Sonny reached out to a number developers, including Sebastian Wick, who has been helping to push forward sandboxing thumbnailers. The proposed intent-apps Specification turns out to be a sensible way to frame URI handlers, and other interfaces have requirements that make it an even better choice.

# Terminal Itchiness

In community-driven software, we've operated on a scratch-an-itch priority model for a very long time. At this point we have several, arguably critical, use cases for an intent system. Some known use cases include:

  • Default Terminal

    This one should be pretty well known and a good example of when you might need an intent system. Terminals aren't really associated with anything, let alone a mime-type or URI scheme, so we've all been hard-coding defaults for decades now. See the proposed terminal-intent Specification for details.

  • Thumbnailers

    If C/C are the languages responsible for most vulnerabilities, thumbnailers have to be high on the list of application code to blame. Intents will allow using or providing thumbnailing services from a sandboxed application.

  • URI Handler

    This intent is probably of interest to the widest range of developers, since it allows a lot freedom for independent applications and provides assurances relied on by everything from authentication flows to personal banking apps.

Below is a hypothetical example of how an application might declare it can handle particular URIs:

[Desktop Entry]
Name=Wise
Icon=com.wise.WiseLinux
Exec=wise %U
Terminal=false
Type=Application
DBusActivatable=true
Implements=org.freedesktop.UriHandler

[org.freedesktop.UriHandler]
Supports=wise.com;
Patterns=https://*.wise.com/link?urn=urn:wise:transfers;

While the Desktop Entry specification states that interfaces can have a named group like above, there are no standardized keys shared by all interfaces. The Supports key proposed by Sebastian is important for both thumbnailers and URI handlers. Unlike a Terminal which lacks any association with data, these need the ability to express additional constraints.

So the proposal is to have the existing Implements key work in tandem with the intentapps.list (similar to the MimeType key and mimeapps.list), while the Supports key allows interfaces to define their own criteria for defaults and fallbacks. Below is a hypothetical example of a thumbnailer's .desktop file:

[Desktop Entry]
Name=Image Viewer
Icon=org.gnome.Loupe
Exec=loupe %U
Terminal=false
Type=Application
MimeType=image/jpeg;image/png;image/gif;image/webp;image/tiff
DBusActivatable=true
Implements=org.freedesktop.Thumbnailer

[org.freedesktop.Thumbnailer]
Supports=image/jpeg;image/png;image/gif;image/svg xml;image/tiff

The Supports key will always be a list of strings, but the values themselves are entirely up to the interface to define. To the intent system, these are simply opaque tags with no implicit ordering. In the URI handler we may want this to be a top-level domain to prevent things like link hijacking, while thumbnailers want to advertise which mime-types they can process.

In the intentapps.list below, we're demonstrating how one could insist that a particular format, like sketchy SVGs, are handled by Loupe:

[Default Applications]
org.freedesktop.Thumbnailer=org.gimp.GIMP
org.freedesktop.Thumbnailer[image/svg xml]=org.gnome.Loupe;org.gimp.GIMP

We're in a time when Linux users need to do things like pass an untrusted file attachment, from an unknown contact, to a thumbnailer maintained by an indepedent developer. So while the intent-apps Specification itself is superficially quite simple, if we get this right it can open up a lot of possibilities and plug a lot of security holes.

# Put this in your backpack, mine's full

First a bit of context for the GLib project, which is comprised of three main parts: GLib, GObject and GIO. GLib contains things you'd generally get from a standard library, GObject defines the OOP semantics (methods/properties/signals, inheritance, etc), and GIO provides reasonably high-level APIs for everything from sockets and files to D-Bus and Gio.DesktopAppInfo.

The GLib project as a whole contains a substantial amount of the XDG implementations for the GLib/GTK-lineage of desktop environments. It also happens to be the layer we implement a lot of our cross-platform support, from OS-level facilities like process spawning on Windows to desktop subsystems like sending notifications on macOS.

A scene from Lord of the Rings, wherein Gandalf shouts, 'You shall not pass!'
Fig. 1. A GLib Maintainer

The merge request I drafted for the initial implementation received what might look like Push Back, but this should really be interpreted as a Speed Bump. GLib goes a lot of places, including Windows and macOS, thus we need maintainers to make prudent decisions that allow us to take calculated risks higher in the stack. It may also be a sign that GLib is no longer the first place we should be looking to carry XDG implementations.

Something that you may be able to help with, is impedance-matching our implementation of the intent-apps Specification with its counterparts in the Apple and Microsoft platforms. Documentation is available (in varying quality), but hands-on experience would be a great benefit.

# Workbench and GSoC

Last year, I was invited by Sonny Piers to co-mentor for both Google Summer of Code and Outreachy, which was really one the best times I've had in the community. He also invited a couple of us Workbenchers from that period to the kick-off meeting for this year's projects.

Recently, he asked if I could step in and help out with this year's programs. This is a very unfortunate set of circumstances to arise during an internship program, but regardless, I'm both honored and thrilled.

I think there's good chance you've run into one of our mentees this year, Shem Angelo Verlain (aka vixalien). He's been actively engaging in the GJS community for some time and contributing to better support for TypeScript, including his application Decibels which is in incubation to become a part of GNOME Core. His project to bootstrap TypeScript support in Workbench is going to play an important role in its adoption by our community.

Our other mentee, Bharat Atbrat, has a familiar origin story. It started as an innocent attempt to fix a GNOME Shell extension, turned into a merge request for GNOME Settings, rolled over into porting Workbench demos to Vala and it's at this point one admits to oneself they've been nerd-sniped. Since then, Bharat has been porting more demos to Vala and working on an indexed code search for the demos. As a bonus, we will get a GOM demo that's being used to prototype and test searching capabilities.

# GNOME Online Accounts

The release notes are not yet finalized for GNOME 47, but there are few highlights worth mentioning.

There have been several improvements to the periodic credential checks, fixing several false positives and now notifying when an account needs to be re-authenticated. The notification policy in GNOME 47.beta turned out overly aggressive, so it has been amended to ensure you are notified at most once per account, per session.

A screengrab of Commander Ransom from 'Star Trek: Lower Decks, Strange Energies', where he turns into a god-like tilt-a-whirl, spraying rainbows everywhere.
Fig. 2. Entirely Reasonable Notification Policy

For Kerberos users, there is rarely any exciting news, however after resurrecting a merge request by Rishi (a previous maintainer) and some help, we now support Linux's general notification mechanism as a very efficient alternative to the default credential polling. If you're using your Kerberos or Fedora account on a laptop or GNOME Mobile, this may improve your battery life noticeably.

The support for Mail Autoconfig and improved handling of app passwords for WebDAV accounts will ship in GNOME 47. The DAV discovery and Mail Autoconfig will form the base of the collection provider, but this won't ship until GNOME 48. Aside from time constraints, this will allow a cycle to shake out bugs while the existing pieces are stitched together.

The Microsoft 365 provider has enabled support for email, calendar and contacts, thanks to more work by Jan Michael-Brummer and Milan Crha. This is available in GNOME OS Nightly now, so it's great time to get in some early testing. We've made progress on verifying our application to supports more organizational accounts and, although this is not constrained by our release schedule, I expect it to be resolved by GNOME 47.

# Acknowledgements

Many thanks again to the Sovereign Tech Fund and everyone who helped make it possible. I would also like to express my appreciation to everyone who helps me catch up on the historical context of the various XDG and GLib facilities. Even when documentation exists, it can be extremely arduous to put the picture together by yourself.

A kitten, sleeping sweetly on its back.
Fig. 3. Ideal Psychological and Emotional State

Until next time, stay sweet.

Meson's New Option Setup ‒ The Largest Refactoring

The problem

Meson has had togglable options from almost the very beginning. These split into two camps. The first one is "common options" like optimizations, warning level, language standard version and so on. The second one is "per project" options that are specific to each project, such as which backend to use. For a long time things were quite nice but as people started using subprojects more and more, the need to configure common options on a per-subproject basis became more and more important.

Meson added a limited way of setting some options per subproject, but it was never really felt like a proper integrated solution. Doing it properly turns out to have a lot of requirements because you want to be able to:

  • Override any shared option for any subproject
  • Do this at runtime from the command line
  • You want to unset any override given
  • Convert existing per-project settings to the new override format
  • Provide an UI that is readable and sensible
  • Do all of this without needing to edit subproject build files

The last one of these is important. It means that you can use deps directly (i.e. from WrapDB) without any local patches.

What benefits do you get out of it?

The benefits are most easily seen via examples. Let's say you are developing a program that uses a dependency that does heavy number crunching. You need to build that (and only that) subproject with optimizations enabled, otherwise your development experience is intolerably slow. This is done by defining an augment, like so:

meson configure -Acruncher:optimization=2

A stronger version of this would be to compile all subprojects with optimizations but the top level project without them. This is how you'd do it:

meson configure -Doptimization=2 -A:optimization=0

Augments can be unset:

meson configure -Usubproject:option

This scheme permits you to do all sorts of useful things, like disable -Werror on specific projects, build some subprojects with a different language version (such as gnu99), compiling LGPL deps as shared libraries and everything else as a static library, and so on.

Implementing

This is a big internal change. How big? Big! This is the largest refactoring operation I have done in my life. It is big enough that it took me over two years of procrastination before I managed to gather enough strength to start work on this. Pretty much all of my Meson work in the last six months or so has been spent on this one issue. The feature is still not done, but the merge request already has 80 commits and 1700 new lines and even that is an understatement. I have chopped off bits of the change and merged them on their own. All in all this meant that the schedule for most days of my summer vacation went like this:

  • Wake up
  • Work on Meson refactoring branch until fed up
  • Work on my next book until fed up
  • Maybe do something else
  • Sleep
FTR I don't recommend this style of working for anyone else. Or even to myself. But sometimes you just gotta.

The main reason this change is so complex lies in the architecture. In existing code each built target "knew" the option settings needed for it (options could and can be overridden in build files on a per-target basis). This does not work any more. Instead the code needs one place that encapsulates all option data and provides methods like "what is the value of option X when building target Y in subproject Z". Option code was everywhere, so changing this meant touching the entire code base and that the huge change blob must be landed in master atomically.

The only thing that made this change even remotely feasible was that Meson has an extensive test suite. The main code changes were done months ago, and all work since then has gone into making existing unit tests pass. They still don't pass, so work continues. Without this test suite there would have been hundreds of regressing projects, people would be angry and everyone would pin their Meson to an old version and refuse to update. These are the sorts of breakages that kill projects dead. So, write tests, even if it does not seem fun. Without them every project will eventually end up in a fork in the road where the choice is between "death by stagnation" and "death by breaking end users". Most projects are not Python 3. They probably won't survive a similar level of breakage.

Refactoring, types and Python

Python is, at the same time, my favourite programming language and very much not my favourite programming language. Python in the small is nice, readable, wonderful and productive. As the project size grows, the lack of static types becomes aggravating and eventually you end up debugging cases like "why does this argument that should be a dict is an array one out of 500 times at random". Types make these problems go away and make refactoring easy.

But not always.

For this very specific case the complete lack of types actually made the refactoring easier. Meson currently supports more than one hundred different compilers. I needed to change the way compiler classes work, but I did not know how. Thus I started by just using the GNU C compiler. I could change that (and its base class) as much as I wanted without having to care about any other compiler class. As long as I did not use any other compiler their code was not called and it did not matter that their method signatures were completely different. In a static language all type changed would need to be done up front just to make the dang thing compile.

Still, you can have my types when you drag them from my cold, dead fingers. But maybe this is something for language designers of the future to consider. It would be kind of cool to have a strictly typed language where you could add a compiler flag to say "convert all variables into Python style variant dictionaries and make all type checks, method invocations etc work at runtime". Yes, people would abuse the crap out of this feature, but the same can be said about every new feature.

When will this land?

It is not done yet, so we don't know. At the earliest this will be in the next release, but more likely in the one after that.

If you like trying out new things and living dangerously, you can try the code from this MR. Be sure to post comments on that page if you do.

August 20, 2024

A technical overview of the Kolibri app for GNOME

This blog post has been floating around as a draft for several years. It eventually split off into a presentation at GUADEC 2022, titled Offline learning with GNOME and Kolibri (YouTube). In that presentation, Manuel Quiñones and I explained how Endless OS reaches a unique audience by providing Internet-optional learning resources, and we provided an overview of our work with Kolibri. This post goes into more detail about the technical implementation of the Kolibri desktop app for GNOME, and in particular how it integrates with Endless OS.

Integrating a flatpak app with an immutable OS

In Endless OS, way back with Endless OS 4 in 2021, we added Kolibri, an app created by Learning Equality, as a new way to discover educational content. Kolibri has a rich library of video lessons, games, documents, e-books, and more; as well as tools for guided learning – both for classrooms, and for families learning at home. The curation means it is safe and comfortable to freely explore. And all of this works offline, with everything stored on your device.

Making this all come together was an interesting challenge, but looking back on it with Endless OS 6 alive and well, I can say that it worked out nicely.

A screenshot of the Kolibri GNOME app. It is showing the Learn / Home page, which includes several items arranged in a grid. At the top, "Continue learning on your own (Watch)", followed by "Hubble image of galaxies (Explore)", "Kepler's Laws", and "Cosmic background radiation (Watch)". Next, "Explore Channels", with three channels visible: "EngageNY", "Blockly Games", and "Khan Academy".

The Kolibri app for GNOME

Learning Equality designed Kolibri with offline, distributed learning in mind. While an organization can run a single large Kolibri instance that everyone reaches with a web browser, it is equally possible for a group of people to use many small instances of Kolibri, where those instances connect with each other intermittently to exchange information. The developers are deeply interested in sneaker net-style use cases, and indeed Kolibri’s resilience has allowed it to thrive in many challenging situations.

Despite using Django and CherryPy at its heart, Kolibri often presents itself as a desktop app which expects to run on end user devices. Behind the scenes, the existing Windows and MacOS apps each bundle a Kolibri server, running it in the background for as long as the desktop app is running.

We worked with Learning Equality to create a new Kolibri app for GNOME. It uses modern GTK with WebKitGTK to show Kolibri itself. It also includes a desktop search provider, so you can search for Kolibri content from anywhere.

Get it on Flathub

The Kolibri GNOME app is distributed as a flatpak, so its dependencies are neatly organized, it runs in a well-defined sandbox, and it is easy to install it from Flathub. For Endless OS, using flatpak means it is trivial to update Kolibri independent from Endless OS’s immutable base system.

Kolibri Daemon

But Endless OS doesn’t just include Kolibri. One of my favourite parts of Endless OS is it provides useful content out of the box, which is great for people with limited internet access. So in addition to Kolibri itself, we want a rich library of Kolibri content pre-installed. And with so much already there, ready to be used, we want it to be easy for people to search for that content right away and start Kolibri for the first time.

A diagram depicting the major components of the Kolibri desktop app. It includes kolibri-gnome, which interacts with kolibri over HTTP. It also includes kolibri-gnome-search-provider. Both kolibri-gnome-search-provider and kolibri interact with Kolibri's content database.
If we add more users, each with their own Kolibri content, we can imagine the size of that database becoming a problem.

This becomes both a technical challenge and a philosophical challenge. Normally, each desktop user has their own instance of Kolibri, with its own hidden directory full of content. Because it is a flatpak, it normally doesn’t see the rest of the system unless we explicitly give it permission to, and every time we do that we need to think carefully about what it means. Should we really grant a WebView the ability to read and write /run/media? We try to avoid it.

At the same time, we want a way to create new apps which use content from Kolibri, so that library of pre-installed content is visible up front, from the apps grid. But it would be expensive if each of these apps ran its own instance of Kolibri. And whatever solution we employ, we don’t want to diverge significantly from the Kolibri people are using outside of Endless OS.

To solve these problems, we split the code which starts and stops the Kolibri service into a separate component, kolibri-daemon. The desktop app (kolibri-gnome) and the search provider each communicate with kolibri-daemon using D-Bus.

A diagram depicting the major components of the Kolibri desktop app after adding kolibri-daemon. The diagram shows kolibri-gnome, which interacts with kolibri over HTTP, but it also shows how kolibri-gnome interacts with kolibri-daemon over D-Bus. The kolibri-daemon component interacts with kolibri directly, and it also interacts with Kolibri's content database.
The desktop app communicates through kolibri-daemon, instead of starting Kolibri itself.

This design is exactly what happens when you start the Kolibri app from Flathub. And with the components neatly separated, on Endless OS we add eos-kolibri, which takes it a step further: it adds a kolibri system user and a service which runs kolibri-daemon on the D-Bus system bus. The resulting changes turn out to be straightforward, because D-Bus provides most of what we need for free.

A diagram depicting the major components of the Kolibri desktop app, as in the previous diagram, but with kolibri-daemon running as a system user. In this diagram, kolibri-gnome and kolibri-gnome-search-provider are associated with a desktop user, while the remaining components are associated with the Kolibri system user. The diagram shows how these components interact with each other over D-Bus, but across the system instead of within a user session.
Kolibri on Endless OS is almost the same, except kolibri-daemon is run by the Kolibri system user.

With this in place, every user on the system shares the same Kolibri content, and it is installed to a single well-known location: /var/lib/kolibri. Now, pre-installing Kolibri content is a problem we can solve at the system level, and in the Endless OS image builder. Independent from the app itself.

Channel apps

Now that we have solved the problem of Kolibri content being duplicated, we can come back to having multiple apps share the same Kolibri service. In Endless OS, we want users to easily see the content they have installed, and we do this by adding launchers to the apps grid.

First, we need to create those apps. If someone has installed content from a Kolibri channel like TED-Ed Lessons or Blockly Games, we want Kolibri to generate a launcher for that channel.

But remember, Kolibri on Endless OS is an unprivileged system service. It can’t talk to the DynamicLauncher portal. That belongs to the user’s session, and we want these launchers to be visible before a user ever starts Kolibri in their own session. Kolibri also can’t be creating files in /usr/share/applications. That would be far too much responsibility.

Instead, we add a Kolibri plugin to generate desktop entries for channels. The desktop entries refer to the Kolibri app using a custom URI scheme, a layer of indirection because Kolibri (potentially inside a flatpak) is unaware of how the host system launches it. The URI scheme provides enough information to start in a channel-specific app mode, instead of in its default configuration.

Finally, instead of placing the desktop entry files in one of the usual places, we place them in a well-known location inside Kolibri’s data directory. That way the channel apps are available, but not visible by default.

In Endless OS, the channel launchers end up in /var/lib/kolibri/data/content/xdg, so in our system configuration we add that directory to XDG_DATA_DIRS. This turns out to be a good choice, because it is trivial to start generating search providers for those apps, as well.

A screenshot of the Endless OS app grid, which is very similar to the GNOME Shell app grid. It shows an Education folder, expanded, with several education apps displayed: "Kolibri", "PhET Interactive Simulations", "Sciensation", and more. Some of the apps are marked as running. It is apparent from the names and icons that these apps all refer to specific channels inside Kolibri.
Kolibri channels along with other Education apps in Endless OS.

Search providers

To make sure people can find everything we’ve included in Endless OS, we add as many desktop search providers as we can think of, and we encourage users to explore them. The search bar in Endless OS is not just for apps.

That means we need a search provider for Kolibri. It’s a simple enough problem. We extended kolibri-daemon‘s D-Bus interface with its own equivalents for the GNOME Shell search provider interface. It is capable of reading directly from Kolibri’s database, so we can avoid starting an HTTP server. But we also want to avoid dealing with kolibri-daemon as much as possible. It is a Python process, heavy with web server stuff and complicated multiprocessing code. And, besides, the daemon could be connecting to the D-Bus system bus, and the shell only talks to search providers on the session bus. That’s why the search provider itself is a separate proxy application, written in C.

A screenshot of GNOME Shell, showing search results after entering "earth" in the search bar. Several results from Kolibri are displayed, such as "Cosmology & astronomy". The results include titles and descriptions. The first three results have purple box icons, indicating that they are interactive. The other two results have orange Play button icons, indicating that they are videos.
Kolibri returning search results in GNOME Shell.

But in Endless OS, we don’t just need one search provider, either. We want one for each of those channel apps we generated. So, I mentioned that our Kolibri plugin generates a search provider to go with each desktop file. Of course, loading and searching through Kolibri’s sqlite database is already expensive once, so it would be absurd to do it for every channel that is installed. That’s a lot of processes!

Fortunately, those search providers are all the same D-Bus service, with a different object path for each Kolibri channel. That one D-Bus service receives a lot of identical search queries for a lot of different object paths, but at least the system is only starting one process for it all. In the search provider code, I added a bespoke task multiplexer, which allows the service to run a single search in kolibri-daemon for a given query, then group the results and return them to different invocations from the shell.

A screenshot of Endless OS, showing search results after entering "earth" in the search bar. The result is similar to the previous screenshot, except the same search results are spread over several different apps. Instead of results for Kolibri, they are results for Ciencia NASA, Khan Academy, PhET Interactive Simulations, and Sciensation.
Kolibri returning search results through several channel apps in Endless OS.

It is a complicated workaround, but it means search results appear in distinct buckets with meaningful names and icons. For our purpose in Endless OS, it was definitely worth the trouble.

User accounts

There was one last wrinkle here: Kolibri kept asking people to set it up, make a user account (with a password!), and sign in. It is, after all, a standalone learning app with a big complicated database that keeps track of learning progress and understands how to sync content between devices. But this isn’t a great experience if you’re just here to watch that lecture about cats.

What we want is for Kolibri to already know who is accessing it. They’re already signed in as a desktop user. And most of the time, we want to blaze right through that initial “set up your device” step, or at least make it as smooth as possible.

To do that, we added an interface in kolibri-daemon so the desktop app can get an authentication token to use over HTTP. On the other side, kolibri-daemon privately communicates with Kolibri itself to verify an authentication token, and it communicates with logind to build a profile for the authenticating user.

It was ugly at first, with a custom kolibri-desktop-auth-plugin which sat on top of Kolibri’s authentication system. But after some iteration, upstream Kolibri now has its own understanding of desktop users. On the surface, it uses Kolibri’s app interface plugin for platform integration. With the newest version of Kolibri we have been able to solve authentication in a way that I am properly happy with.

A screenshot of the Kolibri GNOME app after starting for the first time. It is displaying a setup wizard, which appears as a dialog. The wizard starts with the title "How are you using Kolibri?", with two choices: "On my own" and "Group learning".

My favourite part of the feature has been seeing it come together with Kolibri’s first run wizard. Given a working authentication token, Kolibri knows to skip creating an initial user account, leaving only some simple questions about how the user is planning to use Kolibri; independently or connecting to an existing classroom.

That’s it!

It has been great to work on the Kolibri desktop app, and I expect to take some of the approaches and lessons here over to other projects. It is the first big new Python desktop app I have worked with, and it was interesting using some modern Python tools in tandem with the GNOME ways of doing things. The resulting codebase has some fun details:

  • The source repository includes a Flatpak manifest, so it builds and runs out of the box in GNOME Builder. As soon as that was working, I used Builder for everything.
  • Meson is truly indispensable for this kind of thing. We’re sharing build configuration between a bunch of Python modules, all sorts of configuration and data files, and a pair of C projects – one of which is imported by a Python module using GObject introspection. This all works (in a mere 577 lines of meson.build, if you’re counting) because the build system is language-agnostic, and I love it for that. I know that isn’t a lot to ask, but the go-to for Python is decidedly not language-agnostic, and I do not love it.
  • We added pre-commit to automatically clean up source files and run quick tests against them. It doesn’t actually require you have a Python codebase, but it is written in Python and I think people are afraid of how Pythony it looks? It’s really convenient, and it does a good job taking care of the usual nightmare of setting up a virtual environment to run all its tools. I often don’t bother with the actual git hook part, and instead I remember to run the thing manually, and we use the pre-commit github action to be sure.
  • At some point, I added Python type hinting to every part of the project. This tremendously improved the development experience with Builder, and it allowed me to add a mypy pre-commit hook to catch mistakes.
  • I got annoyed at the problem of needing to write release notes in the appdata file before knowing what the next release is called, so I devised a fun scheme where we add notes under "{current_version} next", and then bump-my-version (another tool that looks very Pythony but everyone should use it) knows to mark that release entry as released, setting the date and version appropriately. I wish it didn’t involve regex, but as a concept it has been nice to use. I was tempted to write a pre-commit hook which actually insists on an up to date “next release” entry in appdata, but I should find another project to try it with.
  • With that said, a better workflow probably involves appstream-util news-to-appdata.
  • Managing history in WebKit can be tricky because the BackForwardList is read-only. That was an issue with the Kolibri app because we (with our UI consisting almost entirely of a WebView) need to communicate about Kolibri’s state before its HTTP server is running. Kolibri upstream provides a static HTML loading screen for this purpose, which is fine, but now we have this file in our WebView’s back / forward list. I solved it by swapping between different WebViews, and later showing one in a dialog just for Kolibri’s setup wizard. At first, that was all to keep the history stack organized, but at the same time I found it made the app feel a little less like a web browser in a trench coat. We can switch from the loading WebView to the real thing with a nice crossfade, and only when the UI is actually for real finished loading.
  • This whole project uses a lot of GObject throughout. At some point I finally read the pygobject manual and found myself happily doing property binding, signals and async functions and all those good things from Python. It was a much better experience than earlier in the project’s life where there was a type of angry mishmash between vanilla Python and GObject. (The thing that really freed this up was when I moved a lot of D-Bus code over to a C helper library with gdbus-codegen, which allowed me to delete the equivalent duplicative Python code, and also introduced a bunch more GObject). It’s easy to see why GObject works best with a language that doesn’t carry its own big standard library, but I was happy with how productive I could be in Python once I started actively preferring GObject, especially with the various magic helpers provided by PyGObject. In a future starting-from-scratch project, I would be tempted to make that a rule when adding imports and writing new classes.
  • I made many commits here because I am obsessive about silly things, but this all works thanks to the genius and hard work of the folks at Learning Equality, as well as everyone at Endless, including Dan Nicholson, Daniel Garcia Moreno, Georges Stavracas, Jian-Hong Pan, Manuel Quiñones, and Will Thompson.

I have to admit I got carried away with certain aspects of this. In the end there is a certain discontent to be had spending creative energy on what is, from many angles, a glorified web browser. It’s frustrating when the web stack leads us to treat an application as a black box behind an HTTP interface, which makes integration difficult: boot it up (in its own complex runtime environment which is heroically not a Docker container); wait until it is ready (Kolibri is good at this, but sometimes you’re just watching a file or polling some well-known port); authenticate; ask it (over HTTP) some trivial question that amounts to a single SQL command; return None. But look at that nice framework we’re using!

At the same time, it isn’t lost on me that a software stack like Kolibri’s simply is a popular choice for a cross-platform app. It’s worth understanding how to work with it in a way that still does the best we can to be useful, efficient, and comfortable to use.

A screenshot of the Kolibri GNOME app showing a video from Khan Academy. The video is titled "Linear graphs word problem: cats".

Beyond all the tech stuff, I want to emphasize that Kolibri is an exceptionally cool project. I truly admire what Learning Equality are doing with it, and if you’re interested in offline-first content, data sovereignty, or just open source learning in general, I highly recommend checking it out – either our app on Flathub, or at learningequality.org/kolibri.

August 19, 2024

javascript weakmaps should be iterable

Good evening. Tonight, a brief position statement: it is a mistake for JavaScript’s WeakMap to not be iterable, and we should fix it.

story time

A WeakMap associates a key with a value, as long as the key is otherwise reachable in a program. (It is an ephemeron table.)

When WeakMap was added to JavaScript, back in the ES6 times, some implementors thought that it could be reasonable to implement weak maps not as a data structure in its own right, but rather as a kind of property on each object. Under the hood, adding an key→value association to a map M would set key[M] = value. GC would be free to notice dead maps and remove their associations in live objects.

If you implement weak maps like this, or are open to the idea of such an implementation, then you can’t rely on the associations being enumerable from the map itself, as they are instead spread out among all the key objects. So, ES6 specified WeakMap as not being iterable; given a map, you can’t know what’s in it.

As with many things GC-related, non-iterability of weak maps then gained a kind of legendary status: the lore states that non-iterability preserves some key flexibility for JS implementations, and therefore it is good, and you just have to accept it and move on.

dynamics

Time passed, and two things happened.

One was that this distributed WeakMap implementation strategy did not pan out; everyone ended up implementing weak maps as their own kind of object, and people use an algorithm like the one Steve Fink described a couple years ago to compute the map×key⇒value conjunction. The main original motivation for non-iterability was no longer valid.

The second development was WeakRef and FinalizationRegistry, which expose some details of reachability as viewed by the garbage collector to user JS code. With WeakRef (and WeakMap), you can build an iterable WeakMap.

(Full disclosure: I did work on ES6 and had a hand in FinalizationRegistry but don’t do JS language work currently.)

Thing is, your iterable WeakMap is strictly inferior to what the browser can provide: its implementation is extraordinarily gnarly, shipped over the wire instead of already in the browser, uses more memory, is single-threaded and high-latency (because FinalizationRegistry), and non-standard. What if instead as language engineers we just did our jobs and standardized iterability, as we do with literally every other collection in the JS standard?

Just this morning I wrote yet another iterable WeakSet (which has all the same concerns as WeakMap), and while it’s sufficient for my needs, it’s not good (lacking prompt cleanup of dead entries), and by construction can’t be great (because it has to be redundantly implemented on top of WeakSet instead of being there already).

I am sympathetic to deferring language specification decisions to allow the implementation space to be explored, but when the exploration is done and the dust has settled, we shouldn’t hesitate to pick a winner: JS weak maps and sets should be iterable. Godspeed, brave TC39 souls; should you take up this mantle, you are doing the Lord’s work!

Thanks to Philip Chimento for notes on the timeline and Joyee Cheung for notes on the iterable WeakMap implementation in the WeakRef spec. All errors mine, of course!

Client-side filtering of private data is a bad idea

(The issues described in this post have been fixed, I have not exhaustively researched whether any other issues exist)

Feeld is a dating app aimed largely at alternative relationship communities (think "classier Fetlife" for the most part), so unsurprisingly it's fairly popular in San Francisco. Their website makes the claim:

Can people see what or who I'm looking for?
No. You're the only person who can see which genders or sexualities you're looking for. Your curiosity and privacy are always protected.


which is based on you being able to restrict searches to people of specific genders, sexualities, or relationship situations. This sort of claim is one of those things that just sits in the back of my head worrying me, so I checked it out.

First step was to grab a copy of the Android APK (there are multiple sites that scrape them from the Play Store) and run it through apk-mitm - Android apps by default don't trust any additional certificates in the device certificate store, and also frequently implement certificate pinning. apk-mitm pulls apart the apk, looks for known http libraries, disables pinning, and sets the appropriate manifest options for the app to trust additional certificates. Then I set up mitmproxy, installed the cert on a test phone, and installed the app. Now I was ready to start.

What became immediately clear was that the app was using graphql to query. What was a little more surprising is that it appears to have been implemented such that there's no server state - when browsing profiles, the client requests a batch of profiles along with a list of profiles that the client has already seen. This has the advantage that the server doesn't need to keep track of a session, but also means that queries just keep getting larger and larger the more you swipe. I'm not a web developer, I have absolutely no idea what the tradeoffs are here, so I point this out as a point of interest rather than anything else.

Anyway. For people unfamiliar with graphql, it's basically a way to query a database and define the set of fields you want returned. Let's take the example of requesting a user's profile. You'd provide the profile ID in question, and request their bio, age, rough distance, status, photos, and other bits of data that the client should show. So far so good. But what happens if we request other data?

graphql supports introspection to request a copy of the database schema, but this feature is optional and was disabled in this case. Could I find this data anywhere else? Pulling apart the apk revealed that it's a React Native app, so effectively a framework for allowing writing of native apps in Javascript. Sometimes you'll be lucky and find the actual Javascript source there, but these days it's more common to find Hermes blobs. Fortunately hermes-dec exists and does a decent job of recovering something that approximates the original input, and from this I was able to find various lists of database fields.

So, remember that original FAQ statement, that your desires would never be shown to anyone else? One of the fields mentioned in the app was "lookingFor", a field that wasn't present in the default profile query. What happens if we perform the incredibly complicated hack of exporting a profile query as a curl statement, add "lookingFor" into the set of requested fields, and run it?

Oops.

So, point 1 is that you can't simply protect data by having your client not ask for it - private data must never be released. But there was a whole separate class of issue that was an even more obvious issue.

Looking more closely at the profile data returned, I noticed that there were fields there that weren't being displayed in the UI. Those included things like "ageRange", the range of ages that the profile owner was interested in, and also whether the profile owner had already "liked" or "disliked" your profile (which means a bunch of the profiles you see may already have turned you down, but the app simply didn't show that). This isn't ideal, but what was more concerning was that profiles that were flagged as hidden were still being sent to the app and then just not displayed to the user. Another example of this is that the app supports associating your profile with profiles belonging to partners - if one of those profiles was then hidden, the app would stop showing the partnership, but was still providing the profile ID in the query response and querying that ID would still show the hidden profile contents.

Reporting this was inconvenient. There was no security contact listed on the website or in the app. I ended up finding Feeld's head of trust and safety on Linkedin, paying for a month of Linkedin Pro, and messaging them that way. I was then directed towards a HackerOne program with a link to terms and conditions that 404ed, and it took a while to convince them I was uninterested in signing up to a program without explicit terms and conditions. Finally I was just asked to email security@, and successfully got in touch. I heard nothing back, but after prompting was told that the issues were fixed - I then looked some more, found another example of the same sort of issue, and eventually that was fixed as well. I've now been informed that work has been done to ensure that this entire class of issue has been dealt with, but I haven't done any significant amount of work to ensure that that's the case.

You can't trust clients. You can't give them information and assume they'll never show it to anyone. You can't put private data in a database with no additional acls and just rely on nobody ever asking for it. You also can't find a single instance of this sort of issue and fix it without verifying that there aren't other examples of the same class. I'm glad that Feeld engaged with me earnestly and fixed these issues, and I really do hope that this has altered their development model such that it's not something that comes up again in future.

(Edit to add: as far as I can tell, pictures tagged as "private" which are only supposed to be visible if there's a match were appropriately protected, and while there is a "location" field that contains latitude and longitude this appears to only return 0 rather than leaking precise location. I also saw no evidence that email addresses, real names, or any billing data was leaked in any way)

comment count unavailable comments

Linux Desktop Migration Tool 1.4

After many months, I finally found the time to finish the GNOME desktop/application settings migration in the Linux Desktop Migration Tool and made another release. It basically involves exporting the dconf keys on the source machine and importing writable keys on the destination machine. I’ve also added some extra code to handle the desktop background. If the dconf key points to a picture that is not present on the destination machine, the picture is copied as well. Be it a custom background, or a system provided one that is no longer shipped (in case you’re doing the migration between OSes of different versions).

The list of migrations the tool can do is already fairly long:

  • Migrate data in XDG directories (Documents, Pictures, Downloads…) and other arbitrary directories in home.
  • Reinstall flatpaks on the new machine.
  • Migrate Flatpak app data.
  • Migrate Toolbx containers.
  • Migrate ssh certificates, PKI certificates and nss database, GPG keys, keyring, GNOME Online Accounts.
  • Migrate GNOME desktop/app settings.

The next milestone is NetworkManager settings so that all networks are set up and available when you finish migrating to a new machine.

August 16, 2024

#161 End-to-End Tests

Update on what happened across the GNOME project in the week from August 09 to August 16.

Sovereign Tech Fund

Martín Abente Lahaye reports

At GNOME we have been using openQA with GNOME OS to run end-to-end tests for different components of our stack. These tests have proven useful, but come with some costs.

To tackle these costs, the team at Codethink has leveraged its previous work with sysext-utils and systemd’s system extensions. As a result, we came up with a workflow that integrates system extensions with development and testing pipelines. This provides an improved workflow for reviewers and testers.

New CI/CD components are now available to all GNOME modules, and we have integrated some of these in mutter and gnome-shell pipelines already.

This project was a collaboration between Codethink and the GNOME Foundation, through the Sovereign Tech Fund (STF).

GNOME Core Apps and Libraries

Bilal Elmoussaoui announces

GDM and gnome-session can now be built without X11 support, following the steps of Mutter and GNOME Shell.

GNOME Circle Apps and Libraries

Hugo Olabera announces

Wike, the Wikipedia reader for GNOME, has been updated to version 3.1.0. The most visible changes have to do with the migration of windows and dialogs to the new responsive widgets from libadwaita.

Two new languages ​​have been added to the UI translations (Simplified Chinese and Hungarian), and many of the existing translations have been updated.

The user folder access permission in the Flatpak sandbox has been removed, which is no longer needed thanks to the recent changes implemented in Webkit.

Finally, the runtime version has been updated to GNOME 46 and features the usual set of minor tweaks and bug fixes.

Third Party Projects

Snoop

Snoop through your files.

Philipp says

Snoop 0.4 offers a variety of little fixes and tweaks.

The search path and search string will now be checked for validity and a warning indicator is shown if a path is invalid or the search string is empty.

Paths containing a ~ or a space are now handled properly.

If you have installed the nautilus extension from the settings menu, there is now also a Remove button to uninstall it.

Phosh

A pure wayland shell for mobile devices.

Guido says

Phosh 0.41.0 is out:

Phosh gained a new Quick Setting to toggle Wi-Fi Hotspot on/off and a Bluetooth status page. The media-player widget now displays track position/ length and got a progress bar. Squeekboard got new layouts and you can now put the phone into silent mode when pressing Vol- on incoming calls.

There’s more. Check the full details here

Keypunch

Practice your typing skills

Brage Fuglseth announces

Ciao! Keypunch 3.1 is out, and it speaks Italian! This release also brings text generation support for Czech, Occitan and Polish, and UI translations for Occitan and Polish.

Thanks to everyone who has requested for their languages to be added and patiently tested the text generation implementations; keep the requests coming!

Get Keypunch on Flathub: https://flathub.org/apps/dev.bragefuglseth.Keypunch

That’s all for this week!

See you next week, and be sure to stop by #thisweek:gnome.org with updates on your own projects!

August 15, 2024

Status update, 15/08/2024

Blogging on the Fediverse

WordPress.com has recently allowed blogs to Enter the Fediverse, so you can now follow my writings on Mastodon and other places. Click the new ‘Follow’ button in the sidebar, or paste this succinct Fediverse address:

@[email protected].

This blog is more serious writing about technology, you can also follow @[email protected] which is mostly music, art and needless complaints over things I can’t change.

Reflections on GUADEC 2024

I was lucky enough to attend GNOME’s 2024 conference in Denver. I had a great time, saw many people I haven’t seen this decade, and it was also my first trip into the USA beyond the easternmost parts of the east coast.

It was a unique edition of the conference, we were about 50 people as you can see in the group photo, with another 30-40 people in Berlin. I posted a thread already on what we learned about hybrid conferences.

I have a more thoughts about this year’s event which are complex and contradictory and better discussed face to face. Sometimes you have to hold multiple conflicting thoughts in your head at once. I’ve written some of these thoughts down below.

The world is huge … like really big. Denver is a full EIGHT timezones away from Berlin, right? If you get up at 9AM in Denver then it’s already 5PM in Berlin, and it’ll be 6PM by the time you’ve brushed your teeth. How can you unite two groups of people across such a big time gap?

Having tried it, my feeling that a “federated” event split across different locations at the same time is not a replacement for a single, global in-person event. The community splits according to the geographical distribution of its members, roughly following Conway’s Law.

Travelling back from Denver to Santiago de Compostela took me about 50 hours in the end. It was supposed to be less but there was a 5 hour delay in Denver due to a shortage of air traffic controllers in Newark, which led to missing a connection to Madrid, the next day some bad weather added two more hours of delay, and then the trains from Madrid were largely full so I couldn’t get back home until early evening.

I kind of enjoy travel chaos (and my feeling is that as a society, we’ll only reduce our dependence on air travel if and when it becomes frustratingly inconvenient to use).

I feel lucky that I don’t have to do this kind of trip very frequently. I have more appreciation now for the significant effort people go through to get to events in Europe.

Multiple people in Denver told me they share the feeling I mentioned last month, that many Free Software communities are Euro-centric.

Some benefits to holding a GUADEC in the USA. Inspirational keynotes by local speakers, both Ryan Sipes’ story of Thunderbird, and Stephanie Taylor who is the force behind Google Summer of Code. Meeting contributors who don’t travel to Europe. Media coverage of the GNOME Foundation AGM, in a good way.

I think we missed an opportunity to make more of the event. Involving “the local community” in a summer event is always tricky, as during the summer holidays, “casual” attendees such as students and hobbyists are mostly on holiday. They aren’t usually looking for new software conferences they might attend. That said, GNOME would definitely would benefit from more hands focused on communication outside the project, what we often call “marketing” and “engagement”. It’s a difficult and often thankless task and we have to pay someone to do it. (With all the endless money we have, of course).

I did mention the thing about multiple conflicting ideas at once, right?

Let’s not pretend that a video conference or a hybrid BOF is the same as an in-person meetup. Once you’ve sung karaoke with someone, or explored Meow Wolf, or camped in the desert in Utah together, your relationship is richer than when you only interacted via Gitlab pull requests and BigBlueButton. You have more empathy and you can resolve conflicts better.

People walking by a river
Evan Welsh doing a great job of herding gnomes along the river

Let’s keep exploring new ways to collaborate. Regional events and hybrid events. And accept that these will form bubbles. If you live near members of the GNOME design team and meet in-person with them, you’re going to be able to influence GNOME’s design, more easily than if you live on a continent such as Africa where (to my knowledge) you can’t meet any existing design team members without leaving your continent. If you have a friend who maintains Nautilus it’s going to be easier to get up to speed with Nautilus development yourself, compared to if you’re starting from scratch and you live in a timezone that’s 10 hours offset from Europe.

We want to rethink technical governance in GNOME, which currently somewhat resembles the 15th century Feudal system. (See also, Tobi’s less flippant explanation of how decisions are made). Let’s keep geography in mind when we discuss this. And let’s also think how we can balance out the implicit advantage you get for being based in a certain place, and how we can grow local communities in places that don’t currently have them. I suspect this effort will need to be larger than just the GNOME project, and we can’t be the only community thinking about this stuff.

The USA is just as crazy as you imagine from its TV output. I was ready for beautiful scenery, long car journeys, no pavements, icy air conditioning, unhealthy food and franchises inside of franchises inside of franchises. I was genuinely surprised how hot it gets everywhere — even during my unwanted stopover in Newark the weather was 35° humid heat. And I wasn’t ready for disposable plates, cups and cutlery at every hotel breakfast. I’ve stayed in very cheap and very expensive hotels in many places, and all of them manage to wash and reuse the plates.

Delicious snacks at Meow Wolf, Denver

I want to see the single location, in-person GUADEC continuing while it’s still possible to do it. Count how many plane tickets were bought this year to attend GNOME events, and compare it to the number of flights taken just by Elon Musk’s private jet. It’s great that we avoided a few dozen plane tickets compared to last year but I’m yet to see a noticeable impact on the airline industry; while the impact on the GNOME project of splitting the community into two physical locations was very noticeable indeed.

We should alternate in-person GUADEC with more radically decentralized events, under a different name. We need to continue developing those as well. Like it or not there are big changes coming in society as the world gets increasingly hot. Tobi outlined all this very well in 2022. Nobody knows exactly what will happen of course, but you can expect that building resilience to change will be worthwhile, to put it very mildly. The key is for this to be something positive rather than a negative. The creation of something new rather than the loss of something we fondly remember. I mean, you can’t claim “GUADEC” is a particularly great name 🙂

It’s easy to write about this stuff in a blog post of course, harder to put into practice, and actually it wasn’t even easy to write…. this has taken me three hours.

Fairphone 5

I was going to write more stuff here but it turns out I had a lot to say about GNOME this month. At FOSDEM 2024, which I could easily attend in-person because I’m European, I saw a great talk by Luca from Fairphone, and decided my next phone would be a Fairphone 5. And I just got one. More on that next month, I guess.

August 14, 2024

GUADEC 2024

I attended GUADEC 2024 last month in Denver, Colorado. I thought I’d write about some of the highlights for me.

It was definitely the smallest GUADEC I’ve been to, and it was unusual in some other ways too, such as having several hybrid presentations, with remote and in-person presenters sharing the stage. That took some adjusting, but it worked well, even if I missed some of the energy of past events. (I shared some thoughts about hybrid GUADEC on a Discourse thread).

I felt this GUADEC was really defined by the keynotes. They were great!

First, we had Ryan Stipes from Thunderbird telling us all about Thunderbird’s journey from a somewhat neglected but well-loved side project and on to a thriving self-funded community project: Thunderbird, The Death and Rebirth of an OSS Project (YouTube). He had a lot to say about the value of metrics to measure the impact of certain features and target platforms, which really resonated with people. (It is interesting to note, for instance, there appear to be more Thunderbird users using Windows 8.1 than Linux). He also had a lot to say about the success Thunderbird had just being direct and asking users for money.

Much of this success comes from Thunderbird doing a good job telling its own story. People clearly understand what Thunderbird is doing for them. And there was plenty of talk for the next few days: what does it mean for GNOME to own its story?

I also really enjoyed Stephanie Taylor’s keynote, all about Google Summer of Code (which started 20 years ago now!): Google Summer of Code 20 years of OSS Mentorship (YouTube). It just made me super happy as a GSoC alumni (one of thousands!) to see that program continuing to do so much good, and how much mentorship in open source has grown over the years.

Scott Jenson’s presentation, How can GNOME explore bigger concepts? (YouTube), is another really important watch. Scott’s advice about breaking free from traps like constraint thinking really resonated with me, especially his suggestion to, at first, treat the software like it is magic and see where that leads.

That approach reminds me of how software improves in implementation, as well. It is natural for a codebase to start off with a whole bunch of “do magic” stub functions, then slowly morph into a chaotic mess until finally it turns into something that actually might just work. And getting to that last step usually involves deleting a lot of code, after it turns out you never needed all that much magic. But you have to be patient with the chaos to get there. You have to believe in it.

Speaking of magic, there is so much around GNOME that is exciting right now, so I spent some time just being excited about things.

  • I am always grateful for presentations about GTK and font rendering, which was satisfied on Friday with GTK’s new renderers – a deep dive (YouTube), and later with Advances in Font Technology (YouTube) – all great to watch if you’re interested in rendering stuff, or just fun Unicode trivia.
  • Eitan Isaacson talked about Spiel, a modern speech synthesis system: The Whole Spiel – A New Speech Synthesis API (YouTube). I loved his examples showing how it is important to satisfy several very different use cases for speech synthesis. While one user may value the precision of eSpeak at chipmunk speed, another user would prefer their computer talks like a human. And if we can get speech synthesis working well for non-accessibility reasons, there’s a real curb cut effect that should benefit everyone, including people who are just starting to use accessibility tools.
  • I went to the newest edition of Jonathan Blandford and Federico Mena Quintero’s presentation about Crosswords, GNOME Crosswords, Year Three (YouTube). It was abridged due to the format, but I especially enjoyed learning about the MVC-like data model for the application. It would be neat to see more GNOME apps using the same pattern.
  • There was a lot to learn about GNOME OS and OpenQA testing. The process for a new developer to get into hacking on a GNOME system component tends to be really awkward – particularly if that developer doesn’t want to mess up their host system. So You’re always breaking GNOME (YouTube) got me pretty excited about what’s coming with GNOME OS and sysext, as well as for testing in general. The OpenQA workshop on Monday was also well attended. Some people were unclear what openqa.gnome.org was doing, or what it can do for them. Just stepping through some conveniently broken tests and fixing them together was an excellent way to demystify the thing.
  • Also the lightning talks are good. Always watch the lightning talks.

Much of this work is being helped along with the Sovereign Tech Fund. This is the GUADEC where a lot of that is up for show, and I think it’s amazing to see so many quiet but high impact projects finally getting the attention (and funding) they deserve.

Outside of the event, it was great hanging out around Denver with all sorts of GNOME folks. I loved how many restaurants were perfectly happy to accommodate giant mobs of people. We saw huge mountains, the Colorado Rockies winning a baseball game, surprisingly good karaoke, and some very unique bars. On the last day, a large contingent of us headed to Meow Wolf, which was just a ridiculously fun way to spend a few hours. It reminded me of a point and click adventure game in the style of Myst and Riven, in all the best ways.

I was also suitably impressed by the 35 minute walk from where I was staying, around Empower Field, over the South Platte River, under some giant highway … which was actually entirely pleasant, for North America. This part of Denver has plenty of pedestrian bridges, which are both nice to walk along and really helpful to guide pedestrians through certain areas, so for me the obvious walking routes were completely different from (and as efficient as) the obvious driving routes.

The GUADEC dinner was, for me, the ideal GUADEC dinner. It was right there at the venue, at the same brewery people had been going to every day – but this time with free tacos! I truly appreciated the consistency there, for Denver has good beer and good tacos. I also appreciated that we were set up both inside and outside, at nice big tables with plenty of room for people to sit. It helped me to feel comfortable, and it was great for people who were there with families (which meant I got to meet said families!). It reminded me of the GUADEC 2022 taco party. An event like this really shines when people are moving around, and there was a lot of it here.

It turns out I didn’t take many pictures this year, but the official ones are better anyway. I did, however, take far too many pictures from the train ride home: I rode Amtrak, mostly for fun, on California Zephyr from Denver to Sacramento; then Coast Starlight from Sacramento to Seattle; and the smaller Cascades train from Seattle to Vancouver. It was beautiful, and seriously I think everyone should have the opportunity to try an overnight roomette on the Zephyr. My favourite part was sitting in the spacious observation car watching the world go by, getting only the tiniest amount of work done. I found tons of fun people to talk to, which I don’t usually do, but something about that space made it oddly comfortable. Everyone there was happy and sociable and relaxed. And I guess I was still in conference mode.

I returned home refreshed and excited for where GNOME is heading, especially with new progress around accessibility and developer tools. And with plenty of ideas for little projects I can work on this year.

Thanks to all the awesome people who make GUADEC happen, as well as my employer, Endless OS Foundation, for giving me the opportunity to spend several work days meeting people from around the GNOME community and wandering around Denver.

August 13, 2024

2 Billion Downloads & Other Milestones from 2024 So Far

It's been a busy year, and our platform and developer community-building efforts are paying off. Let's take a look at what we've been up to over the last six months, and measure its effect.

At the beginning of the year we announced an incredible milestone for Flathub: over one million active users. But we didn't slow down; in February we attended FOSDEM, then announced improved app moderation and app brand colors in preparation for some new features. Those efforts ultimately launched with the new homepage and featured app banners in April.

Milestones

Flathub: 70% of top apps verified, 100  curated quality apps, 4 million active users, 2 billion downloads

We're back with some new milestones thanks to the continued growth of Flathub as an app store and the incredible work of both our largely volunteer team and our growing app developer community:

Over 1,000 apps have been verified by their developers on Flathub, including 70% of the top 30 most popular apps. Developers of verified apps are ultimately in charge of their own app listings, and their updates are delivered directly to Flathub users while passing our automated testing and human review of things like permission changes.

100 curated quality apps

Over 100 apps are now passing our quality guidelines that include checks like icon contrast on both light and dark backgrounds, quality screenshots, and consistent app naming and descriptions so users get a better experience browsing Flathub. These guidelines are what enable us to curate and display visually appealing and consistent banners on the new home page, for example.

This means that between late February and July, the developers of over 100 apps went out of their way to improve—and sometimes make significant changes to—their apps' metadata to get ready for these new guidelines and features on Flathub. We're proud of these developers who have gone above and beyond, and we look forward to even more apps opting in over time.

Developers, if you'd like to see your app featured on the home page, please ensure you are following these guidelines! We've heard from app developers that getting your app featured not only gives a bump in downloads, but can also bring an increase in contributions to your project if it's open source.

4 million active users

Six months ago we passed one million active users based on a simple if conservative estimate of updates to a common runtime version we had served. Using that same methodology, we now estimate we have over 4 million active users!

As a reminder, this data is publicly available and anyone can check our work. In fact, I personally would love if we could work with a volunteer from the community to automate this statistic so we don't have to do manual collation each time. If you're interested, check out this GitHub issue.

Over 2 billion downloads

Those users have been busy, too: to date we have served over two billion downloads of different apps to people using different Linux flavors all around the world. This is a huge community of people trusting Flathub as their source of apps for Linux.

Thank you!

Thank you to our download-happy community of users who have put their trust in Flathub as their source of apps on Linux. Thank you to all of the developers of those apps, and in particular those developers who have chosen to follow the quality guidelines to help make Flathub a more consistent and engaging space. And thank you to every contributor to Flathub itself whether you are someone who fixed a typo in the developer documentation, helped translate the store, contributed mockups and design work, or spent countless hours keeping everything running smoothly.

As a grassroots effort, we wouldn't have become the Linux app store without each and every one of you. ❤️

August 11, 2024

The colors of GTK

Everything is better in color. Even better if it is HDR.

Seven-color and twelve-color color circles from 1708, attributed to Claude Boutet

In this post, we’ll provide an overview of what is happening with color in GTK, without diving too deeply into the weeds of colorimetry and color science.

The high-level goals of this effort are to enable proper handling of HDR content and color-managed workflows.

In the beginning, sRGB

Up until now, colors in GTK were always assumed to be in the sRGB color space. The omnipresent GdkRGBA struct is defined as specifying an sRGB color.

sRGB was a great thing 20 years ago, but there world is moving on to other color spaces that include a wider range of hues (such as Display-P3) or a bigger dynamic range (such as BT.2100-PQ).

After having this on our agenda for quite a while (initial investigations into this topic happened in 2021), we’re finally moving ahead with landing the foundations for a more colorful future.

Coming soon: GdkColorState

Earlier this year, we’ve merged some great work by Alice to support modern color syntax and color spaces in our CSS engine. Having expressive colors in CSS is great, but it can’t really shine if all our rendering machinery still requires colors to be specified in sRGB.

So over the past month or two, we’ve added support for doing our rendering in well-defined color spaces, and introduced an object representing these. It is called GdkColorState (don’t ask me why the name changed from space to state).

As of now, GdkColorState can represent sRGB and BT.2100-PQ, as well as their linearized variants. You cannot yet do much with these objects, they are mainly used internally by our renderers.

One thing you can already do though, is trying out how rendering in a linear color space will look, by setting the

GDK_DEBUG=linear

environment variable.

This is a change that we need to do eventually, to produce correct and understandable results, in particular when working with HDR content.

Doing all our compositing in a linear color state does look subtly different though, so want to delay this change until all of the surrounding work is done, and do it at beginning of a development cycle to give everybody time to adjust.

New protocols

The Wayland color management protocol has been a long time in the making, but it is hopefully close to leaving the experimental phase — kwin already has support for it, and there is a mutter branch as well.

We have added support for the xx-color-management-v4 protocol, so we get the preferred color state from the compositor (if it is supports that protocol), and we can tell it about the color state of the frames that we produce.

It is unlikely that your compositor will prefer an HDR color state like BT.2100-PQ today (unless you find the hidden switch to turn on experimental HDR support), but you can try how things look when GTK is rendering in that color state by setting the

GDK_DEBUG=hdr

environment variable.

Note that this doesn’t make HDR content appear on your screen — we do our rendering in HDR and translate the final frame back to  sRGB as the last step. Making HDR content appear on your screen requires a compositor that accepts such content.

In the future: More color states and linear compositing

We still have a long todo list to fully develop color support in GTK.

The highlights include

  • More color states (including OKLCH for better gradients and YUV for video content)
  • Color state aware rendering api (GdkColor, and new GtkSnapshot APIs)
  • Passing CSS color state information down to the renderer
  • Propagating color state information from gstreamer (for HDR, among other things)
  • Switching to linear compositing

A few of these will hopefully make it in time for the GTK 4.16 release.

Summary

Better color support is coming soon to GTK.

August 10, 2024

Refactoring Python dicts to proper classes

When doing a major refactoring in Meson, I came up with a interesting refactoring technique, which I have not seen before. Some search engineing did not find suitable hits. Obviously it is entirely possible that this is a known refactoring but I don't know its name. In any case, here's my version of it.

The problem

Suppose you have a Python class with the following snippet

class Something:
    def __init__(self):
        self.store = {}

Basically you have a dictionary as a member variable. This is then used all around the class that grows and grows. Then you either find a bug in how the dict is used or you want to add some functionality like, to pick an arbitrary requirement, all keys for this object that are strings, must begin with "s_".

Now you have a problem because you need to do arbitrary changes all around the code. You can't easily debug this. You can't add a breakpoint inside this specific dictionary's setter function (or maybe Python's debugger can do that but I don't know how to do that). Reading code that massages dictionaries directly is tricky, because it's all brackets and open code rather than calling named methods like do_operation_x.

The solution, step one

Create a Python class that looks like this:

class MeaningfulName:
    def __init__(self, *args, **kwargs):
        self.d = dict(*args, **kwargs)

    def contains(self, key):
        return key in self.d

    def __getitem__(self, key):
        return self.d[key]

    def __setitem__(self, key, value):
        self.d[key] = value

    ...

Basically you implement all the special methods that do nothing else than forward to the underlying dictionary. Then replace the self.store dictionary with this object. Nothing should have changed. Run tests to make sure. Then commit this to main. Let it sit in the code base for a while in case there are untested code paths that use functionality that you did not write.

Just doing this gives an advantage: it is easy to add breakpoints to methods that mutate the objects's state.

Step two

Pick any of the special dunder methods and rename it to a more meaningful name. Add validation code if you need. Run tests. Fix all errors by rewriting the calling code to use the new named method. Some methods might need to be replaced with multiple new methods that do slightly different things. For example you might want to add methods like set_value and update_if_changed.

Step three

Repeat step two until all dunder methods are gone.

August 09, 2024

#160 Web Happenings

Update on what happened across the GNOME project in the week from August 02 to August 09.

Sovereign Tech Fund

Thib announces

it’s been a little while since you received STF updates, but here is a high-level status of what is being worked on and why!

The Sovereign Tech Fund contracted with the GNOME Foundation with those 8 objectives:

  1. Encrypt user home directories individually
  2. Increase the range an quality of hardware support
  3. Modernize platform infrastructure
  4. Improve QA and development tooling
  5. Improve the state and compatibility of accessibility
  6. Design a new accessibility stack for the Linux Desktop
  7. Maintenance and modernization of security components
  8. Secure APIs for Wayland/Flatpak

There is work going on in several areas, but for the sake of brevity, here are some highlights from the month of July.

Encrypt user home directories individually

On Linux systems, users are traditionally managed in a specific file called /etc/passwd. It doesn’t make a difference between technical and human accounts. It also only stores very basic information about users, and can only be modified by root. It’s a very rigid users database.

AccountsService is a “temporary” daemon (since 2010) bringing to manage users and store more extensive information about them: profile picture, parental control settings, preferred session to log into, languages, etc. It manages /etc/passwd and its own database.

homed is a modern replacement for AccountsService. It provides encryption for user data, and paves the way for future exciting platform security and flexibility improvements. Adrian made AccountsService integrate with homed in order to make the transition easier

He got the homed support for AccountsService MR merged. This MR was blocking all the homed MRs from landing.

Modernize platform infrastructure

GTK and libadwaita

GTK is a popular development toolkit to create graphical apps in the free desktop. libadwaita is a library based on GTK providing the building blocks for modern GNOME applications.

Alice opened an issue about platform library interface. She also did libadwaita releases: 1.4.7, 1.5.3, 1.6beta, 1.6rc.

She fixed issues on Dialog before beta:

Notifications

There are multiple options for apps to send notifications to the system, each with different features and constraints. Julian works on unwrapping the current situation and turning the XDG portal notification API into the go to standard for notifications with a focus on accessibility, consistency across different desktops, extensibility and modern features. Julian works on improving the platform API for displaying notifications on GNU/Linux desktops with main focus on GNOME Shell.

Intents System

Several platforms offer for native apps to open https links (e.g. opening maps.google.com links in the Google Maps app). The free desktop doesn’t anything other than registering as handlers for custom schemes. Andy is working on an intents system to supports https url.

Improve quality assurance and development tooling

systemd-sysupdate is an update system allowing immutable systems to apply lightweight delta updates. It helps image-based systems support immutability, auto-updates, adaptability, factory reset, uniformity and providing a trust chain from the bootloader all the way up.

sysupdate is a CLI tool. In order to be able to use systemd-sysupdate, sturdier services are needed. sysupdated is a service that provides a dbus API for sysupdate.

systemd-repart is a tool that systemd runs on boot that non-destructively repartitions disks.

Varlink is a kinda simplistic IPC protocol that works early during boot, before the dbus broker has started.

Adrian Vovk is making steady progress on sysupdate, sysupdated, and Varlink support for systemd-repart.

Improve the state and compatibility of accessibility

Joanie is cleaning up Orca’s code and supporting new keygrabs.

Maintenance and modernization of security components

The free desktop standardises the storage and usage of secrets (such as passphrases or SSH keys) via the secrets specification. gnome-keyring was the backend implementation, and libsecret the client-side of said specification.

gnome-keyring and libsecret are written in C and lack maintenance. oo7 is a modern Rust client-side library that respects the secrets specification. Dhanuka is extending oo7 to implement the backend side of secrets management, and ultimately replace gnome-keyring

Secure APIs for Wayland/Flatpak

Georges released xdg-desktop-portal-gnome. He started documenting the Flatpak a11y situation (and why it’s currently unresolvable).

He also merged a11y-related patches for xdg-dbus-proxy (https://github.com/flatpak/xdg-dbus-proxy/pull/61) flatpak (https://github.com/flatpak/flatpak/pull/5828) WebKit (https://github.com/WebKit/WebKit/pull/29052).

He finished and merged GLib documentation updates branch: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4113

And finally he merged gnome-shell and xdg-desktop-portal-gnome bits of the Notifications rework that Julian had worked on.

GNOME Core Apps and Libraries

WebKitGTK

GTK port of the WebKit rendering engine.

Michael Catanzaro says

I have fixed a notable bug in WebKitGTK that prevented cached (unused) subprocesses from suspending properly. WebKit would accidentally suspend the bwrap or flatpak-spawn supervisor process instead of the actual WebKit subprocess. With its supervisor process suspended, the actual WebKit subprocess would remain even after the application is closed, wasting memory. This fix will be present in WebKitGTK 2.44.3.

Web

Web browser for the GNOME desktop.

Jan-Michael Brummer reports

GNOME Web finally received an automatic form filler implementation based on Abdullah Alansari initial work.

Michael Catanzaro reports

Arak has landed several commits improving sorting of bookmarks in Epiphany 47.beta! The previous mysterious sort order has been replaced with alphabetical case-insensitive sort, with an exception for Favorites bookmarks, which sort before non-Favorites.

Michael Catanzaro announces

Jan-Michael Brummer added a Privacy Report dialog to Epiphany 47.beta to better show how the Intelligent Tracking Prevention feature works. You can now see which tracking domains have been blocked on which websites, and conversely on which websites a tracking domain has been blocked. This doesn’t make any changes to the workings of Intelligent Tracking Prevention, which has been supported since Epiphany 3.38; it just provides new visibility into what it is doing.

Mutter

A Wayland display server and X11 window manager and compositor library.

Bilal Elmoussaoui says

I spent the last weekend doing some code reshuffling around the accessibility bits of Mutter / GNOME Shell, documented some parts of it in https://belmoussaoui.com/blog/19-accessibility-adventure/

libspelling

hergertme reports

libspelling got support for spellchecking off of the main thread to improve performance of large documents. Text Editor and Builder have both been updated to use libspelling for spellchecking features.

GNOME Circle Apps and Libraries

gtk-rs

Safe bindings to the Rust language for fundamental libraries from the GNOME stack.

slomo reports

gtk4-rs 0.9 and gtk-rs-core 0.20 were just freshly released, just in time for also being included in the GNOME 47 release.

This release is again relatively small, mostly providing bindings to new APIs and improvements to the glib::clone! and glib::closure! macros to work better with cargo fmt and rust-analyzer.

As usual, at the same time gstreamer-rs 0.23 and gst-plugins-rs 0.13, libadwaita 0.7 and other related crates got a new release compatible with the new gtk4-rs and gtk-rs-core releases and their own set of changes.

See https://gtk-rs.org/blog/2024/07/17/new-release.html for more details.

Third Party Projects

Ronnie Nissan says

This week I releases v0.3.0 of Embellish, a simple nerd fonts manager, the updates include:

  • Upgrade to libadwaiata 1.5
  • Migrate to AboutDialog
  • Separate installed and available fonts
  • Consolidate the install and uninstall buttons.

You can get Embellish on flathub

GNOME Network Displays

Stream the desktop to Wi-Fi Display capable devices

lorbus reports

GNOME Network Displays (https://gitlab.gnome.org/GNOME/gnome-network-displays/) v0.93.0 is out! GND allows for screencasting a window, display or virtual display to Miracast and Chromecast devices. With this release, a couple of issues were fixed and support for VAH264 gstreamer hardware encoding was added. GND is looking for new contributors to help finish the daemonification and dbus API addition efforts that were started as part of last year’s GSoC with the goal of integrating it more tightly with GNOME Shell and GNOME Control Center. Give it a try, user feedback is also very much appreciated! Stable releases are available on Flathub and nightly builds in the GNOME Nightly Flatpak repo.

Internships

IlChitarrista announces

As the last third of GSoC starts we’re almost done with the Ignored Apps implementation, it will soon be possible to make some apps local only. Drivers and Development Tools will also be excluded from synchronization by default as they are automatically installed as necessary. Many thanks for the design feedback by Jamie Gravendeel, Adwaita and GTK suggestions by Alice Mikhaylenko and code review by my mentor Cogitri. If you’re interested in following development, here’s the merge request.

As we’re ahead of schedule, we’re now starting to look into the necessary steps for a future release on Flathub and inclusion in the GNOME Circle!

That’s all for this week!

See you next week, and be sure to stop by #thisweek:gnome.org with updates on your own projects!

August 08, 2024

Threaded Spellchecking

Last I mentioned I was doing an ABI cleanup of libspelling as it was really just a couple hour hack that got used by people.

Part of that was to make way for performing spellchecking off the GTK thread. That just landed in today’s libspelling 0.3.0 release. In one contrived benchmark, spellchecking a 10,000 line document was 8x faster. YMMV.

One of the reasons it’s faster is we don’t need to use the GtkTextIter API. Pango provides all the things we need to do that without the PangoLayout overhead. So we just skip right past that and implement iterators off-thread.

I also extracted the GtkTextBuffer adapter so that we may add a GtkEditable adapter in the future. Internally, the adapters provide a series of callbacks to the SpellingEngine.

You might wonder what happens if you make an edit that collides with a region being spellchecked off thread? The answer is quite simple, it checks for collisions and either adapts by adjusting offsets or discards irreconcilable collisions.

Both Text Editor and Builder have been migrated to using libspelling instead of its ancestor code. If you’re running nightly Flatpak of those, take a crack at it and report bugs.

“It testing could use.”

August 05, 2024

2024-08-05 Monday

  • Mail chew - with newly migrated mail servers; hmm. Amused that an unusual job description from LinkedIn generates more interesting spam:
    "Michael, as the Christian & Hacker at Collabora Ltd you know how hard choosing the right global employment and work payment partner can be."
    presumably some new AI super-brain made the connection.
  • 1:1's with Miklos, Lily, Chris, content review with Richard, catch up with Pedro & Eloy.
  • Enjoyed John Stott's The Message of the Sermon on the Mount in the evening.

I Entered My GitHub Credentials into a Phishing Website!

We all think we’re smart enough to not be tricked by a phishing attempt, right? Unfortunately, I know for certain that I’m not, because I entered my GitHub password into a lookalike phishing website a year or two ago. Oops! Fortunately, I noticed right away, so I simply changed my unique, never-reused password and moved on. But if the attacker were smarter, I might have never noticed. (This particular attack website was relatively unsophisticated and proxied only an unauthenticated view of GitHub, a big clue that something was wrong. Update: I want to be clear that it would have been very easy for the attacker to simply redirect me to the real github.com after stealing my credentials, in which case I would not have noticed the attack and would not have known to change my password.)

You might think multifactor authentication is the best defense against phishing. Nope. Although multifactor authentication is a major security improvement over passwords alone, and the particular attack that tricked me did not attempt to subvert multifactor authentication, it’s actually unfortunately pretty easy for phishers to defeat most multifactor authentication if they wish to do so:

  • Multifactor authentication based on SMS is extremely insecure (because SS7 is insecure)
  • Multifactor authentication based on phone calls is also insecure (because SIM swapping isn’t going away; determined attackers will steal your phone number if it’s an obstacle to them)
  • Multifactor authentication based on authenticator apps (using TOTP or HOTP) is much better in general, but still fails against phishing. When you paste your one-time access code into a phishing website, the phishing website can simply “proxy” the access code you kindly provided to them by submitting it to the real website. This only allows authenticating once, but once is usually enough.

Fortunately, there is a solution: passkeys. Based on FIDO2 and WebAuthn, passkeys resist phishing because the authentication process depends on the domain of the service that you’re actually connecting to. If you think you’re visiting https://example.com, but you’re actually visiting a copycat website with a Cyrillic а instead of Latin a, then no worries: the authentication will fail, and the frustrated attacker will have achieved nothing.

The most popular form of passkey is local biometric authentication running on your phone, but any hardware security key (e.g. YubiKey) is also a good bet.

target.com Is More Secure than Your Bank!

I am not joking when I say that target.com is more secure than your bank (which is probably still relying on SMS or phone calls, and maybe even allows you to authenticate using easily-guessable security questions):

Screenshot of target.com prompting the user to register a passkey
target.com is introducing passkeys!

Good job for supporting passkeys, Target.

It’s probably perfectly fine for Target to support passkeys alongside passwords indefinitely. Higher-security websites that want to resist phishing (e.g. your employer’s SSO service) should consider eventually allowing only passkeys.

No Passkeys in WebKitGTK

Unfortunately for GNOME users, WebKitGTK does not yet support WebAuthn, so passkeys will not work in GNOME Web (Epiphany). That’s my browser of choice, so I’ve never touched a passkey before and don’t actually know how well they work in practice. Maybe do as I say and not as I do? If you require high security, you will unfortunately need to use Firefox or Chrome instead, at least for the time being.

Why Was Michael Visiting a Fake github.com?

The fake github.com appeared higher than the real github.com in the DuckDuckGo search results for whatever I was looking for at the time. :(

Accessibility Adventure

As part of my effort to reduce Mutter dependencies, I finally found some time to focus on removing Cairo now that we can have a Wayland only build. Since the majority of the remaining usages are related to fonts, we would need to move CoglPango to be part of GNOME Shell's libst.

Merging Cally

Moving CoglPango also necessitates moving ClutterText and its corresponding accessibility implementation object, CallyText. The accessibility implementation is an object that inherits from AtkObject and can implement various interfaces to describe the type it corresponds to.

Inside Mutter, you would find the following types

  • ClutterActor -> CallyActor implements AtkComponent
  • ClutterClone -> CallyClone implements AtkComponent
  • ClutterStage -> CallyStage implements AtkComponent, AtkWindow
  • ClutterStageManager -> CallyRoot corresponds to the Atk.Role.APPLICATION
  • ClutterText -> CallyText implements AtkAction, AtkComponent, AtkEditableText, AtkText

Once you initialize accessibility inside Mutter, the first step is to set up the AtkUtil implementation, which overrides the get_root and returns a CallyRoot instance. The remaining types register an AtkObjectFactory using the provided macros.

CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_ACTOR, cally_actor, cally_actor_new);
CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_ACTOR, cally_actor);

Allowing Atk to know which type to instantiate in case it encounters a CLUTTER_TYPE_CLONE, for example. That usually happens when you call atk_gobject_accessible_for_object(actor), which creates an instance of CallyActor using cally_actor_new. Although, the factories are nice, they don't allow out-of-tree AtkObject implementations. But wait, how is GNOME Shell accessible then?

Inside GNOME Shell StWidget, the widget abstraction, overrides the ClutterActorClass.get_accessible virtual function and replaces the default behavior that would call atk_gobject_accessible_for_object to:

  • Getting the corresponding accessibility type GType from another StWidgetClass.get_accessible_type virtual function
  • Create an instance of that type using g_object_new (T->get_accessible_type())
  • Call atk_object_initialize on it
  • Keep a weak reference to it

After "upstreaming" both StWidgetClass.get_accessible_type and their override of ClutterActorClass.get_accessible to become the default behavior, I managed to completely get rid of the factories. The remainder of the migration was mostly renaming things and trying to emit accessibility state changes when the Clutter type updated its state where possible.

The patches are available at:

Testing Accessibility

Now that my branches of Mutter and GNOME Shell are working, I decided to test whether the reshuffling I did has caused any regressions. Unfortunately, Accerciser from the Fedora Rawhide repositories doesn't start due to a Python forward compatibility issue.

I remembered that Georges Basile Stavracas Neto started a re-implementation in C, called Elevado, so between fixing Accerciser or adding some missing features I wanted in Elevado, the choice was easy.

Current accessibility tree

And few hours later, I had managed to add some meaningful accessible names to various objects.

Accessibility tree from local development

Now that I have confirmed that my changes didn't break any obvious thing, my weekend fun is over.

August 04, 2024

2024-08-04 Sunday

  • Up early; All Saints - played Guitar, H. on Piano; Rick drumming - good, David S's run a family service.
  • Back for a Pizza lunch; slugged and read stories with the babes. Checked tent & packed with N.
  • Out to see James, Kate & Penelope, lovely to catch up with them in the afternoon.

Freedesktop Specs Website Update

The Freedesktop.org Specifications directory contains a list of common specifications that have accumulated over the decades and define how common desktop environment functionality works. The specifications are designed to increase interoperability between desktops. Common specifications make the life of both desktop-environment developers and especially application developers (who will almost always want to maximize the amount of Linux DEs their app can run on and behave as expected, to increase their apps target audience) a lot easier.

Unfortunately, building the HTML specifications and maintaining the directory of available specs has become a bit of a difficult chore, as the pipeline for building the site has become fairly old and unmaintained (parts of it still depended on Python 2). In order to make my life of maintaining this part of Freedesktop easier, I aimed to carefully modernize the website. I do have bigger plans to maybe eventually restructure the site to make it easier to navigate and not just a plain alphabetical list of specifications, and to integrate it with the Wiki, but in the interest of backwards compatibility and to get anything done in time (rather than taking on a mega-project that can’t be finished), I decided to just do the minimum modernization first to get a viable website, and do the rest later.

So, long story short: Most Freedesktop specs are written in DocBook XML. Some were plain HTML documents, some were DocBook SGML, a few were plaintext files. To make things easier to maintain, almost every specification is written in DocBook now. This also simplifies the review process and we may be able to switch to something else like AsciiDoc later if we want to. Of course, one could have switched to something else than DocBook, but that would have been a much bigger chore with a lot more broken links, and I did not want this to become an even bigger project than it already was and keep its scope somewhat narrow.

DocBook is a markup language for documentation which has been around for a very long time, and therefore has older tooling around it. But fortunately our friends at openSUSE created DAPS (DocBook Authoring and Publishing Suite) as a modern way to render DocBook documents to HTML and other file formats. DAPS is now used to generate all Freedesktop specifications on our website. The website index and the specification revisions are also now defined in structured TOML files, to make them easier to read and to extend. A bunch of specifications that had been missing from the original website are also added to the index and rendered on the website now.

Originally, I wanted to put the website live in a temporary location and solicit feedback, especially since some links have changed and not everything may have redirects. However, due to how GitLab Pages worked (and due to me not knowing GitLab CI well enough…) the changes went live before their MR was actually merged. Rather than reverting the change, I decided to keep it (as the old website did not build properly anymore) and to see if anything breaks. So far, no dead links or bad side effects have been observed, but:

If you notice any broken link to specifications.fd.o or anything else weird, please file a bug so that we can fix it!

Thank you, and I hope you enjoy reading the specifications in better rendering and more coherent look! 😃

July 31, 2024

Ptyxis in Fedora 40

Ptyxis has arrived in Fedora 40 thanks to my Red Hat colleague Nieves Montero Fernandez. You can sudo dnf install ptyxis to get it.

Discussions are ongoing to make Ptyxis the default terminal for Fedora Workstation (and Silverblue) though it has already been approved by FESCo. One benefit of this is that we can finally remove the downstream patches to gnome-terminal and return it to a more upstream experience.

Additionally, Ptyxis has landed in the developing CentOS Stream (c10s). Project Bluefin also ships Ptyxis as the default terminal.

Some distributions are configuring it with -Dgeneric=terminal which means it will look like a regular ol’ terminal without the Ptyxis name or branding. In fact it uses the previous “prompt” iconography.

If you ship GNOME on your distribution and want help switching to Ptyxis, feel free to reach out.

July 30, 2024

Adding error highlighting to the TinySPARQL web IDE

One of the most recent features I worked on in this GSoC project was to add error highlighting onto the editor if the sparql server returns an error. Parser errors are designed to be returned alongside a byte number where the error originates, so surely that wouldn’t be too difficult right? Well it turned out to involve a bit more engineering than expected.

To start off, the editor we built using CodeMirror cannot be styled directly using CSS, as the styling will not persist over changes in the editor state (e.g. when you start typing or change the position of the cursor). The way this is intended to be done with this package is to use a Decoration object instead. There are different types of decorations including lines and marks, and using the range() method on the Decoration object I would be able to introduce a persisting decoration for a certain byte range in the editor text.

For example, if there is an error on the line starting from byte 16, the following code will produce the decoration I need to highlight the entire line in red:

const errorLine = new Decoration.line({
  class: "redBackground"
}).range(16);

However, this is not enough as the decoration needs to be dispatched to the existing editor view, and the documentations weren’t very clear on how to do this. Luckily, after taking some time to scrutinise this page, I managed to pick out what I need.

There were 2 more key objects I needed from the CodeMirror library: a StateEffect to be dispatched as trigger and a StateField to monitor the effects, producing the error highlights when necessary. The complete code ended up looking something like this:

const errorLine = Decoration.mark({ 
  class: "error-line", // this class gives a red underline
  inclusiveEnd: true
 });

const errorLineEffect = StateEffect.define<{ pos: number }>();
const errorLineField = StateField.define<DecorationSet>({
  create() {
    return Decoration.none;
  },
  update(field, tr) {
    field = field.map(tr.changes);
    let errorLineTriggered = false;

    for (let e of tr.effects) if (e.is(errorLineEffect)) {
      const charAtErrorLine = view.state.doc.toString()[e.value.from - 1];

      // the code below adds the errorLine decoration to the correct positions
      if (charAtErrorLine == "\n") {
        field = field.update({
          add: [errorLine.range(realErrorPos - 1, realErrorPos)]
        });
      } else {
        field = field.update({
          add: [errorLine.range(realErrorPos, realErrorPos   1)]
        });
      }
      errorLineTriggered = true;
    }

    // remove the decoration once editor text changed as error may no longer exist/may now be in a different position
    if (tr.docChanged && !errorLineTriggered)
    {
      field = field.update({
        filter: (f, t, value) => !value.eq(errorLine)
      })
    }
    return field;
  },
  provide: f => EditorView.decorations.from(f)
})

// function to trigger errorLine application
export function setErrorLine (pos: number) {
  let effects: StateEffect<unknown>[] = [ errorLineEffect.of({ pos }) ];

  if (!view.state.field(errorLineField, false)) 
    effects.push(StateEffect.appendConfig.of([errorLineField]))
    
  view.dispatch({ effects });
}

And this works as intended, producing the following in the the interface:

But there was one final problem, this does not work for strings of all encoding, e.g. a query like this: select ('👩🏿‍🦱👩🏽‍🦳' AS ?str) { asdf } will trigger an error response at the wrong position. Luckily, this was a relatively simple fix using TextEncoder/Decoder in JS, as demonstrated with our example query below:

July 29, 2024

How to Get Hacked by North Korea

Good news: exploiting memory safety vulnerabilities is becoming more difficult. Traditional security vulnerabilities will remain a serious threat, but attackers prefer to take the path of least resistance, and nowadays that is to attack developers rather than the software itself. Once the attackers control your computer, they can attempt to perform a supply chain attack and insert backdoors into your software, compromising all of your users at once.

If you’re a software developer, it’s time to start focusing on the possibility that attackers will target you personally. Yes, you. If you use Linux, macOS, or Windows, take a moment to check your home directory for a hidden .n2 folder. If it exists, alas! You have been hacked by the North Koreans. (Future malware campaigns will presumably be more stealthy than this.)

Attackers who target developers are currently employing two common strategies:

  • Fake job interview: you’re applying to job postings and the recruiter asks you to solve a programming problem of some sort, which involves installing software from NPM (or PyPI, or another language ecosystem’s package manager).
  • Fake debugging request: you receive a bug report and the reporter helpfully provides a script for reproducing the bug. The script may have dependencies on packages in NPM (or PyPI, or another language ecosystem’s package manager) to make it harder to notice that it’s malware. I saw a hopefully innocent bug report that was indistinguishable from such an attack just last week.

But of course you would never execute such a script without auditing the dependencies thoroughly! (Insert eyeroll emoji here.)

By now, most of us are hopefully already aware of typosquatting attacks, and the high risk that untrustworthy programming language packages may be malicious. But you might not have considered that attackers might target you personally. Exercise caution whenever somebody asks you to install packages from these sources. Take the time to start a virtual machine before running the code. (An isolated podman container probably works too?) Remember, attackers will target the path of least resistance. Virtualization or containerization raises the bar considerably, and the attacker will probably move along to an easier target.

July 26, 2024

Booting with Rust: Chapter 2

In a previous post I gave the context for my pet project ieee1275-rs, it is a framework to build bootable ELF payloads on Open Firmware (IEEE 1275). OF is a standard developed by Sun for SPARC and aimed to provide a standardized firmware interface that was rich and nice to work with, it was later adopted by IBM, Apple for POWER and even the OLPC XO.

The crate is intended to provide a similar set of facilities as uefi-rs, that is, an abstraction over the entry point and the interfaces. I started the ieee1275-rs crate specifically for IBM’s POWER platforms, although if people want to provide support for SPARC, G3/4/5s and the OLPC XO I would welcome contributions.

There are several ways the firmware takes a payload to boot, in Fedora we use a PReP partition type, which is a ~4MB partition labeld with the 41h type in MBR or 9E1A2D38-C612-4316-AA26-8B49521E5A8B as the GUID in the GPT table. The ELF is written as raw data in the partition.

Another alternative is a so called CHRP script in “ppc/bootinfo.txt”, this script can load an ELF located in the same filesystem, this is what the bootable CD/DVD installer uses. I have yet to test whether this is something that can be used across Open Firmware implementations.

To avoid compatibility issues, the ELF payload has to be compiled as a 32bit big-endian binary as the firmware interface would often assume that endianness and address size.

The entry point

As I entered this problem I had some experience writing UEFI binaries, the entry point in UEFI looks like this:

#![no_main]
#![no_std]
use uefi::prelude::*;

#[entry]
fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
  uefi::helpers::init(&mut system_table).unwrap();
  system_table.boot_services().stall(10_000_000);
  Status::SUCCESS
}

Basically you get a pointer to a table of functions, and that’s how you ask the firmware to perform system functions for you. I thought that maybe Open Firmware did something similar, so I had a look at how GRUB does this and it used a ppc assembler snippet that jumps to grub_ieee1275_entry_fn(), yaboot does a similar thing. I was already grumbling of having to look into how to embed an asm binary to my Rust project. But turns out this snippet conforms to the PPC function calling convention, and since those snippets mostly take care of zeroing the BSS segment but turns out the ELF Rust outputs does not generate one (although I am not sure this means there isn’t a runtime one, I need to investigate this further), I decided to just create a small ppc32be ELF binary with the start function into the top of the .text section at address 0x10000.

I have created a repository with the most basic setup that you can run. With some cargo configuration to get the right linking options, and a script to create the disk image with the ELF payload on the PReP partition and run qemu, we can get this source code being run by Open Firmware:

#![no_std]
#![no_main]

use core::{panic::PanicInfo, ffi::c_void};

#[panic_handler]
fn _handler (_info: &PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
#[link_section = ".text"]
extern "C" fn _start(_r3: usize, _r4: usize, _entry: extern "C" fn(*mut c_void) -> usize) -> isize {
    loop {}
}

Provided we have already created the disk image (check the run_qemu.sh script for more details), we can run our code by executing the following commands:

$ cargo  nightly build --release --target powerpc-unknown-linux-gnu
$ dd if=target/powerpc-unknown-linux-gnu/release/openfirmware-basic-entry of=disk.img bs=512 seek=2048 conv=notrunc
$ qemu-system-ppc64 -M pseries -m 512 --drive file=disk.img
[...]
  Welcome to Open Firmware

  Copyright (c) 2004, 2017 IBM Corporation All rights reserved.
  This program and the accompanying materials are made available
  under the terms of the BSD License available at
  http://www.opensource.org/licenses/bsd-license.php


Trying to load:  from: /vdevice/v-scsi@71000003/disk@8000000000000000 ...   Successfully loaded

Ta da! The wonders of getting your firmware to run an infinite loop. Here’s where the fun begins.

Doing something actually useful

Now, to complete the hello world, we need to do something useful. Remeber our _entry argument in the _start() function? That’s our gateway to the firmware functionality. Let’s look at how the IEEE1275 spec tells us how we can work with it.

This function is a universal entry point that takes a structure as an argument that tells the firmware what to run, depending on the function it expects some extra arguments attached. Let’s look at how we can at least print “Hello World!” on the firmware console.

The basic structure looks like this:

#[repr(C)]
pub struct Args {
  pub service: *const u8, // null terminated ascii string representing the name of the service call
  pub nargs: usize,       // number of arguments
  pub nret: usize,        // number of return values
}

This is just the header of every possible call, nargs and nret determine the size of the memory of the entire argument payload. Let’s look at an an example to just exit the program:

#[no_mangle]
#[link_section = ".text"]
extern "C" fn _start(_r3: usize, _r4: usize, entry: extern "C" fn(*mut Args) -> usize) -> isize {
    let mut args = Args {
        service: "exit\0".as_ptr(),
        nargs: 0,
        nret: 0
    };

    entry (&mut args as *mut Args);
    0 // The program will exit in the line before, we return 0 to satisfy the compiler
}

When we run it in qemu we get the following output:

Trying to load:  from: /vdevice/v-scsi@71000003/disk@8000000000000000 ...   Successfully loaded
W3411: Client application returned.

Aha! We successfully called firmware code!

To be continued…

To summarize, we’ve learned that we don’t really need assembly code to produce an entry point to our OF bootloader (tho we need to zero our bss segment if we have one), we’ve learned how to build a valid OF ELF for the PPC architecture and how to call a basic firmware service.

In a follow up post I intend to show a hello world text output and how the ieee1275 crate helps to abstract away most of the grunt to access common firmware services. Stay tuned!

July 25, 2024

Booting with Rust: Chapter 1

I have been doing random coding experiments with my spare time that I never got to publicize much outside of my inner circles. I thought I would undust my blog a bit to talk about what I did in case it is useful for others.

For some background, I used to manage the bootloader team at Red Hat a few years ago alongside Peter Jones and Javier Martinez. I learned a great deal from them and I fell in love with this particular problem space and I have come to enjoy tinkering with experiments in this space.

There many open challenges in this space that we could use to have a more robust bootpath across Linux distros, from boot attestation for initramfs and cmdline, A/B rollbacks, TPM LUKS decryption (ala BitLocker)…

One that particularly interests me is unifying the firmware-kernel boot interface across implementations in the hypothetical absence of GRUB.

Context: the issue with GRUB

The priority of the team was to support RHEL boot path on all the architectures we supported. Namely x86_64 (legacy BIOS & UEFI), aarch64 (UEFI), s390x and ppc64le (Open Power and PowerVM).

These are extremely heterogeneous firmware interfaces, some are on their way to extinction (legacy PC BIOS) and some will remain weird for a while.

GRUB, (GRand Unified Bootloader) as it names stands, intends to be a unified bootloader for all platforms. GRUB has to support a supersetq of firmware interfaces, some of those, like legacy BIOS do not support much other than some rudimentary support disk or network access and basic graphics handling.

To get to load a kernel and its initramfs, this means that GRUB has to implement basic drivers for storage, networking, TCP/IP, filesystems, volume management… every time there is a new device storage technology, we need to implement a driver twice, once in the kernel and once in GRUB itself. GRUB is, for all intent and purposes, an entire operating system that has to be maintained.

The maintenance burden is actually quite big, and recently it has been a target for the InfoSec community after the Boot Hole vulnerability. GRUB is implemented in C and it is an extremely complex code base and not as well staffed as it should. It implements its own scripting language (parser et al) and it is clear there are quite a few CVEs lurking in there.

So, we are basically maintaining code we already have to write, test and maintain in the Linux kernel in a different OS whose whole purposes (in the context of RHEL, CentOS and Fedora) its main job is to boot a Linux kernel.

This realization led to the initiative that these days are taking shape in the discussions around nmbl (no more boot loader). You can read more about that in that blog post, I am not actively participating in that effort but I encourage you to read about it. I do want to focus on something else and very specific, which is what you do before you load the nmble kernel.

Booting from disk

I want to focus on the code that goes from the firmware interface to loading the kernel (nmbl or otherwise) from disk. We want some sort of A/B boot protocol that is somewhat normalized across the platforms we support, we need to pick the kernel from the disk.

The systemd community has led some of the boot modernization initiatives, vocally supporting the adoption of UKI and signed pre-built initarmfs images, developing the Boot Loader Spec, and other efforts.

At some point I heard Lennart making the point that we should standardize on using the EFI System Partition as /boot to place the kernel as most firmware implementations know how to talk to a FAT partition.

This proposal caught my attention and I have been pondering if we could have a relatively small codebase written in a safe language (you know which) that could support a well define protocol for A/B booting a kernel in Legacy BIOS, S390 and OpenFirmware (UEFI and Open Power already support BLS snippets so we are covered there).

My modest inroad into testing this hypothesis so far has been the development of ieee1275-rs, a Rust module to write programs for the Open Firmware interface, so far I have not been able to load a kernel by myself but I still think the lessons learned and some of the code could be useful to others. Please note this is a personal experiment and nothing Red Hat is officially working on.

I will be writing more about the technical details of this crate in a follow up blog post where I get into some of the details of writing Rust code for a firmware interface, this post is long enough already. Stay tuned.

July 24, 2024

whippet progress update: funding, features, future

Greets greets! Today, an update on recent progress in Whippet, including sponsorship, a new collector, and a new feature.

the lob, the pitch

But first, a reminder of what the haps: Whippet is a garbage collector library. The target audience is language run-time authors, particularly “small” run-times: wasm2c, Guile, OCaml, and so on; to a first approximation, the kinds of projects that currently use the Boehm-Demers-Weiser collector.

The pitch is that if you use Whippet, you get a low-fuss small dependency to vendor into your source tree that offers you access to a choice of advanced garbage collectors: not just the conservative mark-sweep collector from BDW-GC, but also copying collectors, an Immix-derived collector, generational collectors, and so on. You can choose the GC that fits your problem domain, like Java people have done for many years. The Whippet API is designed to be a no-overhead abstraction that decouples your language run-time from the specific choice of GC.

I co-maintain Guile and will be working on integrating Whippet in the next months, and have high hopes for success.

bridgeroos!

I’m delighted to share that Whippet was granted financial support from the European Union via the NGI zero core fund, administered by the Dutch non-profit, NLnet foundation. See the NLnet project page for the overview.

This funding allows me to devote time to Whippet to bring it from proof-of-concept to production. I’ll finish the missing features, spend some time adding tracing support, measuring performance, and sanding off any rough edges, then work on integrating Whippet into Guile.

This bloggery is a first update of the progress of the funded NLnet project.

a new collector!

I landed a new collector a couple weeks ago, a parallel copying collector (PCC). It’s like a semi-space collector, in that it always evacuates objects (except large objects, which are managed in their own space). However instead of having a single global bump-pointer allocation region, it breaks the heap into 64-kB blocks. In this way it supports multiple mutator threads: mutators do local bump-pointer allocation into their own block, and when their block is full, they fetch another from the global store.

The block size is 64 kB, but really it’s 128 kB, because each block has two halves: the active region and the copy reserve. It’s a copying collector, after all. Dividing each block in two allows me to easily grow and shrink the heap while ensuring there is always enough reserve space.

Blocks are allocated in 64-MB aligned slabs, so you get 512 blocks in a slab. The first block in a slab is used by the collector itself, to keep metadata for the rest of the blocks, for example a chain pointer allowing blocks to be collected in lists, a saved allocation pointer for partially-filled blocks, whether the block is paged in or out, and so on.

The PCC not only supports parallel mutators, it can also trace in parallel. This mechanism works somewhat like allocation, in which multiple trace workers compete to evacuate objects into their local allocation buffers; when an allocation buffer is full, the trace worker grabs another, just like mutators do.

However, unlike the simple semi-space collector which uses a Cheney grey worklist, the PCC uses the fine-grained work-stealing parallel tracer originally developed for Whippet’s Immix-like collector. Each trace worker maintains a local queue of objects that need tracing, which currently has 1024 entries. If the local queue becomes full, the worker will publish 3/4 of those entries to the worker’s shared worklist. When a worker runs out of local work, it will first try to remove work from its own shared worklist, then will try to steal from other workers.

Of course, because threads compete to evacuate objects, we have to use atomic compare-and-swap instead of simple forwarding pointer updates; if you only have one mutator thread and are OK with just one tracing thread, you can avoid the ~30% performance penalty that atomic operations impose. The PCC generally starts to win over a semi-space collector when it can trace with 2 threads, and gets better with each thread you add.

I sometimes wonder whether the worklist should contain grey edges or grey objects. MMTk seems to do the former, and bundles edges into work packets, which are the unit of work sharing. I don’t know yet what is best and look forward to experimenting once I have better benchmarks.

Anyway, maintaining an external worklist is cheating in a way: unlike the Cheney worklist, this memory is not accounted for as part of the heap size. If you are targetting a microcontroller or something, probably you need to choose a different kind of collector. Fortunately, Whippet enables this kind of choice, as it contains a number of collector implementations.

What about benchmarks? Well, I’ll be doing those soon in a more rigorous way. For now I will just say that it seems to behave as expected and I am satisfied; it is useful to have a performance oracle against which to compare other collectors.

finalizers!

This week I landed support for finalizers!

Finalizers work in all the collectors: semi, pcc, whippet, and the BDW collector that is a shim to use BDW-GC behind the Whippet API. They have a well-defined relationship with ephemerons and are multi-priority, allowing embedders to build guardians or phantom references on top.

In the future I should do some more work to make finalizers support generations, if the collector is generational, allowing a minor collection to avoid visiting finalizers for old objects. But this is a straightforward extension that will happen at some point.

future!

And that’s the end of this update. Next up, I am finally going to tackle heap resizing, using the membalancer approach. Then basic Windows and Mac support, then I move on to the tracing and performance measurement phase. Onwards and upwards!

July 23, 2024

Accessibility Hackathon

When you stop and think about it, user interfaces are almost entirely designed around sight: they display graphical elements that are mostly interacted with by pointing and clicking (or touching). However, as we know, not everyone has “perfect” vision: some are color blind, some are short sighted, some are long sighted, etc. In many cases, these people can still interact with the same user interfaces as everyone else, but those with more severe visual impairments need a different method to use their computers.

That’s when the Screen Reader comes in! The Screen Reader is a software that adapts computers to the needs of users with low vision or blindness, by reading descriptions of user interfaces out loud and facilitating keyboard navigation.

Today, we will explore the web with Orca, the Screen Reader of the Linux Desktop! After that, we will contribute towards improving the experience of Orca users.

Mind you that Screen Readers have tons of advanced features to empower users with as much efficiency as possible. Because of that, it can be challenging to use this kind of software if you are not used to it. I invite you to embrace this challenge as an opportunity to experience the desktop from a different perspective, and to appreciate the way other people user their computers.

Without further ado, let’s get going!

Part I - Exploring the Screen Reader

Enabling and Disabling Orca

You can enable and disable Orca by pressing Super Alt s. This can also be configured via GNOME Settings, under “Accessibility” > “Seeing” > “Screen Reader”.

&ldquo;Seeing&rdquo; panel within GNOME Settings

Turn up and the volume and make sure you hear a robotic voice saying “Screen Reader on” and “Screen Reader off”. Then, open the Firefox web browser and check if Orca describes the current open tab. If it is quiet, try closing and re-opening Firefox again or disabling and re-enabling Orca.

Controlling Orca

Orca is controlled entirely from the keyboard, having dozens of shortcuts. Besides, these keyboard shortcuts are slightly different if you have a Number Pad (NumPad) or not. I have laid out the most important ones below, but an exhaustive list can be found in the quite excellent Orca documentation.

Orca Modifier

Key Without a NumPad (Laptop) With a NumPad (Desktop)
Orca Modifier Caps Lock NumPad Ins

Important shortcuts

Action Without a NumPad With a NumPad
Toggle screen reader Super Alt s Super Alt s
Interrupt screen reading Orca Modifier (press) Orca Modifier (press)
Toggle caret mode in Firefox F7 F7
Where am I NumPad Enter Orca Modifier Enter
Display page structure Alt Shift h Alt Shift h
Display page buttons Alt Shift b Alt Shift b
Read current line NumPad 8 Orca Modifier i
Read current word NumPad 5 Orca Modifier k
Read onward NumPad Orca Modifier ;
Next link k k
Previous link Shift k Shift k
Next paragraph p p
Previous paragraph Shift p Shift p
Next button p p
Previous paragraph Shift p Shift p

Activity: Exploring the web with Orca

Open the Firefox web browser and head to https://flathub.org. Find an app that looks interesting to you and explore its page using Orca. Try to navigate the entire page!

Feel free to use the questions below as a guide to your exploration:

  1. Can you read the app description? Try to do it twice.
  2. Can you click the “Install” button? What happens when you do?
  3. Can you figure out if the app is verified or not? How so?
  4. Can you guess what the screenshots of the app are showing?
  5. Can you find the link to the source code of the app?

Part II - Captioning Screenshots in Flathub

Flatpak and Flathub

Flatpak is a modern packaging technology, which aims to work on all Linux distributions. In turn, Flathub is an app store for software that was packaged using Flatpak. Flathub has been embraced as the main channel for publishing applications by many projects, most notably GNOME!

App listings

Each app that is published on Flathub has a page for itself, which is called “app listing”. You will find lots of important information about an app on its listing, such as:

  • Name
  • Description
  • Author
  • License
  • Permissions
  • Screenshots

All this information is sourced from a special file called “MetaInfo File”, that is typically hosted on the app’s repository. Its filename usually ends with .metainfo.xml.in.in. You can click here to see an example of a MetaInfo File. You can read more about this file format on Flathub’s MetaInfo Guidelines.

Screenshot captions

To promote high-quality listings, Flathub publishes its own Quality Guidelines.. These guidelines encourage maintainer’s to add captions to their images, which, as we learned in the first activity, helps people who use Screen Readers understand the content of the screenshots of the app. To quote the Quality Guidelines:

Every screenshot should have a caption briefly describing it. Captions should only be one sentence and not end with a full stop. Don’t start them with a number.

To be more precise, good captions should clearly convey the functionality demonstrated in the screenshot, as well as give a general idea of the user interface on display. I have cataloged dozens of Flathub listings on this Google Sheet:

GUADEC 2024 - Accessibility Hackathon

There you find listings with Caption status set “Exemplary”, which are great models of how captions should be written.

Activity: Contributing with captions

Now, we will improve the accessibility of the Linux Desktop (albeit slightly), by contributing to Flathub with screenshot captioning. To do that, follow the instructions laid out below:

  1. Open the Google Sheet on this link
  2. Choose an app with Caption status set “Absent”
  3. Assign “In-progress” to its Caption status
  4. Put your name(s) on its Group column
  5. Head to the Flathub listing link of the app
  6. Find the repository of the app (usually under “Browser the source code” on “Links”)

    Flathub listing links tab

  7. Log in or create an account on the platform that hosts the repository of the app (GitHub, GitLab, etc)
  8. Go back to the repository of the app, and find its MetaInfo File. It is typically placed in a data/ directory, at the root of the repository.
  9. On the MetaInfo File, look for the screenshot tags and find the files referenced in it
  10. Inside each screenshot element, there is an image element. For each of those, create a new line with caption tags. The caption goes in between them, like so:
<screenshot>
 <image>https://raw.githubusercontent.com/SeaDve/Kooha/main/data/screenshots/screenshot2.png</image>
  <caption>In-progress recording duration and button to stop recording</caption>
</screenshot>
  1. When you are done writing your captions, proof read them and run them through a spellchecker. I haven’t found any great ones online, but Language Tool’s is passable.
  2. Commit your changes, following the commit message conventions of the project you are contributing to. If this convention is not documented anywhere, try to infer it looking at the commit history.
  3. Open a Pull Request (PR) / Merge Request (MR) with your contribution, explaining what you did and why it is important. You can use my explanation as a model.
  4. Put a link to your PR/MR on the sheet.
  5. Wait for the maintainer’s feedback

Part III: Farewell

Thank you for attending this workshop. I hope you had a good time!

Until the next one!

July 22, 2024

Renaming multiple files

After World War II, Jack Kirby and his partner Joe Simon began their first foray into the genre of crime comics. (Kirby would return to the topic briefly in 1954 and 1971.) Beginning in 1947 and tailing into 1951, the stories appeared largely in Headline Comics and Justice Traps the Guilty for Crestwood’s Prize Publications. In 2011, Titan Books published a selection of these stories in hardcover, but sixty percent of the stories from this time period aren’t included in the book, and at least 20 stories have never been reprinted. Unlike Simon & Kirby’s much more prolific romance offerings, all of the comics in question are in the public domain and available on Digital Comic Museum and Comic Book Plus sites, thanks to multiple volunteers. I set about creating my own collection of scanned pages.

When the downloaded .cbz files are extracted into a folder, the resulting image files have names like scan00.jpg, scan01.jpg, etc. In GNOME Files, selecting all the files for a given issue and pressing F2 brings up the batch rename dialogue.

Selecting the Find and replace text option, I replace “scan” with the book title and issue number, with “p” as a separator for the page number.

When all the stories have been added, the pages will be sorted by title and issue number. To enable sorting chronologically, a four- or six-digit prefix can be used to specify the cover date, in this case “4706” for June 1947. To add this I press F2 on the same selected files and use the Rename using a template option.

Using the Jack Kirby Checklist as a guideline, and discarding (very few) stories for insufficient Kirby content, my project yielded a folder containing 633 pages including covers.


Jack Kirby (1917-1994) was a pioneer in American comic books. He grew up on the Lower East Side of New York City (where he encountered real and aspiring gangsters first hand) and fought in northeastern France in 1944 as an infantryman in Patton’s Third Army. As a partner in Simon and Kirby and the manager of the S&K studio, Kirby defined the word cartoonist—he generally wrote, penciled, inked, and occasionally coloured his own stories.
Jack Kirby photo property of the Kirby Estate. Used with permission.

GUADEC 2024

Goodness, it’s been a long time since I blogged. I’ve got a lot of updates to give, but perhaps let’s keep this post short, and dedicated to publishing the details of the two talks I gave at GUADEC this year, for posterity. I plan to do some more blog posts in the near future with more updates from the past year and more details of the features I’ve been working on.

An update on parental controls and digital wellbeing for GNOME 47

This was my first talk at GUADEC this year, serving as a little teaser of the work I’ve been doing recently (sponsored by Endless Network via the GNOME Foundation) to add features to parental controls and digital wellbeing.

Thank you to Allan Day for fitting in work on the design for digital wellbeing this cycle, to Florian Müllner and Felipe Borges for reviewing all the code I’ve thrown at them, and to Dylan McCall for feedback on earlier versions of break reminders.

Here’s the recording, the slides, slide notes and source.

Somewhat merging gobject-introspection into GLib

This was the second talk, and a companion to Emmanuele’s talk about changes in introspection in GNOME 46. It gives an overview of how we’ve merged half of gobject-introspection into GLib recently, and what this means for app authors (basically nothing), binding developers (something, on a timeline of your choosing) and distributions (some packaging rework, for the GLib 2.78 and 2.80 releases).

Thank you to Emmanuele for spearheading this work in GLib and doing the gobject-introspection side of it, and the many GLib and gobject-introspection contributors for helping us stabilise this (in particular with build system improvements) after it landed.

Here’s the recording, the slides, slide notes and source.

July 19, 2024

Installing Nvidia Driver Will Be Easy Again in Fedora Workstation

The feature my team worked on – Nvidia Driver Installation with Secure Boot Supportwas approved by FESCo earlier this week and its upstream implementation was also approved several days ago, so it’s on its way to Fedora 41 and I decided to write a blog post with more context and our motivations behind it.

Installing the Nvidia drivers in Fedora Linux was not easy in the past. You had to add 3rd party repos and then install specific packages. Not very intuitive for beginners. That’s why we teamed up with the RPMFusion community which created a separate repository with the Nvidia driver that was enabled in Fedora Workstation if you agreed to enable third-party software sources. It also shipped AppStream metadata to integrate with app catalogs like GNOME Software. So all the user had to do was open GNOME Software, look up “nvidia”, and click to install it. Simple enough.

It only had one problem: it didn’t work with Secure Boot enabled. The next boot would simply fail if Secure Boot was enabled and the reason was not obvious for many users. It was not that significant when we came up with the solution, but it grew in significance as more and more machines had Secure Boot enabled.

The Fedora Workstation Working Group decided earlier this year that it would be better to remove the driver from GNOME Software given the fact that the current solution doesn’t work with Secure Boot. The repository remained among the approved third-party sources, but the user experience of installing the Nvidia driver was significantly degraded.

It’s really not something Fedora Workstation can afford because the Nvidia driver is more popular than ever in the AI craze. So we started thinking about a solution that would meet the criteria and work with Secure Boot. The most seamless solution would be to sign the module with the Fedora key, but that’s pretty much out of the question. Fedora wouldn’t sign a piece of closed source software from a third party repo.

So basically the only solution left is self-signing. It’s not ideal from the UX perspective. The user has to create a password for the machine owner key. The next time they boot, they have to go through several screens in terminal user interface of mokutil and enter the password. At such an early stage of the boot process the charset is pretty much limited to ASCII, so you can’t let the user use any other characters when creating the password in GNOME Software. But I think Milan Crha (devel) and Jakub Steiner (UX design), who worked on it, handled the problems pretty well.

The password is generated for the user.

When I was submitting the change, I was not expecting a lot of resistance. And if any, then questions about why we’re making proprietary software easily installable. But the biggest resistance was related to security. By enrolling a MOK, you allow all modules installed in the future to be signed by it as well.

I understand the security implications of it, but you’re already trusting any software from the package repository, you’ve enabled, with the root privileges anyway and the only other alternative is to disable Secure Boot completely which removes that security measure entirely. In addition, the solution with disabled Secure Boot has other problems: it is done differently on different computers, there is no single set of step-by-step instructions which we could give to all users. And they may not be able to disable Secure Boot at all.

On the other hand, we didn’t do a good job of informing users about the security implications in the original implementation and feedback from the community helped us come up with a better implementation with a reworked dialog. We’ve also added information about the security implications and an instruction how to remove the MOK when it’s no longer needed to the docs.

The approved version of the dialog.

So in Fedora Workstation 41, installing the Nvidia driver will be as easy as it can be within the constraints of Fedora policies. We still see this as a temporary solution for older Nvidia cards and until Nvidia rolls out its open source kernel module. Then, hopefully, this perennial pain for Linux users will finally be over.

July 15, 2024

Status update, 15/07/2024

Was a great weekend for me with a trip to Ovar, Portugal to watch amazing Bixiga 70 and eat delicious Portuguese food. Not such a great weekend for the England men’s football team or Donald trump.

Work has been hard this month, my client work transitioned into something of a support role for a system which we are supposed to be decommissioning, and gave me little head space to focus on other work.

Looking back at the last year, I’ve done a lot of interesting open source work, and I’m heading to the GNOME conference in Denver next week to talk about some of it. I’ll talk about QA on Friday during “You’re Always Breaking GNOME” and then on Monday: “Workshop: openQA testing for your GNOME app, module or service” which will cover how to write tests with openQA and how to use the in-development support for testing unmerged branches in modules.

Hybrid events

This is an interesting year for the GNOME project as it’s the first GUADEC split between two locations – one group in Denver and one in Berlin, plus many other folk at home. It’s great that we are trying this approach, especially this year where the choice of host country makes it very hard for many people to get a visa.

There’s a few reasons I opted to go to Denver.

Partly I was hoping to meet the Outreachy interns from the openQA internship we did in Q1, but due to minimal transport sponsorship this year they aren’t able to go. Partly I’ve always said that if I could visit one place in the US, it would be Colorado. But the main reason is a conversation I had two years ago with an American contributor, who said something like … “Of course its always the Americans who have to travel to Europe, you guys never make the trip to the US.”

I stopped and thought a while over that. I grew up in 1990’s Britain, and there was a very clear heirarchy of who is most important in the world.

  1. The USA
  2. England
  3. Everyone else

All the news, music, politics, technology, TV and cinema in the UK reflected this, and i imagine it’s the same today. The idea stayed with me that the USA is the dominant force in all things. And I never questioned that in open source software it was also the case.

Until I thought about it a bit. All the conferences I’ve been to have been in Europe. I’ve told a lot of people “You should come to FOSDEM” or “You should come to GUADEC” but nobody’s ever told me the inverse, “You should come to $X event that happens in the US.” Every friend I have in open source is someone who either lives in Europe or has travelled to Europe so we could meet. Maybe it’s true that for once, things are a bit Euro-centric?

Most of my free software contributions now are talking to people and making connections. I haven’t done any substantial development work in a long time, nor would I have capacity to start anything. (Which I hate, but at least it’s not chatting to chatbots).

I find it much easier to communicate in person, for better or worse. This year it’s time for the openQA testing work to meet the wider community, and to “sink or swim” based on how useful everyone finds it. There are some interesting things to announce in the world of desktop search as well, such as the death of Tracker.

I hope that this Denver trip will be an opportunity to share what I know with people who don’t or can’t travel to Europe, who I’m otherwise never going to meet in person.

I’ve been to a few hybrid events in the past which had only minimal connection between the remote and local participants, hopefully this year we will learn some ways to build better connections. Lets see how it goes… I’ll report back in a couple of weeks.

July 14, 2024

Goodbye Tracker, hello TinySPARQL and LocalSearch

It is our pleasure (Sam’s and mine) to announce the following project renames:

– The Tracker SPARQL library will now be known as TinySPARQL
– The Tracker Miners indexer will now be known as LocalSearch, or GNOME LocalSearch if you prefer.

This on one hand will meet the increasing popular demand to have the terms “Tracker” and “Miner” avoided (In case the subtleties of language evade you, these have gained some bad connotations over the years), and on the other hand fixes the blunder done when Tracker 1.x conceded its name to the SPARQL library when library and indexer were split for 2.x. This has been a source of confusion wrt the extent and intent of each component, even among GNOME peers.

I am personally hopeful for the rename of TinySPARQL, as there has been a great amount of effort spent in making it a most credible implementation of the SPARQL/RDF/RDFS sets of standards, which feels underutilized with the library being mostly typecast as a gateway to the indexer data. Perhaps the new name will help reach to wider audiences that will be appreciative of a lightweight alternative to the available enterprise-level SPARQL engines.

But I shouldn’t diminish the importance of the rename of LocalSearch, especially at a time when other operating systems are approaching search in orwellian ways in the name of convenience, it becomes important to take a stand. We think the new name is a simple enough guide of our goals and non-goals.

This is not just a superficial rename, however we the maintainers have grown wary of approaches that would require us to bump version to 4.x, and updating all the projects depending on TinySPARQL/LocalSearch in a synchronized manner. So we have gone for a soft approach that will be backwards compatible for the most part, and allow a transitional path. Here’s what you can do to “update”:

In C projects

You are encouraged to use the tinysparql-3.0.pc file to detect the TinySPARQL library. The tracker-sparql-3.0.pc file is still provided for backwards compatibility. In code, use #include <tinysparql.h> to include the SPARQL library headers.

In the future we might consider an API migration scheme that deprecates all API under the old namespace and replaces with with objects in the new namespace. Meanwhile, all API objects and symbols keep the “Tracker” namespace.

From gobject-introspection bindings

You can get a “sneak peek” by using the new namespaces right away, e.g. from python code:

#!/usr/bin/python3

import gi, sys
gi.require_version('Tsparql', '3.0')
from gi.repository import GLib, Gio, Tsparql

try:
    connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        None, # Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), # Ontology location
        None)

    # Create a resource containing RDF data
    resource = Tsparql.Resource.new(None)
    resource.set_uri('rdf:type', 'nmm:MusicPiece')

    # Create a batch, and add the resource to it
    batch = connection.create_batch()
    batch.add_resource(None, resource)

    # Execute the batch to insert the data
    batch.execute()

    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

Remember to make your project depend on tinysparql-3.0.pc to ensure an up-to-date enough version to have these new binding definitions. Of course backwards compatibility is preserved, and there will be GIR/typelib files for the old namespaces.

In Vala code

There is also a new VAPI file, to describe the TinySPARQL API under the Tsparql namespace:

// valac --pkg tsparql-3.0 tsparql.vala
using Tsparql;

int main (string[] argv) {
  try {
    var conn = Tsparql.Connection.new (Tsparql.ConnectionFlags.NONE, null, Tsparql.get_ontology_nepomuk(), null);

    // Create a resource containing RDF data
    var resource = new Tsparql.Resource(null);
    resource.set_uri ("rdf:type", "nmm:MusicPiece");

    // Create a batch, and add the resource to it
    var batch = conn.create_batch ();
    batch.add_resource (null, resource);

    // Execute the batch to insert the data
    batch.execute ();

    conn.close ();
  } catch (GLib.Error e) {
    stderr.printf ("Error: %s\n", e.message);
  }

  return 0;
}

And same as before, there will be also a VAPI file with the old namespace.

Accessing LocalSearch data

The LocalSearch indexer data will now alternatively be offered via the org.freedesktop.LocalSearch3 D-Bus name, as well as through the org.freedesktop.Tracker3.Miner.Files D-Bus name for backwards compatibility. This can be used as usual with the TinySPARQL D-Bus based connection, e.g. from Python:

#!/usr/bin/python3

import gi, sys
gi.require_version('Tsparql', '3.0')
from gi.repository import Tsparql

conn = Tsparql.SparqlConnection.bus_new(
    'org.freedesktop.LocalSearch3', None)
cursor = conn.query('select ("Hello World" as ?str) {}', None)
cursor.next()
print(cursor.get_string(0))

Command line utilities

The command line utilities for both projects have parted ways. Instead of a single command line utility with extensible subcommands, the will be distinct tinysparql and localsearch command line tools, each providing their own sensible set of subcommands. We trust it will be reasonably low effort to adapt to these changes, and reasonably intuitive.

Test utilities

In order to test search-related code, some projects were using the tracker-testutils-3.0.pc file to locate the tracker-sandbox helper script to help them implement a test harness for search features. This has now become more of a first class citizen as a localsearch test-sandbox subcommand. There is no longer a pkgconf file, we recommend to detect the presence of the localsearch command in build systems.

A note to distributors

The repositories for these projects are https://gitlab.gnome.org/GNOME/tinysparql and https://gitlab.gnome.org/GNOME/localsearch, and the tarballs distributed will have tinysparql-x.y.z and localsearch-x.y.z filenames (starting with tinysparql-3.8.alpha and localsearch-3.8.alpha which are already out of the door). We are understanding that a project rename will involve a more changes on your end(s) than ours, but we will greatly appreciate if you help us establish the new identities and follow up on the rename.

July 13, 2024

TinySPARQL Web IDE update

Hi GNOME community! It’s been a while 🙂 I caught COVID a while back so it took some time to catch back up on work but we’ve been making progress on the GSoC project. We now have a fully functional IDE with a smooth interface and syntax highlighting – take a look!

It is also equipped with basic error reporting functionalities.

With the basic structure and functionalities complete, our next steps will be to extend the error response to also reflect the line from which the error is originating from, as well as implementing a bookmarking feature to allow users to “save” their queries using links.

There have also been suggestions to create an RDF browsing interface on the project. This will be a challenge to implement as it would require quite a lot of flexibility in the display, but given that we still have quite a bit of time until the end of GSoC, it is a challenge I hope I would be ale to take on.

July 12, 2024

GNOME Foundation Announces Transition of Executive Director

Today, the GNOME Foundation announces that Holly Million will be departing from her role as Executive Director of the GNOME Foundation. An Interim Executive Director, Richard Littauer, has joined the Foundation this week. This arrangement ensures a smooth transition and continuity as we prepare for a new Executive Director search. Holly’s last day will be July 31, and she and Richard will be able to coordinate their efforts during this period. 

“I’m very proud of what I have accomplished in my time with GNOME,” Holly said, “and I am dedicated to working with Richard through a transition to ensure that everything I have been working on is handed off smoothly. I will be pursuing a PhD in Psychology and dedicating myself to my own private practice. I want to thank all the wonderful people I have had the pleasure of connecting with here at GNOME, including the staff, board, advisors, founders, and volunteers. You have created something of real value in the world, and I look forward to seeing what you do next.”

On behalf of the whole GNOME community, the Board of Directors would like to give our utmost thanks to Holly for her achievements during the past 10 months, including drafting a bold five-year strategic plan for the Foundation, securing two important fiscal sponsorship agreements with GIMP and Black Python Devs, writing our first funding proposal that will now enable the Foundation to apply for more grants, vastly improving our financial operations, and implementing a break-even budget to preserve our financial reserves. 

The Foundation’s Interim Executive Director, Richard Littauer, brings years of open source leadership as part of his work as an organizer of SustainOSS and CURIOSS, as a sustainability coordinator at the Open Source Initiative, and as a community development manager at Open Source Collective, and through open source contributions to many projects, such as Node.js and IPFS. The Board appointed Richard in June and is confident in his ability to guide the Foundation during this transitional period.

The Board will be meeting next week to set up a search plan for a permanent Executive Director, which will be announced after GUADEC. The Foundation looks forward to talking with GNOME members and the community and answering any questions next week at the Annual General Meeting (AGM) GUADEC in Denver, as well as asynchronously with those in Berlin and around the globe.

Profiling a web engine

One topic that interests me endlessly is profiling. I’ve covered this topic many times in this blog, but not enough to risk sounding like a broken record yet. So here we are again!

Not everyone may know this but GNOME has its own browser, Web (a.k.a. Epiphany, or Ephy for the intimates). It’s a fairly old project, descendant of Galeon. It uses the GTK port of WebKit as its web engine.

The recent announcement that WebKit on Linux (both WebKitGTK and WPE WebKit) switched to Skia for rendering brought with it a renewed interest in measuring the performance of WebKit.

And that was only natural; prior to that, WebKit on Linux was using Cairo, which is entirely CPU-based, whereas Skia had both CPU and GPU-based rendering easily available. The CPU renderer mostly matches Cairo in terms of performance and resource usage. Thus one of the big promises of switching to Skia was better hardware utilization and better overall performance by switching to the GPU renderer.

A Note About Cairo

Even though nowadays we often talk about Cairo as a legacy piece of software, there’s no denying that Cairo is really good at what it does. Cairo can and often is extremely fast at 2D rendering on the CPU, specially for small images with simple rendering. Cairo has received optimizations and improvements for this specific use case for almost 20 years, and it is definitely not a low bar to beat.

I think it’s important to keep this in mind because, as tempting as it may sound, simply switching to use GPU rendering doesn’t necessarily imply better performance.

Guesswork is a No-No

Optimizations should always be a byproduct of excellent profiling. Categorically speaking, meaningful optimizations are a consequence of instrumenting the code so much that the bottlenecks become obvious.

I think the most important and practical lesson I’ve learned is: when I’m guessing what are the performance issues of my code, I will be wrong pretty much 100% of the time. The only reliable way to optimize anything is to have hard data about the behavior of the app.

I mean, so many people – myself included – were convinced that GNOME Software was slow due to Flatpak that nobody thought about looking at app icons loading.

Enter the Profiler

Thanks to the fantastic work of Søren Sandmann, Christian Hergert, et al, we have a fantastic modern system profiler: Sysprof.

Sysprof offers a variety of instruments to profile the system. The most basic one uses perf to gather stack traces of the processes that are running. Sysprof also supports time marks, which allow plotting specific events and timings in a timeline. Sysprof also offers extra instrumentation for more specific metrics, such as network usage, graphics, storage, and more.

  • Screenshot of Sysprof's callgraph view
  • Screenshot of Sysprof's flamegraphs view
  • Screenshot of Sysprof's mark chart view
  • Screenshot of Sysprof's waterfall view

All these metrics are super valuable when profiling any app, but they’re particularly useful for profiling WebKit.

One challenging aspect of WebKit is that, well, it’s not exactly a small project. A WebKit build can easily take 30~50min. You need a fairly beefy machine to even be able to build a debug build of WebKit. The debug symbols can take hundreds of megabytes. This makes WebKit particularly challenging to profile.

Another problem is that Sysprof marks require integration code. Apps have to purposefully link against, and use, libsysprof-capture to send these marks to Sysprof.

Integrating with Sysprof

As a first step, Adrian brought the libsysprof-capture code into the WebKit tree. As libsysprof-capture is a static library with minimal dependencies, this was relatively easy. We’re probably going to eventually remove the in-tree copy and switch to host system libsysprof-capture, but having it in-tree was enough to kickstart the whole process.

Originally I started sprinkling Sysprof code all around the WebKit codebase, and to some degree, it worked. But eventually I learned that WebKit has its own macro-based tracing mechanism that is only ever implemented for Apple builds.

Looking at it, it didn’t seem impossible to implement these macros using Sysprof, and that’s what I’ve been doing for the past few weeks. The review was lengthy but behold, WebKit now reports Sysprof marks!

Screenshot of Sysprof with WebKit marks highlighted

Right now these marks cover a variety of JavaScript events, layout and rendering events, and web page resources. This all came for free from integrating with the preexisting tracing mechanism!

This gives us a decent understanding of how the Web process behaves. It’s not yet complete enough, but it’s a good start. I think the most interesting data to me is correlating frame timings across the whole stack, from the kernel driver to the compositor to GTK to WebKit’s UI process to WebKit’s Web process, and back:

Screenshot of Sysprof with lots of compositor and GTK and WebKit marks

But as interesting as it may be, oftentimes the fun part of profiling is being surprised by the data you collect.

For example, in WebKit, one specific, seemingly innocuous, completely bland method is in the top 3 of the callgraph chart:

Screenshot of Sysprof showing the callgraph view with an interesting result highlighted

Why is WebCore::FloatRect::contains so high in the profiling? That’s what I’m investigating right now. Who guessed this specific method would be there? Nobody, as far as I know.

Once this is out in a stable release, anyone will be able to grab a copy of GNOME Web, and run it with Sysprof, and help find out any performance issues that only reproduce in particular combinations of hardware.

Next Plans

To me this already is a game changer for WebKit, but of course we can do more. Besides the rectangular surprise, and one particular slowdown that comes from GTK loading Vulkan on startup, no other big obvious data point popped up. Specially in the marks, I think their coverage is still fairly small compared to what it could have been.

We need more data.

Some ideas that are floating right now:

  • Track individual frames and correlate them with Sysprof marks
  • Measure top-to-bottom-to-top latency
  • Measure input delay
  • Integrate with multimedia frames

Perhaps this will allow us to make WebKit the prime web engine for Linux, with top-tier performance, excellent system integration, and more. Maybe we can even redesign the whole rendering architecture of WebKit on Linux to be more GPU friendly now. I can dream high, can’t I? 🙂

In any case, I think we have a promising and exciting time ahead for WebKit on Linux!

Graphics offload continued

We first introduced support for dmabufs and  graphics offload last fall, and it is included in GTK 4.14. Since we last talked about, more improvements have happened, so it is time for another update.

Transformations

When you rotate your monitor, it changes its aspect from landscape (say, 1920 x 1200 to portrait (1200 x 1920). But the framebuffer that the compositor deals with stays 1920 x 1200.

There are some other situations where the content may need some transformation before it can be displayed. For example when showing the video stream from a front-facing camera.

D4 cycle graph

So far, GTK has treated flipped or rotated textures as not offloadable, but after some recent improvements, we will now pass such transformations along to the compositor when offloading content.

Quick detour

This is a good opportunity to explain that the GTK transformation API relies on you to provide classification into useful categories such as 2D translations, scales, rotations or arbitrary 3D transforms.

Therefore, it is much better to use specific methods such as gsk_transform_rotate or gsk_transform_translate instead of computing the transformation matrix yourself and using  gsk_transform_matrix, which does not give us any information about the kind of transformation we are dealing with. And without this information, we miss out on optimizations like the transformed offloading described above.

Benjamin gave a quick overview of our approach to transformations here.

Textures

The initial goal for the offload work was to enable passthrough of dmabuf content. Often, that is not what we get though. It is still far more common that we receive content in GL textures (e.g. from gstreamer). Thankfully, mesa has GL extensions that let us export textures as dmabufs. We’ve recently used this to enable offloading for GL textures.

Note that these extensions might not be available everywhere, and individual drivers can have problems with dmabuf support.

Summary

In the coming GTK 4.16 release, graphics offloading should work much more often.

You can try these improvements today, e.g. in the nightly build of the new Showtime app:

Showtime

July 11, 2024

Summer Maps

 I usually write a “Summer Maps” blog post around midsummer. It's a bit later this year. But here it comes!

 


 Since the last time there has been some visual updates.

Search Bar Tweak

Since version 45 we had an “Explore POIs” interface that is accessed via a menu button next to the main search entry. This button used a linked style to get shared rounded corner. This has had some visual glitches when showing the search results popover (resutling in the rounded corner getting „straightened” giving jarred look).

After playing with putting the menu button at the side without being linked, but that gives a little „unbalanced” look with the popover. So I decided to try out something different.

Using the secondary icon of the entry, indicating the „explore” action when no search term is entered.




More Adwaita Dialogs

Contributor “DaPigGuy” has converted the export image dialog to use the new dialog widget from libadwaita. Also the save file UI has been streamlined, and is now handled by the file select dialog when clicking „Save”.

One more Icon

One additional POI icon for playgrounds has been contributed by Jakub Steiner.

Playgrounds

Transitous Support

I've written about the Transitous project before.

There is now a work-in-progress branch implementing public transit routing using MOTIS: https://gitlab.gnome.org/GNOME/gnome-maps/-/merge_requests/440

Currently it works best when using the service at europe.motis-project.de.

This can be tested by setting the following environement variables when running the branch (from GNOME Builder it can be set either by entering the „execution terminal”, or by creating a custom run command).

TRANSITPLUGIN=Motis

MOTIS_BASE_URL=https://europe.motis-project.de


 
A sample of itineraries going from the  Louvre to the Eiffel tower in Paris.


And I would like to re-iterate that you can also help contributing public transit feeds to the Transitous project for your locality by creating pull requests at https://github.com/public-transport/transitous

Hill Shading

Another project James Westman has been working on is hillshading. This would make the map show terrain topology, which not only gives the map a nicer, more live appearance. It is also beneficial for example when hiking.


Currently it awailable in the web preview for the map style at https://maps.jwestman.net/

In the screenshot above showing the Sylarna mountain range: Wikipedia

Contrasting with how it looks currently when rendered by libshumate in Maps

 

Adding this in libshumate will require some additional work, as it will need support for transparent „overlay sources” to render the „TopoRGB” tiles (which will basically be an alpha channel rendering „on top” of the vector rendering (I hope I explained that somewhat correctly).

 

And that's going to be it for this time!

And stay tuned 😀 



July 10, 2024

Stepping down as GNOME Internships organizer

For the past nine years, I’ve been actively involved in our internship initiatives with Google Summer of Code and Outreachy. As an alumnus of these programs, I firmly believe they are great ways to onboard new contributors to GNOME and help students start a career in open-source software.

However, as my work responsibilities have grown and I’ve started some personal (non-software) projects, I’ve found myself with less time and energy for this type of contribution.

I’ve been discussing this with key people over the past year, and I plan to stay around to hand over responsibilities to other members of the Internship Committee. I will continue helping manage things for 2024, but I won’t be directly involved beyond that.

If you’re interested in helping with these activities, please reach out to the GNOME Internship Committee.

July 09, 2024

Outreachy Internship Blog Series: Understanding GNOME

When I first encountered GNOME, I struggled a lot with understanding what it was. This was during the Outreachy contribution phase, and its description included the term “desktop environment”.

At the time I had no idea what a desktop environment was. While developing skills in user experience design, I learned that a digital product could be an application, a website, or something in between: a web app. However, I didn’t realize it could also be a desktop environment, because I was oblivious to the notion of a desktop environment being a distinct component of an operating system.

Nonetheless, I braced myself for the journey ahead and took the first steps towards contributing to this design-driven project that I knew nothing about.

And So It begins!

It didn’t take long into my search for me to realize that I was a complete novice in the world of computers as understanding GNOME required me to be familiar with it.

I had begun my quest for understanding with a quick Google search for “desktop environment”, this led me to answers that intensified my confusion, prompting further questions that developed as follows:

What is an operating system? What is a computer? What are the core computer software? What are the functions of an operating system? What is Linux? Why do Linux distributions exist instead of a single Linux operating system? And so on…

By the end of my search, I knew a lot more than I did at the start, but I still lacked a satisfying understanding of what GNOME was. To make things worse, I also couldn’t tell what I was missing.

My research had revealed that GNOME, in some ways, transcended the definitions I’d found for a desktop environment, but it was also not an operating system. And yet, you could run GNOME OS independently in a virtual machine. So, what was it? A family of products? A desktop environment? Or a quasi-operating system?

Finding Answers

The entire time, I was reluctant to seek help from the mentors. I thought the subject of my confusion was too basic to bring to their attention. This, and a fear of appearing inadequate. Instead, I buried myself in more research hoping to somehow figure things out on my own, only returning to the channel intermittently to check for instructions and updates.

Yet, it was during one of these brief returns to the matrix channel that something unexpected caught my attention.

Upon close observation, some of the questions and comments on the channel revealed that I wasn’t the only one unsure about what GNOME was, and the mentors, having picked up on it, were responding insightfully to the questions behind the questions being asked.

I found my missing piece in one of these responses, and just like that, this comment flipped a switch and connected the dots in a manner that put things in the right perspective for me.

Armed with this new-found clarity, I was able to revisit my notes from earlier research and properly answer my discovery questions about GNOME.

The Lessons

I came out of this experience with a few solid realizations that continue to shape my perspective on embracing new challenges.

Firstly, I recognized that I was getting in my own way. Feeling insecure about my struggles was keeping me stuck, to the extent that I might have given up on making contributions to this project, save for the courage of a peer who could ask for help when they needed it.

Secondly, I learned that struggling is a natural part of growth and something that everyone experiences. It really isn’t something to be insecure about.

Thirdly, I realized that I was in an enabling community. The mentors anticipated that we would need help in different areas and their focus was on helping us get to a place where we could make the contributions that we wanted to. Watching their interactions with my peers dissipated my fears, and I’ve never hesitated to ask for help since then.

My Understanding of GNOME

My understanding of GNOME has evolved considerably since that first encounter and continues to deepen.

I now understand GNOME as an ecosystem that provides an inclusive, free, user-friendly computing experience for computers and devices powered by the Linux kernel.

From a technical point of view, this means that it is both the GUI and its interactions (i.e. how the operating system looks and feels), the programs and applications that constitute the computing experience, and the stacks that make if possible for distros to integrate and customize this computing experience.

I sadly cannot fit all that I’ve learned about GNOME in this blog post. However, you can visit the official GNOME website to learn more about the software and discover its powerful features and intuitive desktop experience.

It’s a gratifying thing to witness one’s own growth. And I am deeply grateful for the guidance of the mentors, the platforms (Outreachy and GNOME), and the cultural factors within these platforms that have enabled this particular one.

Thank you for reading!

 

July 07, 2024

GSoC 2024: Week 3-4 Report

GSoC 2024: Week 3-4 Report 


Project

Add support for the latest GIR attributes and GI-Docgen formatting to Valadoc

Mentor

Lorenz Wildberg


Week 3 : default-value attribute for property

Parsing the "default-value" attribute into a member access: In continuation of the work done previously, we tried to set the prop.initializer of the corresponding Vala.Property from the default-value attribute by parsing it as an enumerator member. We parsed the default-value into a member access in GirParser.process(), but trying to regenerate the bindings resulted in errors like this:


We got these errors because the default-value attribute in the vala GIRs actually contains the "c:identifier" of the enum member. However the function   parse_enumeration_member() in the GirParser parses it into an EnumValue using its "name" attribute and not the "c:identifier" attribute. Thus when we try to parse the default-value attribute into a member access, the parser cannot find any enum member corresponding to the value in the default-value attribute:

  The enum member with c:identifier="GTK_FILE_CHOOSER_ACTION_OPEN" is parsed with the name "open"

A temporary workaround is to parse enum members using their "c:identifier" attribute so that the value of the "default-value" attribute is the same as the symbol name of the EnumValue (the name of the symbol would now be the "c:identifier" of the enum member). This would fix the above error but it would also change the names of all enum members in the bindings to the value of their corresponding "c:identifier". This might be risky, so we are working on finding a viable solution that does not cause any breaking changes. Furthermore, we are also working on cases where the "default-value" attribute has two enum members separated by a binary OR - an example of this is in line 64623 in Gtk-4.0.gir :

default-value="GTK_FONT_CHOOSER_LEVEL_STYLE | GTK_FONT_CHOOSER_LEVEL_SIZE"

Here, the default-value cannot be parsed into a single enum member because it contains two members of type Gtk.FontChooserLevel.With the resolution of the above error and some minor tweaks, we will be able to successfully parse the default-value attribute into the bindings.

Week 4: glib:finish-func attribute for method

In the GirParser: In the previous week, we added a new property, finish_func in Vala.Method and set this property to the value of glib:finish-func. To further refine the support for this attribute, we used the finish_func property to decide whether the method is asynchronous, as there is currently no definitive rule for this in the GirParser. Since only async functions in GObject can have the glib:finish-func attribute, a method is async if it's finish-func property is not null. We added this feature, and then added a test case in tests/gir/method-class.gir to check whether a method with this attribute is declared as asynchronous in the bindings.

In the GirWriter: Further improvement was done to add the support for glib:sync-func, glib:async-func,and glib:finish-func in:
  • Synchronous methods: Made the GirWriter write the glib:async-func attribute if the method is synchronous and has a corresponding asynchronous version.
  • Asynchronous methods: Made the GirWriter write the glib:sync-func attribute if the method is asynchronous (and has a corresponding synchronous version), and the glib:finish-func attribute.
This involved making tweaks in the write_signature and do_write_signature methods because they write the GIR attributes for method. Finally, we updated the test cases in tests/girwriter to test these changes.

So this was the summary of our work in the previous two weeks. Thanks for reading :). In the coming days we plan to work on Valadoc and add support for the remaining GObject Introspection attributes!




July 01, 2024

GNOME- What I’d like to know as a newcomer

Navigating an open-source community for the first time can be challenging. Understanding the project and deciding which aspects or areas to contribute can be difficult. Today, I aim to answer a few common questions that newcomers to GNOME might have and help someone get started.

What is GNOME?

GNOME Logo

Simply put, GNOME is a free and open-source desktop interface that lets users interact with their computers through graphical elements like icons, windows, and menus, instead of just command lines. GNOME is widely used in Linux distributions like Fedora and Ubuntu.

Who Participates in the GNOME Community?

The GNOME community comprises a diverse group of people, including software developers, designers, translators, documentation writers, and users. These contributors come together to improve and maintain GNOME and a collection of other Free and Open Source Projects (FOSS) that use GNOME.

The community is friendly and virtually anyone who has a computer can be a part of it.

Why does GNOME exist?

GNOME aims to provide a consistent and enjoyable user experience for all Linux users, regardless of their technical background, and to make Linux operating systems easy for non-programmers. The community focuses on making the desktop environment intuitive and accessible while ensuring it remains powerful enough for advanced users.

What do I do in GNOME?

As part of my work as an Outreachy intern with GNOME, I’m involved in a project focused on conducting a series of short user research exercises. The goal is to gather insights into how people use particular apps on GNOME, their challenges, and what improvements can be made to enhance their experience. This project is crucial because user feedback directly informs design and development decisions, leading to a better overall product.

What excites me most about this project is the opportunity to directly impact the user experience of thousands of GNOME users worldwide. I can make GNOME more user-friendly and efficient by gathering and analyzing user feedback.

You can visit the GNOME handbook to explore different teams and learn how to join them.