Concurrency, Parallelism, I/O Scheduling, Thread Pooling, and Work-Stealing

Around 15 years ago I worked on some interesting pieces of software which are unfortunately still not part of my daily toolbox in GNOME and GTK programming. At the time, the world was going through changes to how we would write thread pools, particularly with regards to wait-free programming and thread-pooling.

New trends like work-stealing were becoming a big thing, multiple-CPUs with multiple NUMA nodes were emerging on easy to acquire computers. We all were learning that CPU frequency was going to stall and that non-heterogeneous CPUs were going to be the “Next Big Thing”.

To handle those changes gracefully, we were told that we need to write software differently. Intel pushed that forward with Threading Building Blocks (TBB). Python had been doing things with Twisted which had an ergonomic API and of course “Stackless Python” and similar was a thing. Apple eventually came out with Grand Central Dispatch. Microsoft Research had the Concurrency and Coordination Runtime (CCR) which I think came out of robotics work.

Meanwhile, we had GThreadPool which honestly hasn’t changed that much since. Eventually the _async/_finish paring we’re familiar with today emerged followed by GTask to provide a more ergonomic API on top of it.

A bit before the GTask we all know today, I had written libgtask which was more of a Python Twisted-style API which provided “deferreds” and nice ways to combine them together. That didn’t come across into GLib unfortunately. To further the pain there, what we got in the form of GTask has some serious side-effects which make it unsuitable as a general construct in my humble opinion.

After realizing libgtask was eclipsed by GLib itself, I set off on another attempt in the form of libiris. That took a different approach that tried to merge the mechanics of CCR (message passing, message ports, coordination arbiters, actors), the API ergonomics of Python Twisted’s Deferred, and Apple’s NSOperation. It provided a wait-free work-stealing scheduler to boot. But it shared a major drawback of GLib’s GTask (beyond correctness bugs that plague it today). Primarily that thread pools can only process the work queue and therefore if you need to combine poll() or GSource that attach to a GMainContext you’re going to require code-flow to repeatedly bounce between threads.

This is because you can simplify a thread pool worker to while (has_work()) do_work ();. Any GSource or I/O either needs to bounce to the main thread where the applications GMainContext exists or to another I/O worker thread if doing synchronous I/O. On Linux, for a very long time, synchronous I/O was the “best” option if you wanted to actually use the page cache provided by the kernel, so that’s largely what GLib and GIO does.

The reason we couldn’t do something else is that to remove an item from the global work queue required acquiring a GMutex and blocking until an item is available. On Linux at least, we didn’t have APIs to be able to wait on a Futex while also poll()ing a set of file-descriptors. (As a note I should mention for posterity that FD_FUTEX was a thing for a short while, but never really usable).

In the coming years, we got a lot of new features in Linux that allowed improvements to the stack. We got signalfd to be able to poll() on incoming Unix signals. We got eventfd() which allowed a rather low-overhead way to notify coordinating code with a poll()able file-descriptor. Then EFD_SEMAPHORE was added so that we can implement sem_t behavior with a file-descriptor. It even supports O_NONBLOCK.

The EFD_SEMAPHORE case is so interesting to me because it is provides the ability to do something similar to what IRIX did 20+ years ago, which is a pollable semaphore! Look for usnewpollsema() if you’re interested.

There was even some support in GLib to support epoll(), but that seems to have stalled out. And honestly, making it use io_uring might be smarter option now.

After finishing the GTK 4 port of GNOME Builder I realized how much code I’ve been writing in the GAsyncReadyCallback style. I don’t particularly love it and I can’t muster the energy to write more code in that style. I feel like I’m writing top-half/bottom-half interrupt handlers yet I lack the precision to pin things to a thread as well as having to be very delicate with ownership to tightly control object finalization. That last part is so bad we basically don’t use GLib’s GTask in Builder in favor of IdeTask which is smart about when and where to release object references to guarantee finalization on a particular thread.

One thing that all these previous projects and many hundreds of thousands of async-oriented C code written has taught me is that all these components are interlinked. Trying to solve one of them without the others is restrictive.

That brings me to 2022, where I’m foolishly writing another C library that solves this for the ecosystem I care most about, GTK. It’s goal is to provide the pieces I need in both applications as well as toolkit authoring. For example, if I were writing another renderer for GTK, this time I’d probably built it on something like this. Given the requirements, that means that some restrictions exist.

  • I know that I need GMainContext to work on thread pool threads if I have any hope of intermixing workloads I care about on worker threads.
  • I 100% don’t care about solving distributed computing workloads or HTTP socket servers. I refuse to race for requests-per-second at the cost of API ergonomics or power usage.
  • I know that I want work stealing between worker threads and that it should be wait-free to avoid lock contention.
  • Worker threads should try to pin similar work to their own thread to avoid bouncing between NUMA nodes. This increases data cacheline hits as well as reduces chances of allocations and frees moving between threads (something you want minimized).
  • I know that if I have 32 thread pool threads and 4 jobs are added to the global queue, I don’t want 32 threads waking up from poll() to try to take that those work items.
  • The API needs to be simple, composable, and obvious. There is certainly a lot of inspiration to be taken from things like std::future, JavaScript’s Promise, Python’s work on deferred execution.
  • GObject has a lot of features and because of that it goes through great lengths to provide correctness. That comes at great costs for things you want to feel like a new “primative”, so avoiding it makes sense. We can still use GTypeInstance though, following in the footsteps of GStreamer and GTK’s Scene Kit.
  • Cancellation is critical and not only should it cause the work you created to cancel, that cancellation should propagate to the things your work depended on unless non-cancelled work also depends on it.

