Updates from inside GTK

Some of the core GTK developers recently got together for a few days to do some focused work and talk about current and future plans.

It is difficult to summarize three days of discussions in a blog post, but here are some of the highlights.

Icon themes

The GtkIconTheme code has been with us for a long time. It implements the icon theme spec, and comes from an era when we were shipping big sets of icons with the desktop and themes were expected to switch them out. That is not really how icons are made or used today.

We need a better solution for the workflow from a designer making icons a set of icons in a sheet to the developer copying individual icons into their app.

Inside GTK, this will need some form of “asset manager” to maintain the mapping from icon name to image / file / resource.

Languages

While we can’t get away from providing a C interface with gobject-introspection metadata for all our language bindings, it could be nice to use a more expressive language and a more powerful compiler than C has to offer.

Of course we can’t and won’t rewrite all of GTK in a different language. It would be nice to experiment with replacing smaller parts. Allowing new code to be written in different languages would also potentially bring in new contributors.

Input

We discussed the scroll speed question, and decided to write up an explanatory comment in the issue about what we consider the right solution:

  • treat wheel and touchpad scrolling separately
  • inject configuration from the control-center/compositor into libinput
  • gtk gets it via events

The other big input question we talked about is ‘asynchronous event handling’ and its problems. The two main cases where this comes up are webkit, with its ui<>web process communication, and IBus. In both cases, we think that there is no actual interest in reinjecting unhandled events into the GTK capture/bubble propagation. Instead, such leftover event should just be handled ‘locally’ (in the IBus case, adding/removing characters to the entry, or moving the cursor).

Platform libraries

With GTK4, we’ve made the intentional change to move away from having everything in GTK itself, and instead introduced the idea of  ‘platform libraries’ like libadwaita to carry the more platform-specific widgetry.

Overall, we are happy with how this has turned out, and we would like to continue with this approach. There is maybe room for moving some things that are more plumbing than widgetry back into GTK itself.

GTK5 ?

We need to open a .90 branch to do things that would break the APIs that we have deprecated now (like the file chooser, and more general the chooser dialog/widget split). Some of us have been itching to start on that work. But there’s also still a lot of work to be done in 4.x (GtkListView fixes, for example).

With an eye towards the color management work that is planned to land in 4.12, the suggestion is to open a 4.90 development branch after 4.12. That would put it towards the end of this year, and 3 years after the 4.0 release, which seems reasonable.

The End

On the last day, we had the pleasure of hosting both the documentation and the tracker teams at our place.

Three hackfests in a room!

We’d like to thank the GNOME foundation for supporting our meeting. ❤️

A grid for the file chooser

In the last post, we discussed deprecating treeviews and cell renderers, among other things. All these deprecations cause a lot of work for applications and libraries using these APIs, so why are we doing this?

One of the reasons is to enable new features. Such as a grid view for the file chooser. It only took us 18 years! You can see the original feature request in Bugzilla. This is easily possible now because GtkListView and GtkGridView can use the same data models.

Here is the file chooser, with a new view toggle:

And here is the grid view itself:
Judging from the number of likes on the merge request, this is a popular feature. We hope you enjoy it. ❤️

If you want to support this work, please consider donating to the GNOME Foundation, which supports GTK development. You can do so by going here.

On deprecations

If you are paying attention to GTK’s git repository, you may have noticed a change in the last weeks.

We have a directory gtk/deprecations, which is destined to contain source files that implement deprecated APIs and will be dropped in the next major release. For the 4.0 release, we emptied it out, and it has been empty ever since. But recently, it started to accumulate files again.

This is a good opportunity to remind folks how we are using deprecations in GTK. But first, lets take a look at the details.

The details, part 1: cell renderers

In GTK 4, we introduced a new family of list and grid widgets that are based around list models: GtkListView, GtkColumnView, GtkGridView. There is also a new combo box implementation using list models, called GtkDropDown. Taken together, these are meant to provide replacements for everything you can do with cell renderers in GTK 3.

The ultimate goal was to remove cell renderers, since they are a whole separate rendering and layout system that tends to interfere with GTK’s CSS and layout machinery, and makes everything more complicated.

