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!

Matthias Clasen


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.


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.


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.


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:


Marcus Lundblad


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.


Transitous Support

I've written about the Transitous project before.

There is now a work-in-progress branch implementing public transit routing using MOTIS:

Currently it works best when using the service at

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).



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

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

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 😀 

Michael Catanzaro


Forcibly Set Array Size in Vala

Vala programmers probably already know that a Vala array is equivalent to a C array plus an integer size. (Unfortunately, the size is gint rather than size_t, which seems likely to be a source of serious bugs.) When you create an array in Vala, the size is set for you automatically by Vala. But what happens when receiving an array that’s created by a library?

Here is an example. I recently wanted to use the GTK function gdk_texture_new_for_surface(), which converts from a cairo_surface_t to a GdkTexture, but unfortunately this function is internal to GTK (presumably to avoid exposing more cairo stuff in GTK’s public API). I decided to copy it into my application, which is written in Vala. I could have put it in a separate C source file, but it’s nicer to avoid a new file and rewrite it in Vala. Alas, I hit an array size roadblock along the way.

Now, gdk_texture_new_for_surface() calls cairo_image_surface_get_data() to return an array of data to be used for creating a GBytes object to pass to gdk_memory_texture_new(). The size of the array is cairo_image_surface_get_height (surface) * cairo_image_surface_get_stride (surface). This is GTK’s code to create the GBytes object:

bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
                                    cairo_image_surface_get_height (surface)
                                    * cairo_image_surface_get_stride (surface),
                                    (GDestroyNotify) cairo_surface_destroy,
                                    cairo_surface_reference (surface));

The C function declaration of g_bytes_new_with_free_func() looks like this:

g_bytes_new_with_free_func (
  gconstpointer data,
  gsize size,
  GDestroyNotify free_func,
  gpointer user_data

Notice it takes both the array data and its size size. But because Vala arrays already know their size, Vala bindings do not contain a separate parameter for the size of the array. The corresponding Vala API looks like this:

public Bytes.with_free_func (owned uint8[]? data, DestroyNotify? free_func, void* user_data)

Notice there is no way to pass the size of the data array, because the array is expected to know its own size.

I actually couldn’t figure out how to pass a DestroyNotify in Vala. There’s probably some way to do it (please let me know in the comments!), but I don’t know how. Anyway, I compromised by creating a GBytes that copies its data instead, using this simpler constructor:

public Bytes (uint8[]? data)

My code looked something like this:

unowned uchar[] data = surface.get_data ();
return new Gdk.MemoryTexture (surface.get_width (),
                              surface.get_height (),
                              BYTE_ORDER == ByteOrder.LITTLE_ENDIAN ? Gdk.MemoryFormat.B8G8R8A8_PREMULTIPLIED : Gdk.MemoryFormat.A8R8G8B8_PREMULTIPLIED,
                              new Bytes (data),
                              surface.get_stride ());

But this code crashes because the size of data is unset. Without any clear way to pass the size of the array to the Bytes constructor, I was initially stumped.

My first solution was to create my own array on the stack, use Posix.memcpy to copy the data to my new array, then pass the new array to the Bytes constructor. And that actually worked! But now the data is being copied twice, when the C version of the code doesn’t need to copy the data at all. I knew I had to copy the data at least once (because I didn’t know how else to manually call cairo_surface_destroy() and cairo_surface_ref() in Vala, at least not without resorting to custom language bindings), but copying it twice seemed slightly egregious.

Eventually I discovered there is an easier way:

data.length = surface.get_height () * surface.get_stride ();

It’s that simple. Vala arrays have a length property, and you can assign to it. In this case, the bindings didn’t know how to set the length, so it defaulted to the maximum possible value, leading to the crash. After setting the length manually, the code worked properly. Here it is.

Felipe Borges


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.

Andy Wingo


copying collectors with block-structured heaps are unreliable

Good day, garbage pals! This morning, a quick note on “reliability” and garbage collectors, how a common GC construction is unreliable, and why we choose it anyway.

on reliability

For context, I’m easing back in to Whippet development. One of Whippet’s collectors is a semi-space collector. Semi-space collectors are useful as correctness oracles: they always move objects, so they require their embedder to be able to precisely enumerate all edges of the object graph, to update those edges to point to the relocated objects. A semi-space collector is so simple that if there is a bug, it is probably in the mutator rather than the collector. They also have well-understood performance, as as such are useful when comparing performance of other collectors.

But one other virtue of the simple semi-space collector is that it is reliable, in the sense that given a particular set of live objects, allocated in any order, there is a single heap size at which the allocation (and collection) will succeed, and below which the program fails (not enough memory). This is because all allocations go in the same linear region, collection itself doesn’t allocate memory, the amount of free space after an object (the fragmentation) does not depend on where it is allocated, and those object extents just add up in a commutative way.

Reliability is a virtue. Sometimes it is a requirement: for example, the Toit language and run-time targets embeded microcontrollers, and you there you have finite resources and either they workload fits or it doesn’t. You can’t really tolerate a result of “it works sometimes”. So, Toit uses a generational semi-space + mark-sweep collector that never makes things worse.

on block-structured heaps

But, threads make reliability tricky. With Whippet I am targetting embedders with multiple mutator threads, and a classic semi-space collector doesn’t scale – you could access the allocation pointer atomically, but that would be a bottleneck, serializing mutators, not to mention the cache contention.

The usual solution for this problem is to arrange the heap in such a way that different threads can allocate in different areas, so they don’t need to share an allocation pointer and so they don’t write to the same cache lines. And, the most common way to do this is to use a block-structured heap; for example you might have a 256 MB heap, but divided into 4096 blocks, each of which is 64 kB. That’s enough granularity to dynamically partition out space between many threads: you keep a list of available blocks and allocator threads compete to grab fresh blocks as needed. There’s central contention on the block list, so you want blocks big enough that you aren’t fetching blocks too often.

To break a heap into blocks requires a large-object space, to allow for allocations that are larger than a block. And actually, as I mentioned in the article about block-structured heaps, usually you choose a threshold for large object allocations that is smaller than the block size, to limit the maximum and expected amount of fragmentation at the end of each block, when an allocation doesn’t fit.

on unreliability

Which brings me to my point: a copying collector with a block-structured heap is unreliable, in the sense that there is no single heap size below which the program fails and above which it succeeds.

Consider a mutator with a single active thread, allocating a range of object sizes, all smaller than the large object threshold. There is a global list of empty blocks available for allocation, and the thread grabs blocks as needed and bump-pointer allocates into that block. The last allocation in each block will fail: that’s what forces the thread to grab a new fresh block. The space left at the end of the block is fragmentation.

Assuming that the sequence of allocations performed by the mutator is deterministic, by the time the mutator has forced the first collection, the total amount of fragmentation will also be deterministic, as will the set of live roots at the time of collection. Assume also that there is a single collector thread which evacuates the live objects; this will also produce deterministic fragmentation.

However, there is no guarantee that the post-collection fragmentation is less than the pre-collection fragmentation. Unless objects are copied in such a way that preserves allocation order—generally not the case for a semi-space collector, and it would negate any advantage of a block-structured heap—then different object order could produce different end-of-block fragmentation.

causes of unreliability

The unreliability discussed above is due to non-commutative evacuation. If your collector marks objects in place, you are not affected. If you don’t commute live objects—if you preserve their allocation order, as Toit’s collector does—then you are not affected. If your evacuation commutes, as in the case of the simple semi-space collector, you are not affected. But if you have a block-structured heap and you evacuate, your collector is probably unreliable.

There are other sources of unreliability in a collector, but to my mind they are not as fundamental as this one.

  • Multiple mutator threads generally lead to a kind of unreliability, because the size of the live object graph is not deterministic at the time of collection: even if all threads have the same allocation trace, they don’t necessarily proceed in lock-step nor stop in the same place.

  • Adding collector threads to evacuate in parallel adds its own form of unreliability: if you have 8 evacuator threads, then there are 8 blocks local to the evacuator threads which also contribute to post-collection wasted space, possibly an entire block per thread.

  • Some collectors will allocate memory during collection, for example to represent a worklist of objects that need tracing. This allocation can fail. Also, annoyingly, collection-time allocation complicates comparison: you can no longer compare two collectors at the same heap size, because one of them cheats.

  • Virtual memory and paging can make you have a bad time. For example, you go to allocate a large object, so you remove some empty blocks from the main space and return them to the OS, providing you enough budget to allocate the new large object. Then the new large object is collected, so you reclaim the pages you returned to the OS, adding them to the available list. But probably you don’t page them in already, because who wants a syscall? They get paged in lazily when the mutator needs them, but that could fail because of other processes on the system.

embracing unreliability

I think it only makes sense to insist on a reliable collector if your mutator does not have threads; otherwise, the fragmentation-related unreliability pales in comparison.

What’s more, fragmentation-related unreliability can be entirely mitigated by giving the heap more memory: the maximum amount of fragmentation is an object just shy of the large object threshold, per block, so in our case 8 kB per 64 kB. So, just increase your heap by 12.5%. You will certainly not regret increasing your heap by 12.5%.

And happily, increasing heap size also works to mitigate unreliability related to multiple mutator threads. Consider 10 threads each of which has a local object graph that is usually 10 MB but briefly 100MB when calculating: usually when GC happens, total live object size is 10×10MB=100MB, but sometimes as much as 1 GB; there is a minimum heap size for which the program sometimes works, but also a minimum heap size at which it always works. The trouble is, of course, that you generally only know the minimum always-works size by experimentation, and you are unlikely to be able to actually measure the maximum heap size.

Which brings me to my final point, which is that virtual memory and growable heaps are good. Unless you have a dedicated devops team or you are evaluating a garbage collector, you should not be using a fixed heap size. The ability to just allocate some pages to keep the heap from being too tight buys us a form of soft reliability.

And with that, end of observations. Happy fragmenting, and until next time!

Felix Häcker


#155 Overhauled Keyrings

Update on what happened across the GNOME project in the week from June 28 to July 05.

Sovereign Tech Fund

Sophie (she/her) says

Key Rack 0.4 has been released. This release contains all the work that Felix has done as part of STF. The version brings a completely overhauled user interface. But the changes go much further than a better look. Key Rack now allows to view all host keyrings as well to change passwords of keyrings. Passwords can now be found more easily via the new search function and new passwords can be added via the interface.

You can use Key Rack for to lookup password that apps have store or use it as a personal password manager with multiple keyrings.

GNOME Core Apps and Libraries


GTK port of the WebKit rendering engine.

Georges Stavracas (feaneron) says

WebKit can now be profiled using Sysprof on Linux. This includes both the WebKitGTK and the WPE WebKit ports. WebKit is the first web engine on Linux to integrate with Sysprof!

For now, this integration exposes all pre-existing tracing points in WebKit. This includes JSC, WebProcess, and more. New tracing points are being considered for addition.

This is an important step for WebKit on Linux as it open the door for potential performance improvements, reduce resource usage, and more.

Third Party Projects

elvishcraftsman says

I’ve created my first GNOME app, Time Tracker, which is now available on Flathub.

Time Tracker is local-first and can sync through cloud storage (saving to a CSV file). This makes it able to load significantly faster than online-first time tracking software.

Time Tracker supports custom filtering, where it reports on the amount of time spent during a particular date range and by project.


A pure wayland shell for mobile devices.

Guido says

Phosh 0.40.0 is out:

Phosh got (optional) dark style an mobile data quick toggles. One can suspend from the lock screen now and we have a bunch of bug and UI fixes. Phoc now supports a fling gesture to make opening and closing the panels a bit nicer.

There’s more. Check the full details here


Practice your typing skills

Brage Fuglseth reports

Bonjour! Sziasztok! 안녕! This week I released Keypunch 2.0. Get ready to practice typing in many new languages.

  • Text generation support for Bulgarian, Hindi, Korean, Nepali, Russian, Swiss German, and Ukranian
  • User interface translations for Bulgarian, Czech, Dutch, French, German, Hindi, Hungarian, Spanish and Ukranian
  • More accepted ways to type certain characters, such as “oe” for “œ”
  • Better handling of scripts using intelligent input engines, like Korean Hangul
  • Recognition of Ctrl+Backspace
  • Special accomodations for on-screen keyboards and touch screens

A special thanks to everybody who has contributed orthographic information and tested the text generation and input machinery for their languages. Keypunch’s diverse language support would not be possible without you. You can get Keypunch on Flathub.

GDM Settings

Customize your login screen.

Mazhar Hussain reports

I am calling for contributors for GDM Settings. Lately, I have been busy in my day job, and haven’t had much time to work on GDM Settings.

About 2 months ago, I requested the users for donations for the development of the app. At the time of writing this, we have $179.47 USD in our OpenCollective. I know it is not much, but meaningful contributions may be rewarded with little gifts (payouts) in the range of $30 to $50. If you want to work on something specific for some fixed amount of money, you can do that as well. Just reach out to me in GitHub Discussions.

If you have any questions or suggestions, please, don’t hesitate to reach out in GitHub Discussions.


Matrix messaging app for GNOME written in Rust.

Kévin Commaille reports

It’s been all over the news, so you probably already have heard about what’s happening in France, but in case you haven’t yet: a new Fractal beta just dropped!

What have we been up to for Fractal 8.beta?

  • Mentions are sent intentionally
  • Authenticated media are supported
  • Draft messages are kept per-room
  • The verification and account recovery processes have been polished
  • HTML rendering should be more robust, as we switched to the same library already used by the Rust SDK to cleanup tags in messages
  • Speaking of HTML rendering, code blocks gained syntax highlighting, lists can be nested, numbered lists can start from an arbitrary number, the details tag is supported, and @room mentions are detected and rendered with a pill

As usual, this release includes other improvements, fixes and new translations thanks to all our contributors, and our upstream projects.

It is available to install via Flathub Beta, see the instructions in our README.

As the version implies, there might be a slight risk of regressions, but it should be mostly stable. If all goes well the next step is the release candidate!

As always, you can try to fix one of our issues. Any help is greatly appreciated!



Keep your Flatpak apps synchronized between devices

IlChitarrista announces

We’ve finally landed the Initial Setup as part of GSoC, now FlatSync is configurable through a nicely designed UI. Many thanks for the feedback throughout development from my mentor Cogitri and various people in the Design channel.

Now I’m starting to work on implementing Filtering to bring more granular control over FlatSync’s behaviour.

GNOME Foundation

Allan Day says

The GNOME Foundation Board of directors has been getting its new members up and running this week. Newly elected directors have been joining the board Matrix channels, and training and briefing sessions have been scheduled for next week. Planning is also underway for the 2024 board annual meeting, which will happen ahead of GUADEC, on 17 July.

That’s all for this week!

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

Andy Wingo


enable persistent history in gdb

Friends. I have been using GDB for more than two decades and have been annoyed by the fact that, unlike the shell, it doesn’t keep a persistent history.

Of course, it has always been able to do that, but history saving is just not on by default. So do yourself a favor and turn it on by pasting this into your terminal:

mkdir -p ~/.config/gdb
mkdir -p ~/.cache/gdb
echo 'set history filename ~/.cache/gdb/history' >> ~/.config/gdb/gdbinit
echo 'set history save on' >> ~/.config/gdb/gdbinit
echo 'set history size unlimited' >> ~/.config/gdb/gdbinit

You are welcome!

scikit-survival 0.23.0 released

I am pleased to announce the release of scikit-survival 0.23.0.

This release adds support for scikit-learn 1.4 and 1.5, which includes missing value support for RandomSurvivalForest. For more details on missing values support, see the section in the release announcement for 0.23.0.

Moreover, this release fixes critical bugs. When fitting SurvivalTree, the sample_weight is now correctly considered when computing the log-rank statistic for each split. This change also affects RandomSurvivalForest and ExtraSurvivalTrees which pass sample_weight to the individual trees in the ensemble. Therefore, the outputs produced by SurvivalTree, RandomSurvivalForest, and ExtraSurvivalTrees will differ from previous releases.

This release fixes a bug in ComponentwiseGradientBoostingSurvivalAnalysis and GradientBoostingSurvivalAnalysis when dropout is used. Previously, dropout was only applied starting with the third iteration, now dropout is applied in the second iteration too.

Finally, this release adds compatibility with numpy 2.0 and drops support for Python 3.8.


scikit-survival is available for Linux, macOS, and Windows and can be installed either

via pip:

pip install scikit-survival

or via conda

 conda install -c conda-forge scikit-survival

Christian Hergert


Bisect’ing outside of the box

There is a sort of thrill to a bug hunt once you dig your heels in deep enough. This is one of those stories.

Earlier in the week a fellow Red Hatter messaged me about a bug in Ptyxis where if you right click on a tab, the tab stays in the active state. Clearly not a very good look. My immediate thought was that we don’t do anything special there, so maybe the issue is lower in the stack.

A screenshot of Ptyxis with 4 terminal tabs open. Two of which appear "focused" due to an overzealous active state on the tab widget.



The first layer beneath that is of course libadwaita and so naturally I’ll test Text Editor to see if I get the same behavior. Lo-and-behold it exists there. Cool, file an issue in libadwaita while I simultaneously see if I can fix it.

Nothing stands out too obvious, but I try a few things which make it slightly better but I can still get into this state with enough attempts. Bummer. Also, Alice thinks the real issue is in GTK because we’ve seen this in a number of places. It appears to be something wrong with active state tracking.


No worries, I know that code-base well, so we hop on over there and see what is going on. First things first though, where does active state tracking happen? A quick git grep later we see it is largely in gtkmain.c in response to incoming GdkEvent.


Having written a large portion of the macOS back-end for GTK 4, I know that events can be extremely platform specific. So lets cut this problem in half by determining if the issue really is in the GTK-side or GDK-side. So we run the test again with GDK_BACKEND=x11 ptyxis -s and see if we can reproduce.

The issue appears to go away, lovely! Although, let’s test it on macOS too just to be certain. And again, it does not reproduce there. So that must mean the issue lies somewhere in gdk/wayland/ or related.


Another important detail that Alice mentioned was that it seemed to be a regression. That would be great because if so, it would mean that we can bisect to find an answer. However, since both gdk/wayland/ and compositors move forward at a somewhat lock-step pace, that can be quite difficult to prove reliably enough for bisect.

So instead I move on to something a bit whacky. I have a CentOS 7 installation here that I use to test that Ptyxis crazy GLibc hacks work for ptyxis-agent. That has GNOME 3.28 which can run as a Wayland compositor if you install the appropriate session file. Thus I do, and what do you know, it has the same issue there. That was surely to hit the “old wayland protocol” code-paths which are a bit different than the newer extensions, which was what I had hoped to test.

Either way, very old Mutter still equals broken. Womp.

Weston Enters the Ring

We have a “reference compositor” in the form of Weston which can help shake out protocol issues (assuming the diligence to Weston correctness is upheld). So I try running Ptyxis on my local machine inside a nested Weston instance (which appears to be 13.0.0). Surprising, things actually work!

Okay, but I still have that CentOS 7 VM running, I wonder if it has Weston available? It does not. Bummer. But I also have an Alma Linux 9 VM around that I use for testing occasionally and that must have it. Let’s try there.

In fact it does, but it’s version 8.0.0. Let’s see if it works there too!

A screenshot of GNOME Boxes running Alma Linux 9 in a GNOME session with a nested Weston compositor containing Ptyxis with 2 of 3 tabs broken.

Bisecting Weston

So now we know Weston 8.0.0 is broken and 13.0.0 works. Now we have something I can bisect!

So I fire up meson locally and ensure that my local build fails when it is 8.0.0. Indeed it does. And 13.0.0 appears to work. Bisect commence!

We find the commit which “fixes” things from a Weston point of view to be “Set grab client before calling grab functions“.

That gives me enough information to start commenting out lines of code in Mutter like the uncivilized monkey I am. Eventually, Shakespeare has been written and the bug goes away. Though certainly not in an upstream-able fashion. Bugs filed, lines of code pointed to, and here I hope for our lovely Mutter maintainers to take over.

Back to GDK

Lots of amazing things have happened since we got Wayland compositors. One of the more undeniable difficulties though has been equal event ordering amongst them. It’s almost certainly the case that other compositors are doing things in slightly different ways and it may not even be possible to change that based on their use-case/designs.

So some sort of mitigation will be required on the GDK side.

After some interpretation of what is really going on here, a very small patch to drop certain leave events.

And that’s the story of how I bisected Weston to find an issue in Mutter (and GDK).

Felix Häcker


#154 Pride Day!

Update on what happened across the GNOME project in the week from June 21 to June 28.

GNOME Core Apps and Libraries


Building blocks for modern GNOME apps using GTK4.

Alice (she/her) reports

as a last minute change before libadwaita 1.6.alpha, we landed a new alert/message dialog style/layout. It covers AdwAlertDialog, AdwMessageDialog (minus a few features) and even GtkMessageDialog/GtkAlertDialog (as best as it can). It’s now using standard button styles, fixing the long-standing issue where suggested and destructive buttons would look the same when using red accent color, and generally refreshing the look


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

Bilal Elmoussaoui says

After various necessary cleanups in both Mutter and GNOME Shell, you can finally build Mutter as a Wayland-only compositor. GNOME Shell will automatically detect at build time whether Mutter was built with or without X11, with or without Xwayland and adapt to that.

GNOME Circle Apps and Libraries


A distraction free Markdown editor.

Manu (he/they/she) says

Today is the international LGBTIQA+ pride day. As a queer person myself I wanted to celebrate, especially in a moment where our rights are questioned more and more. It’s available now on Flathub a new release of Apostrophe, with some small fixes and updated translations, but first and foremost new pride styles for the save animation. Thanks to Fina Wilke for the original implementation! Check Apostrophe on Flathub and, if you like it, donate to your favorite local queer association 🏳️‍🌈🏳️‍⚧️

Déjà Dup Backups

A simple backup tool.

Michael Terry announces

Déjà Dup Backups 46 is now available!• New Adwaita dialogs and UI polish

  • It now uses Rclone for cloud support (more reliable than the Python libraries used before)
  • Better feedback in several error cases
  • The Restic backend is a lot more robust
  • Plus the usual bug fixes and translation updates Download from flathub:

Third Party Projects

Swapnil Devesh reports

I have released version 1.1.0 of my app Luminance and now its built using GTK4 and Libadwaita 🎉 TWIG-Bot

d-k-bo says

Televido 0.4.0 is available on Flathub.

Televido is an app to access German-language public broadcasting live streams and archives based on APIs provided by the MediathekView project.

As a major change in version 0.4.0, the mediathek search field now uses MediathekViewWeb’s advanced search syntax. Additionally, support for removing and reordering live channels and an option to copy the subtitles URL have been added.

Izzy (she/they) reports

Binary 0.3 is officially out!

New features and improvements include:

  • Conversions to and from octal (base 8) numbers
  • A cleaner headerbar with flat dropdowns for the base selectors.
  • Reworked number conversions to be more reliable and quicker.
  • Added translations for Finnish and German.

Get it from Flathub here:


Browse the Fediverse.

Evangelos “GeopJr” Paterakis reports

Tuba 0.8 is now available, with many new features and bug fixes!

✨ Highlights:

  • Admin Dashboard
  • Advanced Search Dialog
  • Accessibility improvements
  • Graceful network recovery & in-app proxy settings
  • Post translations
  • Profile notes
  • Notification requests & filtering
  • Mini profiles when clicking avatars
  • Optional Clapper support

🏳️‍🌈 Happy International LGBTQIA+ Pride Day! 🏳️‍⚧️

GNOME Foundation

Caroline Henriksen reports

GUADEC 2024 is only a few weeks away! Make sure to let us know how you’re attending by registering.

Remote Registration

If you plan to attend from home or at a satellite event such as in Berlin, please remember to register as a remote participant at Knowing who participates in GUADEC (and where they take part) helps us make events as relevant and engaging as possible, and helps explain the reach and impact of our events to sponsors. Knowing how many remote participants we have also helps us continue to make our events accessible with a lower carbon footprint by justifying costs such as AV Teams and live streaming.

In-Person Registration

If you are joining us in Denver, Colorado make sure to register as an in-person attendee at Signing up ahead of the conference helps ensure you receive all the important updates and information we email to registered participants. It also allows us to print your conference badge in time for early pick up at our July 18th pre-registration party!

That’s all for this week!

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

Felipe Borges


Rethinking Planet GNOME with GitLab Pages/CI

Some GNOME websites are getting modernized and simplified, but Planet GNOME has fallen behind. Not anymore. I started a prototype for a Python script to publish Planet GNOME with GitLab Pages/CI.

As Planet GNOME Editor, I am often asked to look for blog and syndication issues I couldn’t really address due to limited server-side access. With this, debugging indexing issues should be easier as it is just about looking at the CI job output.

Also, the Planet website is perceived as messy and outdated. So this work allowed Jakub Steiner to quickly jump in and restyle the page from a clean state.

Try it live at and let me know what you think. Keep in mind this is a proof of concept. Tips, feedback, and contributions are welcome in the project repo.

This still doesn’t produce the global Planet rss feed, just the webpage, but that’s in my TODO list too.

P.S.: I know feed readers/parsers can over-request rss/atom feeds. So I plan to cache data and use metadata to avoid redundant downloads before this is even considered as a replacement for the current Planet implementation. No worries. 😉

GNOME tablet support papercut fixes

Over the last months I've started looking into a few of the papercuts that affects graphics tablet users in GNOME. So now that most of those have gone in, let's see what has happened:

Calibration fixes and improvements (GNOME 47)

The calibration code, a descendent of the old xinput_calibrator tool was in a pretty rough shape and didn't work particularly well. That's now fixed and I've made the calibrator a little bit easier to use too. Previously the timeout was quite short which made calibration quite stressfull, that timeout is now per target rather than to complete the whole calibration process. Likewise, the calibration targets now accept larger variations - something probably not needed for real use-cases (you want the calibration to be exact) but it certainly makes testing easier since clicking near the target is good enough.

The other feature added was to allow calibration even when the tablet is manually mapped to a monitor. Previously this only worked in the "auto" configuration but some tablets don't correctly map to the right screen and lost calibration abilities. That's fixed now too.

A picture says a thousand words, except in this case where the screenshot provides no value whatsoever. But here you have it anyway.

Generic tablet fallback (GNOME 47)

Traditionally, GNOME would rely on libwacom to get some information about tablets so it could present users with the right configuration options. The drawback was that a tablet not recognised by libwacom didn't exist in GNOME Settings - and there was no immediately obvious way of fixing this, the panel either didn't show up or (with multiple tablets) the unrecognised one was missing. The tablet worked (because the kernel and libinput didn't require libwacom) but it just couldn't be configured.

libwacom 2.11 changed the default fallback tablet to be a built-in one since this is now the most common unsupported tablet we see. Together with the new fallback handling in GNOME settings this means that any unsupported tablet is treated as a generic built-in tablet and provides the basic configuration options for those (Map to Monitor, Calibrate, assigning stylus buttons). The tablet should still be added to libwacom but at least it's no longer a requirement for configuration. Plus there's now a link to the GNOME Help to explain things. Below is a screenshot on how this looks like (after modifying my libwacom to no longer recognise the tablet, poor Intuos).

Monitor mapping names (GNOME 47)

For historical reasons, the names of the display in the GNOME Settings Display configuration differed from the one used by the Wacom panel. Not ideal and that bit is now fixed with the Wacom panel listing the name of the monitor and the connector name if multiple monitors share the same name. You get the best value out of this if you have a monitor vendor with short names. (This is not a purchase recommendation).

Highlighted SVGs (GNOME 46)

If you're an avid tablet user, you may have multiple stylus tools - but it's also likely that you have multiple tools of the same type which makes differentiating them in the GUI hard. Which is why they're highlighted now - if you bring the tool into proximity, the matching image is highlighted to make it easier to know which stylus you're about to configure. Oh, and in the process we added a new SVG for AES styli too to make the picture look more like the actual physical tool. The <blink> tag may no longer be cool but at least we can disco our way through the stylus configuration now.

More Pressure Curves (GNOME 46)

GNOME Settings historically presents a slider from "Soft" to "Firm" to adjust the feel of the tablet tip (which influences the pressure values sent to the application). Behind the scenes this was converted into a set of 7 fixed curves but thanks to a old mutter bug those curves only covered a small amount of the possible range. This is now fixed so you can really go from pencil-hard to jelly-soft and the slider now controls an almost-continous range instead of just 7 curves. Behold, a picture of slidery goodness:

Miscellaneous fixes

And of course a bunch of miscellaneous fixes. Things that I quickly found were support for Alt in the tablet pad keymappings, fixing of erroneous backwards movement when wrapping around on the ring, a long-standing stylus button mismatch, better stylus naming and a rather odd fix causing configuration issues if the eraser was the first tool ever to be brought into proximity.

There are a few more things in the pipe but I figured this is enough to write a blog post so I no longer have to remember to write a blog post about all this.

Cassidy James Blaede


How & Why to Connect Threads to the Fediverse—including Mastodon

This week Threads announced it is rolling out wider support for connecting with “the fediverse,” and it’s a no-brainer to opt in to this feature if you’re on Threads.

Abstract animation of "the fediverse"

What is the fediverse?

The fediverse, sometimes referred to as “the open social web,” is a network of individual social networks that interoperate with one another using open standards. This includes social networks like Mastodon, but also includes news aggregation service Flipboard, individual WordPress sites, video streaming services powered by PeerTube, photo sharing communities using Pixelfed, link-aggregators using Lemmy, among many others.

It’s a tired-but-true comparison to think of it like email: you can use Gmail, I can use Fastmail, and we can all interact with a friend using their company’s self-hosted email service because email is fundamentally interoperable using open standards and protocols. The fediverse is just like that, but for social communities.

If you want to dive in to understand even more, The Verge has a great fediverse explainer—or if you want to hear it from Meta, they’ve written up this fancy release from their perspective.

Why connect to the fediverse?

So as someone who uses Threads, why should you opt into connecting your account to the fediverse? In short, because it’s an easy way to expand your reach! It’s difficult to definitively count the number of users across the entire fediverse due to the network’s decentralized nature, but current estimates show there are around 11 million users on the fediverse, and growing. That’s a lot of people out there!

Even if you’re someone who doesn’t really care about reaching a bunch of people with your posts, think of it this way: connecting to the fediverse makes it so you’re not artificially forcing anyone who wants to keep in touch (or see what you’re up to, or laugh at your shitposts) to use Facebook—since fundamentally, Threads is Instagram is Facebook (and accounts/posts are shared between Meta’s different platforms).

In practice, this means that I can be on my own Mastodon server (with the fancy handle and follow your account, e.g. @YourThreadsUsername​ Remember that email comparison earlier? You can see how it’s pretty similar! Any of your public posts on Threads will then show up in my social feed alongside posts from people I follow on Mastodon and other sites.

Screenshots from Threads and Mastodon showing how the same post is presented

On your side, you’ll see when anyone on the fediverse “likes” or replies to your post, too—all without leaving Threads.

Screenshots from Threads showing what fediverse integration looks like

But for now, Meta is making you opt in to this feature since it’s pretty new. If you don’t turn on sharing to the fediverse on your Threads account, nobody will be able to find you outside of Threads itself.

If you want to learn even more, Meta has this fediverse-specific privacy guide for Threads.

How to turn on fediverse sharing

Luckily, turning on fediverse sharing for your Threads account is simple:

  1. Go to your Account Settings in Threads
  2. Select “Fediverse sharing”
  3. Follow the on-screen instructions

Fediverse sharing screen in Threads Account Settings

That’s it! You’re set up to share across the fediverse so that people like me—and millions of others—can follow you from our preferred social networks. Plus, you can always turn it back off later if you want from the same place if you change your mind!

Images via Meta newsroom

Fixing a memory leak of xmlEntityPtr in librsvg

Since a few weeks ago, librsvg is now in oss-fuzz — Google's constantly-running fuzz-testing for OSS projects — and the crashes have started coming in. I'll have a lot more to say soon about crashes in Cairo, which is where the majority of the bugs are so far, but for now I want to tell you about a little bug I just fixed.

The fuzzer found a memory leak that happens when librsvg tries to parse an invalid XML document that has definitions for XML entities — the things that you normally reference like &foo; in the middle of the XML.

For example, this invalid document causes librsvg to leak:


Valgrind reports this:

$ valgrind --leak-check=full ./target/debug/rsvg-convert leak.svg 
Error reading SVG leak.svg: XML parse error: Error domain 1 code 37 on line 2 column 1 of data: xmlParseEntityDecl: entity a not terminated

==3750== HEAP SUMMARY:
==3750==     in use at exit: 78,018 bytes in 808 blocks
==3750==   total heap usage: 1,405 allocs, 597 frees, 205,161 bytes allocated
==3750== 247 (144 direct, 103 indirect) bytes in 1 blocks are definitely lost in loss record 726 of 750
==3750==    at 0x4845794: malloc (in /usr/libexec/valgrind/
==3750==    by 0x4BD857F: xmlCreateEntity (entities.c:158)
==3750==    by 0x4BD932B: xmlNewEntity (entities.c:451)
==3750==    by 0x2EBC75: rsvg::xml::xml2_load::sax_entity_decl_cb (
==3750==    by 0x4BED6D8: xmlParseEntityDecl (parser.c:5647)
==3750==    by 0x4BEF4F3: xmlParseMarkupDecl (parser.c:7024)
==3750==    by 0x4BEFB95: xmlParseInternalSubset (parser.c:8558)
==3750==    by 0x4BF50E9: xmlParseDocument (parser.c:11072)
==3750==    by 0x2ED266: rsvg::xml::xml2_load::Xml2Parser::parse (
==3750==    by 0x4A8C49: rsvg::xml::XmlState::parse_from_stream::{{closure}} (
==3750==    by 0x2ACA92: core::result::Result<T,E>::and_then (
==3750==    by 0x34D4E2: rsvg::xml::XmlState::parse_from_stream (
==3750== LEAK SUMMARY:
==3750==    definitely lost: 144 bytes in 1 blocks
==3750==    indirectly lost: 103 bytes in 3 blocks
==3750==      possibly lost: 0 bytes in 0 blocks
==3750==    still reachable: 73,947 bytes in 746 blocks
==3750==         suppressed: 0 bytes in 0 blocks

Let's see what happened.

The code in question

Even after the port to Rust, librsvg still uses libxml2 for parsing XML. So, librsvg has to deal with raw pointers incoming from libxml2 and it must do their memory management itself, since the Rust compiler doesn't know what to do with them automatically.

Librsvg uses the SAX parser, which involves setting up callbacks to process events like "XML element started", or "an entity was defined".

If you have a valid document that has entity definitions like these:

<!ENTITY foo "#aabbcc">
<!ENTITY bar "some text here">

Then libxml2's SAX parser will emit two events to instruct your code that it should define entities, one for foo and one for bar, with their corresponding content. Librsvg stores these in a hash table, since it has to be able to retrieve them later when the SAX parser requests it. In detail, libxml2 requires that you create an xmlEntityPtr by calling xmlNewEntity() and then keep it around.

xmlEntityPtr xmlNewEntity (xmlDocPtr      doc,
                           const xmlChar *name,
                           int            type,
                           const xmlChar *ExternalID,
                           const xmlChar *SystemID,
                           const xmlChar *content);

Later, you must free each of your stored entities with xmlFreeNode() (it supports different data types, including entities), or if you are using libxml2 2.12.0 or later, with xmlFreeEntity().

void xmlFreeNode (xmlNodePtr node);
void xmlFreeEntity (xmlEntityPtr entity);

Librsvg creates a SAX parser from libxml2, calls it to do the parsing, and then frees the entities at the end. In the following code, XmlState is the struct that librsvg uses to hold the temporary state during parsing: a partially-built XML tree, some counters on the number of loaded elements, the current element being processed, things like that. The build_document() method is called at the very end of XmlState's lifetime; it consumes the XmlState and returns either a fully-parsed and valid Document, or an error.

struct XmlState {
    inner: RefCell<XmlStateInner>,  // the mutable part

    // ... other immutable fields here

type XmlEntityPtr = *mut libc::c_void;

struct XmlStateInner {
    // ... a few fields for the partially-built XML tree, current element, etc.
    document_builder: DocumentBuilder,

    // Note that neither XmlStateInner nor Xmlstate implement Drop.
    // An XmlState is finally consumed in XmlState::build_document(), and that
    // function is responsible for freeing all the XmlEntityPtr from this field.
    // (The structs cannot impl Drop because build_document()
    // destructures and consumes them at the same time.)
    entities: HashMap<String, XmlEntityPtr>,

impl XmlState {
    fn build_document(
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        // does the actual parsing with a libxml2 SAX parser
        self.parse_from_stream(stream, cancellable)?;

        // consume self, then consume inner, then consume document_builder by calling .build()
        let XmlState { inner, .. } = self;
        let mut inner = inner.into_inner();

        // Free the hash of XmlEntityPtr.  We cannot do this in Drop because we will
        // consume inner by destructuring it after the for() loop.
        for (_key, entity) in inner.entities.drain() {
            unsafe {

        let XmlStateInner {
            document_builder, ..
        } = inner;

There are many Rust-isms in this code.

  • After doing the actual parsing with parse_from_stream(), self is destructured to consume it and extract its inner field, which is the actual mutable part of the XML loading state.

  • The code frees each xmlEntityPtr stored in the hash table of entities.

  • The inner value, which is an XmlStateInner, is destructured to extract the document_builder field, which gets asked to .build() the final document tree.

Where's the bug?

The bug is in this line at the beginning of the build_document() function:

        self.parse_from_stream(stream, cancellable)?;

The ? after the function call is to return errors to the caller. However, if there is an error during parsing, we will exit the function here, and it will not have a chance to free the values in the key-value pairs among the entities ! Memory leak!

This code had already gone through a few refactorings. Initially I had an impl Drop for XmlState which did the obvious thing of freeing the entities by hand:

impl Drop for XmlState {
    fn drop(&mut self) {
        unsafe {
            let mut inner = self.inner.borrow_mut();

            for (_key, entity) in inner.entities.drain() {
                // entities are freed with xmlFreeNode(), believe it or not

But at one point, I decided to clean up the way the entire inner struct was to be handled, and decided to destructure it at the end of its lifetime, since that made the code simpler. However, destructuring an object means that you cannot have an impl Drop for it, since then some fields are individually moved out and some are not during the destructuring. So, I changed the code to free the entities directly into build_document() as above.

I missed the case where the parser can exit early due to an error.

The Rusty solution

Look again at how the entities hash table is declared in the struct fields:

type XmlEntityPtr = *mut libc::c_void;

struct XmlStateInner {
    entities: HashMap<String, XmlEntityPtr>,

That is, we are storing a hash table with raw pointers in the value part of the key-value pairs. Rust doesn't know how to handle those external resources, so let's teach it how to do that.

The magic of having an impl Drop for a wrapper around an unmanaged resource, like xmlEntityPtr, is that Rust will automatically call that destructor at the appropriate time — in this case, when the hash table is freed.

So, let's use a wrapper around XmlEntityPtr, and add an impl Drop for the wrapper:

struct XmlEntity(xmlEntityPtr);

impl Drop for XmlEntity {
    fn drop(&mut self) {
        unsafe {

And then, let's change the hash table to use that wrapper for the values:

    entities: HashMap<String, XmlEntity>,

Now, when Rust has to free the HashMap, it will know how to free the values. We can keep using the destructuring code in build_document() and it will work correctly even with early exits due to errors.

Valgrind's evidence without the leak

# valgrind --leak-check=full ./target/debug/rsvg-convert leak.svg 
==5855== Memcheck, a memory error detector
==5855== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==5855== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==5855== Command: ./target/debug/rsvg-convert leak.svg
Error reading SVG leak.svg: XML parse error: Error domain 1 code 37 on line 2 column 1 of data: xmlParseEntityDecl: entity a not terminated

==5855== HEAP SUMMARY:
==5855==     in use at exit: 77,771 bytes in 804 blocks
==5855==   total heap usage: 1,405 allocs, 601 frees, 205,161 bytes allocated
==5855== LEAK SUMMARY:
==5855==    definitely lost: 0 bytes in 0 blocks
==5855==    indirectly lost: 0 bytes in 0 blocks
==5855==      possibly lost: 0 bytes in 0 blocks
==5855==    still reachable: 73,947 bytes in 746 blocks
==5855==         suppressed: 0 bytes in 0 blocks

Moral of the story

Resources that are external to Rust really work best if they are wrapped at the lowest level, so that destructors can run automatically. Instead of freeing things by hand when you think it's right, let the compiler do it automatically when it knows it's right. In this case, wrapping xmlEntityPtr with a newtype and adding an impl Drop is all that is needed for the rest of the code to look like it's handling a normal, automatically-managed Rust object.

Michael Meeks


2024-06-21 Friday

  • Up earlyish; mail chew, call with Aron & Anna.
  • Worked on slides for our first public Tea Time Training; hopefully we can get more people discussing LibreOfficeKit & COOL development weekly & interactively.
    Tea Time Training on async events, queues etc. (Hybrid PDF)
  • Lunch; J. drove us to Durham to meet up with H, finally a lovely sunny day.
  • Worked in the car - annoyingly mobile internet is now good enough that you can do back-to-back admin in the car; COOL co-editing, Docusigning etc. squeezing out precious hacking time.

New Cambalache development release 0.91.1!

I am please to announce a new development version of Cambalache

This comes with two major dependencies changes, the first one is a very basic port to Adwaita which fixes dark mode support with Gtk4The biggest one is that I have replaced the WebKit WebView used to show widgets in the workspace for a custom Wayland compositor widget based on wlroots.

So far, this is how Cambalache showed windows from a different process in its workspace.Workspace diagram using Bradwayd and WebKit WebView

It would run broadwayd or gtk4-broadwayd backend depending on the gtk version of your project and use a WebView to show all the windows.

With the new approach we do not need the extra broadway backend and also we do not need to run a whole web browser just to show a window.On top of that we get all the obvious optimizations from using Wayland instead of a protocol meant to go over the internet.

For example, with broadway, the client would render the window in memory, the broadway backend would compress the image and sent it over TCP to the webview which has to uncompress it and render it on an HTML5 canvas using JS api.

But now, the client just renders it in shared memory which the compositor use it to create a cairo surface from it and renders it directly in a GtkDrawingArea.

And this is how the new Cambalache looks like when editing its own UI.This also leaves room for improvement by leveraging all the new Gtk infrastructure for graphics offloading (see this blog post from Matthias about it)

As usual with so many changes I expect new bugs so please if you find any,  file them here.

Special thanks goes to emersion, kennylevinsen, vyivel and the wlroots community for their support and awesome project, I would not have been able to do this without wlroots and their help.

Where to get it?

You can get it from Flathub Beta

flatpak remote-add --if-not-exists flathub-beta

flatpak install flathub-beta ar.xjuan.Cambalache

or checkout main branch at gitlab

git clone

Matrix channel

Have any question? come chat with us at


Follow me in Mastodon @xjuan to get news related to Cambalache development.

Happy coding!






Jussi Pakkanen


Advanced text features and PDF

The basic text model of PDF is quite nice. On the other hand its basic design was a very late 80s "ASCII is everything everyone really needs, but we'll be super generous and provide up to 255 glyphs using a custom encoding that is not in use everywhere else". As you can probably guess, this causes a fair bit of problems in the modern world.

To properly understand the text that follows you should know that there are four different ways in which text and letters need to be represented to get things working:

  • Source text is the "original" written text in UTF-8 (typically)
  • Unicode codepoints represent unique Unicode IDs as specified by the Unicode standard
  • A glyph id uniquely specifies a glyph (basically a series of drawing operations), these are arbitrary and typically unique for each font
  • ActualText is sort of like an AltText for PDF but uses UTF-16BE as was the way of the future in the early 90s


The most common advanced typography feature in use is probably kerning, that is, custom spacing between certain letter pairs like "AV" and "To". The PDF text model has native support for kerning and it even supports vertical and horizontal kerning. Unfortunately the way things are set up means that you can only specify horizontal kerning when laying out horizontal text and vertical kerning for vertical text. If your script requires both, you are not going to have a good time.

There are several approaches one can take. The simplest is to convert all text to path drawing operations, which can be placed anywhere with arbitrary precision. This works great for printed documents but also means that document sizes balloon and you can't copypaste text from the document, use screen readers or do any other operation that needs the actual text those shapes represent.

An alternative is to render each glyph as its own text object with exact coordinates. While verbose this works, but since every letter is separate, text selection becomes wonky again. PDF readers seem to have custom heuristics to try to detect these issues and fix text selection in post-processing. Sometimes it works better than at other times.

Everything in PDF drawing operations is based on matrices. Text has its own transform matrix that defines where the next glyph will go. We could specify kerning manually with a custom translation matrix that translates the rendering location by the amount needed. There are two main downsides to this. First of all it would mean that instead of having a stream of glyphs to render, you'd need to define 9 floating point numbers (actually 6 due to reasons) between every pair of glyphs. This would increase the size of you output by a factor of roughly ten. The other downside is that unlike for all other matrices, PDF does not permit you to multiply an existing text state matrix with a new one. You can only replace it completely. So the actual code path would become "tell PDF to draw a glyph, work out what changes it would make to the currently active text matrix, undo that, multiply that matrix with one that has the changes that you wanted to happen and proceed to the next glyph".

Glyph substitution

Most of the time (in most scripts anyway) source text's Unicode codepoints get mapped 1:1 to a font glyph in the final output. Perhaps the most common case where this does not happen is ligatures.

The actual rules when and how this happens are script, font and language dependent. This is something you do not want to do yourself, instead use a shaping engine like Harfbuzz. If you give it the source text as UTF-8 and a font that has the ffi ligature, it will return a list of four glyph ids in the font to use, the way they map back to the original text, kerning (if any) and all of that good stuff.

What it won't give you is the information of what ligatures it replaced your source text with. In this example it will tell you the glyph id of the ffi ligature (2132) but not which Unicode codepoint it corresponds to (0xFB03). You need to tell that number in PDF metadata for the text to work properly in copypaste operations. At first this does not seem like such a big problem, because we have access to the original font file and Freetype. You'd think you can just ask Freetype for the Unicode codepoint for a given font glyph, but you can't. There is a function for finding a glyph for a given Unicode codepoint but mot the other way around. The stackoverflow recommended way of doing this is to iterate over all glyphs until you find the one that is mapped to the desired codepoint. For extra challenge you need to write an ActualText tag in the PDF command stream so that when users copypaste that text they get the original form with each individual letter rather than the ffi Unicode glyph.

All of this means that glyph lookup is basically a O(n^2) operation if it was possible to do. Sometimes it isn't, as we shall now find out.

Alternate forms

OpenType fonts can have multiple different glyphs for the same Unicode codepoint, for example the small caps versions of Noto Serif look like this.

These are proper hand-drawn versions of the glyphs, not ones obtained by scaling down upper case letters. Using these is simple, you tell Harfbuzz to use the small caps versions when shaping and then it does everything for you. For this particular font upper case small caps glyphs are the same as regular upper case glyphs. The lower case ones have their own glyphs in the font. However, according to Freetype at least, those glyphs are not mapped to any Unicode codepoint. Conceptually a small caps lower case "m" should be the same as a regular lower case "m". For some reason it is not and, unless I'm missing something, there is no API that can tell you that. The only way to do it "properly" is to track this yourself based on your input text and requirements.

How does CapyPDF handle all this?

In the same way pretty much all PDF generator libraries do: by ignoring all of it. CapyPDF only provides the means to express all underlying functionality in the PDF library. It is the responsibility of the client application to form glyph sequences and related PDF metadata in the way that makes sense for their application and document structure.

Michael Meeks


2024-06-20 Thursday

  • Early call with Adfinis Australia - good to have great partners worldwide; worked with Lily, tech planning call, COOL community call.
  • Lunch; more document massage; poked at some code, late call.
  • J. feeling unwell, so kebab instead of going out to celebrate the end of exams, please to have N. return home and H. pass the 2nd year.

Update on Newton, the Wayland-native accessibility project

Several months ago, I announced that I would be developing a new accessibility architecture for modern free desktops. Now, I’m happy to provide an update on this project, code-named Newton. Before I begin, I’d like to thank the Sovereign Tech Fund for funding this work, and the GNOME Foundation for managing the contract.

A word on the name

When choosing a working name for this project, I decided to follow the convention established by Wayland itself, and followed by a couple of other projects including the Weston compositor, of naming Wayland and related projects after places in New England. Newton, Massachusetts is the town where the Carroll Center for the Blind is located.


Here’s a demo of me using a modified GNOME OS image with a couple of GTK 4 apps running inside Flatpak sandboxes without the usual accessibility exception.


Builds for testing

The following builds are based on GNOME 46.2 with my current unmerged Newton modifications. The corresponding Git branches are linked below.

I’ve also built a Flatpak repository, but it isn’t currently signed, so it doesn’t have a .flatpakrepo file. You can add it manually with this command:

flatpak remote-add --user --no-gpg-verify newton

Because the Flatpak repository is based on GNOME 46, you can use Flatpak apps that were built for GNOME 46 with the Newton version of the org.gnome.Platform runtime. You can install that runtime with this command:

flatpak install newton org.gnome.Platform

Source repositories

Here are the links to the unmerged Newton branches of the individual components:

Here are my branches of the Buildstream metadata repositories, used to build the GNOME OS image and Flatpak runtime:

freedesktop-sdk gnome-build-meta

Only this last repository needs to be checked out directly. With it, one should be able to reproduce my builds.

If you want to do your own builds of the relevant components, my addition to the Orca README has instructions. The Orca GitLab project linked above is also a good place to provide end-user feedback.

What’s working so far

I’ve now implemented enough of the new architecture that Orca is basically usable on Wayland with some real GTK 4 apps, including Nautilus, Text Editor, Podcasts, and the Fractal client for Matrix. Orca keyboard commands and keyboard learn mode work, with either Caps Lock or Insert as the Orca modifier. Mouse review also works more or less. Flat review is also working. The Orca command to left-click the current flat review item works for standard GTK 4 widgets.

As shown in the recorded demo above, Newton-enabled applications can run inside a Flatpak sandbox without the usual exception for the AT-SPI bus, that is, with the --no-a11y-bus option to flatpak run. Support for such sandboxing was one of the major goals of this project.

The standard GTK text widgets, including GtkEntry and GtkTextView have fairly complete support. In particular, when doing a Say All command, the caret moves as expected. I was also careful to support the full range of Unicode, including emoji with combining characters such as skin tone modifiers.

What’s broken or not done yet

The GNOME Shell UI itself is not yet using Newton, but AT-SPI. The UI is still accessible with the Newton versions of Mutter and Orca, but it’s being accessed via AT-SPI, meaning that performance in this UI is not representative of Newton, and mouse review doesn’t work for this UI.

Synthesizing mouse events isn’t yet supported on Wayland. This means that while the Orca command for left-clicking the current flat review item is expected to work for standard GTK 4 widgets, that command doesn’t work for widgets that don’t support the appropriate accessible action, and the right-click command doesn’t work.

AccessKit doesn’t currently support sentences as text boundaries. This means that Orca’s Say All command fallback so treading by line, leading to unnatural breaks in the speech.

The GTK AccessKit implementation doesn’t yet support out-of-tree text widgets that implement the GtkAccessibleText interface, such as the GTK 4 version of the vte terminal widget. This means that GTK 4-based terminal apps like kgx don’t yet work with Newton. I don’t yet know how I’ll solve this, as the current GtkAccessibleText interface is not a good fit for the push-based approach of AccessKit and Newton.

Text attributes such as font, size, style, and color aren’t yet exposed. AccessKit has properties for these attributes, but the AccessKit AT-SPI backend, much of which is reused by the Newton AT-SPI compatibility library, doesn’t yet support them.

Tables aren’t yet supported. AccessKit has properties for tables, and the GTK AccessKit backend is setting these properties, but the AccessKit AT-SPI backend doesn’t yet expose these properties.

Some states, such as “expanded”, “has popup”, and “autocomplete available”, aren’t yet exposed.

I’m aware that some GTK widgets don’t have the correct roles yet.

When Caps Lock is set as the Orca modifier, you can’t yet toggle the state of Caps Lock itself by pressing it twice quickly.

Orca is the only assistive technology supported so far. In particular, assistive technologies that are implemented inside GNOME Shell, like the screen magnifier, aren’t yet supported.

Bonus: Accessible GTK apps on other platforms

Because we decided to implement Newton support in GTK by integrating AccessKit, this also means that, at long last, GTK 4 apps will be accessible on Windows and macOS as well. The GTK AccessKit implementation is already working on Windows, and it shouldn’t be much work to bring it up on macOS. To build and test on Windows, check out the GTK branch I linked above and follow the instructions in its README. I’ve built and tested this GTK branch with both Visual Studio (using Meson and the command-line Visual C++ tools) and MSYS 2. I found that the latter was necessary for testing real-world apps like gnome-text-editor.

Architecture overview

Toolkits, including GTK, push accessibility tree updates through the new accessibility Wayland protocol in the wayland-protocols repository linked above. The latest accessibility tree update is part of the surface’s committed state, so the accessibility tree update is synchronized with the corresponding visual frame. The toolkit is notified when any accessibility clients are interested in receiving updates for a given surface, and when they want to stop receiving updates, so the toolkit can activate and deactivate its accessibility implementation as needed. This way, accessibility only consumes CPU time and memory when it’s actually being used. The draft Wayland protocol definition includes documentation with more details.

Assistive technologies or other accessibility clients currently connect to the compositor through a D-Bus protocol, defined in the Mutter repository linked above. By exposing this interface via D-Bus rather than Wayland, we make it easy to withhold this communication channel from sandboxed applications, which shouldn’t have this level of access. Currently, an assistive technology can find out about a surface when it receives keyboard focus or when the pointer moves inside it, and can then ask to start receiving accessibility tree updates for that surface.

The same D-Bus interface also provides an experimental method of receiving keyboard events and intercepting (“grabbing”) certain keys. This is essential functionality for a screen reader such as Orca. We had originally planned to implement a Wayland solution for this requirement separately, but decided to prototype a solution as part of the Newton project to unblock realistic testing and demonstration with Orca. We don’t yet know how much of this design for keyboard event handling will make it to production.

The compositor doesn’t process accessibility tree updates; it only passes them through from applications to ATs. This is done using file descriptor passing. Currently, the file descriptors are expected to be pipes, but I’ve thought about using shared memory instead. The latter would allow the AT to read the accessibility tree update without having to block on I/O; this could be useful for ATs that run inside Mutter itself, such as the screen magnifier. (The current Newton prototype doesn’t yet work with such ATs.) I don’t know which approach is overall better for performance though, especially when one takes security into account.

The serialization format for accessibility tree updates is currently JSON, but I’m open to alternatives. Obviously we need to come to a decision on this before this work can go upstream. The JSON schema isn’t yet documented; so far, all code that serializes and deserializes these tree updates is using AccessKit’s serialization implementation.

In addition to tree updates, this architecture also includes one other form of serialized data: accessibility action requests. These are passed in the reverse direction, from the AT to the application via the compositor, again using file descriptor passing. Supported actions include moving the keyboard focus, clicking a widget, setting the text selection or caret position, and setting the value of a slider or similar widget. The notes about serialization of tree updates above also apply to action requests.

Note that the compositor is the final authority on which tree updates are sent to the ATs at what time, as well as on which surface has the focus. This is in contrast with AT-SPI, where ATs receive tree updates directly from applications, and any application can claim to have the focus at any time. This is important for security, especially for sandboxed applications.

Open architectural issues

The biggest unresolved issue at this point is whether the push-based approach of Newton, the motivation for which I described in the previous post, will have unsolvable drawbacks, e.g. for large text documents. The current AccessKit implementation for GtkTextView pushes the full content of the document, with complete text layout information. On my brand new desktop computer, this has good performance even when reading an 800 KB ebook, but of course, there are bigger documents, and that’s a very fast computer. We will likely want to explore ways of incrementally pushing parts of the document based on what’s visible, adding and removing paragraphs as they go in and out of view. The challenge is to do this without breaking screen reader functionality that people have come to depend on, such as Orca’s Say All command. My best idea about how to handle this didn’t occur to me until after I had finished the current implementation. Anyway, we should start testing the current, naive implementation and see how far it takes us.

The current AT protocol mentioned above doesn’t provide a way for ATs to explore all accessible surfaces on the desktop; they can only find out about an accessible surface if it receives keyboard focus or if the pointer moves inside it. A solution to this problem may be necessary for ATs other than Orca, or for automated testing tools which currently use AT-SPI.

The current architecture assumes that each Wayland surface has a single accessibility tree. There isn’t yet an equivalent to AT-SPI’s plugs and sockets, to allow externally generated subtrees to be plugged into the surface’s main tree. Something like this may be necessary for web rendering engines.

I’m not yet sure how I’ll implement Newton support in the UI of GNOME Shell itself. That UI runs inside the same process as the compositor, and isn’t implemented as Wayland surfaces but as Clutter actors (the Wayland surfaces themselves map to Clutter actors). So the existing AccessKit Newton backend won’t work for this UI as it did for GTK. One option would be for Mutter to directly generate serialized tree updates without going through AccessKit. That would require us to finalize the choice of serialization format sooner than we otherwise might. While not as convenient as using the AccessKit C API as I did in GTK, that might be the least difficult option overall.

Newton doesn’t expose screen coordinates, for individual accessible nodes or for the surfaces themselves. ATs are notified when the pointer moves, but the compositor only gives them the accessible surface ID that the pointer is inside, and the coordinates within that surface. I don’t yet have a solution for explore-by-touch, alternative input methods like eye-tracking, or ATs that want to draw overlays on top of accessible objects (e.g. a visual highlight for the screen reader cursor).

Next steps

The earlier section on what’s broken or not done yet includes several issues that should be straightforward to fix. I’ll fix as many of these as possible in the next several days.

But the next major milestone is to get my GTK AccessKit integration reviewed and merged. Since Newton itself isn’t yet ready to go upstream, the immediate benefit of merging GTK AccessKit support would be accessibility on Windows and macOS. The current branch, which uses the prototype Newton backend for AccessKit, can’t be merged, but it wouldn’t be difficult to optionally support AccessKit’s AT-SPI backend instead, while keeping the Newton version on an unmerged branch.

The main challenge I need to overcome before submitting the GTK AccessKit integration for review is that the current build system for the AccessKit C bindings is not friendly to distribution packagers. In particular, one currently has to have rustup and a Rust nightly toolchain installed in order to generate the C header file, and there isn’t yet support for installing the header file, library, and CMake configuration files in FHS-compliant locations. Also, that build process should ideally produce a pkg-config configuration file. My current gnome-build-meta branch has fairly ugly workarounds for these issues, including a pre-generated C header file checked into the repository. My current plan for solving the nightly Rust requirement is to commit the generated header file to the AccessKit repository. I don’t yet know how I’ll solve the other issues; I might switch from CMake to Meson.

The other major thing I need to work on soon is documentation. My current contract with the GNOME Foundation is ending soon, and we need to make sure that my current work is documented well enough that someone else can continue it if needed. This blog post itself is a step in that direction.

Help wanted: testing and measuring performance

I have not yet systematically tested and measured the performance of the Newton stack. To be honest, measuring performance isn’t something that I’m particularly good at. So I ask that Orca users try out the Newton stack in scenarios that are likely to pose performance problems, such as large documents as discussed above. Then, when scenarios that lead to poor performance are identified, it would be useful to have someone who is skilled with a profiler or similar tools help me investigate where the bottlenecks actually are.

Other desktop environments

While my work on Newton so far has been focused on GNOME, I’m open to working with other desktop environments as well. I realize that the decision to use D-Bus for the AT client protocol won’t be universally liked; I suspect that wlroots-based compositor developers in particular would rather implement a Wayland protocol extension. Personally, I see the advantages and disadvantages of both approaches, and am not strongly attached to either. One solution I’m considering is to define both D-Bus and Wayland protocols for the interface between the compositor and ATs, and support both protocols in the low-level Newton client library, so each compositor can implement whichever one it prefers. Anyway, I’m open to feedback from developers of other desktop environments and compositors.


While the Newton project is far from done, I hope that the demo, builds, and status update have provided a glimpse of its potential to solve long-standing problems with free desktop accessibility, particularly as the free desktop world continues to move toward Wayland and sandboxing technologies like Flatpak. We look forward to testing and feedback from the community as we keep working to advance the state of the art in free desktop accessibility.

Thanks again to the Sovereign Tech Fund and the GNOME Foundation for making this work possible.

Status update, 18th June 2024


If you’re into podcasts, the Blindboy Podcast is possibly the best one. Recent episode The State of the World begins on a pointy rock off the coast of Ireland that houses a 6th century monastery.

Photo of Skellig Michael
Skellig Michael – Photo credit – Tristan Reville

Then he raises the question of why, when the Russian government & the Russian military commit war crimes, US & European leaders apply punishing economic sanctions to Russia, and when the government and the military of Israel commit atrocities and war crimes, US & European leaders ask them nicely if they could please stop at some point, or at least commit slightly fewer of them.

On that note, I’d like to shout out the Bands Boycott Barclays movement in the UK — our politicians have failed, but at least our musicians aren’t afraid to follow the money and stand up for human rights.

Heisenbugs and spaghetti code

In computer programming jargon, a heisenbug is a software bug that seems to disappear or alter its behavior when one attempts to study it. (Wikipedia)

In the year 2018 I was passing time in Barcelona waiting to start a new job (and a new life, as it turned out). It was the middle of July and everyone had left for the summer. Meanwhile GNOME had recently gained a Gitlab instance and for the first time we could run automated test pipelines in Gitlab CI, so I set up initial CI pipelines for Tracker and Tracker Miners. (Which, by the way, are undergoing a rename).

Tracker Miners already has a testsuite, but in 2018 it was only run when someone decided to run it locally. Remember that our filesystem indexer is implemented as many GIO async callbacks, which means it is complex spaghetti code. These callbacks can fire in a different order depending on various factors. Tracker responds to filesystem notifications and disk reads, which are unpredictable. And of course, it has bugs — in 2018 nobody much was funding maintenance Tracker or Tracker Miners, and many of these would trigger, or not, depending on the execution order of the internal async callbacks.

So, many of the tests would just randomly fail, making the new CI rather useless.

Thanks to a lot of hard work by Carlos and myself, we documented and fixed these issues and the indexer is in a much better state. It was not much fun! I would rather be eating spaghetti than tracing through spaghetti code. I guess somewhere I made bad life choices.

Bowl of spaghetti

In 2021, somehow not learning my lesson, I adopted the nascent openQA tests for GNOME OS that were developed by one of my Codethink colleagues. These have been running for 3 years now at a “beta” quality service, and have caught some interesting bugs along the way.

In late 2023 we noticed a seemingly random test failure, where the machine under test makes it through initial setup, but didn’t get to a user session. Let’s call this issue 62.

This issue reproduces a lot, when you don’t want it to, and rarely if ever when you do. Here’s a recent example. See how the initial setup stage (gnome_welcome) passes, but the transfer to the final user session never happens:

openQA test result with gnome_desktop test failing

Random test failures make a test framework completely useless – if “test failed” can mean “your code is fine but fate is not on your side today”, then it’s not a useful signal for anyone. So for the GNOME OS automated testing to progress, we need to fix this intermittent failure.

GNOME OS runs many different processes during startup, which can start in whatever order the systemd daemon decides to start them, and can then start child processes of their own. No two boots are exactly the same. The graphical startup is driven by systemd, GDM, gnome-session, gnome-initial-setup and GNOME Shell, context switching between themselves hundreds of times following D-Bus message traffic. You could say that this is an extra large portion of spaghetti, with toxic meatballs.

As part of the STF engagement with GNOME OS, Neill Whillans spent a considerable amount of time comparing logs from good and bad boots, investigating the various processes that run at start-up time, and trying to catch the boot failure locally. I’ve tried my best to help as time permits (which it mostly does not).

Fundamentally, the issue is that there are two gnome-shell processes running, one for the initial-setup user and one for the new ‘testuser’ which should take over the session. GDM is supposed to kill the initial-setup shell when the new ‘testuser’ shell begins. This doesn’t happen.

We have a theory that its something failing during the startup of the new gnome-shell. The layout mechanism looks very complex, so its entirely possible that there is a heisenbug in there somewhere. As you would expect, enabling debug logs for GNOME Shell causes the issue to go away completely.

There’s a lesson here, which is that excessive complexity kills motivation, and ultimately kills projects. We could have done quite a lot in the last 7 months if not for this issue. Let’s all try to keep our code simple, debuggable and documented. We have succeeded in the past at killing overly complex abstractions in GNOME — remember CORBA? Perhaps we need to do so again.

What happens next? I am not sure – I think we may just have to give up on the end-to-end tests for the time being, as we can’t justify spending more of the current budget on this, and I’ve done enough volunteer debugging and documentation efforts for the time being – I plan to spend the summer evenings in a hammock, far away from any GNOME code, perhaps eating spaghetti.

Debarshi Ray


Toolbx now enables the proprietary NVIDIA driver

… and why did it take so long for that to happen?

If you build Toolbx from Git and install it to your usual prefix on a host operating system with the proprietary NVIDIA driver, then you will be able to use the driver on all freshly started Toolbx containers. Just like that. There’s no need to recreate your containers or to use some special option. It will just work.

How does it work?

Toolbx uses the NVIDIA Container Toolkit to generate a Container Device Interface specification on the host during the toolbox enter and toolbox run commands. This is a JSON or YAML description of the environment variables, files and other miscellaneous things required by the user space part of the proprietary NVIDIA driver. Containers share the kernel space driver with the host, so we don’t have to worry about that. This specification is then shared with the Toolbx container’s entry point, which is the toolbox init-container command running inside the container. The entry point handles the hooks and bind mounts, while the environment variables are handled by the podman exec process running on the host.

It’s worth pointing out that right now this neither uses nvidia-ctk cdi generate to generate the Container Device Interface specification nor podman create --device to consume it. We may decide to change this in the future, but right now this is the way it is.

The main problem with podman create is that the specification must be saved in /etc/cdi or /var/run/cdi, both of which require root access, for it to be visible to podman create --device. Toolbx containers are often used rootless, so requiring root privileges for hardware support, something that’s not necessary on the host, will be a problem.

Secondly, updating the toolbox(1) binary won’t enable the proprietary NVIDIA driver in existing containers, because podman create only affects new containers.

Therefore, Toolbx uses the Go APIs, which are also used by podman create, to parse and apply the specification itself. The hooks in the specification are a bit awkward to deal with. So, at the moment only ldconfig(8) is supported.

The issue with nvidia-ctk is relatively minor and is because it’s another different binary. It makes error handling more difficult, and downstream distributors and users of Toolbx need to be aware of the dependency. Instead, it’s better to directly use the and Go APIs that nvidia-ctk also uses. This offers all the usual error handling facilities in Go and ensures that the dependency won’t go missing.

Why did it take so long?

Well, hardware support needs hardware, and sometimes it takes time to get access to it. I didn’t want to optimistically throw together a soup of find(1), grep(1), sed(1), etc. calls without any testing in the hope that it will all work out. That approach may be fine for some projects, but not for Toolbx.

Red Hat recently got me a ThinkPad P72 laptop with a NVIDIA Quadro P600 GPU that let me proceed with this work.

Christian Schaller


Fedora Workstation development update – Artificial Intelligence edition

There are times when you feel your making no progress and there are other times when things feel like they are landing in quick succession. Luckily this definitely is the second when a lot of our long term efforts are finally coming over the finish line. As many of you probably know our priorities tend to be driven by a combination of what our RHEL Workstation customers need, what our hardware partners are doing and what is needed for Fedora Workstation to succeed. We also try to be good upstream partners and do patch reviews and participate where we can in working on upstream standards, especially those of course of concern to our RHEL Workstation and Server users. So when all those things align we are at our most productive and that seems to be what is happening now. Everything below is features in flight that will at the latest land in Fedora Workstation 41.

Artificial Intelligence

Granite LLM

IBM Granite LLM models usher in a new era of open source AI.

One of the areas of great importance to Red Hat currently is working on enabling our customers and users to take advantage of the advances in Artificial Intelligence. We do this in a lot of interesting ways like our recently announced work with IBM to release the high quality Granite AI models under terms that make them the most open major vendor AI models according to the Stanford Foundation Model Transparency Index , but not only are we releasing the full LLM source code, we are also creating a project to make modifying and teaching the LLM a lot easier through a project we call Instructlab. Instructlab is enabling almost anyone to quickly download a Granite LLM model and start teaching it specific things relevant to you or your organization. This put you in control of the AI and what it knows and can do as opposed to being demoted to a pure consumer.

And it is not just Granite, we are ensuring other other major AI projects will work with Fedora too, like Meta’s popular Llama LLM. And a big step for that is how Tom Rix has been working on bringing in AMD accelerated support (ROCm) for PyTorch to Fedora. PyTorch is an optimized tensor library for deep learning using GPUs and CPUs. The long term goal is that you should be able to just install PyTorch on Fedora and have it work hardware accelerated with any of the 3 major GPU vendors chipsets.

NVIDIA in Fedora
Fedora Workstation
So the clear market leader at the moment for powering AI workloads in NVIDIA so I am also happy to let you know about two updates we are working on that will make you life better on Fedora when using NVIDIA GPUs, be that for graphics or for compute or Artificial Intelligence. So for the longest time we have had easy install of the NVIDIA driver through GNOME Software in Fedora Workstation, unfortunately this setup never dealt with what is now the default usecase, which is using it with a system that has secure boot enabled. So the driver install was dropped from GNOME Software in our most recent release as the only way for people to get it working was through using mokutils on the command line, but the UI didn’t tell you that. Well we of course realize that sending people back to the command line to get this driver installed is highly unfortunate so Milan Crha has been working together with Alan Day and Jakub Steiner to come up with a streamlined user experience in GNOME Software to let you install the binary NVIDIA driver and provide you with an integrated graphical user interface help to sign the kernel module for use with secure boot. This is a bit different than what we for instance are doing in RHEL, where we are working with NVIDIA to provide pre-signed kernel modules, but that is a lot harder to do in Fedora due to the rapidly updating kernel versions and which most Fedora users appreciate as a big plus. So instead what we are for opting in Fedora is as I said to make it simple for you to self-sign the kernel module for use with secure boot. We are currently looking at when we can make this feature available, but no later than Fedora Workstation 41 for sure.

Toolbx getting top notch NVIDIA integration


Container Toolbx enables developers quick and easy access to their favorite development platforms

Toolbx, our incredible developer focused containers tool, is going from strength to strength these days with the rewrite from the old shell scripts to Go starting to pay dividends. The next major feature that we are closing in on is full NVIDIA driver support with Toolbx. As most of you know Toolbx is our developer container solution which makes it super simple to set up development containers with specific versions of Fedora or RHEL or many other distributions. Debarshi Ray has been working on implementing support for the official NVIDIA container device interface module which should enable us to provide full NVIDIA and CUDA support for Toolbx containers. This should provide reliable NVIDIA driver support going forward and Debarshi is currently testing various AI related container images to ensure they run smoothly on the new setup.

We are also hoping the packaging fixes to subscription manager will land soon as that will make using RHEL containers on Fedora a lot smoother. While this feature basically already works as outlined here we do hope to make it even more streamlined going forward.

Open Source NVIDIA support
Of course being Red Hat we haven’t forgotten about open source here, you probably heard about Nova our new Rust based upstream kernel driver for NVIDIA hardware which will provided optimized support for the hardware supported by NVIDIAs firmware (basically all newer ones) and accelerate Vulkan through the NVK module and provide OpenGL through Zink. That effort is still quite early days, but there is some really cool developments happening around Nova that I am not at liberty to share yet, but I hope to be able to talk about those soon.

High Dynamic Range (HDR)
Jonas Ådahl after completing the remote access work for GNOME under Wayland has moved his focus to help land the HDR support in mutter and GNOME Shell. He recently finished rebasing his HDR patches onto a wip merge request from
Georges Stavracas which ported gnome-shell to using paint nodes,

So the HDR enablement in mutter and GNOME shell is now a set of 3 patches.

With this the work is mostly done, what is left is avoiding over exposure of the cursor, and inhibiting direct scanout.

We also hope to help finalize the upstream Wayland specs soon so that everyone can implement this and know the protocols are stable and final.

DRM leasing – VR Headsets
VR Googles
The most common usecase for DRM leasing is VR headsets, but it is also a useful feature for things like video walls. José Expósito is working on finalizing a patch for it using the Wayland protocol adopted by KDE and others. We where somewhat hesitant to go down this route as we felt a portal would have been a better approach, especially as a lot of our experienced developers are worried that Wayland is in the process of replicating one of the core issues with X through the unmanageable plethora of Wayland protocols that is being pushed. That said, the DRM leasing stuff was not a hill worth dying on here, getting this feature out to our users in a way they could quickly use was more critical, so DRM leasing will land soon through this merge request.

Explicit sync
Another effort that we have put a lot of effort into together with our colleagues at NVIDIA is landing support for what is called explicit sync into the Linux kernel and the graphics drivers.The linux graphics stack was up to this point using something called implicit sync, but the NVIDIA drivers did not work well with that and thus people where experiencing ‘blinking’ applications under Wayland. So we worked with NVIDIA and have landed the basic support in the kernel and in GNOME and thus once the 555 release of the NVIDIA driver is out we hope the ‘blinking’ issues are fully resolved for your display. There has been some online discussion about potential performance gains from this change too, across all graphics drivers, but the reality of this is somewhat uncertain or at least it is still unclear if there will be real world measurable gains from adding explicit sync. I heard knowledgeable people argue both sides with some saying there should be visible performance gains while others say the potential gains will be so specific that unless you write a test to benchmark it explicitly you will not be able to detect a difference. But what is beyond doubt is that this will make using the NVIDIA stack with Wayland a lot better a that is a worthwhile goal in itself. The one item we are still working on is integrating the PipeWire support for explicit sync into our stack, because without it you might have the same flickering issues with PipeWire streams on top of the NVIDIA driver that you have up to now seen on your screen. So for instance if you are using PipeWire for screen capture it might look fine on screen with the fixes already merged, but the captured video has flickering. Wim Taymans landed some initial support in PipeWire already so now Michel Dänzer is working on implementing the needed bits for PipeWire in mutter. At the same time Wim is working on ensuring we have a testing client available to verify the compositor support. Once everything has landed in mutter and we been able to verify that it works with the sample client we will need to add support to client applications interacting with PipeWire, like Firefox, Chrome, OBS Studio and GNOME-remote-desktop.

Jakub Steiner


Sketch Friday

I’ve been posting a few sketches on my mastodon every Friday. They are mostly sketches of application icon design process, but not always. Follow, like, subscribe!

04e5ae648d8b3640 Exhibit2 05521b4858a9cdef Exhibit 0934a02f1c915ef7 f340603a0cd5eb83 0f02657ab2fb4012 Gameeky 10 1e6abb0c31ee6066 Gameeky 11 286a44fb410d5ad3 Gameeky 8 2f6c03bd10824d1d Gimp 2 48d49e6ce2bcebb5 Gimp 5af3244c6aef1539 Handbook_And_Developers Afternoon 3 Inkscape Afternoon Key_Rack 1 AI Laptop 1 Alpaca 1 2 Laptop3 Alpaca 2 Laptop Alpaca 4 Luminance 1 Alpaca Memorize 1 Aurea 3 Memorize 2 Aurea Memorize Captive_Portal4 Mypaint Captive_Portal5 Nvidia_Toolbx cbad883c19680ae2 Office_Runner 1 cd2b1f4788bd40c9 Office_Runner Collector Orca 2 Convolution 2 Orca Convolution 3 Papers 4 Convolution Papers 7 d6d20716bd43b122 Plots 2 d711da1c0aeec75f Power_Modes dabc00e72dc94dca Seabird 2 ddff8a8e07260602 Seabird Dew_Duct Valuta 1 Valuta e0d5efac768fe1aa Videos 2 Exhibit 1 Videos 3 Exhibit 2

Flathub Blog


Linter breaking change: restricting automatic merge

Starting next week, June 17th, the linter will no longer allow enabling automatic merge for pull requests created by flatpak-external-data-checker, unless the app uses an extra-data source or has been added to the exceptions list.

The verified apps utilizing automerge-flathubbot-prs before May 28th have been grandfathered and no explicit action is required from the maintainers. If your app does not meet these criteria, you can still request an exception by creating a pull request to the flatpak-builder-lint repository. Exception requests will be judged on a case-by-case basis on providing valid reasons.

The automerge-flathubbot-prs feature has been introduced back in 2019 to reduce maintenance overhead of applications dependent on non-redistributable data. If an extra-data source used by the app disappears because of an update, the automation would ensure that it is still possible to install the app, without requiring manual intervention from the maintainers.

Over time, the feature has started to be misused as a general purpose automation for publishing updates without proper testing. This has led to a number of issues where apps became broken overnight, which could have been easily prevented if the update was tested before merging.

SSH agent extensions as an arbitrary RPC mechanism

A while back, I wrote about using the SSH agent protocol to satisfy WebAuthn requests. The main problem with this approach is that it required starting the SSH agent with a special argument and also involved being a little too friendly with the implementation - things worked because I could provide an arbitrary public key and the implementation never validated that, but it would be legitimate for it to start doing so and then break everything. And it also only worked for keys stored on tokens that ssh supports - there was no way to extend this to other keystores on the client (such as the Secure Enclave on Macs, or TPM-backed keys on PCs). I wanted a better solution.

It turns out that it was far easier than I expected. The ssh agent protocol is documented here, and the interesting part is the extension support extension mechanism. Basically, you can declare an extension and then just tunnel whatever you want over it. As before, my goto was the go ssh agent package which conveniently implements both the client and server side of this. Implementing the local agent is trivial - look up SSH_AUTH_SOCK, connect to it, create a new agent client that can communicate with that by calling NewClient, and then implement the ExtendedAgent interface, create a new socket, and call ServeAgent against that. Most of the ExtendedAgent functions should simply call through to the original agent, with the exception of Extension(). Just add a case statement against extensionType, define some reasonably namespaced extension, and you're done.

Now you need to use this agent. You probably don't want to use this for arbitrary hosts (agent forwarding should only be enabled for remote systems you trust, not arbitrary machines you connect to - if you enabled agent forwarding for github and github got compromised, github would be able to use any private keys loaded into your agent, and you probably don't want that). So the right approach is to add a Host entry to the ssh config with a ForwardAgent stanza pointing at the socket you created in your new agent. This way the configured subset of remote hosts will automatically talk to this new custom agent, while forwarding for anything else will still be at the user's discretion.

For the remote end things are even easier. Look up SSH_AUTH_SOCK and call NewClient as before, and then simply call client.Extension(). Whatever you stick in the contents argument will simply end up being received at the client end. You now have a communication channel between a the remote system and the local client, and what you do with that is up to you. I'm using it to allow a remote system to obtain auth tokens from Okta and forward WebAuthn challenges that can either be satisfied via a local WebAuthn token or by passing the query off to Mac TouchID, but there's fundamentally no constraints whatsoever on what can be done here.

(If you want to do this on Windows and still have everything work with existing clients you'll need to take this into account - Windows didn't really do Unix sockets until recently so everything there is awful)

comment count unavailable comments

Lennart Poettering


Announcing systemd v256

Yesterday evening we released systemd v256 into the wild. While other projects, such as Firefox are just about to leave the 7bit world and enter 8bit territory, we already entered 9bit version territory! For details about the release, see our announcement mail.

In the weeks leading up to this release I have posted a series of serieses of posts to Mastodon about key new features in this release. Mastodon has its goods and its bads. Among the latter is probably that it isn't that great for posting listings of serieses of posts. Hence let me provide you with a list of the relevant first post in the series of posts here:

I intend to do a similar series of serieses of posts for the next systemd release (v257), hence if you haven't left tech Twitter for Mastodon yet, now is the opportunity.

And while I have you: note that the All Systems Go 2024 Conference (Berlin) Call for Papers ends 😲 THIS WEEK 🤯! Hence, HURRY, and get your submissions in now, for the best low-level Linux userspace conference around!

Miguel de Icaza


11 Jun 2024


To celebrate that RealityKit's is coming to MacOS, iOS and iPadOS and is no longer limited to VisionOS, I am releasing SwiftNavigation for RealityKit.

Last year, as I was building a game for VisionPro, I wanted the 3D characters I placed in the world to navigate the world, go from one point to another, avoid obstacles and have those 3D characters avoid each other.

Almost every game engine in the world uses the C++ library RecastNavigation library to do this - Unity, Unreal and Godot all use it.

SwiftNavigation was born: Both a Swift wrapper to the underlying C++ library which leverages extensively Swift's C++ interoperability capabilities and it directly integrates into the RealityKit entity system.

This library is magical, you create a navigation mesh from the world that you capture and then you can query it for paths to navigate from one point to another or you can create a crowd controller that will automatically move your objects.

Until I have the time to write full tutorials, your best bet is to look at the example project that uses it.

Andy Holmes


GNOME 46 and Beyond

With GNOME 46.2 released, it seems like a good time to write a post about goings on in GNOME Online Accounts and other STF-funded initiatives. There's a lot to be excited about this cycle and most of it is leading to more improvements in the near future.

# GNOME Online Accounts

A lot happened in GNOME 46 for GNOME Online Accounts, including two new providers, a port to GTK4 and Adwaita, authentication in the desktop browser, and a large refactoring towards contemporary platform conventions.

The new WebDAV and Microsoft 365 providers contrast quite a bit, although both made progress in the general direction we want to move. The existing Nexcloud provider was a good starting point for WebDAV, but support for more implementations and auto-discovery were important goals for our push towards open protocols and local-first principles.

# WebDAV

The WebDAV provider seemed like it would be fairly straightforward, however feedback from the community has shown that several popular services like Fastmail and offer support for features like custom domain names and app passwords restricted by content type. These are great features, but not supported by a naive implementation of standard service discovery.

The large refactoring in GNOME 46 de-duplicated a lot of code and is much easier to use, but a lot of the account setup process is still tightly coupled to the user interface. The new work being done to support more service configurations and improve the user experience is separate from the provider code, which has already led to the easy integration of some nice features:

In the video above, you can see the implementation of Mail Autoconfig at work detecting settings for Fastmail from the email address, in the otherwise unchanged Email setup dialog. I'd like to thank Tyler and the rest of the development team at Fastmail for extending me an account for additional testing this cycle. By design, Mail Autoconfig doesn't need authentication, but this account was very helpful while improving support for content-restricted app passwords.

These app passwords are especially relevant to WebDAV, which received a few adjustments to adopt an internal API compatible with the Mail Autoconfig implementation. While the WebDAV setup dialog hasn't landed the same UI feedback improvements yet, there is a work-in-progress that does:

In this video, you can see an early prototype for a goal of a lot of this work. Thanks to the research and work of Éloi Rivard we have an excellent whiteboard for Generic Service Providers, detailing a large amount of the subject matter. The immediate goal then is to offer an account type that's easy to set up, supports the common set of services (mail, calendar, contacts and files) and adapts to the available services using open protocols and standards.

We still have longer-term changes planned to support a sandbox-friendly ecosystem, but it's not yet clear what form GNOME Online Accounts will take or how applications will interact with it. For this reason, all the new code supporting Mail Autoconfig and WebDAV was written for potential reuse later, without investing in becoming yet another Accounts SSO.

# Microsoft 365

Much of the recent work on GNOME Online Accounts was funded by the Sovereign Tech Fund, while the Microsoft 365 provider is the work of Jan-Michael Brummer. The initial support includes access to OneDrive with Files and GVfs, with support for email, contacts and calendar planned for the near future. While our focus remains on open protocols and local-first principles, it's still a goal to support services that the people use in their work and provide benefit to the community.

Something interesting the project gained from Jan's work is the first OAuth provider with support for user-provided client IDs. Currently, every distributor and fork of GNOME Online Accounts has been using the GNOME Foundation's client IDs for OAuth providers. This can be problematic depending on the terms of service and restrictive for those using enterprise or organizational accounts.

A screenshot of the Microsoft 365 setup dialog in GNOME 46.2, with confusingly optional entry fields

Unfortunately, the first iterations of the setup dialog did not result in a good user experience. Microsoft's services come with their own concepts and terminology, which really resulted in a lack of clear direction in the design of the interface during an already busy release cycle. The tight coupling between logic and user interface did not help here either, as evidenced by the double-modal:

A screenshot of the Microsoft 365 setup dialog in GNOME 46.2, with a "Sign In with your browser" dialog stacked on top

The amount of feedback and issues reported has been the most help here, as we learn how Microsoft 365 is being used by the open-source community in both personal and work-related environments. Support for more services like email, calendar and contacts are planned, and hopefully some better support for organizational accounts.

# Orca and Spiel

Something I am thrilled to have the opportunity to take part in is Spiel, a new speech synthesis service by Eitan Isaacson that's a bit reminiscent of MPRIS. New speech providers can be easily written in C, Rust or any other language binding and libspiel will aggregate them for the client.

An interesting difference with Speech Dispatcher is that while the speech provider takes responsibility for speech synthesis, the client application takes responsibility for the audio output. Internally, the speech provider returns a file descriptor over D-Bus and GStreamer is used to output the audio on the client side.

While Spiel does have some exciting possibilities outside of screen readers, including new synthesizers like Piper, you may be surprised to find the speech rate that many users operate a screen reader at. Léonie Watson has an excellent blog post titled Notes on synthetic speech, with plenty of audio clips and insights into how screen readers are used by real people.

I was fortunate enough to have the opportunity to integrate Spiel into Orca, which was a great learning experience and re-sparked my interest in accessibility in general. Something else to watch out for is Matt Campbell's Newton project, bringing a modern accessibility stack to Wayland.

# Acknowledgements

I'd like to again thank the Sovereign Tech Fund for investing in the GNOME project. Their targeted funding of infrastructure and accessibility has empowered a lot of overdue improvements for open source and the GNOME platform.

I'd also like to thank the development team at Fastmail, who upon request graciously granted us an extended account, to continue testing support for their service. The support staff at also extended a trial period as a show of good faith. It's been really encouraging to have these companies show support for the community, thank you!

As always, GNOME's community of contributors and users have been the most help, with diligent reporting, code reviews and advice. There are so many things happening in open source, I really wouldn't be able to keep up without all your help.

CSS Happenings

This cycle GTK got a lot of updates to its CSS engine.

I started this work as part of the Sovereign Tech Fund initiative, and later on Matthias Clasen joined in and we ended up splitting the work on colors.

Let’s go through the changes, as well as how they affect GTK, libadwaita and apps.


The most notable addition is CSS variables, more correctly known as custom properties.

Since before GTK switched to CSS for styles, it has had named colors, a non-standard addition providing a similar functionality. You could define a color, then refer to it by name. Unfortunately, they had big limitations. First, they are global. You can only define colors for the whole stylesheet, and if you want to override them for a single widget subtree – you’re out of luck. Second, they were obviously only for colors.

The only option to have them local to a widget was to use gtk_style_context_add_provider() on a GtkStyleContext obtained via gtk_widget_get_style_context. However, it had an even bigger problem: being local to a widget, it didn’t propagate to children, which made it practically useless. Even for widgets that seemingly don’t have children: for example, if you add a provider to a GtkEntry, it won’t affect its text, or the icons, or the progress bar – they are all separate widgets within the entry. So, it shouldn’t be a big surprise that this API is deprecated.

Variables, meanwhile, don’t have any of these limitations. They can be defined anywhere, propagate to children, and they can be anything – colors, numbers, dimensions, etc. For example, we can override them for a specific widget as follows:

:root {
  --some-color: red;

my-widget {
  --some-color: green;

After defining, they can be accessed with the var() function:

my-widget {
  color: var(--some-color);

This function also allows to specify a fallback in case a variable doesn’t exist, as follows:

my-widget {
  color: var(--some-color, var(--some-other-color, red));

Here it will try, in order, --some-color, then --some-other-color, and if neither exists, red.

The standard place to declare global variables is the :root selector, and so it’s now supported too. While GTK root widgets usually have the window selector, that’s not always the case. For example, GtkCheckButton within a GtkTreeView are their own toplevels and targeting window would not include them. While tree view is deprecated and hopefully on its way out, we have to support it anyway for now, and who knows what other root widgets appear in future, and having :root solves it all nicely.

Variables can even be animated, though that’s not particularly useful: since they can be anything and you may potentially be animating red into 2px, the only way to interpolate them is to have a jump from the initial value to final value at 50%. Either way, the spec allows that and we implement it. You can still use them within another property’s value and animate that property instead – that will work as expected.

There are some things we don’t support at the moment, like the @property at-rule. It allows to declare variable types, optionally prevent inheriting, and specify the default value. Having a type not only provides type checking (and hence more informative error messages), but also allows to actually interpolate variables in animations. See, if a variable can be anything, we can’t interpolate it other than with a jump in the middle. But if we can guarantee it’s a color, or a dimension, or a number, or whatever, at every keyframe, then we can. But, while that would be neat to have, it’s really niche compared to having variables themselves, and so not very important.

Another thing is that it’s not possible to use variables within named colors, like this:

@define-color my_color var(--something); /* this is an error */

Since named colors will be going away in future, that’s not a big deal, but it is worth mentioning anyway. The other way around does work though:

@define-color something red;

:root {
  --my-color: @something; /* this is perfectly fine */

(and that’s fairly important, as it allows us to switch to variables without breaking backwards compatibility)


Next, colors. A lot of features from CSS Color Module Level 4 and Level 5 were implemented as well.

Modern syntax

In the past, CSS has used the following syntax for defining colors:

rgb(255, 0, 0)
rgba(255, 0, 0, 0.5)
hsl(0, 100%, 50%)
hsla(0, 100%, 50%, 0.5)

That’s still supported, but modern CSS also has a simpler and more flexible syntax:

rgb(255 0 0)
rgb(255 0 0 / 50%)
hsl(0 100 50)
hsl(0 100 50 / 50%)

It allows to freely mix percentages and numbers, and makes alpha an optional parameter separated by solidus instead of having separate functions. And now GTK supports it too.

Modern syntax also supports specifying missing components, for example: hsl(none 100% 50%). In most situations they behave same as 0, but they make a difference for interpolation, so we’ll talk about that in more details later.

Additionally, it supports specifying hue in hsl() as an angle instead of a number (meaning that it’s possible to use units like turn instead of degrees), as well as calc() anywhere within these functions:

hsl(calc(0.5turn - 10deg) 100% 50% / calc(1 / 2))

More color spaces

GTK also supports a bunch more color spaces now, all using the modern syntax:

Color space CSS
Linear sRGB color(srgb-linear 1 0 0)
HWB hwb(0deg 0 0)
Oklab oklab(62.8% 0.22 0.13)
Oklch oklch(62.8% 0.25 29)

    I won’t be describing the color spaces in detail, but that information can be easily found online. For example, Oklab and Oklch are very well described in their creator’s blog post.

    color() also supports sRGB, but it works same as rgb(), except the channels have 0-1 range instead of 0-255.

    color() in the spec supports a lot more color spaces, for example Display P3, but since we don’t have HDR support in GTK just yet, supporting them wouldn’t be of much use at the moment, so that’s omitted. Also omitted are Lab, LCh and various XYZ color spaces for now. Oklab/Oklch work better than Lab/LCh for UI colors anyway, and XYZ is fairly niche and not widely used in this context.

    However, just defining colors in different color spaces isn’t that interesting to me. What’s more interesting is deriving new colors from from existing ones in those color spaces, so let’s look at that.

    Color mixing

    First, we have support for the color-mix() function. While GTK has had a non-standard mix() function for more than a decade, color-mix() is not only standard CSS, but also a whole lot more flexible. Most important is the fact it allows to mix colors in different color spaces instead of just sRGB – all of the ones mentioned above:

    color-mix(in oklch, red, green)

    For HSL, HWB and Oklch, it’s possible to specify the hue interpolation mode too – for example, color-mix(in hsl longer hue, red, green).

    color-mix() also allows more sophisticated mixing via missing components. They allow some channels to be taken from one of the colors without taking the other one into account at all. For example, the following mix:

    color-mix(in srgb, rgb(100% none 100%), rgb(none 50% 0))

    takes the red channel from the first color, the green channel from the second color, and mixes the blue channel from both colors, resulting in the following color: color(srgb 1 0.5 0.5).

    For comparison, the same mix but with none replaced by 0:

    color-mix(in srgb, rgb(100% 0 100%), rgb(0 50% 0))

    mixes every channel and produces color(srgb 0.5 0.25 0.5).

    While mix() specifies a single fraction for the mix, color-mix() specifies two percentages. Usually they are normalized so that they add up to 100%, but if their sum is lower than that, it’s used as an alpha multiplier, allowing to add transparency to the mix:

    color-mix(in srgb, red 30%, blue 20%)
    /* color(srgb 0.6 0 0.4 / 0.5) */

    Percentages are optional though. When one is omitted, it’s assumed to be the other one subtracted from 100%. When both are omitted, they are assumed to be 50%/50%.

    Relative colors

    GTK now also supports relative colors. Unlike mixing, these take one color and change its individual channels. For example, we can create a complementary color by inverting the hue:

    hsl(from var(--some-color) calc(h + 0.5turn) s l)

    Change a color’s lightness to a given value:

    oklab(from var(--some-color) 0.9 a b)

    Or add transparency to a color, like the SASS transparentize() function:

    rgb(from var(--some-color) r g b / calc(alpha - 25%))

    Combined with calc() and variables, this is a very flexible system, and I’m really curious to see how it will get used in future.

    Math functions

    There’s also support for a lot of math functions:

    • min(), max(), clamp()
    • round()
    • rem()
    • mod()
    • sin(), cos(), tan(), asin(), acos(), atan(), atan2()
    • pow(), sqrt(), hypot(), log(), exp()
    • abs(), sign()
    • e, pi, infinity, NaN

    They can also be used in calc(), of course. I already found an interesting use case for two of them in libadwaita.

    Misc changes

    The opacity property can now accept percentages in addition to numbers. This wouldn’t matter much, but it means it can accept the same values as color-mix().

    Now let’s look at other changes all of this allowed.

    GTK deprecations

    GTK has a bunch of non-standard additions to CSS, related to colors: most notably, @define-color and named colors, but also the alpha(), mix(), shade(), lighter() and darker() functions. They allow to manipulate colors. Some are more useful than others, but, for example, alpha() is used extensively in libadwaita and modern GNOME apps – especially in combination with currentColor. For example, libadwaita buttons have the following background color: alpha(currentColor, 0.1); and can work with both light and dark background – they just follow the text color.

    As useful as they are, CSS now has standard ways to replace every single one of them, and apps are encouraged to do so.

    Named colors

    @define-color and named colors can be replaced with variables. For example, this snippet:

    @define-color my_color red;
    my-widget {
      color: @my_color;


    :root {
      --my-color: red;
    my-widget {
      color: var(--my-color);


    This is straightforward. color-mix() works exactly same when using the sRGB color space.

    /* mix(red, blue, .3) */
    color-mix(in srgb, red 30%, blue)


    There are multiple ways to replace it. Both color-mix() and relative colors will do the job:

    /* alpha(currentColor, 0.15); */
    color-mix(in srgb, currentColor 15%, transparent)
    rgb(from currentColor r g b / calc(alpha * 0.15))

    Note that only the latter works for factors larger than 1.

    alpha() is also often used with hardcoded colors, usually black. In that case just defining the color as is is sufficient:

    /* alpha(black, .8) */
    rgb(0 0 0 / 80%)


    Shading is less obvious. One might assume that it would work same as mixing with black or white color, but… not quite. It was converting the colors to HSL, multiplying lightness and saturation, and converting back to sRGB. As such, mixing wouldn’t exactly work here, it will produce close but subtly different results, even when done in the HSL color space. For some color/factor combinations it would be same, but other times it will be different.

    Relative colors, meanwhile, allow to manipulate each channel separately, and together with calc() we can do exactly same thing:

    /* shade(red, 1.1) */
    hsl(from red h calc(s * 1.1) calc(l * 1.1))

    This is a lot more verbose, but it produces the same result as shade() for any color and factor. Of course, one could go further and use color spaces like Oklch or Oklab instead, and have more consistent results due to their perceptual nature. More on that later.

    lighter() and darker()

    Well, these are simple. They are just shade() with hardcoded factor parameter: 1.3 for lighter() and 0.7 for darker(). As such, they are replaced the same way:

    /* lighter(red) */
    hsl(from red h calc(s * 1.3) calc(l * 1.3))
    /* darker(red) */
    hsl(from red h calc(s * 0.7) calc(l * 0.7))

    Libadwaita changes

    This has allowed to clean up a lot of things in libadwaita styles too.

    Named colors?

    First of all, all of the existing named colors are available as variables too now. For backwards compatibility reasons their defaut values reference the old colors, for example:

    :root {
      --window-bg-color: @window_bg_color;
      --window-fg-color: @window_fg_color;

    They are named the same way as for the old colors, but with dashes in the names instead of underscores. One exception is that @borders became --border-color instead, for consistency with other colors.

    However, being variables they can still be overridden per widget, have fallbacks and so on.

    NOTE: since they reference named colors, using these variables will produce deprecation warnings with GTK_DEBUG=css. This is a known issue, but there isn’t much that can be done here, other than breaking compatibility for all existing apps with custom styles.

    Also, overriding these variables won’t affect styles that use the old named colors directly. This isn’t a big issue if you’re porting your app (because you can replace both definitions and mentions at once), but may be a problem with libraries like libpanel which provide their own styles and use a lot of named colors. Once those libraries are ported though, it will work fine for both apps that have and haven’t been updated.

    All of the available CSS variables are documented on their page, just like the old named colors. If you’re using libadwaita 1.5 or below, the old docs are still where they were.

    It should be noted that compability colors like @theme_bg_color don’t have equivalent variables. Those colors only exist for compatibility, and apps using libadwaita shouldn’t be using them in the first place – specifically that color can be replaced by --window-bg-color, and so on. This can be a problem for libraries like WebKitGTK, however, so maybe there should be an agreed upon set of variables too. For now though, named colors still exist and won’t go away until GTK5, but this is something that needs be figured out at some point.

    New variables

    There are a few additions as well:

    • The opacity used for borders is also available separately now, as --border-opacity.
    • The opacities used for the .dim-label style class and disabled widgets are available as well, as --dim-opacity and --disabled-opacity respectively.
    • --window-radius refers to the window’s current corner radius. Normally it’s 12px, but it becomes 0 when the window is maximized, tiled and so on. It can be used for things like rounding focus rings near window corners only for floating windows, instead of using a long and unwieldy selector like this:

      window:not(.maximized):not(.tiled):not(.tiled-left):not(.tiled-right):not(.tiled-top):not(.tiled-bottom):not(.fullscreen):not(.solid-csd) my-widget {
        /* yikes */

    Style changes

    There were a few things that we wanted to do for a while, but that needed variables to be feasible. Now they are feasible, and are implemented.

    Screenshot of a toolbar with .osd style class. It's dark, with light text. It has a GtkScale in it, which is light grey instead of blue.

    For example, the .osd style overrides accent color to be light grey. Previously this was done with separate overrides for every single widget that uses accent, and in some cases blue accents sneaked through anyway. It also didn’t work with custom widgets defined in apps, unless they special cased it themselves. Now it just overrides the accent color variables and is both much simpler and actually consistent.

    Screenshot of a destructive button saying "Destructive Button". It's focused, and the focus ring is red.

    Destructive buttons previously had blue focus rings, and now they are red, matching the buttons themselves. Moreover, apps can easily get custom-colored widgets like this themselves, with matching focus rings.

    Since previously we couldn’t override accent color per-widget, the way to recolor buttons (as well as checks, switches etc) was to just set background-color and color properties manually. It worked, but obviously didn’t affect focus rings, and focus rings are complicated, so doing it manually wouldn’t be feasible. They have a very specific color with a specific opacity, a different opacity for the high contrast mode, and the actual selector changes a lot between widgets. In case of buttons it’s button:focus:focus-visible, for entries it’s entry:focus-within and so on. Not very good, so we didn’t encourage apps to change focus rings, and didn’t change them on destructive buttons either.

    But now that we have variables, people can just override the accent color, and focus rings will follow the suit. For example, to make a green button, an app can just apply .suggested-action and override accent on it to green:

    Screenshot of a green pill button saying "Very Green Button" on top of a light background. It's focused and the focus ring is green.Screenshot of a green pill button saying "Very Green Button" on top of a dark background. It's focused and the focus ring is green.

    So, for example, Calculator can now make its big orange result button orange and have matching focus ring on it without changing accent color in the whole app.

    Because of that, the .opaque style class has been deprecated. Instead, apps are encouraged to use .suggested-action like above.

    Meanwhile, GtkEntry with .error, .warning and .success style classes have had red focus rings, as an exception. Later, AdwEntryRow gained the same styles, but that was messy overall. But now these style classes just set accent color in addition to text color, so these styles aren’t special cases anymore – they will work with any widget.

    Deriving accent colors

    Since 1.0, libadwaita has had two separate accent colors: --accent-bg-color and --accent-color. The former is suitable for use as a background on widgets like buttons, checks and switches, but usually has too low contrast to be used as a text color. The latter has a higher contrast, suitable for use as text, but it doesn’t work well as a background.

    That’s a bit confusing, and I’ve seen a lot of apps misusing them. Some apps set a custom accent color and just set them to the same value, so they don’t have enough contrast. Some apps mix up the colors and use the wrong one. And so on.

    It would be really nice if we could generate one from the other one. Previously this was firmly in the realm of pipe dreams, but now that we have relative colors, it’s a lot more feasible.

    For example, the following functions produce consistently good colors for light and dark styles respectively:

    /* light style */
    --accent-color: oklab(from var(--accent-bg-color) min(l, 0.5) a b);
    /* dark style */
    --accent-color: oklab(from var(--accent-bg-color) max(l, 0.85) a b);

    Here are some examples, for both light and dark style:

    Screenshot of a window with its left half being light and right half being dark. Each half has 7 rows, each row has a button, a label and an entry. The row colors are as follows, top to bottom: neutral, red, orange, yellow, green, blue, purple

    Unlike the HSL color space, Oklab is perceptual and lightness stays uniform regardless of other channels. So, as simple as it sounds, just limiting the lightness does the job. In fact, we could even do this:

    /* light style */
    --accent-color: oklab(from var(--accent-bg-color) 0.5 a b);
    /* dark style */
    --accent-color: oklab(from var(--accent-bg-color) 0.85 a b);

    The downside is that --accent-bg-color: black; in light style would produce a dark gray --accent-color instead of black, and --accent-bg-color: white; in dark style would produce a light gray accent instead of white. This may be fine, but the goal here is to ensure minimum contrast, not to prevent too much contrast.

    These functions are also in the docs, but there’s one problem.

    So why not do it automatically?

    Say, we define these variables at :root, as follows:

    :root {
      --accent-bg-color: var(--blue-3);
      --accent-color: oklab(from var(--accent-bg-color) min(l, 0.5) a b);

    Then, we override accent color for a specific widget:

    my-green-widget {
      --accent-bg-color: var(--green-3);

    But --accent-color would still be blue, so the we would need to re-declare it – not very good.

    There is a way to get around that – use a wildcard:

    * {
      --accent-color: oklab(from var(--accent-bg-color) min(l, 0.5) a b);

    But that has its own downsides – it may impact performance and memory use, like wildcards in CSS tend to do. That said, I haven’t profiled it and maybe it’s not that bad.

    Either way, for now we’re keeping --accent-color separate, but apps overriding it are welcome to use the above functions themselves, instead of picking the color by hand. Maybe in future we’ll figure something out.


    There are a lot more things we could do if we didn’t need to care about backwards compatibility. For example, instead of having colors like --shade-color that are always supposed to be partially-transparent black, we could just provide the opacity as a variable. (or maybe even derive it from background color lightness?) We could specify accent color as a hue and/or chroma in the Oklch color space, while keeping the lightness consistent. And so on.

    Can we drop SCSS entirely?

    A lot of people asked this or assumed that these additions were the few missing pieces for dropping it.

    As much as I’d like to, not yet. While this helps with reducing dependency on it a bit, there are still a lot of things that cannot be replicated, like @mixin and @extend. It also allows us to have private variables and control what gets exposed as API, which is pretty important for a library.

    In fact, there are situations where we had to add more SASS-specific things, tho mostly because we’re using the older (and unmaintained) sassc compiler instead of dart-sass. Being old and unmaintained, sassc doesn’t support the modern rgb() syntax, and errors out. There is a way to placate it, by using RGB() instead, but that’s a hack. It also doesn’t like the slash symbol (well, solidus) and thinks it’s division. This can be worked around with string interpolation, but it’s a hack too.

    So, instead of this:

    rgb(0 0 0 / if($contrast == 'high', 80%, 5%))

    we have to do this:

    RGB(0 0 0 / #{if($contrast == 'high', 80%, 5%)})

    (and then keep in mind that SASS won’t see this as a color, so you can’t use functions like transparentize() on it, not that we really need to)

    Finally, the opacity() function (used within the filter property) kept getting replaced with alpha(), but only when it contained a variable instead of a literal:

    filter: opacity(55%);
    /* SASS output: filter: opacity(55%); */
    filter: opacity(var(--disabled-opacity));
    /* SASS output: filter: alpha(var(--disabled-opacity)); */

    I worked around this by once again manipulating case. opacity() was affected, but Opacity() wasn’t.

    All of this could be solved by migrating to dart-sass. This is difficult, however – GNOME SDK doesn’t have any Dart components at the moment, and it would need to build that first. A lot of manifests I’ve seen on Flathub just use pre-built SASS compiler, but I doubt this would be acceptable for the SDK. So, for now we’re stuck with sassc. Help is very much welcome, particularly with the SDK side of things.

    Programmatic API?

    Another question I see fairly often: is it possible to read variables programmatically?

    The short answer is: no.

    The longer answer is: variables can be anything. The only way to read them would be as a string. At that point, you’d need to have a CSS parser in your app to actually parse them into a useful value.

    For example, let’s say we have a variable declaring a color, like red. One might think: so what, just use gdk_rgba_parse(). Now, what about rgb(0 0 0 / 50%), the modern syntax? gdk_rgba_parse() doesn’t support that, but one might argue it should. Alright, what about this:

    color-mix(in oklab, currentColor, var(--something) calc(var(--base-percentage) * 0.5))

    This is a potentially valid color, and yet there’s no way gdk_rgba_parse() would be able to parse this – how would it know what currentColor is when you don’t supply it a widget? It would need to resolve other variables somehow. And it would also need a full-blown calc() parser. Yeah, colors get complicated fast. It’s fine when we’re already inside a CSS parser, but gdk_rgba_parse() isn’t one.

    Now, could we have a function to get a variable value specifically as a color? Sure, except variables don’t have to be colors. You might also get 3px, 0 or asjdflaskjd. They don’t even have to be valid values, they can also be parts of the values, or even random gibberish. CSS doesn’t even try to compute variables on their own, only insert them into other values when you reference them with var() and then parse and compute that once every reference is resolved. The following is perfectly valid:

    my-widget {
      --space: srgb-linear;
      --r: 100%;
      --gb: 0 0;
      color: color(var(--space) var(--r) var(--gb) / 50%);

    So, with that in mind, would API for fetching them be useful? IMO not really. There are very specific cases where it may work (e.g. if you define a variable containing an integer and then treat it as pixels in your code), but they are far too limited, break easily, and arguably are a misuse of CSS anyway.

    It would be a bit more feasible if we had @property, as then you can at least guarantee the variable type. Even then the type system is very flexible and one can define things like <color># | <integer>#. The valid variables with this type would be comma-separated lists of colors, and comma-separated lists of integers, so good luck with that. And we don’t even need to go that far: take a look at <image>. It can be a gradient (linear (possibly repeating), radial or conic), a URL, image() which produces a solid color image from a given color, or many other things. At best I can see getters being limited to, say, colors, numbers and dimensions.

    Many thanks to:

    • Matthias Clasen for reviews and implementing HWB, Oklch and Oklab color spaces, relative colors, color interpolation and math functions.
    • STF for funding this work.
    • Sonny Piers and Tobias Bernard for organizing everything.

goodbye xsetwacom, hello gsetwacom

Back in the day when presumably at least someone was young, the venerable xsetwacom tool was commonly used to configure wacom tablets devices on Xorg [1]. This tool is going dodo in Wayland because, well, a tool that is specific to an X input driver kinda stops working when said X input driver is no longer being used. Such is technology, let's go back to sheep farming.

There's nothing hugely special about xsetwacom, it's effectively identical to the xinput commandline tool except for the CLI that guides you towards the various wacom driver-specific properties and knows the right magic values to set. Like xinput, xsetwacom has one big peculiarity: it is a fire-and-forget tool and nothing is persistent - unplugging the device or logging out would vanish the current value without so much as a "poof" noise [2].

If also somewhat clashes with GNOME (or any DE, really). GNOME configuration works so that GNOME Settings (gnome-control-center) and GNOME Tweaks write the various values to the gsettings. mutter [3] picks up changes to those values and in response toggles the X driver properties (or in Wayland the libinput context). xsetwacom short-cuts that process by writing directly to the driver but properties are "last one wins" so there were plenty of use-cases over the years where changes by xsetwacom were overwritten.

Anyway, there are plenty of use-cases where xsetwacom is actually quite useful, in particular where tablet behaviour needs to be scripted, e.g. switching between pressure curves at the press of a button or key. But xsetwacom cannot work under Wayland because a) the xf86-input-wacom driver is no longer in use, b) only the compositor (i.e. mutter) has access to the libinput context (and some behaviours are now implemented in the compositor anyway) and c) we're constantly trying to think of new ways to make life worse for angry commenters on the internets. So if xsetwacom cannot work, what can we do?

Well, most configurations possible with xsetwacom are actually available in GNOME. So let's make those available to a commandline utility! And voila, I present to you gsetwacom, a commandline utility to toggle the various tablet settings under GNOME:

$ gsetwacom list-devices
- name: "HUION Huion Tablet_H641P Pen"
  usbid: "256C:0066"
- name: "Wacom Intuos Pro M Pen"
  usbid: "056A:0357"
$ gsetwacom tablet "056A:0357" set-left-handed true
$ gsetwacom tablet "056A:0357" set-button-action A keybinding "<Control><Alt>t"
$ gsetwacom tablet "056A:0357" map-to-monitor --connector DP-1

Just like xsetwacom was effectively identical to xinput but with a domain-specific CLI, gsetwacom is effectively identical to the gsettings tool but with a domain-specific CLI. gsetwacom is not intended to be a drop-in replacement for xsetwacom, the CLI is very different. That's mostly on purpose because I don't want to have to chase bug-for-bug compatibility for something that is very different after all.

I almost spent more time writing this blog post than on the implementation so it's still a bit rough. Also, (partially) due to how relocatable schemas work error checking is virtually nonexistent - if you want to configure Button 16 on your 2-button tablet device you can do that. Just don't expect 14 new buttons to magically sprout from your tablet. This could all be worked around with e.g. libwacom integration but right now I'm too lazy for that [4]

Oh, and because gsetwacom writes the gsettings configuration it is persistent, GNOME Settings will pick up those values and they'll be re-applied by mutter after unplug. And because mutter-on-Xorg still works, gsetwacom will work the same under Xorg. It'll also work under the GNOME derivatives as long as they use the same gsettings schemas and keys.

Le utilitaire est mort, vive le utilitaire!

[1] The git log claims libwacom was originally written in 2009. By me. That was a surprise...
[2] Though if you have the same speakers as I do you at least get a loud "pop" sound whenever you log in/out and the speaker gets woken up
[3] It used to be gnome-settings-daemon but with mutter now controlling the libinput context this all moved to mutter
[4] Especially because I don't want to write Python bindings for libwacom right now

Christian Hergert


Manuals on Flathub

Manuals contains the documentation engine from Builder as a standalone application. Not only does it browse documentation organized by SDK but can install additional SDKs too. This is done using the same techniques Builder uses to manage your project SDKs.

It should feel very familiar if you’re already using the documentation tooling in Builder.

In the past, we would just parse all the *.devhelp2 files up-front when loading. GMarkupParseContext is fast enough that it isn’t too much overhead at start-up for a couple hundred files.

However, once you start dealing with SDKs and multiple versions of all these files the startup performance can take quite a hit. So Manuals indexes these files into SQLite using GOM and performs queries using that instead. It conveniently makes cross-referencing easy too so you can jump between SDK revisions for a particular piece of documentation.


More ways to install software in SteamOS: Distrobox and Nix


In my previous post I talked about how to use systemd-sysext to add software to the Steam Deck without modifying the root filesystem. In this post I will give a brief overview of two additional methods.


distrobox is a tool that uses containers to create a mutable environment on top of your OS.

Distrobox running in SteamOS

With distrobox you can open a terminal with your favorite Linux distro inside, with full access to the package manager and the ability to install additional software. Containers created by distrobox are integrated with the system so apps running inside have normal access to the user’s home directory and the Wayland/X11 session.

Since these containers are not stored in the root filesystem they can survive an OS update and continue to work fine. For this reason they are particularly suited to systems with an immutable root filesystem such as Silverblue, Endless OS or SteamOS.

Starting from SteamOS 3.5 the system comes with distrobox (and podman) preinstalled and it can be used right out of the box without having to do any previous setup.

For example, in order to create a Debian bookworm container simply open a terminal and run this:

$ distrobox create -i debian:bookworm debbox

Here debian:bookworm is the image that this container is created from (debian is the name and bookworm is the tag, see the list of supported tags here) and debbox is the name that is given to this new container.

Once the container is created you can enter it:

$ distrobox enter debbox

Or from the ‘Debian’ entry in the desktop menu -> Lost & Found.

Once inside the container you can run your Debian commands normally:

$ sudo apt update
$ sudo apt install vim-gtk3


Nix is a package manager for Linux and other Unix-like systems. It has the property that it can be installed alongside the official package manager of any distribution, allowing the user to add software without affecting the rest of the system.

Nix running in SteamOS

Nix installs everything under the /nix directory, and packages are made available to the user through a new entry in the PATH and a ~/.nix-profile symlink stored in the home directory.

Nix is more things, including the basis of the NixOS operating system. Explaning Nix in more detail is beyond the scope of this blog post, but for SteamOS users these are perhaps its most interesting properties:

  • Nix is self-contained: all packages and their dependencies are installed under /nix.
  • Unlike software installed with pacman, Nix survives OS updates.
  • Unlike podman / distrobox, Nix does not create any containers. All packages have normal access to the rest of the system, just like native SteamOS packages.
  • Nix has a very large collection of packages, here is a search engine:

The only thing that Nix needs from SteamOS is help to set up the /nix directory so its contents are not stored in the root filesystem. This is already happening starting from SteamOS 3.5 so you can install Nix right away in single-user mode:

$ sudo chown deck:deck /nix
$ wget
$ sh ./install --no-daemon

This installs Nix and adds a line to ~/.bash_profile to set up the necessary environment variables. After that you can log in again and start using it. Here’s a very simple example (refer to the official documentation for more details):

# Install and run Midnight Commander
$ nix-env -iA
$ mc

# List installed packages
$ nix-env -q

# Uninstall Midnight Commander
$ nix-env -e mc-4.8.31

What we have seen so far is how to install Nix in single-user mode, which is the simplest one and probably good enough for a single-user machine like the Steam Deck. The Nix project however recommends a multi-user installation, see here for the reasons.

Unfortunately the official multi-user installer does not work out of the box on the Steam Deck yet, but if you want to go the multi-user way you can use the Determinate Systems installer:


Distrobox and Nix are useful tools and they give SteamOS users the ability to add additional software to the system without having to modify the base operating system.

While for graphical applications the recommended way to install third-party software is still Flatpak, Distrobox and Nix give the user additional flexibility and are particularly useful for installing command-line utilities and other system tools.

Hari Rana


Libadwaita: Splitting GTK and Design Language


Recently, the Linux Mint Blog published Monthly News – April 2024, which goes into detail about wanting to fork and maintain older GNOME apps in collaboration with other GTK-based desktop environments.

Despite the good intentions of the author, Clem, many readers interpreted this as an attack against GNOME. Specifically: GTK, libadwaita, the relationship between them, and their relevance to any desktop environment or desktop operating system. Unfortunately, many of these readers seem to have a lot of difficulty understanding what GTK is trying to be, and how libadwaita helps.

In this article, we’ll look at the history of why and how libadwaita was born, the differences between GTK 4 and libadwaita in terms of scope of support, their relevance to each desktop environment and desktop operating system, and the state of GTK 4 today.

What Is GTK?

First of all, what is GTK? GTK is a cross-platform widget toolkit from the GNOME Project, which means it provides interactive elements that developers can use to build their apps.

The latest major release of GTK is 4, which brings performance improvements over GTK 3. GTK 4 also removes several widgets that were part of the GNOME design language, which became a controversy. In the context of application design, a design language is the visual characteristics that are communicated to the user. Fonts, colors, shapes, forms, layouts, writing styles, spacing, etc. are all elements of the design language.(Source)

Unnecessary Unification of the Toolkit and Design Language

In general, cross-platform toolkits tend to provide general-purpose/standard widgets, typically with a non-opinionated styling, i.e. widgets and design patterns that are used consistently across different operating systems (OSes) and desktop environments.

However, GTK had the unique case of bundling GNOME’s design language into GTK, which made it far from generic, leading to problems of different lexicons, mainly philosophical and technical problems.

Clash of Philosophies

When we look at apps made for the GNOME desktop (will be referred to as “GNOME apps”) as opposed to non-GNOME apps, we notice that they’re distinctive: GNOME apps tend to have hamburger buttons, header bars, larger buttons, larger padding and margins, etc., while most non-GNOME apps tend to be more compact, use menu bars, standard title bars, and many other design metaphors that may not be used in GNOME apps.

This is because, from a design philosophy standpoint, GNOME’s design patterns tend to go in a different direction than most apps. As a brand and product, GNOME has a design language it adheres to, which is accompanied by the GNOME Human Interface Guidelines (HIG).

As a result, GTK and GNOME’s design language clashed together. Instead of being as general-purpose as possible, GTK as a cross-platform toolkit contained an entire design language intended to be used only by a specific desktop, thus defeating the purpose of a cross-platform toolkit.

For more information on GNOME’s design philosophy, see “What is GNOME’s Philosophy?”.

Inefficient Diversion of Resources

The unnecessary unification of the toolkit and design language also divided a significant amount of effort and maintenance: Instead of focusing solely on the general-purpose widgets that could be used across all desktop OSes and environments, much of the focus was on the widgets that were intended to conform to the GNOME HIG. Many of the general-purpose widgets also included features and functionality that were only relevant to the GNOME desktop, making them less general-purpose.

Thus, the general-purpose widgets were being implemented and improved slowly, and the large codebase also made the GNOME widgets and design language difficult to maintain, change, and adapt. In other words, almost everything was hindered by the lack of independence on both sides.

Libhandy: the Predecessor

Because of the technical bottlenecks caused by the philosophical decisions, libhandy was created in 2017, with the first experimental version released in 2018. As described on the website, libhandy is a collection of “[b]uilding blocks for modern adaptive GNOME applications.” In other words, libhandy provides additional widgets that can be used by GNOME apps, especially those that use GTK 3. For example, Boxes uses libhandy, and many GNOME apps that used to use GTK 3 also used libhandy.

However, some of the problems remained: Since libhandy was relatively new at the time, most GNOME widgets were still part of GTK 3, which continued to suffer from the consequences of merging the toolkit and design language. Furthermore, GTK 4 was released at the end of December 2020 — after libhandy. Since libhandy was created before the initial release of GTK 4, it made little sense to fully address these issues in GTK 3, especially when doing so would have caused major breakages and inconveniences for GTK, libhandy, and app developers. As such, it wasn’t worth the effort.

With these issues in mind, the best course of action was to introduce all these major changes and breakages in GTK 4, use libhandy as an experiment and to gain experience, and properly address these issues in a successor.

Libadwaita: the Successor

Because of all the above problems, libadwaita was created: libhandy’s successor that will accompany GTK 4.

GTK 4 was initially released in December 2020, and libadwaita was released one year later, in December 2021. With the experience gained from libhandy, libadwaita managed to become extensible and easy to maintain.

Libadwaita is a platform library accompanying GTK 4. A platform library is a library used to complement a specific platform. In the case of libadwaita, the platform it targets is the GNOME desktop.

Porting Widgets to Libadwaita

Some GNOME widgets from GTK 3 (or earlier versions of GTK 4) were removed or deprecated in GTK 4 and were reimplemented in / transferred to libadwaita, for example:

These aforementioned widgets only benefited GNOME apps, as they were strictly designed to provide widgets that conformed to the GNOME HIG. Non-GNOME apps usually didn’t use these widgets, so they were practically irrelevant to everyone else.

In addition, libadwaita introduced several widgets as counterparts to GTK 4 to comply with the HIG:

Similarly, these aforementioned GTK 4 (the ones starting with Gtk) widgets are not designed to comply with the GNOME HIG. Since GTK 4 widgets are supposed to be general-purpose, they should not be platform-specific; the HIG no longer has any influence on GTK, only on the development of libadwaita.

Scope of Support

The main difference between GTK 4 and libadwaita is the scope of support, specifically the priorities in terms of the GNOME desktop, and desktop environment and OS support. While most resources are dedicated to GNOME desktop integration, GTK 4 is not nearly as focused on the GNOME desktop as libadwaita. GTK 4, while opinionated, still tries to get closer to the traditional desktop metaphor by providing these general-purpose widgets, while libadwaita provides custom widgets to conform to the GNOME HIG.

Since libadwaita is only made for the GNOME desktop, and the GNOME desktop is primarily officially supported on Linux, libadwaita thus primarily supports Linux. In contrast, GTK is officially supported on all major operating systems (Windows, macOS, Linux). However, since GTK 4 is mostly developed by GNOME developers, it works best on Linux and GNOME — hence “opinionated”.

State of GTK 4 Today

Thanks to the removal of GNOME widgets from GTK 4, GTK developers can continue to work on general-purpose widgets, without being influenced or restricted in any way by the GNOME HIG. Developers of cross-platform GTK 3 apps that rely exclusively on general-purpose widgets can be more confident that GTK 4 won’t remove these widgets, and hopefully enjoy the benefits that GTK 4 offers.

At the time of writing, there are several cross-platform apps that have either successfully ported to GTK 4, or are currently in the process of doing so. To name a few: Freeciv gtk4 client, HandBrake, Inkscape, Transmission, and PulseAudio Volume Control. The LibreOffice developers are working on the GTK 4 port, with the gtk4 VCL plugin option enabled. For example, the libreoffice-fresh package from Arch Linux has it enabled.

Here are screenshots of the aforementioned apps:

Freeciv gtk4 client in the game view, displaying a title bar, a custom background, a menu bar, a tab view with the Chat tab selected, an entry, and a few buttons.

HandBrake in the main view, displaying a title bar, a menu bar, a horizontal toolbar below it with custom buttons, entries, popover buttons, a tab view with the Summary tab selected, containing a popover button and several checkboxes.

Development version of Inkscape in the main view, displaying a title bar, a menu bar, a horizontal toolbar below, vertical toolbars on the left and right, a canvas grid on the center left, a tab view on the center right with the Display Properties tab selected, and a toolbar at the bottom.

LibreOffice Writer with the experimental gtk4 VCL plugin in workspace view, displaying a title bar, a menu bar, two horizontal toolbars below, a vertical toolbar on the right, a workspace grid in the middle with selected text, and a status bar at the bottom.

Transmission in the main view, displaying a title bar, a menu bar, a horizontal toolbar, a filter bar, an empty field in the center of the view, and a status bar at the bottom.

PulseAudio Volume Control in the Output Devices view, displaying a title bar, a tab section, a list of output devices, and a bottom bar with a combo box.

A GNOME App Remains a GNOME App, Unless Otherwise Stated

This is a counter-response to Thom Holwerda’s response to this article.

An app targeting a specific platform will typically run best on that platform and will naturally struggle to integrate with other platforms. Whether the libraries change over time or stay the same forever, if the developers are invested in the platform they are targeting, the app will follow the direction of the platform and continue to struggle to integrate with other platforms. At best, it will integrate in other platforms by accident.

In this case, developers who have and will continue to target the GNOME desktop will actively adapt their apps to follow the GNOME philosophy, for better or worse. Hamburger buttons, header bars, typography, and distinct design patterns were already present a decade ago (2014).(Source) Since other platforms were (and still are) adhering to different design languages, with or without libhandy/libadwaita, the GTK 3 apps targeting GNOME were already distinguishable a decade ago. Custom solutions such as theming were (and still are) inadequate, as there was (and still is) no 🪄 magical 🪄 solution that converts GNOME’s design patterns into their platform-agnostic counterparts.

Whether the design language is part of the toolkit or a separate library has no effect on integration, because GNOME apps already looked really different long before libhandy was created, and non-GNOME apps already looked “out of place” in GNOME as well. Apps targeting a specific platform that unintentionally integrate with other platforms will eventually stop integrating with other platforms as the target platform progresses and apps adapt. In rare cases, developers may decide to no longer adhere to the GNOME HIG.

Alternate Platforms

While libadwaita is the most popular and widely used platform library that accompanies GTK 4, there are several alternatives to libadwaita:

There are also several alternatives to libhandy:

  • libxapp is developed and maintained by Linux Mint, and focuses on multiple GTK desktop environments, such as Cinnamon, MATE, and Xfce.
  • libxfce4ui is developed and maintained by Xfce, and focuses on Xfce.

Just like libadwaita and libhandy, these platform libraries offer custom widgets and styling that differ from GTK and are built for their respective platforms, so it’s important to realize that GTK is meant to be built with a complementary platform library that extends its functionality when targeting a specific platform.

Similarly, Kirigami from KDE accompanies Qt to build Plasma apps. MauiKit from the Maui Project (another KDE project) also accompanies Qt, but targets Nitrux. Libcosmic by System76 accompanies iced to build COSMIC apps.


A cross-platform toolkit should primarily provide general-purpose widgets. Third parties should be able to extend the toolkit as they see fit through a platform library if they want to target a specific platform.

As we’ve seen throughout the philosophical and technical issues with GTK, a lot of effort has gone into moving GNOME widgets from GTK 4 to libadwaita. GTK 4 will continue to provide these general-purpose widgets for apps intended to run on any desktop or OS, while platform libraries such as libadwaita, Granite and libhelium provide styling and custom widgets that respect their respective platforms.

Libadwaita is targeted exclusively at the GNOME ecosystem, courtesy of the GNOME HIG. Apps built with libadwaita are intended to run best on GNOME, while GTK 4 apps that don’t come with a platform library are intended to run everywhere.

  1. The core functionality of GtkDialog, i.e. creating dialogs, has been moved to GtkWindow

rpmlint: Google Summer of Code 2024

I'm glad to say that I'll participate again in the GSoC, as mentor. This year we will continue the work done during the past year, as part of the openSUSE project.

So this summer I'll be mentoring an intern and we'll continue working on improving the testing framework of the rpmlint project.

This year we've a better testing framework, thanks to the work done during the past Summer of Code, by Afrid. So the goal for this year is to try to modernize existing tests and remove as much files as possible from test/binary directory, replacing those with mock packages defined with python code.

The selected intern is Luz Marina Montilla Marín. She has done some initial work in the rpmlint project, creating the mock packages for some tests and we've just started with the work to do during the GSoC program, evaluating the tests that we've right now and planning were to start.

She studies at Córdoba, Spain, my hometown. Every year I try to reach young people at different local universities, here in Andalucía, and sometimes I'm able to convince some students to participate, like the GSoC 2020, when Alejandro Dominguez, from Seville, were working on Fractal. So I'm happy that I'm increasing the number of free software developers in my local community :D

I'm sure that she will be able to achieve great things during these three months, so I'm looking forward to start to code and see how far can we go.

Sonny Piers


Workbench News


Workbench is now available on the GNOME nightly repository.

Please prefer Workbench from Flathub but if you're a GNOME contributor, Workbench nightly can come handy

flatpak remote-add --if-not-exists gnome-nightly
flatpak install gnome-nightly re.sonny.Workbench.Devel

It is the first app on GitHub to be available on GNOME nightly. Thanks to Jordan Petridis and Bilal Elmoussaoui for the help.


I'm very happy to announce that as of yesterday we are mentoring 2 students on Workbench.

Angelo Verlain (aka vixalien) is a student from Kigali, Rwanda. Angelo is already a GNOME Foundation member and made significant contributions including an audio player app “Decibels” that is being incubated to join GNOME core apps.

Bharat Tyagi is a student from Jaipur, India. Bharat made great contributions to Workbench during the GSoC contribution period, and I'm looking forward to seeing more of it. You can read their introduction here.

Angelo is working on TypeScript support in Workbench and GNOME.

Bharat is working on porting remaining demos to Vala, redesigning the Library and add code search to it.

Very happy working with both of them

Cassidy James Blaede


Recovering the BIOS on a Dell XPS 13 (9310)

It seems like every few months my work Dell XPS 13 just… dies. Or at least, it seems like it for a few minutes, making me panic about being able to do my work and wondering about backups. And then I remember that it just likes to play dead for no reason—maybe I recently did a BIOS update, maybe I had to open its chassis for some reason (like a jittery trackpad… another blog post I need to write), maybe the battery was totally discharged, or maybe it just wants to troll me. Idk. But it happens more regularly than it should.

The symptoms are more or less the same:

  1. It doesn’t turn on

  2. If it does turn on (e.g. after leaving it plugged in for a while and/or holding the power button), I get a Dell logo and some blinking lights

  3. If I disconnect the battery and try to boot up, I get other blinky lights and/or an exciting RGB rave on the display

  4. The BIOS is just… inaccessible

Perhaps the most frustrating thing about this situation is that Dell’s own documentation is terrible. They lean too heavily on forums with bad advice, their support site is hard to search, and different documentation will tell you to do different things for no discernible reason. The service manual is hard to find and has some useful information, but then too often tells you to contact their technical support—who will refuse to help if it’s out of warranty.

I last documented how to recover from this on Mastodon when it happened to me in December, but surprise, it happened to me again! At this point I figured I’d make it more easily searchable/findable by documenting in a blog post. Here goes.

Charge the Battery

First, if your Dell XPS is failing to boot, try plugging it into power for an hour or so, then try booting after that. It’s possible the battery is just low and the laptop is refusing to boot—I’ve had this more than once, and the laptop frustratingly does a bad job of telling you what’s happening—and it seems to require being charged up more than just a little bit to turn back on after this happens.

I don’t know why you can’t just connect power and boot up with a dead battery; I guess that would be too easy, and Dell prefers you to waste an hour without being able to use your computer. Neat stuff!

If you’ve done this and still just get the Dell logo (and maybe some blinky lights from the battery/status LED strip below the trackpad), carry on. But note that you need at least 10–15% battery charge to do the BIOS recovery, anyway, so don’t skip this first step.

Discharge (a.k.a. “flea power release”)

I’m not sure how critical this is, but several versions of Dell’s documentation told me to do it.

  1. Unscrew the bottom panel (with a T5 torx bit)
  2. Disconnect the internal battery cable
  3. Hold the power button for 30 secs

Yes, you will look like a fool doing this with the laptop propped up on its display so you can reach things. Next:

  1. Important! Keep the laptop display open until told otherwise! The XPS 13 9310 (and probably newer) power on when the display is opened, which will undo your hard work

  2. Re-connect the internal battery cable

  3. Carefully snap the bottom panel on—I’m not 100% sure this is needed, but I think it is because there’s a hardware tamper switch that tells the computer when the bottom panel is off

Remember to keep the display open this whole time, too—I know, it’s a pain.

Download the Latest BIOS Version

  1. Search Dell’s site for your model and find the latest BIOS file; for my XPS 13 9310 (not 2-in-1), it’s here.

    Don’t just Google for the model/BIOS, because Dell’s support site has terrible SEO/tagging. Instead, head to the Dell website, go to the support section for drivers and downloads, and search for your specific serial number/model to find the very latest BIOS version.

  2. Download the latest BIOS file in the Recovery Image (.rcv) format if offered, otherwise rename .exe to BIOS_IMG.rcv.

  3. Format a USB flash drive as FAT32, then drop the BIOS file to the root of the drive. You’ll also want a USB-C to USB-A adapter handy (unless you’re using a fancy USB-C flash drive).

Recover the BIOS

Important! If you can’t get this step to work, repeat the previous step, trying with a different flash drive.

  1. While keeping the laptop powered off, plug in your flash drive; again, I’m not sure how much this matters, but I seemed to only have success when using the right-hand USB port (near the power button) for the flash drive—it could just be a coincidence, but it seems to be the case for me

  2. While still keeping the laptop powered off, hold the Ctrl+Esc keys on the keyboard

  3. While still holding the keys, plug in your AC adapter (e.g. USB-C charger); once the Caps Lock key light lights up, release the keys

If all goes well, you should eventually get a BIOS recovery screen; it might reboot/flash a few times before getting to this screen, so be patient

Let me know on Mastodon if this worked for you, or if you have other tips. I still don’t know why I have to do this every once in a while, but it’s honestly making me reconsider Dells altogether. I really like the hardware, otherwise, but these sorts of issues have just been pretty common across models and years—I think for my next laptop, I’m eyeing that Star Labs StarFighter. 👀

Crosswords 0.3.13: Side Quests

It’s time for another Crosswords release.

I’ll keep this update short and sweet. I had grand plans last cycle to work on the word data and I did work a little on it — just not in the way I intended. Instead, a number of new contributors showed up which sent me in a different direction. I’m always happy to get new contributors and wanted to make sure they had a good experience. It ended up being a fun set of side quests before returning to the main set of features in the editor.

Cursor behavior

New contributor Adam filed a series of absolutely fantastic bug reports about the cursor behavior in the game. Adam fixed a couple bugs himself, and then pointed out that undo/redo behavior is uniquely weird with crosswords. Unlike text boxes, cursors have a natural direction associated with them that matters for the flow of editing.

In a nutshell, when you undo a word you want the cursor to be restored at the same orientation as where it was at the start of the guess. On the other hand, when redoing a guess, you want the cursor to advance normally, which might be in a different place or orientation. It’s subtle, but is the kind of user touch that you would normally never notice. It just feels “clunky” without a fix. With all these changes, the cursor behavior feels a lot more natural.

Can you spot the difference?

Selections and Intersections

Another side quest was to change the Autofill dialog to operate in-place. I foolishly thought that this would be a relatively quick change, but it ended up being a lot more work than expected. I’ll spare the details, but along the way, I also had to add three more features as dependencies.

First, I’ve wanted a way to leave pencil markings for a long time. These would transient markings that show possibilities for a square without marking. We use it to show the results of the in-place autofill operation.

Autofilling a section of the in place selection. Potential grids are written in pencil.

Second I fixed an old bug that I’ve wanted to fix for a long time. Previously, the word list showed all possible words independently. Now it only shows words that show in both directions. As an example, in the grid below we don’t show “ACID — (80)”  in the Down list as that final “D” would mean the Across clue would have “WD” as its prefix.

The acid test. WD-40 isn’t in our dictionary

This required writing code to efficiently calculate the intersection of two lists. It sounds easy enough, but the key word here is “efficient”. While I’m sure the implementation could be improved, it’s fast enough for now for it to be used synchronously.

Finally, I was able to use the intersection function to optimize the autofill algorithm itself. It’s significantly faster also more correct than the previous implementation, which means that the resulting boards will be more usable. It still can’t do a full 15×15 grid in a reasonable time, but it can solve about 1/3 of a grid.


  • Federico and I are working with Pranjal as a GSOC student for the summer. He’s going to work on porting libipuz to rust, and we spent a good amount of time planning the approach for that as well as prepping the library.
  • Tanmay has continued to work on the acrostic generator as part of last summer’s GSoC project. I’m so proud of his continued efforts in this space. Check out his recent post!
  • Gwyneth showed up with support for UTF-8 embedding in puz files as well as support for loading .xd crossword files.
  • I updated our use of libadwaita widgets to the latest release, and enabled style settings per-cell in the editor.

Until next time!

Patrick Griffis


Introducing the WebKit Container SDK

Developing WebKitGTK and WPE has always had challenges such as the amount of dependencies or it’s fairly complex C++ codebase which not all compiler versions handle well. To help with this we’ve made a new SDK to make it easier.

Current Solutions

There have always been multiple ways to build WebKit and its dependencies on your host however this was never a great developer experience. Only very specific hosts could be “supported”, you often had to build a large number of dependencies, and the end result wasn’t very reproducable for others.

The current solution used by default is a Flatpak based one. This was a big improvement for ease of use and excellent for reproducablity but it introduced many challenges doing development work. As it has a strict sandbox and provides read-only runtimes it was difficult to use complex tooling/IDEs or develop third party libraries in it.

The new SDK tries to take a middle ground between those two alternatives, isolating itself from the host to be somewhat reproducable, yet being a mutable environment to be flexible enough for a wide range of tools and workflows.

The WebKit Container SDK

At the core it is an Ubuntu OCI image with all of the dependencies and tooling needed to work on WebKit. On top of this we added some scripts to run/manage these containers with podman and aid in developing inside of the container. It’s intention is to be as simple as possible and not change traditional development workflows.

You can find the SDK and follow the quickstart guide on our GitHub:

The main requirements is that this only works on Linux with podman 4.0+ installed. For example Ubuntu 23.10+.

In the most simple case, once you clone, using the SDK can be a few commands:

source /your/path/to/webkit-container-sdk/
wkdev-create --create-home

From there you can use WebKit’s build scripts (./Tools/Scripts/build-webkit --gtk) or CMake. As mentioned before it is an Ubuntu installation so you can easily install your favorite tools directly like VSCode. We even provide a wkdev-setup-vscode script to automate that.

Advanced Usage


A workflow that some developers may not be familiar with is making use of entirely disposable development environments. Since these are isolated containers you can easily make two. This allows you to do work in parallel that would interfere with eachother while not worrying about it as well as being able to get back to a known good state easily:

wkdev-create --name=playground1
wkdev-create --name=playground2

podman rm playground1 # You would stop first if running.
wkdev-enter --name=playground2

Working on Dependencies

An important part of WebKit development is working on the dependencies of WebKit rather than itself, either for debugging or for new features. This can be difficult or error-prone with previous solutions. In order to make this easier we use a project called JHBuild which isn’t new but works well with containers and is a simple solution to work on our core dependencies.

Here is an example workflow working on GLib:

wkdev-create --name=glib
wkdev-enter --name=glib

# This will clone glib main, build, and install it for us. 
jhbuild build glib

# At this point you could simply test if a bug was fixed in a different versin of glib.
# We can also modify and debug glib directly. All of the projects are cloned into ~/checkout.
cd ~/checkout/glib

# Modify the source however you wish then install your new version.
jhbuild make

Remember that containers are isoated from each other so you can even have two terminals open with different builds of glib. This can also be used to test projects like Epiphany against your build of WebKit if you install it into the JHBUILD_PREFIX.

To Be Continued

In the next blog post I’ll document how to use VSCode inside of the SDK for debugging and development.

Python 3.13 Beta 1

Python 3.13 beta 1 is out, and I've been working on the openSUSE Tumbleweed package to get it ready for the release.

Installing python 3.13 beta 1 in Tumbleweed

If you are adventurous enough to want to test the python 3.13 and you are using openSUSE Tumbleweed, you can give it a try and install the current devel package:

# zypper addrepo -p 1000
# zypper refresh
# zypper install python313

What's new in Python 3.13

Python interpreter is pretty stable nowadays and it doesn't change too much to keep code compatible between versions, so if you are writing modern Python, your code should continue working whit this new version. But it's actively developed and new versions have cool new functionalities.

  1. New and improved interactive interpreter, colorized prompts, multiline editing with history preservation, interactive help with F1, history browsing with F2, paste mode with F3.
  2. A set of performance improvements.
  3. Removal of many deprecated modules: aifc, audioop, chunk, cgi, cgitb, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev, pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib, lib2to3.

Enabling Experimental JIT Compiler

The python 3.13 version will arrive with an experimental functionality to improve performance. We're building with the --enable-experimental-jit=yes-off so it's disabled by default but it can be enabled with a virtualenv before launching:

$ PYTHON_JIT=1 python3.13

Free-threaded CPython

The python 3.13 has another build option to disable the Global Interpreter Lock (--disable-gil), but we're not enabling it because in this case it's not possible to keep the same behavior. Building with disabled-gil will break compatibility.

In any case, maybe it's interesting to be able to provide another version of the interpreter with the GIL disabled, for specific cases where the performance is something critical, but that's something to evaluate.

We can think about having a python313-nogil package, but it's not something trivial to be able to have python313 and python313-nogil at the same time in the same system installation, so I'm not planning to work on that for now.

Jiri Eischmann


Fedora 40 Release Party in Prague

Last Friday I organized a Fedora 40 release party in Prague. A month ago I got a message from Karel Ziegler of Etnetera Core if we could do a Fedora release party in Prague again. Etnetera Core is a mid-sized company that does custom software development and uses Red Hat technologies. They have a really cool office in Prague which we used as a venue for release parties several times in pre-covid times.

We got a bit unlucky with the date this time. It was really terrible weather in Prague on Friday. It was pouring outside. Moreover the Ice Hockey World Championship is taking place in Prague now and the Czech team played against Austria at the time of the release party. These two things contributed to the less than expected attendance. But in the end roughly 15 people showed up.

A round table with Fedora swag.
Fedora swag for party attendees.

The talk part was really interesting. In the end it took almost 4 hours because there was a lot of discussion. The first talk was mine, traditionally on Fedora Workstation that switched to a long discussion about Distrobox vs Toolbx. As a result of that Luboš Kocman of SUSE got interested in Ptyxis saying that it’s something they may actually adopt, too.

Lukáš Kotek on stage.
Lukáš Kotek talking about his legacy computing machine.

The second talk was delivered by Lukáš Kotek who talked about building a retro-gaming machine based on Fedora and Atomic Pi. Several people asked us to provide his slides online, so here they are:

The third talk was delivered by Karel Ziegler who spoke on the new release of his favorite desktop environment – Plasma 6. The last talk was supposed to be delivered by Ondřej Kolín, but at the beginning of the party we were not sure if he’d make it because he was travelling from Berlin and was stuck in Friday traffic. The first three talks took so long due to interesting discussions that Ondřej arrived just on time for his talk.

He spoke about his experience building a simple app and distributing it on Flathub. This again started an interesting discussion about new and traditional models of Linux app distribution.

In the middle of the party we were joined by Andre Klapper, a long-time GNOME contributor living in Prague, and Keywan Tonekaboni, a German open source journalist who is currently on his holidays travelling on trains around the Czech Republic. We found out that we were taking the same train to Brno next day, so on Saturday we had another two hours for Linux software topics. 🙂

I’d like to thank the Fedora Project for sponsoring my travel to Prague to organize the event and also big thanks to Etnetera Core for providing just perfect venue for the party and sponsoring refreshment (They even had a beer tap!) and the party cake.

Fedora 40 cake
Fedora 40 cake.

Analysis of GNOME Foundation’s public economy: concerns and thoughts

Apart from Software Development, I also have an interest in governance and finances. Therefore, last July, I was quite happy to attend my first Annual General Meeting (AGM), taking place in  GUADEC in Riga. I was a bit surprised by the format, as I was expecting something closer to an assembly than to a presentation with a Q&A at the end. It was still interesting to witness, but I was even more shocked by the huge negative cash flow (difference between revenue and expenditures). With the numbers presented, the foundation had lost approximately 650 000 USD in the 2021 exercise, and 300 000 USD in the 2022 exercise. And nobody seemed worry about it. I would have expected that such difference would come consequence of a great investment aimed at improving the situation of the foundation long-term. However, nothing like that was part of the AGM. This left me thinking, and a bit worried about what was going on with the financials and organization of the foundation. After asking a member of the Board in private, and getting no satisfactory response, I started doing some investigations

Public information research

The GNOME Foundation (legally GNOME Foundation Inc) has the 501(c)3 status. It means it is tax exempt. As part of such status, the tax payments, economic status and whereabouts of the GNOME Foundation Inc are public. So I had some look at the tax filling declarations of the last years. These contain detailed information about income and expenses, net assets (e.g: money in bank accounts), retribution of the Board, Executive director, and key employees, amount of money spent on fulfilling the goals of the foundation, and lots of other things. Despite their wide goal, the tax fillings are not very hard to read, and it’s easy to learn how much money the  foundation made or spent. Looking at the details, I found several worrying things, like the fact that revenue and expenses in the Annual Report presented in the AGM did not match those in the tax reports, that most expenses were aggregated in sections that required no explanation, or that there were some explanations for expenses required but missing. So I moved on to open a confidential issue in the Board Team in gitlab expressing my concerns.

The answer mostly covered an explanation of the big deficits in the previous year (that would have been great to have in the Annual Report), but was otherwise generally disappointing. Most of my concerns (all of which are detailed below) were answered with nicely-written variations of: “that’s a small problem, we are aware of it and working on it”, or “this is not common practice and you can find unrelated information in X place”. It has been 6 months, a new tax statement and annual report are available, but problems persist. So I am sharing publicly my concerns with several goals:

  • Make these concerns available to the general GNOME community. Even though everything I am presenting comes from public sources, it is burdensome to research, and requires some level of experience with bureaucracy.
  • Show my interest about the topic, as I plan to present myself to the Board of Directors in the next elections. My goal is to become part of the Finance Committee to help improve in the transparency and efficiency of accounting.
  • Make the Board aware of my concerns (and hopefully show that others also share them), so things can be improved regardless of whether I get or don’t get elected for the board

Analysis of data and concerns

The first analysis I did some months ago was not very detailed, and quite manual. This time, I gather information in more detailed, and compiled it in an spread sheet that I made publicly available. All the numbers are taken from GNOME’s Annual Reports and from the Tax declarations available in Pro Publica. I am very happy to get those values reviewed, as there could always be mistakes. I am still fairly certain that small errors won’t change my concerns, since those are based on patterns and not on one-time problems. So to my concerns:

  • Non-matching values between reports and taxes: in the last 3 years, for revenue and income, only the revenue in presented for Fiscal Year 2021/2022 matches what is actually declared. For the rest, differences vary, but go up to close to 9%. I was told that some difference is expected (as these numbers are crafted a bit earlier than taxes), the Board had worked on it, and the last year (the only one with at least revenue matching) is certainly better. But there are still something like 18 000 USD of mismatch in expenses. For me, this is a clear sign, that something is going wrong with the accounting of the foundation, even if improved in the last year.
  • Non-matching values between reports from different years: each Annual Report contains not only the results for that year, but also from the previous one. However, numbers only match half of the time. This is still the case for the latest report in 2023, where suddenly 10 000 USD disappeared from 2022’s expenses, growing the difference from what was declared that year to 27 000 USD. This again shows accountability issues, as previous-years’ numbers should certainly not diverge even more from the tax declarations than initial numbers.
  • Impossibility to match tax declarations and Annual Reports: the way the annual reports are presented, makes it impossible to get a more detailed picture of how are expenses and revenue split. For example, more than 99% of the revenue in 2023 is grouped under a single tax category, while the previous year at least 3 where used. However, the split in the Annual Reports remains roughly the same. So either the accounting is wrong in one of those years, or the split of expenses for the Annual Report was crafted from different data sources. Another example is how “Staff” makes the greatest expense until it ceases to exist in the latest report. However, staff-related expenses in the taxes do not make up for the “Staff” expense in the repots. The chances are that part of that is due to subcontracting, and thus counted in “Fees for services, Other” in the taxes. Unfortunately that category has its own issues.
  • Missing information in the tax declaration: most remarkably, in the tax fillings of fiscal years 2020/2021 and 2021/2022, the category: “Fees for services, Other” represents more than 10% of the expense, which is clearly stated that it should be explained in a later part of the tax filling. However, it is not. I was told 6 months ago that might have to be with some problem with ProPublica not getting the data, and that they would try to fix it. But I was not provided with the information, and 6 months later the public tax fillings still have not been amended.
  • Lack of transparency on expenses:
    • First, in the last 2 tax fillings, more than 50% of expenses lay under “Other salaries and wages”, and “Fees for services, Other”. These fields do not provide enough transparency (maybe they would if the previous point was addressed), and means most of the expenses actually go unaccounted.
    • Second, in the Annual Reports. For the previous 2 years, the biggest expenses were by far “Staff”. There exists a website with the staff and their roles, but there is no clear explanation of which money goes to whom or why. This can be a great problem if some part of the community does not feel supported in its affairs by the foundation. Compare for example with Mastodon’s Annual Report, where everybody on a pay-slip of free-lancing is accounted and written down how much they earn. This is made worse since the current year’s Annual Report has completely removed that category in favor of others. Tax fillings (once available) will, however, provide more context if proper explanations regarding “Fees for services, Other” is finally available.
  • Different categories and reporting formats: the reporting format changed completely in 2021/2022 compared to previous years, and changed completely again this year. This is a severe issue for transparency, since continuously updating formats make it hard to compare between years (which as noted above, is useful!). One of course can understand that things need to be updated to improve things, but such drastic changes do not help with transparency.

There are certainly other small things that I noticed that caught my attention. However, I hope these examples are enough to get my point across. And there is no need to make this blog post even longer!


My main conclusion from the analysis is that the foundation accounting and decision-making regarding expenses has been sub-par in the last years. It is also a big issue that there is a huge lack in transparency regarding the economic status and decision-making of the foundation. I learned more about the economic status of the foundation by reading tax fillings than by reading Annual Reports. Unfortunately, opening an issue with the Board six months ago to share these concerns has not make it better. It could possibly be, that things are much better than they look from outside, but the lack of transparency is making it not appear as so. I hope that I can join the Finance Committee, and help address these issues in the short term!

Status update, 19/05/2024 – GNOME OS and more

Seems this is another of those months where I did enough stuff to merit two posts. (See Thursday’s post on async Rust). Sometimes you just can’t get out of doing work, no matter how you try. So here is part 2.

A few weeks ago I went to the USA for a week to meet a client team who I’ve been working with since late 2022. This was actually the first time I left Europe since 2016*. Its wild how a Euro is now pretty much equal value to a US dollar, but everything costs about double compared to Europe. It was fun though and good practice for another long trip to the Denver GUADEC in July.

* The UK is still part of Europe, it hasn’t physically moved, has it?

GNOME OS stuff

The GNOME OS project has at least 3 active maintainers and a busy Matrix room, which makes it fairly healthy as GNOME modules go. There’s no ongoing funding for maintenance though and everyone who contributes is doing so mostly as a volunteer — at least, as far as I’m aware. So there are plenty of plans and ideas for how it could develop, but many of them are incomplete and nobody has the free time to push them to completion.

We recently announced some exciting collaboration between Codethink, GNOME and the Sovereign Tech Fund. This stint of full time work will help complete several in-progress tasks. Particularly interesting to me is finishing the migration to systemd-sysupdate (issue 832), and creating a convenient developer workflow and supporting tooling (issue 819) so we can finally kill jhbuild. Plus, of course, making the openQA tests great again.

Getting to a point where the team could start work, took a lot of work, most of which isn’t visible to the outside world. Discussions go back at least to November 2023. Several people worked over months on scoping, estimates, contracts and resourcing the engineering team before any of the coding work started: Sonny Piers working to represent GNOME, and on the Codethink side, Jude Onyenegecha and Weyman Lo, along with Abderrahim Kitouni and Javier Jardón (who are really playing for both teams ;-).

I’m not working directly on the project, but I’m helping out where I can on the communications side. We have at least 3 IRC + Matrix channels where communication happens every day, each with a different subset of people and cocumentation is scattered all over the place. Some of the Codethink team are seasoned GNOME contributors, others are not, and the collaborative nature of the GNOME OS project – there is no “BDFL” figure who takes all the decisions – means it’s hard to get clear answers around how things should be implemented. Hopefully my efforts will mean we make the most of the time available.

You can read more about the current work here on the Codethink blog: GNOME OS and systemd-sysupdate, the team will hopefully be posting regular progress updates to This Week In GNOME, and Martín Abente Lahaye (who very recently joined the team on the Codethink side \o/) is opening public discussions around the next generation developer experience for GNOME modules – see the discussion here.

Tiny SPARQL, Twinql, Sqlite-SPARQL, etc.

We’re excited to welcome Demigod and Rachel to the GNOME community, working on a SPARQL web IDE as part of Google Summer of Code 2024.

Since this is going to hopefully shine a new light on the SPARQL database project, it seems like a good opportunity to start referring to it by a better name than “Tracker SPARQL”, even while we aren’t going to actually rename the whole API and release 4.0 any time soon.

There are a few name ideas already, the front runners being Tiny SPARQL or Twinql, which I still can’t quite decide which I prefer. The former is unique but rather utilitarian, while the latter is a nicer name but is already used by a few other (mostly abandoned) projects. Which do you prefer? Let me know in the comments..

Minilogues and Minifreaks

I picked up a couple of hardware synthesizers, the Minilogue XD and the Minifreak. I was happy for years with my OP-1 synth, but after 6 years of use it has so many faults to be unplayable, and replacing it would cost more than a second hand car, plus its a little too tiny for on-stage use.

The Minilogue XD is one of the only mainstream synths to have an open SDK for custom oscillators and effects, full respect to Korg for their forward thinking here … although their Linux tooling is a closed source binary with an critical bug that they won’t fix, so, still some way to go before they get 10/10 for openness.

The Minifreak, by contrast, has a terrible Windows-only firmware update system, which works so poorly that I already had to the return the synth once to Arturia after a firmware update caused it to brick itself. There’s a stark lesson here in having open protocols which hopefully Arturia can pick up on. This synth has absolutely incredible sound design capabilities though so I decided to keep it and just avoid ever updating the firmware.

Here’s a shot of the Minifreak next to another mini freak:

Allan Day


GNOME maintainers: here’s how to keep your issue tracker in good shape

One of the goals of the new GNOME project handbook is to provide effective guidelines for contributors. Most of the guidelines are based on recommendations that GNOME already had, which were then improved and updated. These improvements were based on input from others in the project, as well as by drawing on recommendations from elsewhere.

The best example of this effort was around issue management. Before the handbook, GNOME’s issue management guidelines were seriously out of date, and were incomplete in a number of areas. Now we have shiny new issue management guidelines which are full of good advice and wisdom!

The state of our issue trackers matters. An issue tracker with thousands of open issues is intimidating to a new contributor. Likewise, lots of issues without a clear status or resolution makes it difficult for potential contributors to know what to do. My hope is that, with effective issue management guidelines, GNOME can improve the overall state of its issue trackers.

So what magic sauce does the handbook recommend to turn an out of control and burdensome issue tracker into a source of calm and delight, I hear you ask? The formula is fairly simple:

  • Review all incoming issues, and regularly conduct reviews of old issues, in order to weed out reports which are ambiguous, obsolete, duplicates, and so on
  • Close issues which haven’t seen activity in over a year
  • Apply the “needs design” and “needs info” labels as needed
  • Close issues that have been labelled “need info” for 6 weeks
  • Issues labelled “needs design” get closed after 1 year of inactivity, like any other
  • Recruit contributors to help with issue management

To some readers this is probably controversial advice, and likely conflicts with their existing practice. However, there’s nothing new about these issue management procedures. The current incarnation has been in place since 2009, and some aspects of them are even older. Also, personally speaking, I’m of the view that effective issue management requires taking a strong line (being strong doesn’t mean being impolite, I should add – quite the opposite). From a project perspective, it is more important to keep the issue tracker focused than it is to maintain a database of every single tiny flaw in its software.

The guidelines definitely need some more work. There will undoubtedly be some cases where an issue needs to be kept open despite it being untouched for a year, for example, and we should figure out how to reflect that in the guidelines. I also feel that the existing guidelines could be simplified, to make them easier to read and consume.

I’d be really interested to hear what changes people think are necessary. It is important for the guidelines to be something that maintainers feel that they can realistically implement. The guidelines are not set in stone.

That said, it would also be awesome if more maintainers were to put the current issue management guidelines into practice in their modules. I do think that they represent a good way to get control of an issue tracker, and this could be a really powerful way for us to make GNOME more approachable to new contributors.

Jussi Pakkanen


Generative non-AI

In last week's episode of the Game Scoop podcast an idea was floated that modern computer game names are uninspiring and that better ones could be made by picking random words from existing NES titles. This felt like a fun programming challenge so I went and implemented it. Code and examples can be found in this GH repo.

Most of the game names created in this way are word salad gobbledigook or literally translated obscure anime titles (Prince Turtles Blaster Family). Running it a few times does give results that are actually quite interesting. They range from games that really should exist (Operation Metroid) to surprisingly reasonable (Gumshoe Foreman's Marble Stadium), to ones that actually made me laugh out loud (Punch-Out! Kids). Here's a list of some of my favourites:

  • Ice Space Piano
  • Castelian Devil Rainbow Bros.
  • The Lost Dinosaur Icarus
  • Mighty Hoops, Mighty Rivals
  • Rad Yoshi G
  • Snake Hammerin'
  • MD Totally Heavy
  • Disney's Die! Connors
  • Monopoly Ransom Manta Caper!
  • Revenge Marble
  • Kung-Fu Hogan's F-15
  • Sinister P.O.W.
  • Duck Combat Baseball

I emailed my findings back to the podcast host and they actually discussed it in this week's show (video here starting at approximately 35 minutes). All in all this was an interesting exercise. However pretty quickly after finishing the project I realized that doing things yourself is no longer what the cool kids are doing. Instead this is the sort of thing that is seemingly tailor-made for AI. All you have to do is to type in a prompt like "create 10 new titles for video games by only taking words from existing NES games" and post that to tiktokstagram.

I tried that and the results were absolute garbage. Since the prompt has to have the words "video game" and "NES", and LLMs work solely on the basis of "what is the most common thing (i.e. popular)", the output consists almost entirely of the most well known NES titles with maybe some words swapped. I tried to guide it by telling it to use "more random" words. The end result was a list of ten games of which eight were alliterative. So much for randomness.

But more importantly every single one of the recommendations the LLM created was boring. Uninspired. Bland. Waste of electricity, basically.

Thus we find that creating a list of game names with an LLM is easy but the end result is worthless and unusable. Doing the same task by hand did take a bit more effort but the end result was miles better because it found new and interesting combinations that a "popularity first" estimator seem to not be able to match. Which matches the preconception I had about LLMs from prior tests and seeing how other people have used them.

Marcus Lundblad


May Maps


It's about time for the spring update of goings on in Maps!

There's been some changes going on since the release of 46.

Vector Map by Default

The vector map is now being used by default, and with it Maps supports dark mode (also the old raster tiles has been retired, though there still exists the hidden feature of running with a local tile directory. Which was never really intended for general use but more as a way to experiment with offline map support). The plan will be to eventually support proper offline map support with a way to download areas in a more user-friendly and organized way then to provide a raw path…).

Dark Improvements

Following the introduction of dark map support the default rendering of public transit routes and lines has been improved for the dark mode to give better contrast (something that trickier before when the map view was always light even when the rest of the UI, such as the sidebar itinerary was shown in dark mode).

More Transit Mode Icons

Jakub Steiner and Sam Hewitt has been working on designing icons for some additional modes of transit, such as trolley buses, taxi, and monorail.

Trolley bus routes

This screenshot was something I “mocked” by changing the icon for regular bus to temporarily use the newly designed trolley bus icon as we don't currently have any supported transit route provider in Maps currently that exposed trolley bus routes. I originally made this for an excursion with a vintage trolley bus I was going to attend, but that was cancelled in the last minute because of technical issues.

Showing a taxi station

And above we have the new taxi icon (this could be used both for showing on-demand communal taxi transit and for taxi stations on the map.

These icons have not yet been merged into Maps, as there's still some work going on finalizing their design. But I thought I still wanted to show them here…

Brand Logos

For a long time we have shown a title image from Wikidata or Wikipedia for places when available. Now we show a logo image (using the Wikidata reference for the brand of a venue) when available, and the place has no dedicated article).

Explaining Place Types

As sometimes it can be a bit hard to determine the exact type from the icons shown on the map. And especially for more generic types, such as shops where we have dedicated icons for some, and a generic icon. We now show the type also in the place bubble (using the translations extracted from the iD OSM editor).

Places with a name shows the icon and type description below the name, dimmed.

For unnamed places we show the icon and type instead of the name, in the same bold style as the name would normally use.

Additional Things

Another detail worth mentioning is that you can now clear the currently showing route from the context menu so you won't have to open the sidebar again and manually erase the filled in destinations.


Another improvement is that if you already enter a starting point with ”Route from Here“, or enter an address in the sidebar and then use the “Directions”  button from a place bubble, that starting point will now be used instead of the current location.

Besides this, also some old commented-out code was removed… but there's no screenshots of that, I'm afraid ☺

Tanmay Patil


Acrostic Generator: Part one

It’s been a while since my last blog post, which was about my Google Summer of Code project. Even though it has been months since I completed GSoC, I have continued working on the project, increasing acrostic support in Crosswords.

We’ve added support for loading Acrostic Puzzles in Crosswords, but now it’s time to create some acrostics.

Now that Crosswords has acrostic support, I can use screenshots to help explain what an acrostic is and how the puzzle works.

Let’s load an Acrostic in Crosswords first.

Acrostic Puzzle loaded in Crosswords

The main grid here represents the QUOTE: “CARNEGIE VISITED PRINCETON…” and if we read out the first letter of each clue answer (displayed on the right) it forms the SOURCE. For example, in the image above, name of the author is “DAVID ….”.
Now, the interesting part is answers for the clues fit it in the SOURCE.

Let’s consider another small example:
QUOTE: “To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment.”
AUTHOR: “Ralph Waldo Emerson”

If you see correctly, letters of SOURCE here are part of the QUOTE. One set of answers to clues could be:

Solutions generated using AcrosticGenerator. Read the first letter of each Answer from top to bottom. It forms ‘Ralph Waldo Emerson’.

Coding the Acrostic Generator

As seen above, to create an acrostic, we need two things: the QUOTE and the SOURCE string. These will be our inputs from the user.

Additionally, we need to set some constraints on the generated word size. By default, we have set MIN_WORD_SIZE to 3 and MAX_WORD_SIZE to 20.. The user is allowed to change it. However, users are allowed to change these settings.

Step 1: Check if we can create an acrostic from given input

You must have already guessed it. We check if the characters in the SOURCE are available in the QUOTE string. To do this, we utilize IPuzCharset data structure.
Without going in much detail, it simply stores characters and their frequencies.
For example, for the string “MAX MIN”, it’s charset looks like [{‘M’: 2}, {‘A’: 1}, {‘X’:1}, {‘I’: 1}, {’N’: 1}].

First, We build a charset of the source string and then iterate through it. For the source string to be valid, the count of every character in the source charset should be less than or equal to the count of that character in the quote charset.

for (iter = ipuz_charset_iter_first (source_charset);
iter = ipuz_charset_iter_next (iter))
IPuzCharsetIterValue value;
value = ipuz_charset_iter_get_value (iter);

if (value.count > ipuz_charset_get_char_count (quote_charset, value.c))
// Source characters are missing in the provided quote
return FALSE;

return TRUE;

Since, now we have a word size constraint, we need to add one more check.
Let’s understand through this an example.


Since, MIN_WORD_SIZE is set to 3, the generated answers should have a minimum of three letters in them.

Possible solutions considering every solution
has a length equal to the minimum word size:
T _ _
O _ _
L _ _
S _ _
T _ _
O _ _
O _ _

If we take the sum of the number of letters in the above solutions, It’s 21. That is greater than number of letters in the QUOTE string (14). So, we can’t create an acrostic from the above input.

if  ((n_source_characters * min_word_size) > n_quote_characters)
//Quote text is too short to accomodate the specificed minimum word size for each clue
return FALSE;

While writing this blog post, I found out we called this error “SOURCE_TOO_SHORT”. It should be “QUOTE_TOO_SHORT” / “SOURCE_TOO_LARGE”.

Stay tuned for further implementation in the next post!

Vala Blog


ValaBot: an AI coding assistant fine-tuned for Vala

Enhancing AI Coding Assistants for Vala Developers#

As a programmer, I've been impressed by AI coding assistants like GitHub Copilot and Codeium, which have significantly boosted my productivity. These tools excel at reducing disruptive interruptions, saving time on typing, and often completing lines of code accurately. However, I've encountered limitations with Copilot while working with the Vala programming language. Its suggestions often get muddled with similar languages like Java and C#, and it lacks training on the more common Vala libraries.

Like many other developers, I'm also dissatisfied with the dominance of Github Copilot and that open source code has been monetized without attribution or consideration for the original authors (some of whom now pay for the service).

This challenge inspired me to enhance the Copilot concept by creating an AI coding assistant that is finely tuned to provide a viable and superior alternative that caters for the specific needs of Vala developers. I began with an open-source Large Language Model that had been trained on source code - the powerful Deepseek Coder 6.7b model. This model has been trained from scratch by Deepseek AI on 2 trillion tokens sourced from GitHub. Deepseek Coder significantly outperforms other open-source coding models, such as Codellama.

I chose the Deepseekcoder-6.7b-base model as the foundation for fine-tuning because of its great benchmark performance and also because it was trained on Java and C# – languages syntactically close to Vala. This allowed me to build upon its existing capabilities and adapt it to the specific needs of Vala.

Fine-Tuning for Vala#

I fine-tuned the model on Vala programming language datasets. This involved downloading as many Vala projects as I could find from GitHub, extracting the Vala source files, and splitting them into ~40 line segments. I then used Llama3 to create logical and predictable "holes" in each segment, which were then used to create the FIM (fill-in-the-middle) dataset. This data preparation process took 96 hours of GPU time using my quad-RX6800 machine over a weekend. The resulting dataset was cleaned to remove non-code elements, such as license headers, and personal identifiable information.

The Training#

The fine-tuning process took 10 hours on an RTX 3090. The result was a LoRA, which was merged back into the base model, converted to GGUF, and quantized to q8_0, which is the format required by TabbyML.

The Result#

The outcome was a model that is more helpful and productive for Vala-related projects. By fine-tuning Deepseek Coder, I was able to create a more accurate and effective AI coding assistant that understands the nuances of the Vala programming language. The model is hosted on Huggingface ( It can be used in VSCode, VIM, and other popular IDEs with TabbyML. The complete instructions, training scripts, and dataset are available on GitHub (

Licensing and Fair Use#

As a project rooted in the principles of Free and Open-Source Software (FOSS), I believe in promoting freedom, community, and sharing knowledge. To facilitate collaboration and innovation, I've made the following resources publicly available:

  • The fine-tuned model weights are hosted on Hugging Face for anyone to access and utilize in a TabbyML or other deployment.
  • The training scripts and dataset preparation process are open-sourced on GitHub, providing a transparent and reproducible framework for others to build upon.
  • A comprehensive list of repositories used during the fine-tuning process is available on GitHub, ensuring that contributors and users can easily identify and explore the sources that made this project possible.

I hope that this openness will pave the way for further advancements and refinements, ultimately benefiting the Vala developer community as a whole.


In this blog post, I've shared my experience with fine-tuning the Deepseek Coder for the Vala programming language, demonstrating how targeted adjustments can significantly enhance AI coding assistants. This project is just the beginning. I aim to continue developing models specifically optimized for Vala to support the Vala developer community. With the release of new base models, such as CodeQwen 7B, there are exciting possibilities for further advancements. Through this work, I hope to highlight the potential of AI fine-tuning in creating more effective coding assistants and inspire others to explore the possibilities of AI-assisted coding.

Get started with ValaBot here:

Ismael Olea


This website now has GPDR friendly statistics

Libre Counter logotype

Now this website uses a simple statistics system which is GDPR compatible and privacy friendly. It uses Libre Counter which not need user registration neither configuration beyond adding some code like this:

<a href="" target="_blank">
    <img src="" alt="GPDR friendly statistics" width="14" style="filter: grayscale(1);" title="GPDR friendly statistics" referrerpolicy="unsafe-url title="/> 

No cookies also.

Thanks Pinchito!

Outreachy May 2024: A letter to Fedora applicants

The post Outreachy May 2024: A letter to Fedora applicants appeared first on /home/jwf/.

/home/jwf/ - Free & Open Source, technology, travel, and life reflections

To all Outreachy May 2024 applicants to the Fedora Project,

Today is May 2nd, 2024. The Outreachy May 2024 round results will be published in a few short hours. This year, the participation in Fedora for Outreachy May 2024 was record-breaking. Fedora will fund three internships this year. During the application and contribution phase, over 150 new contributors appeared in our Mentored Project contribution channels. For the project I am mentoring specifically, 38 applicants recorded contributions and 33 applicants submitted final applications. This is my third time mentoring, but this Outreachy May 2024 round has been a record-breaker for all the projects I have mentored until now.

But breaking records is not what this letter is about.

This day can be either enormously exciting and enormously disappointing. It is a tough day for me. There are so many Outreachy applicants who are continuing to contribute after the final applications were due. I see several applicants from my project who are contributing across the Fedora community, and actually leveling up to even bigger contributions than the application period. It is exciting to see people grow in their confidence and capabilities in an Open Source community like Fedora. Mentoring is a rewarding task for me, and I feel immensely proud of the applicants we have had in the Fedora community this round.

But the truth is difficult. Fedora has funding for three interns, hard and simple. Hard decisions have to be made. If I had unlimited funding, I would have hired so many of our applicants. But funding is not unlimited. Three people will receive great news today, and most people will receive sad news. Throughout this entire experience in the application phase, I wanted to design me and Joseph Gayoso’s project so that even folks who were not selected would have an enriching experience. We wanted to put something real in the hands of our applicants at the end. We also wanted to boost their confidence in showing up in a community and guide them on how to roll up your sleeves and get started. Looking at the portfolios that applicants to our project submitted, I admire how far our applicants came since the day that projects were announced. Most applicants never participated in an open source community before. And for some, you would never have known that either!

So, if you receive the disappointing news today, remember that it does not reflect badly on you. The Outreachy May 2024 round was incredibly competitive. Literally, record-breaking. We have to say no to many people who have proved that they have what it takes to be a capable Fedora Outreachy intern. I hope you can look at all the things you learned and built over these past few months, and use this as a step-up to the next opportunity awaiting you. Maybe it is an Outreachy internship in a future round, or maybe it is something else. If there is anything I have learned, it is that life takes us on the most unexpected journeys sometimes. And whatever is meant to happen, will happen. I believe that there is a reason for everything, but we may not realize what that reason is until much later in the future.

Thank you to all of the Fedora applicants who put in immense effort over the last several months. I understand if you choose to stop contributing to Fedora. I hope that you will not be discouraged from open source generally though, and that you will keep trying. If you do choose to continue contributing to Fedora, I promise we will find a place for you to continue on. Regardless of your choice in contributing, keep shining and be persistent. Don’t give up easily, and remember that what you learned in these past few months can give a leading edge on that next opportunity waiting around the corner for you.

Freedom, Friends, Features, First!

— Justin