I’ve built most of the base of this already. The primary API you interact with is DexFuture and there are many types of futures. You have futures for IO (using io_uring). Futures for unix signals. A polled semaphore where dex_semaphore_wait() is a future. It can wrap _async/_finish pairs and provide the result as a Future. Thread pools are efficient in waiting on work (so staying dormant until necessary and minimal thread wake-ups) while also coordinating to complete work items.

There is still a lot of work to go, and so far I’ve only focused on the abstractions and Linux implementations. But I feel like there is promise (no pun intended) and I’m hoping to port swaths of code in Builder to this in the coming months. If you’d like to help, I’d be happy to have you, especially if you’d like to focus on alternate DexAioBackends, DexSemaphore using something other than eventfd() on BSD/Solaris/macOS/Windows, and additional future types. Additionally, working to GLib to support GMainContext directly using io_uring would be appreciated.

You can find the code here, but it will likely change in the near future.

GNOME Builder 43.0

After about 5 months of keeping myself very busy, GNOME Builder 43 is out!

This is the truly the largest release of Builder yet, with nearly every aspect of the application improved. It’s pretty neat to see all this come together after having spent the past couple years doing a lot more things outside of Builder like modernizing GTKs OpenGL renderer, writing the new macOS GDK backend, shipping a new Text Editor for GNOME, and somehow getting married during all that.

Modern and Expressive Theming

The most noticeable change, of course, is the port to GTK 4. Builder now uses WebKit, VTE, libadwaita, libpanel, GtkSourceView, and many other libraries recently updated to support GTK 4.

Like we did for GNOME Text Editor, Builder will restyle the application window based on the syntax highlighting scheme. In practice this feels much less jarring as you use the application for hours.

a screenshot of the editor with code completion

a screenshot of the syntax color selector

The Foundry

Behind the scenes, the “Foundry” behind Builder has been completely revamped to make better use of SDKs and runtimes. This gives precise control over how processes are created and run. Such control is important when doing development inside container technologies.

Users can now define custom “Commands” which are used to run your project and can be mapped to keyboard shortcuts. This allows for the use of Builder in situations where it traditionally fell short. For example, you can open a project without a build system and use commands to emulate a build system.

a screenshot of creating a new run command

Furthermore, those commands can be used to run your application and integrate with tooling such as the GNU debugger, Valgrind, Sysprof, and more. Controlling how the debugger was spawned has been a long requested feature by users.

a screenshot of the gdb debugger integration, stopped on a breakpoint

You can control what signal is sent to stop your application. I suspect that will be useful for tooling that does cleanup on signals like SIGHUP. It took some work but this is even plugged into “run tools” so things like Sysprof can deliver the signal to the right process.

If you’re using custom run commands to build your project you can now toggle-off installation-before-run and likely still get what you want out of the application. This can be useful for very large projects where you’re working on a small section and want to cheat a little bit.

application preferences

Unit Testing

In previous version of Builder, plugins were responsible for how Unit Tests were run. Now, they also use Run Commands which allows users to run their Unit Tests with the debugger or other tooling.

Keyboard Shortcuts

Keyboard shortcuts were always a sore spot in GTK 3. With the move to GTK 4 we redesigned the whole system to give incredible control to users and plugin authors. Similar to VS Code, Builder has gained support for a format similar to “keybindings.json” which allows for embedding GObject Introspection API scripting. The syntax matches the template engine in Builder which can also call into GObject Introspection.

keyboard shortcuts

Command Bar and Project Search

We’ve unified the Command Bar and Project Search into one feature. Use Ctrl+Enter to display the new Global Search popover.

We do expect this feature to be improved and expanded upon in upcoming releases as some features necessary are still to land within a future GTK release.

A screenshot of the search panel

Movable Panels and Sessions

Panels can be dragged around the workspace window and placed according to user desire. The panel position will persist across future openings of the project.

Additionally, Builder will try to save the state of various pages including editors, terminals, web browsers, directory listings, and more. When you re-open your project with Builder, you can expect to get back reasonably close to where you left off.

Closing the primary workspace will now close the project. That means that the state of secondary workspaces (such as those created for an additional monitor) will be automatically saved and restored the next time the project is launched.

A screenshot of panels rearranged in builder


Core editing features have been polished considerably as part of my upstream work on maintaining GtkSourceView. Completion feels as smooth as ever. Interactive tooltips are polished and working nicely. Snippets too have been refined and performance improved greatly.

Not all of our semantic auto-indenters have been ported to GtkSourceIndenter, but we expect them (and more) to come back in time.

There is more work to be done here, particularly around hover providers and what can be placed in hover popovers with expectation that it will not break input/grabs.

Redesigned Preferences