But we did not quite get to the finish line for 4.0, mainly because we still had significant uses of treeviews in GTK itself. First and foremost, the file chooser.  Since the filechooser is getting ported to use a GtkColumnView in 4.10, now is the right time to deprecate the cell renderer machinery and all the widgets that use them.

This is a significant amount of code, more than 75.000 lines.

The details, part 2: dialogs

In GTK 4, we dropped gtk_main() and gtk_dialog_run(), since recursive mainloops are best avoided. Again, we did not get to the finish line and could not remove GtkDialog itself, since it is used as the base class for all our complex dialogs.

GTK 4.10 introduces replacement APIs for our ‘Chooser’ dialogs. The new APIs follow the gio async pattern. Here is an example:

GtkFileDialog * gtk_file_dialog_new (void);

void            gtk_file_dialog_open (GtkFileDialog *self,
                                      GtkWindow *parent,
                                      GFile *current_file,
                                      GCancellable *cancellable,
                                      GAsyncReadyCallback callback,
                                      gpointer user_data);

GFile *        gtk_file_dialog_open_finish (GtkFileDialog *self,
                                            GAsyncResult *result,
                                            GError **error);

This may look a bit unwieldy in C, but it translates very nicely to languages that have a concept of promises and exceptions:

try {
  const file = await dialog.open(parent, ...);
  
  ...
} catch (e) {
  ...
};

To learn more about the new APIs, you can look at their online docs: GtkColorDialog, GtkFontDialog, GtkFileDialog, GtkAlertDialog.

With these replacements in place, we could deprecate the Chooser interfaces, their widget implementations, and their base class GtkDialog.

No need to panic

Deprecations in GTK are an early outlook at changes that will appear in the next major release that is breaking API compatibility.  But the eventual GTK 5 release is still far away. We have not even made a plan for it yet.

There is absolutely no need to rush towards ‘deprecation cleanup’. You only need to remove all uses of deprecations when you want to port to GTK 5 – which does not exist yet.

There are still things you can do, though. We are introducing deprecations in 4.10 as a way to give our users time to adapt, and to provide feedback on our ideas. If you want to do so, you can file an issue in gitlab, start a discussion in discourse, or find us on matrix.

In the meantime…

Deprecation warnings can be annoying, but thankfully there are easy ways to turn them off. For the occasional call to a deprecated function, it is best to just wrap it in G_GNUC_BEGIN/END_IGNORE_DEPRECATIONS:

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_dialog_add_button (dialog, "Apply", GTK_RESPONSE_APPLY);
G_GNUC_END_IGNORE_DEPRECATIONS

If you are sure that you never ever want to see any deprecation warnings, you can also just pass -Wno-deprecated-declarations to gcc.

Inside the GTK font chooser

I’ve written about the handling of fonts in GTK before. This post is going to focus on how to use the more advanced font (and font chooser) features in your application.

Finding fonts

The most prominent end-user feature of the file chooser is of course that you can search for fonts by name, using the search entry:

A more hidden feature is that you can filter the list by various criteria. One criterium is to show only monospace fonts, another is to only show fonts covering a certain language:

A little detail to notice here is that GTK automatically changes the preview text to match the language you are filtering by.

Less is more

The font chooser returns a PangoFontDescription which contains the full details of the selected font: family, style, size, etc. If your application only needs the family, then it is confusing to let the user select a style and size only to have them be ignored.

If this is the case for your application, you can instruct GTK about the font details you need, using gtk_font_chooser_set_level(), and the GtkFontChooserLevel flags:

typedef enum {
  GTK_FONT_CHOOSER_LEVEL_FAMILY     = 0,
  GTK_FONT_CHOOSER_LEVEL_STYLE      = 1 << 0, 
  GTK_FONT_CHOOSER_LEVEL_SIZE       = 1 << 1,
  GTK_FONT_CHOOSER_LEVEL_VARIATIONS = 1 << 2,
  GTK_FONT_CHOOSER_LEVEL_FEATURES   = 1 << 3
} GtkFontChooserLevel;

For example, after