Preferences have been completely redesigned and integrated throughout Builder. Many settings can be tweaked at either the application-level as a default, or on a per-project basis. See “Configure Project” in the new “Build Menu” to see some of those settings. Many new settings were added to allow for more expressive control and others improved open.

Use Ctrl+, to open application preferences, and Alt+, to open your project’s preferences and configurations.

A screenshot showing app preferences vs project preferences

Document Navigation

Since the early versions of Builder, users have requested tabs to navigate documents. Now that we’re on GTK 4 supporting that in a maintainable fashion is trivial and so you can choose between tabs or the legacy “drop down” selector. Navigation tabs are enabled by default.

Some of the UI elements that were previously embedded in the document frame can be found in the new workspace statusbar on the bottom right. Additionally, controls for toggling indentation, syntax, and encoding have been added.

Switching between similar files is easy with Ctrl+Shift+O. You’ll be displayed a popover with similarly named files to the open document.

The symbol tree is also still available, but moved to the statusbar. Ctrl+Shift+K will bring it up and allow for quick searching.

a screenshot of the similar file popover

A screenshot of the symbol selector


A new web browser plugin was added allowing you to create new browser tabs using Ctrl+Shift+B. It is minimal in features but can be useful for quick viewing of information or documentation.

Additionally, the html-preview, markdown-preview, and sphinx-preview plugins have been rewritten to build upon this WebKit integration.

Integrated webkit browser within Builder

Plugin Removals

Some features have been removed from Builder due to the complexity and time necessary for a proper redesign or port. The Glade plugin (which targets GTK 3 only) has been removed for obvious reasons. A new designer will replace it and is expected as part of GNOME 44.

Devhelp has also been removed but may return after it moves to supporting GTK 4. Additionally, other tooling may supersede this plugin in time.

The code beautifier and color-picker were also removed and will likely return in a different form in future releases. However, language servers providing format capabilities can be enabled in preferences to format-on-save.

Project Templates

Project templates have been simplified and improved for GTK 4 along with a new look and feel for creating them. You’ll see the new project template workflow from the application greeter by clicking on “Create New Project”.

project creation assistant

Top Matches

Heavy users of code completion will notice a new completion result which contains a large star (★) next to it. This indicates that the proposal is a very close match for the typed text and is getting resorted to the top of the completion results. This serves as an alternative to sorting among completion providers which is problematic due to lack of common scoring algorithms across different data sources.

a screenshot of top matches support

Sysprof Integration

Tooling such as Sysprof went through a lot of revamp too. As part of this process I had to port Sysprof to GTK 4 which was no small task in it’s own right.

Additionally, I created new tooling in the form of sysprof-agent which allows us to have more control when profiling across container boundaries. Tools which need to inject LD_PRELOAD (such as memory profilers) now work when combined with an appropriate SDK.

A screenshot of sysprof integration

Language Servers

Language servers have become a part of nearly everyone’s development toolbox at this point. Builder is no different. We’ve added support for a number of new language servers including jdtls (Java), bash-language-server (Bash), gopls (Golang) and improved many others such as clangd (C/C++), jedi-language-server (Python), ts-language-server (JavaScript/Typescript), vls (Vala), rust-analyzer (Rust), blueprint, and intelephense (PHP).

Many language servers are easier to install and run given the new design for how cross-container processes are spawned.

A screenshot of the rust-analyzer language server providing completion results

Quick Settings

From the Run Menu, many new quick settings are available to tweak how the application runs as well as well as configure tooling.

For example, you can now toggle various Valgrind options from the Leak Detector sub-menu. Sysprof integration also follows suit here by allowing you to toggle what instruments will be used when recording system state.

To make it easier for developers to ensure their software is inclusive, we’ve added options to toggle High Contrast themes, LTR vs RTL, and light vs dark styling.

A screenshot of the build menu


For language tooling that supports it, you can do things like rename symbols. This has been in there for years, but few knew about it. We’ve elevated the visibility a bit now in the context menu.

Renaming a symbol using clang-rename

Vim Emulation

In GTK 3, we were very much stuck with deep hacks to make something that looked like Vim work. Primarily because we wanted to share as much of the movements API with other keybinding systems.

That changed with GtkSourceView 5. Part of my upstream maintainer work on GtkSourceView included writing a new Vim emulator. It’s not perfect, by any means, but it does cover a majority of what I’ve used in more than two decades as a heavy Vim user. It handles registers, marks, and tries to follow some of the same pasteboard semantics as Vim (“+y for system clipboard, for example).

I made this available in GNOME Text Editor for GNOME 42 as well. Those that wonder why we didn’t an external engines to synchronize with, can read the code to find out.


We have been struggling with our use of PyGObject for sometime. It’s a complex and difficult integration project and I felt like I spent more time debugging issues than I was comfortable with. So this port also included a rewrite of every Python-based plugin to C. We still enable the Python 3 plugin loader from libpeas (for third-party plugins), but in the future we may switch to another plugin language.

Maintainers Corner


A special thanks to all those that sent me merge requests, modernized bits of software I maintain, fixed bugs, or sent words of encouragement.