gtk_font_chooser_set_level (chooser, 
                            GTK_FONT_CHOOSER_LEVEL_FAMILY);

the font chooser looks like this:

Much simpler!

Into the abyss

Modern fonts are complicated beasts, and there’s much that’s lurking under the surface. The GTK font chooser can make many of these font features available if you tell it to.

First, there are font variations. These let you continuously vary the characteristics of a font (as long as those characteristics are exposed as variation axes).

 

Typical variation axes are weight, width and slant of a font, but there can others (such as Optical Size in this example).

The selected variations are part of the PangoFontDescription that the font chooser returns, applications don’t have to do any extra work to apply them. Just use the font description as usual.

To enable the font variation support in the GTK file chooser, use GTK_FONT_CHOOSER_LEVEL_VARIATIONS flag:

level = level | GTK_FONT_CHOOSER_LEVEL_VARIATIONS;
gtk_font_chooser_set_level (chooser, level);

More features

Fonts contain not just the glyph contours, but lots of other data that can be applied in various ways when rendering those glyphs. This includes traditional data like kerning and ligatures, but also things like optional glyph shape or positioning variants or even color palettes. Many of these can be enabled by the user with the help of OpenType features.

Here is an example of an OpenType feature for glyph shape variations:


The feature that is toggled on here when going from left to right is called ss12. Thankfully, the font provides the more meaningful name “Single-story g” as well.

This example shows the effect of the frac feature on the display of fractions.

In the GTK font chooser, OpenType features are presented on the same page as variations. As you see, there can be quite a few of them:

Note that Pango treats OpenType features as separate from the font itself. They are not part of the font description, but have to be applied to text either with PangoAttributes or via Pango markup.

To apply the selected font features from a GTK font chooser, call gtk_font_chooser_get_font_features () and pass the returned string to pango_attr_font_features_new().

To enable the OpenType features support in the GTK file chooser, use GTK_FONT_CHOOSER_LEVEL_FEATURES flag:

level = level | GTK_FONT_CHOOSER_LEVEL_FEATURES;
gtk_font_chooser_set_level (chooser, level);

Summary

In summary, you can use the level property of GtkFontChooser to influence the granularity of font selection you offer to users of your application. If you include font features in it, don’t forget to apply the selected features, using PangoAttributes or markup.

All of this is enabled by harfbuzz providing us with a cross-platform API to fonts and all their features. It would not be possible otherwise. It is worth pointing out that this is done by accessing harfbuzz objects directly, rather than wrapping all the harfbuzz APIs in Pango.

 

 

GTK 4.4

GTK 4.4.0 is now available for download in the usual places. Here are some highlights of the work that has gone into it.

The NGL renderer and GL support

The NGL renderer has continued to see improvements. This includes speedups, fixes for transformed rendering, avoiding huge intermediate textures, and correct handling of partial color fonts. After some help from driver developers, NGL now works correctly with the Mali driver. We are planning to drop the original GL renderer in the next cycle.

Outside of GSK, our OpenGL setup code has been cleaned up and simplified. We increasingly rely on EGL, and require EGL 1.4 now. On X11 we use EGL, falling back to GLX if needed. On Windows, we default to using WGL.

Our GL support works fine with the latest NVidia driver.

Themes

The included themes have been reorganized and renamed. We now ship themes that are called Default, Default-dark, Default-hc and Default-hc-dark. The Adwaita theme is moving to libadwaita.

Among the smaller theme improvements are new error underlines (they are now dotted instead of squiggly) and support for translucent text selections.

Input

Input handling has seen active development this cycle. We’ve matched the behavior of the built-in input method with IBus for displaying and handling compose sequences and dead keys. As part of this, we now support multiple dead keys and dead key combinations that don’t produce a single Unicode character (such as ẅ).

We fully support 32-bit keysyms now, so using Unicode keysyms (e.g. for combining marks) works.

Emoji

Our Emoji data has been updated to CLDR 39, and we can are looking for translated Emoji data by both language and territory (e.g. it-ch).

Debugging

The Inspector is now enabled by default, so debugging GTK applications should be a litte easier.