I’m very proud of where we’ve gotten. However, it’s been an immense amount of work. Builder could be so much more than it is today with your help with triage of bugs, designing and writing features, project and product management, writing documentation, maintaining plugins, improving GNOME OS, and everything in-between.

The biggest lesson of this cycle is how a strong design language is transformative. I hope Builder’s transformation serves as an example for other GNOME applications and the ecosystem at large. We can make big leaps in short time if we have the right tooling and vision.

Builder 43.alpha0

It’s been an absolute mad dash this cycle porting Builder to GTK 4, but 43.alpha0 is out and available on GNOME Nightly.

Builder is one of the larger applications within GNOME, especially if you include the libraries I had to write and maintain to make that possible. Porting an application to a new toolkit is always a big undertaking. However, it also provides an opportunity to rethink how major components work and simplify them while you’re there.

So that is what has happened this cycle. It’s going to end up being a much more polished product due to the enormous amount of simplification going on.

GTK 4 has simplified how a lot of things work and provided APIs that feel so obvious when you use them. Of course, that also means lots of code needs to be changed (well deleted, mostly). Having focused heavily on using GListModel in previous releases also paid off massively this cycle.

Anyway, here it is. It’s still missing plenty of features as I dash towards the finish line implementing them as quick as I can.

For those that want to test it out, note that our application-id has changed so that you can install Builder’s stable branch and nightly branch side-by-side.

flatpak --user remote-add --if-not-exists gnome-nightly \
flatpak --user install gnome-nightly org.gnome.Builder.Devel

A screenshot of Builder's new about dialog

Builder GTK 4 Porting, Part VII

It’s been another couple weeks of porting, along with various distractions.

The big work this time around has been deep surgery to Builder’s “Foundry”. This is the sub-system that is responsible for build-systems, pipelines, external-devices, SDKs, toolchains, deployments-strategies and more. The sub-system was starting to show it’s age as it was one of the first bits of Builder to organically emerge.

One of the things that become so difficult over the years is dealing with all the container layers we have to poke holes through. Running a command is never just running a command. We have to setup PTYs (and make sure the TTY setup ioctl()s happen in the right place), pass environment variables (but to only the right descendant process), and generally a lot more headaches.

What kicked off this work was my desire to remove a bunch of poorly abstracted bits and we’re almost there. What has helped considerably is creating a couple new objects to help manage the process.

The first is an IdeRunContext. It is sort of like a GSubprocessLauncher but allows you to create layers. At the end you can convert those layers into a subprocess launcher but only after each layer is allowed to rewrite the state as you pop back to the root. In practice this has been working quite well. I finally have control without crazy amounts of argument rewriting and guesswork.

To make that possible, I’ve introduced an IdeUnixFDMap which allows to manage source↔dest FD translations for FDs that will end up in the subprocess. It has a lot of helpers around it to make it fit well into the IdeRunContext world.

All of this has allowed the new IdeRunCommand to really shine. We have various run command providers (e.g. plugins) all of which can seamlessly be used across the sub-systems supporting IdeRunContext. Plugins such as meson can even export unit tests as run commands.

The shellcmd plugin has also been rewritten upon these foundations. You can create custom commands and map them to keyboard shortcuts. The commands, like previous version of Builder, can run in various localities. A subprocess, from the build pipeline, as an app runner, or on the host. What has improved, however, is that they can also be used in surrogate of your projects run command. These two features combined means you can make Builder work for a lot of scenarios it never did before by configuring a few commands.

There aren’t a lot of screenshots for things like this, because ideally it doesn’t look too different. But under the hood it’s faster, more reliable, and far more extensible than it was previously. Hopefully that helps us cover a number of highly requested use-cases.

a screenshot of the debugger

a screenshot of the build menu with debug selected

a screenshot of the run command selection selection dialog

a screenshot showing the location of the select run command menu item

a screenshot editing a command

Builder GTK 4 Porting, Part VI

Short update this week given last Monday was Memorial Day in the US. I had a lovely time relaxing in the yard and running errands with my wife Tenzing. We’ve been building such a beautiful home together that it’s nice to just sit back and enjoy it from time to time.

A picture of my yardA picture of me


  • Merged some work on debug features for testing RTL vs LTR from Builder. There is a new GTK_DEBUG=invert-text-dir to allow rudimentary testing with alternate text directions.


  • Landed a new clone design using libadwaita.
  • Fixed rendering of symbolic icons in the gutter for diagnostics, etc
  • Fixed error underlines for spellcheck when dealing with languages where the glyph baseline may change
  • Added a new IdeVcsCloneRequest which can do most of the clone work so the UI bits can be very minimal.
  • Added interfaces to allow for retrieving a list of branches on a remote before you’ve cloned it. Useful to help selecting an initial branch, but do to how libgit2 works, we have to create a temporary directory to make it work (and then unlink it). Handy nonetheless.
  • Make gnome-builder --clone work again.
  • Make cloning newcomer applications automatically work again.
  • Made a lot of our popover’s use menu styling, despite being backed by GListModel and GtkListView.
  • Even more menuing cleanups. Amazing how each pass of this really tends to clarify things from a user perspective.
  • Made all of the editor menu buttons in the statusbar functional now.
  • New gsetting and preference toggle to set default license for new projects.
  • A new IdeWebkitPage page implementation which is a very rudimentary web-browser. This will end up being re-used by the html-preview, markdown-preview, and sphinx plugins.
  • Removed the glade plugin
  • Fixed presentation of clang completion items.

I’m pretty satisfied with the port of the cloning workflow, but it really needs to have a PTY plumbed through to the peer process so we can get better/more complete information. We’ll see if there is time before 43 though given how much else there is to get done.

All of this effort is helping me get a more complete vision of what I’d like to see out of a GTK 5. Particularly as we start attacking things from a designer tooling standpoint.

A screenshot of Builder with an integrated web-browser
A screenshot of Builder with the clone dialog choosing a branch to clone
A screenshot of Builder with the clone dialog

Builder GTK 4 Porting, Part V

Previously Part IV, Part III, Part II, and Part I.

Still working through medicine changes which have wreaked havoc on my sleep, but starting to settle in a bit more.


Small changes here and there for template-glib to cover more cases for us in our keybindings effort. Improved type comparisons, fixed some embarrassing bugs, improved both GObject Introspection and GType method resolution.

Had some interesting talks with Benjamin about expression language needs within GTK and what things I’ve learned from Template-GLib that could be extracted/rewritten with a particular focus on continuously-evaluating-expressions.

Text Editor

I include gnome-text-editor in these updates because I tend to share code between Builder and g-t-e frequently.

  • Improved session resiliency
  • The Save-As dialog will now suggest filenames based on your current language syntax
  • Tracked down some property orderings which ended up being a GTK bug, so fixed that too
  • Persisted maximized window state to the session object on disk
  • Support to inhibit logout while documents are modified
  • Allow starting a single instance of the app with -s|--standalone like we do with Builder


  • More API strawmen for things we need in Builder
  • Fix some checkbutton annoyances
  • Removed assertions from debug builds during failure cases, converted to g_criticals()


  • Updated CI to use a newer Fedora release for more recent wayland protocols and what not
  • More work on source assistants and how measure/present are applied to popovers
  • Improved when and how we show informative tooltips with snippets
  • Add a bunch of “suggested-name” and “suggested-suffix” metadata properties to language specifications so that applications may suggest filenames for Save-As
  • Made Vim emulation of registers global to the application rather than per-view which makes things actually useful and expected behavior to share content between documents
  • Squash some testsuite issues


Merged a bunch of cleanup commits from the community which is very helpful and appreciated!

I also decided that we’re going to remove all PyGObject plugins from Builder itself. We’ll still have it enabled for third-party plugins, at least for now. Maybe someday we’ll get a GJS back-end for libpeas and we could go that route instead. I’ve spent too much time tracking down bindings issues which made me feel very much like I was still working on MonoDevelop circa 2004. That experience was the whole reason I wrote Builder in C to begin with.

None of our PyGObject plugins are all that complicated so I’ve rewritten most of them in C and had help for a few others. So far that covers: blueprint, clangd, go-langserv (now gpls), intelephense, jedi-language-server, ts-language-server, vala-language-server, buildstream, cargo, copyright, eslint, find-other-file, jhbuild, make, mono, phpize, rstcheck, rubocop, stylelint, and waf.

A few got removed instead for various reasons. That includes gvls (replaced by vala-language-server), rls (replaced by rust-analyzer), and gjs-symbols (to be replaced by ts-language-server eventually).

I added support for two new language servers: bash-language-server and jdtls (Java) although we don’t have any bundling capabilities for them yet with regards to Flatpak.

I’ve landed a new “Create New Project” design which required a bunch of plumbing cleanup and simplification of how templates work. That will help me in porting the meson-templates and make-templates plugins to C too.

A screenshot of the "Create New Project" design

I’ve added quick access to Errors and Warnings in the statusbar so that we can remove it from the (largely hidden) pane within the left sidebar. Particularly I’d like to see someone contribute an addition to limit the list to the current file.

A screenshot of the errors/warnings popover

I updated the support for Sysprof so that it can integrate with Builder’s application runners and new workspace designs. You can how have Sysprof data in pages which provides a lot more flexibility. Long term I’d like to see us add API hooks in Sysprof so that we can jump from symbol names in the callgraphs to source code.

A screenshot of Sysprof embedded within Builder

We cleaned up how symbolic icons are rendered in the greeter as well as how we show scroll state with a GtkScrolledWindow when you have a AdwHeaderBar.flat.

A screenshot of the greeter window

Our Valgrind plugin got more tweakables from the Run menu to help you do leak detection.

A screenshot of the valgrind menu

Keybindings for “Build and Run” along with various tooling got simplified to be more predictable. Also a lot of work on the menuing structure to be a bit simpler to follow.

A screenshot of the updated Run menu

You can force various a11y settings now to help get developers testing things they might otherwise never test.

A screenshot of the a11y menu containing high-contrast and ltr/rtl controls

Same goes for testing various libadwaita and libhandy settings. Both this and the RTL/LTR settings have a few things that still need to propagate through the stack, but it will happen soon enough.

A screenshot showing the forced-appearance modes for libadwaita/libhandy

Using Sysprof will be a lot easier to tweak going forward now that there are menu entries for a lot of the instruments.