Windows

Apart from the WGL improvements that were already mentioned, we now use GL for media playback on Windows. A big change that landed late in 4.4 is that we use the WinPointer API for tablets and other input devices now, replacing the outdated wintab API. DND support on Windows is also improved, and the local DND protocol has been dropped.

The numbers

GTK 4.4 is the result of 5 months of development, with 838 individual commits from 71 developers; a total of 88133 lines were added and 63094 removed.

Developers with the most changesets
Matthias Clasen 456 54.4%
Benjamin Otte 82 9.8%
Emmanuele Bassi 48 5.7%
Alexander Mikhaylenko 35 4.2%
Chun-wei Fan 30 3.6%
Christian Hergert 18 2.1%
Luca Bacci 17 2.0%
Carlos Garnacho 10 1.2%
Bilal Elmoussaoui 10 1.2%
Florian Müllner 7 0.8%
Yuri Chornoivan 6 0.7%
Maximiliano Sandoval R 6 0.7%
Marc-André Lureau 5 0.6%
Marco Trevisan (Treviño) 5 0.6%
Pawan Chitrakar 5 0.6%
Piotr Drąg 4 0.5%
Timm Bäder 4 0.5%
Xavier Claessens 4 0.5%
Zhi 4 0.5%
Sebastian Cherek 4 0.5%

Text input in GTK 4

To wrap up the recent series of posts about input topics, lets talk about text editing in GTK 4.

The simple: shortcuts

Maybe you just need to handle a few keys as editing commands, for example Ctrl-z to undo. In that case, you can just use a shortcut with an action, and set it all up in your widgets class_init:

/* install an undo action */ 
gtk_widget_class_install_action (widget_class,
                                  "text.undo", NULL,
                                  my_undo_func);

/* bind Ctrl-z to the undo action */
 gtk_widget_class_add_binding_action (widget_class,
                                      GDK_KEY_z, GDK_CONTROL_MASK,
                                      "text.undo", NULL);

The complex: a text editor

When you need full text editing, the best approach is to re-use one of the ready-made widgets in GTK for this purpose: one of the entries, or if you need a full-blown text editor, GtkTextView.

If none of the existing entries fit your use case, you can also wrap your own GtkEditable implementation around a GtkText widget, and get all the hard parts of a text editing widget for free. The GTK docs explain how to do that.

The middle ground

But what if you don’t want an entry, but still need to let your users enter individual Unicode characters such as ñ or Å conveniently? I’ll let you come up with a use case for this (although I have one in mind).

One thing you can do is to use a GtkIMContext directly, and let it process key events for you. The way this works is that you attach a key event controller to your widget and connect an input method context to it:

controller = gtk_event_controller_key_new ();
gtk_widget_add_controller (widget, controller);

im_context = gtk_im_multicontext_new ();
gtk_event_controller_key_set_im_context (controller, im_context);

Now key events that reach your widget will be passed to the input method context. Connect a handler to its ::commit signal to receive the completed input:

static void
commit_cb (GtkIMContext *context,
           const char   *str,
           DemoWidget   *demo)
{
  pango_layout_set_text (demo->layout, str, -1);
  pango_layout_set_attributes (demo->layout, NULL);
  gtk_widget_queue_draw (GTK_WIDGET (demo));
}

...

g_signal_connect (im_context, "commit",
                  G_CALLBACK (commit_cb), demo);

You can connect a similar handler to the ::preedit-changed signal to provide the user feedback during preedit like GtkEntry does.

The complete example for single-character input can be found here.

More on input

I’ve written about input before (here and here), and more recently, Carlos and myself gave a Guadec talk about input-related topics (slides). In those writings, I have explained how dead keys work, and how you can type

<dead_acute> A

to produce an Á character.

But input is full of surprises, and I’ve just learned about an alternative to dead keys that is worth presenting here.

Background

First lets recap what happens when you send the <dead_acute> A sequence to GTK.