A screenshot of the sysprof menu

A lot of new infrastructure is starting to land, but not really visible at the moment. Of note is the ability to separate build artifacts and runnables. This will give users a lot more control over what gets built by default and what gets run by default.

For example, a lot of people have asked for run support with better environment variable control. This should be trivial going forward. It also allows for us to do the same when it comes to tooling like “run this particular testsuite under valgrind”.

As always, freshest content tends to be found here 🐦 before I manage to find a spare moment to blog.

Builder GTK 4 Porting, Part IV

This week was a little slower as I was struggling with an adjustment to my new medication. Things progress nonetheless.

Text Editor

I spent a little time this week triaging some incoming Text Editor issues and feature requests. I’d really like this application to get into maintenance mode soon because I have plenty of other projects to maintain.

  • Added support for gnome-text-editor - to open a file from standard input, even if you’re communicating to a single instance application from terminal.
  • Branch GNOME 42 so we can add new strings.
  • Fix a no-data-loss crash during shutdown.


  • Fix template evaluation on macOS.
  • Make boolean expression precedence more predictable.
  • Cleanup output of templates with regards to newlines.


  • Propagate modified page state to tabs
  • Some action tweaks to make things more keyboard shortcut friendly.


  • Merged support for configuration editing from Georges.
  • Add lots of keybindings using our new keybinding engine.
  • Track down and triage that shortcut controllers do not capture/bubble to popovers. Added workarounds for common popovers in Builder.
  • Teach Builder to load keybindings from plugins again and auto-manage them.
  • Lots of tweaks to the debugger UI and where widgetry is placed.
  • Added syntax highlighting for debugger disassembly.
  • Added menus and toggles for various logging and debugger hooks. You can get a breakpoint on g_warning() or g_critical() by checking a box.
  • Ability to select a build target as the default build target finally.
  • More menuing fixes all over the place, particularly with treeviews and sourceviews.
  • Fix keyboard navigation and activation for the new symbol-tree
  • Port the find-other-file plugin to the new workspace design which no longer requires using global search.
  • GTK 4 doesn’t appear to scroll to cells in textview as reliably as I’d like, so I dropped the animation code in Builder and we jump strait to the target before showing popovers.
  • Various work on per-language settings overrides by project.
  • Drop the Rust rls plugin as we can pretty much just rely on rust-analyzer now.
  • Lots of CSS tweaks to make things fit a bit better with upcoming GNOME styling.
  • Fix broken dialog which prevented SDK updates from occurring with other dependencies.

A screenshot of builder's find-other-file plugin

A screenshot of Builder's debugger

A screenshot showing the build target selection dialog

A screenshot of the run menu A screenshot of the logging menu

Builder GTK 4 Porting, Part III

Another week of porting Builder which ultimately sent me on a few fun tangents. I especially enjoyed the work on Template-GLib which brought me back to my days working on languages and runtimes.


  • Prototype and submit a merge request to add support for setting an action parent on a widget. This allows you to alter the normal GtkActionMuxer action resolution. Very handy for situations like Builder and other document-oriented applications.


  • GtkSourceView updates to make it possible to write an external “snippet editor” application (Günther is working on one already)
  • Improve Solarized style schemes a bit for better IDE integration by specifying colors for diff as they can be used by git-based gutter renderers.
  • A little more performance work on gutter renderers as they have always been a major source of runtime costs.
  • Allow gutter renderers to overdraw atop the textview so they can do some more fancy things.


  • Figured out why I was getting spinning CPU with the VTE port for GTK 4. Submitted a diagnosis/fix upstream.


  • Prototyped and landed a new shortcut manager with uses a keybindings.json-like file similar to VS Code (albeit with slightly different syntax which makes more sense for GTK applications). Plugins will be able to override and extend these, as will the user.
  • Prototyped and landed support for showing “selection area” within the gutter renderer.
  • Compress information in a bit tighter space for gutter, as we have a lot of information to display there. Still more we can do should anyone have free time to work on this.
  • Improve breakpoints drawing now that we have some overlapping selections to worry about.
  • Lots of menu cleanup across plugins.
  • More porting from our old IdeSourceView into a new implementation.
  • Lots of object-lifecycle fixes now that Builder more aggressively shuts down components.

Keybindings look something like this.

I would like to specify how wonderful the new shortcut components are in GTK 4, particularly if you’re writing complex applications that have to manage layered shortcuts, user overrides, and such. I’m thinking applications in the class of Inkscape, GIMP, Builder, Darktable, and such will really benefit from this someday.

A screenshot of Builders updated line selections


One of the things that Builder needed was the ability to express when a shortcut is active. In VS Code, they have a "when" element which works well enough for this. However, it has some basic expressions that need to be evaluated at runtime.

Builder already has an expression engine which is used for project templates and what not and it even supports GObject Introspection. It was in sore need of some updates but is very capable for the problem here. I have the tendency to only implement enough to solve problems so this was a nice return to finishing some of that up.

One of the nice things here, when the use case is so focused, is that there is no need for a runtime, JIT, etc. It’s just an AST that has a tmpl_expr_eval() API where you pass in scope and all the objects continue to live in C land, nothing special to deal with.

  • Add anonymous functions (no lambdas though).
  • Allow assigning functions to a symbol.
  • Improve function calling under various situations (named, anonymous, and GObject Introspection functions).
  • Make constructors work.
  • Add a bunch of builtins for things like asserts, casts, math.
  • Fix typeof() builtin for a number of GObject Introspection cases.
  • Add unit tests!
  • Add null keyword.
  • You can now do things like typeof(widget).is_a(typeof(Ide.Page))
  • The require Gtk version "4.0" style imports made it so you couldn’t call .require() due to the Bison parser. That is fixed and you can use @keyword for reserved keywords.
  • A nop has been added with the keyword pass.
  • Fix a bunch of valgrind discovered things.
  • Add linewise comments using the # python style.

I don’t know how much time I’ll spend on this in the future, or if it will go another few years without much work. But with a maintainer who had time, it could be a nice little glue language without all the “muchness” that more well known languages tend to bring in.

Builder GTK 4 Porting, Part II

Another week of work towards porting Builder to GTK 4. Since I can’t add to TWIG from IRC, I’ll try harder to drop some occasional updates here.


  • Merged fixes for highlighting unicode literals for C
  • Improved parsing of language values in snippet bundles
  • GtkSourceGutter will not correctly mark prelit and selection quarks within GtkSourceGutterLines.
  • Fixed a bunch of little mouse pointer annoyances when using GtkSourceHover interactive tooltips.
  • GtkSourceGutterRenderers can now opt-out of signal emission for GtkSourceGutterRenderer::query_data(). Signal emission with GObject is rather slow, so avoiding it on every line can be helpful. Just set the virtual method pointer to NULL. The signal was only ever added to make porting easier anyway.


  • Merged fixes to be used as a subproject with static libraries only
  • CSS styling matches recent changes in libadwaita, particularly around making navigation tabs and panel frame headers more unified.
  • PanelWidget no longer uses a GtkBinLayout so that it’s easier for subclasses to integrate with popovers from size_allocate() to call gtk_popover_present().


  • Georges did a live coding stream where they ported a bunch of the “buildui” plugin. That is merged to the GTK 4 port now. It brings a number of features back to the UI including the build terminal, run terminal, build panel (with pipeline stages, warnings, and errors), and project information in the omnibar popover.
  • Günther did a bunch of work porting our old snippet files to the new XML-based snippet bundle format upstream in GtkSourceView. Along with that came porting of the snippets plugin for Builder’s new GTK 4-based editor.
  • Workspace windows have a bit better predictability when restoring sizes.
  • The project creation workflow was ported, albeit needs to have the redesign implemented still.
  • Lots of resiliency fixes for clang and symbol-tree plugins to improve life-cycle management.
  • The Valgrind plugin was ported to C. This was also back-ported to main because it fixed a number of oddities (crashes) occurring in PyGObject.
  • Builder’s “omni-gutter” GtkSourceGutterRenderer was ported to GTK 4 and got a lot of cleanups along the way. I believe there is still some outstanding things to fix such as handling rendering for symbolic icons as I’m pretty sure that’s not correct yet.
  • The “debuggerui” plugin has been ported to GTK 4 and appears to be working well now. This plugin is responsible for bridging the internal IdeDebugger interfaces to the UI interface.
  • Style schemes were updated for GtkSourceView 5
  • Tabs are now the default navigation interface for pages. There is likely still a lot to do around how we want empty frames to look and behave though.
  • The project-tree is now most ported, and with some workarounds to allow making GtkPopover work you can actually display popovers and activate menu items.
  • A long-standing plugin ordering issue has been fixed so that gtk/menus.ui embedded in plugin resources have menu-merging in the proper order.
  • Some incremental work landed to support per-project configuration of languages.
  • The “ls” plugin (directory views) supports “humanized” timestamps again and columns are resizable once more.
  • You can place panels in the right-side-bar now.


There are a bunch of foundational things to still get landed before I feel I can get Builder flipped over to our Nightly builds. In particular we need to land support for things like:

  • Keyboard shortcuts using GtkShortcutController. This was all done with libdazzle previously.
  • Allow plugins to define custom keyboard shortcuts and merge them into the controller.
  • Port “shellcmd” plugin and integrate keyboard shortcuts to apply those commands.
  • Finish rewrite of the search popover. I’m trying to delay this until GtkListView with sections is supported, as it would provide us a much greater path for performance.
  • A lot of our problems would be simpler if we could make GtkActionMuxer use an alternate action muxer parent from another (non-descendant) widget.
  • Configuration editing in the project configuration window. This is a new design so the port is not 1:1.

A screenshot of Builder with the project-tree context menu and debugger on display

A screenshot of Builder with various panels moved around to non-standard locations

Builder GTK 4 Porting, Part I

Now that GNOME 42 was released in March I’ve had spare cycles to focus on porting Builder to GTK 4 in April. I’ve made a lot of progress thus far, but there is still plenty to go.

Builder is certainly one of the larger GTK applications out there and so it can be a bit daunting unless you embrace your inner hubris and just forget where the end of the tunnel is.