We receive the first key event and notice that it is a dead key, so we stash it in what we call the preedit, and wait for the next event.  When the next key arrives, and it represents a letter (more precisely, is in one of the Unicode categories Ll, Lu, Lt, Lm or Lo), we look up the Unicode combining mark matching the dead_acute, which is U+301 COMBINING ACUTE ACCENT, and then we flip the sequence around. So the text that gets committed is

A <combining acute>

The reason that we have to flip things around is that combining marks go after the base character, while dead keys go before.

This works, but it is a bit unintuitive for writing multi-accented characters. You have to think about the accents you want to apply from top to bottom, since they get applied backwards. For example to create an  with an acute accent on top, you type

<dead_acute> <dead_circumflex> A

which then gets flipped around and ends up as:

A <combinining circumflex> <combining acute>

A better way

To me, it feels much more natural to specify the accents in that order:

  1. give me an A
  2. then put a ^ on top
  3. and then put an ´ on top

The good news is: we can do just that! Keyboard layouts can use any Unicode character as keysyms, so we can just use the combining marks directly, without the detour through dead keys.

For example, the “English (US,  Intl, AltGr Unicode combining)” layout contains keys for combining marks. A slight hurdle to using this layout is that it does not show up in the GNOME Settings keyboard panel by default. You have to run

gsettings set org.gnome.desktop.input-sources show-all-sources true

to make it show up.

The combining marks in this layout are placed in a “3rd level”. To use them, you need to set up a “3rd level chooser” key. In the keyboard panel, this is called the “Alternative Characters Key”. A common choice is the right Alt key.

After all these preparations, you can now type A Alt+^ Alt+’ to get an   with an   ́ on top. Neat!

Adventures in graphics APIs

Various people are working on porting desktop virtualization UIs to GTK4. This typically involves virgl, and the GTK3 solution was to use GtkGLArea.

With GTK4, rendering is happening in GL anyway, so it should be enough to just wrap your content in a GdkTexture and hand it to GTK, either by using it as a paintable with GtkPicture, or with a GskTextureNode in your own snapshot() implementation.

dmabuf detour

This is a nice theory, but the practice is more complicated – the content is typically available as a dmabuf object, and with 4k rendering, you really want to avoid extra copies if you can help it. So we had to look at the available solutions for importing dmabufs as textures into GL without copies.

This turned into a quick tour through the maze of graphics APIs: OpenGL, EGL, GL ES, GLX, DRI, … the list goes on. In the end, it turns out that you can use EGL  to wrap a dmabuf into an EGLImage, and use the GL_OES_EGL_image extension to create a GL texture from it.

GLX to EGL

This works fine with our Wayland backend, which uses EGL. Unfortunately, our much older X11 backend has a GL implementation using GLX, and there doesn’t seem to be a way to get a dmabuf imported into a GLX context.

So we had to do a little bit of extra work, and make our X11 backend use EGL as well. Thankfully Emmanuele had an old unfinished branch with  such a conversion from a few years ago, which could be made to work (after some initial head scratching why it would not render anything – as always the case when doing GL work).

The solution

It turns out that importing dmabufs with EGL can be done outside of GTK just fine, so we don’t need to add Linux-specific API for it. To save you the trouble of writing such code yourself, here is what I’ve come up with.

After we had already decided to port the X11 backend to EGL, I learned that another possibility for importing dmabufs might be to use DRI3PixmapFromBuffer to create  an X11 pixmap from a dmabuf, turn it in a GLXPixmap and use glxBindTexImageEXT to make a texture.

Aren’t graphics APIs wonderful! :-)

Input, revisited

My last update talked about better visual feedback for Compose sequences in GTK’s input methods. I did not explicitly mention dead keys back then, but historically, X11 has treated dead keys and Compose sequences in exactly the same way.

Dead keys are a feature of certain keyboard layouts where you can hit a key that does not produce a character by itself, but modifies the next key you type. Typically, this is used for accents that can be combined with different base characters. For example, type <dead_acute> <a> to produce á or  <dead_acute> <o> to produce ó.

Traditionally, dead keys were really dead – you didn’t get any visual feedback before the final result appears. With the improvements described in the last update, we now show dead keys as they are entered:

That is a nice improvement. But as it turned out, not everybody was happy.

The shared treatment of Compose sequences and dead keys has some implications: one is that entering a non-existing sequence such as <dead_grave> <x> will produce a beep, and no output. That is acceptable for a Compose sequence that you explicitly started with the Compose key, but not so great when you maybe meant to enter `x.

The people who decided to use Compose sequences for dead keys foresaw the need to actually enter spacing accents every now and then, and added sequences such as <dead_grave> <space> and <dead_grave> <dead_grave> for producing a single ` character.

While that is a nice thought, it is still pretty inconvenient, since you need to type <dead_grave> six time to produce `‍`‍`, e.g. for entering code examples in markdown.

After thinking about this for a while and comparing what other systems do, we’ve made two changes, that will hopefully make dead keys as convenient to use as any other keys on your keyboard.

  • When a <dead key> <key> sequence does not match one of our Compose sequences, commit the individual keys
  • When a <dead key> follows another <dead key>, commit the first one, and treat the second as the beginning of a new Compose sequence

Together, this makes it so that typing <dead_acute> <a> produces á, typing <dead_grave> <x>  produces `x, and you only need to type <dead_grave> three times to enter `‍`‍`:

Much better!

GTK happenings

GTK 4.2 is due out in March – it will not be an enormous release, just incremental improvements. But besides the usual bug fixes and performance improvements, there are a few things that are worth calling out indvidually.

A new GL Renderer

Christian Hergert has been hard at work, creating a new GL renderer for GTK. The initial motivation for this work was the desire to improve our rendering performance on MacOS, where the GL drivers are not quite as forgiving as on Linux. Apart from that, starting over with a new renderer gives us a chance to apply all the things we’ve learned while working on our current GL renderer, and to reorganize the code with an eye towards future improvements, such as reordering and batching of draw commands.

The new renderer hasn’t been merged  yet, but it is closing in on feature parity and may well make it into 4.2. We will likely include both the old and the new renderer, at least for a while.

Popover Shadows

Ever since GtkPopover was introduced with its signature ‘beak’, popovers have clipped everything outside their border, since we needed the tip of the beak to be consistently placed. With the new xdg-popup based implementation in GTK4, we have an positioning protocol that is expressive enough to place popovers in a way that makes the ‘beak’ point where it is supposed to while allowing shadows underneath and around the popover. As with window shadows, the popover shadows are outside of the input region, so clicks go through to the underlying window.

This is a minor thing, but it may have a noticeable impact in giving the UI depth and structure.

Better Input

GtkIMContextSimple is the input method implementation that is built into GTK. It is used when we don’t have a platform method to use, such as the Wayland text protocol. GtkIMContextSimple only does a few things. One of them is to interpret hexadecimal input for Unicode characters, with Control-Shift-u. Another is that it handles Compose sequences, such as

<Compose Key> <a> <acute>

to enter an á  character.

Most Compose sequences start with that Compose Key, and the keyboard settings in GNOME 40 will include a way to assign a key on your keyboard to this function.

On the GTK side, we’ve addressed a few longstanding complaints with the Compose sequence support. Apart from its built-in sequences, GTK parses X11 Compose files. The format of these files is described in Compose(5), but until now, GTKs support for this format was pretty incomplete. With GTK 4.2, we’re improving this to

  • Allow sequences of up to 20 keys (previously, the limit was 7)
  • Generate multiple characters (notably, this allows Unicode Emoji sequences)
  • Support hexadecimal codepoints

These are nice improvements for people who make their own Compose sequences by editing ~/.Compose. But what about the rest of us? One traditionally difficult aspect of using Compose sequence is that you have to know the sequences by heart, and type them blindly. There is no visual feedback at all until the sequence is complete and the final character appears. A while ago, IBus improved on this by showing the characters of the incomplete sequence as underlined preedit text, similar to what we do for hexadecimal Unicode input.

After copying their approach for GTK, initial user feedback was mixed, mainly because the official glyph for the Compose Key (⎄) is a bit distracting when it appears unexpectedly. So I went back to the drawing board and came up with a different approach:

I hope this works better. Feedback welcome!

All of these input changes will also appear in GTK 3.24.26.