During this porting effort I’ve also been taking time to redo a number of things that just weren’t working well for us. Additionally I’ve spent significant time on what would appear to be tangents, with the goals of easing the porting effort. For example, before I set off on the port I spent time doing things like

  • Work with Matthias Clasen to make GtkTextView support GPU rendering, then make it fast. More recently we’ve even experimented with drawing font shapes at any scale on the GPU thanks to work by Behdad on GLyphy.
  • Write a new macOS back-end for GTK 4 to improve our cross-platform portability and performance story
  • Modernize GTK 4’s GL renderer to help reduce overhead. And then modernize that a bit more to work around Apple’s lack of something like eglSwapBuffersWithDamage().
  • Port Sysprof to GTK 4 and continue helping integrate it into layers of the stack like GLib, GJS, GTK, Pango, GtkSourceView, GNOME Shell, and more.
  • Port D-Spy to GTK 4 and abstract a library for Builder usage
  • Port GtkSourceView to GTK 4
    • Improve GPU rendering by premultiplying alpha, altering when/where some nodes are snapshotted, and how we draw certain textures.
    • JIT’d regexes for syntax highlighting sub-system using PCRE2 instead of the old GRegex APIs
    • JIT’d regexes for text search sub-system also with PCRE2
    • Port Builder’s completion engine upstream to reduce time-to-display and memory overhead
    • Port Builder’s snippet engine and syntax upstream
    • Port Builder’s interactive tool-tips (Hover Providers) upstream
    • Port Builder’s auto-indentation engine upstream
    • Write a new Vim emulation engine upstream in GtkSourceView so we can drop Builder’s
  • Work with Alexander to implement application-wide recoloring support for libadwaita-based applications using GtkSourceView style-schemes
  • Write GNOME Text Editor to prove all the components are working
  • Write a new spell-check engine for GTK 4 (also used by Text Editor) including the creation of a new hybrid data-structure combining a piecetable and a B+Tree
  • Write a new docking/panel engine for GTK 4
  • Write a GTK 4 based designer (Drafting) in coordination with my upstream GTK 4 work so that we can support separation of designer and build/dev containers where code may run. More on that later though when it lands in Builder.

We’ve also been eagerly awaiting GTK 4 ports of both WebKit and VTE, neither of which are trivial efforts to do well. And by well I mean passing GL composited GL textures for a browser or taking advantage of GTK’s fast text rendering/caching/layout. The good news is that these are starting to come together.

People that follow my Twitter have already seen these of course. My goal is to land the GTK 4 port in Builder’s Nightly in May some time so that we get plenty of testing before GNOME 43. It’s a large application, so it will need all the testing it can get.

A screenshot of Builder's GTK 4 port


Preferences is getting a major revamp in our GTK 4 port to make things a bit nicer to look at and also easier to locate features and tweaks. The tricky part with things like this is making it easy for plugins to extend it without “reeking of plug-in based design”.

A screenshot of Builder's new preferences window

Another thing we’ve always wanted in Builder but couldn’t really do at the time was to allow per-project overrides. I’m happy to say that we will likely have this landed in our GTK 4 port. A number of things like per-language settings can be overriden on a per-project basis. I also hope to see build systems and tooling extend the project settings much more than we did in the past. For example, the Meson plugin could use meson_options.txt to add appropriate toggles.


Builder has always had a minimalist design to keep “machinery” out of the way and in most cases just do the right thing for you. We still want that design but we also want to expand on the things you can do. So you’ll find some new menuing in Builder’s GTK 4 port which will get wired up soon to do just that.

Things that have to do with Building can be found near the Build button.

A screenshot of Builder's build menu in the center top of the window

Things that have to do with Running can be found near the Run button.

A screenshot of the run menu in the center top of the window.

We’ve also dropped the “surface” abstraction in the GTK 4 port as it really only added confusion without much benefit. Our new paneling engine will make our reasoning for having that abstraction a non-issue.


You’ll notice that the GTK 4 port of Builder has a statusbar which can be extended by plugins. I tried for years to avoid it, but honestly, it just makes designing things easier and everyone is already using them in similar products. No need to begrudgingly hold out from a design standpoint.

Text Editor with More

Some people really don’t like IDEs, and I totally get that.

However, if you like the new Text Editor I wrote for inclusion in GNOME 42 but find it’s not quite powerful enough for the type of programming you do, Builder might still be able to help you out. It has an editor mode which is a much more powerful version of Text Editor with a robust and mature plugin architecture.

A screenshot of Builder's simple editor mode.

Additionally, users of Builder’s IDE functionality can add one of these supplemental editor windows to additional monitors. The window will be linked to your project’s primary workspace meaning all the same functionality is there, simply on an additional monitor.


It even runs on macOS! If you’d like to see this work better there, I imagine there are opportunities to improve transparent podman support so that people who use macOS can still take advantage of Linux containers. Even though I wrote the new macOS back-end for GTK, I don’t actually use it in my daily life, so my effort there is going to be limited.

A screenshot of Builder's GTK 4 port running on macOS

Additionally, if you’d like to help on things, the scope of Builder can grow considerably now that we have a toolkit that will enable us to do that. If you’d like to help do things like simulators, cross-device integration, debuggers, language server integration, or other fancy new features, we’d love your contributions!