Building and testing GTK

… or: how GTK developers check their work on the toolkit.

Since GNOME’s collective move to GitLab, GTK has taken advantage of the features provided by that platform—especially when it comes to its continuous integration pipeline.

In days of old, the only way to check that our changes to the toolkit were correct was to wait until the Continuous build bot would notify us of any breakage on the main development branches. While this was better than nothing, it didn’t allow us to prevent breakage during the development phase of anything—from features to bug fixes, from documentation improvements to adding new tests.

These days, the CI pipeline available in GitLab is run on every branch and merge request, long before the changes reach the public development branches used by everybody else.

Topic branches and merge requests

When developing a topic branch against the GTK 4 main development one, we run a CI pipeline that starts with a simple coding style check for the changes applied in the branch. The style check uses clang-format, which is often good enough for the GTK coding style; the coding style has a few “special” caveats, and clang-format can raise false negatives and false positives. For that reason, the style check is allowed to fail, but contributors and reviewers are strongly encouraged to check the logs in case of failure.

Once the style check is passed, we run the build phase, which currently contains three separate jobs:

  • a Linux debug build, using a Fedora container
  • an MSYS2 build on Windows
  • a Linux release build

The Linux debug build is pretty standard fare.

The MSYS2 build catches any issue with a GNU toolchain on Windows.

The release build is necessary to ensure that we don’t rely on side effects of the debugging code we have in place during development.

All of these jobs run the GTK test suite.

We publish the tests reports both as a JUnit file, taking advantage of GitLab’s support; and as an HTML report, stored as a pipeline artifact. This makes it easier for us to check what failed and what succeeded.

Ideally, we want to add more environments:

  • Linux builds based on other mainstream distributions
  • a Windows build using the MSVC toolchain
  • a macOS build, once the GDK backend is fixed

After the build and testing jobs pass, we step into an analysis phase. We run the Clang static analysis tool on GTK’s code base and generate a report. In the near future we could also run sanitizer tools like UBSan and ASan; fuzzying tools for our parsers, like GtkBuilder and CSS; or tools that verify that our UI definitions contain the appropriate accessibility descriptors.

Just like the tests, we publish the analysis reports as GitLab artifacts for review.

Once the analysis phase is passed, we build the API references, and check the result so that newly added symbols are properly documented.

Finally, we have manual CI jobs to build Flatpak bundles for the GTK demo application; the widget factory; and the icon browser. This allows designers to immediately test changes in Adwaita, or newly added widgets, without necessarily building GTK from a scratch on their systems.

Mainline development branches

Once the CI pipeline for a topic branch/merge request passes, we can merge the changes into the main development branch with a certain level of confidence that the code is correct and does what we want.

The main development branch runs the same pipeline as previously described, except that the Flatpak jobs are not manual any more—thus is always possible to test locally the current bleeding edge of GTK. Additionally, the documentation is published online, so it’s always up to date.

The GTK CI pipeline

What about GTK 3?

In the GTK 3 branch we have a simpler pipeline that runs the following jobs:

  • a full Meson debug build on Linux and Windows/MSYS2, for both static and shared libraries artifacts, on the current stable versions of Fedora and Debian
  • a full Meson release build on Linux, which also generates the API reference
  • an Autotools build on Linux and Windows/MSYS2
  • an optional Autotools distcheck build on Linux

The Autotools jobs will be in place for as long as GTK 3 supports Autotools. Ideally, we want to add other jobs for macOS and Windows/MSVC, taking advantage of the Meson build.

The GTK3 CI pipeline

Once the GTK 4 CI pipeline reaches a certain level of features and stability, we’re going to backport it to GTK 3, so we can be even more confident that the current stable branch does not regress.


For more information, you can check the GTK repository:

Constraint layouts

What are constraints

At its most basic, a constraint is a relation between two values. The relation
can be described as a linear equation:

target.attribute = source.attribute × multiplier + constant

For instance, this:

Can be described as:

blue.start = red.end × 1.0 + 8.0

Or:

  • the attribute, “start”, of the target, “blue”, which is going to be set by the constraint; this is the left hand side of the equation
  • the relation between the left and right hand sides of the equation, in this case equality; relations can also be greater than or equal to,
    and less than or equal to
  • the attribute, “end”, of the source, “red”, which is going to be read by the constraint; this is the right hand side of the equation
  • the multiplier, “1.0”, applied to the attribute of the source
  • the constant, “8.0”, an offset added to the attribute

A constraint layout is a series of equations like the one above, describing all the relationships between the various parts of your UI.

It’s important to note that the relation is not an assignment, but an equality (or an inequality): both sides of the equation will be solved in a way that satisfies the constraint; this means that the list of constraints can be rearranged; for instance, the example above can be rewritten as:

red.end = blue.start × 1.0 - 8.0

In general, for the sake of convenience and readability, you should arrange your constraints in reading order, from leading to trailing edge, from top to bottom. You should also favour whole numbers for multipliers, and positive numbers for constants.

Solving the layout

Systems of linear equations can have one solution, multiple solutions, or even no solution at all. Additionally, for performance reasons, you don’t really want to recompute all the solutions every time.

Back in 1998, the Cassowary algorithm for solving linear arithmetic constraints was published by Greg J. Badros and Alan Borning, alongside its implementation in C++, Smalltalk, and Java. The Cassowary algorithm tries to solve a system of linear equations by finding its optimal solution; additionally, it does so incrementally, which makes it very useful for user interfaces.

Over the past decade various platforms and toolkits started providing layout managers based on constraints, and most of them used the Cassowary algorithm. The first one was Apple’s AutoLayout, in 2011; in 2016, Google added a ConstraintLayout to the Android SDK.

In 2016, Endless implemented a constraint layout for GTK 3 in a library called Emeus. Starting from that work, GTK 4 now has a GtkConstraintLayout layout manager available for application and widget developers.

The machinery that implements the constraint solver is private to GTK, but the public API provides a layout manager that you can assign to your GtkWidget class, and an immutable GtkConstraint object that describes each constraint you wish to add to the layout, binding two widgets together.

Guiding the constraints

Constraints use widgets as sources and targets, but there are cases when you want to bind a widget attribute to a rectangular region that does not really draw anything on screen. You could add a dummy widget to the layout, and then set its opacity to 0 to avoid it being rendered, but that would add unnecessary overhead to the scene. Instead, GTK provides GtkConstraintGuide, and object whose only job is to contribute to the layout:

An example of the guide UI element

In the example above, only the widgets marked as “Child 1” and “Child 2” are going to be visible, while the guide is going to be an empty space.

Guides have a minimum, natural (or preferred), and maximum size. All of them are constraints, which means you can use guides not just as helpers for alignment, but also as flexible spaces in a layout that can grow and shrink.

Describing constraints in a layout

Constraints can be added programmatically, but like many things in GTK, they can also be described inside GtkBuilder UI files, for convenience. If you add a GtkConstraintLayout to your UI file, you can list the constraints and guides inside the special “<constraints>” element:

  <object class="GtkConstraintLayout">
    <constraints>
      <constraint target="button1" target-attribute="width"
                     relation="eq"
                     source="button2" source-attribute="width" />
      <constraint target="button2" target-attribute="start"
                     relation="eq"
                     source="button1" source-attribute="end"
                     constant="12" />
      <constraint target="button1" target-attribute="start"
                     relation="eq"
                     source="super" source-attribute="start"
                     constant="12" />
      <constraint target="button2" target-attribute="end"
                     relation="eq"
                     source="super" source-attribute="end"
                     constant="-12"/>
    </constraints>
  </object>

You can also describe a guide, using the “<guide>” custom element:

  <constraints>
    <guide min-width="100" max-width="500" />
  </constraints>

Visual Format Language

Aside from XML, constraints can also be described using a compact syntax called “Visual Format Language”. VFL descriptions are row and column oriented: you describe each row and column in the layout using a line that visually resembles the layout you’re implementing, for instance:

|-[findButton]-[findEntry(<=250)]-[findNext][findPrev]-|

Describes an horizontal layout where the findButton widget is separated from the leading edge of the layout manager by some default space, and followed by the same default amount of space; then by the findEntry widget, which is meant to be at most 250 pixels wide. After the findEntry widget we have some default space again, followed by two widgets, findNext and findPrev, flush one against the other; finally, these two widgets are separated from the trailing edge of the layout manager by the default amount of space.

Using the VFL notation, GtkConstraintLayout will create all the required constraints without necessarily having to describe them all manually.

It’s important to note that VFL cannot describe all possible constraints; in some cases you will need to create them using GtkConstraint’s API.

Limits of a constraint layout

Constraint layouts are immensely flexible because they can implement any layout policy. This flexibility comes at a cost:

  • your layout may have too many solutions, which makes it ambiguous and unstable; this can be problematic, especially if your layout is very complex
  • your layout may not have any solution. This is usually the case when you’re not using enough constraints; a rule of thumb is to use at least two constraints per target per dimension, since all widgets should have a defined position and size
  • the same layout can be described by different series of constraints; in some cases it’s virtually impossible to say which approach is better, which means you will have to experiment, especially when it comes to layouts that dynamically add or remove UI elements, or that allow user interactions like dragging UI elements around

Additionally, at larger scales, a local, ad hoc layout manager may very well be more performant than a constraint based one; if you have a list box that can grow to an unknown amount of rows you should not replace it with a constraint layout unless you measure the performance impact upfront.

Demos

Of course, since we added this new API, we also added a few demos to the GTK Demo application:

A constraints demo
The constraints demo window, as part of the GTK demo application.

As well as a full constraints editor demo:

The GTK constraints editor demo
A screenshot of the GTK constraints editor demo application, showing the list of UI elements, guides, and constraints in a side bar on the left, and the result on the right side of the window

More information

News from GLib 2.58

Next September, GLib will hit version 2.58. There have been a few changes during the past two development cycles, most notably the improvement of the Meson build, which in turn led to an improved portability of GLib to platforms such as Windows, macOS, and Android. It is time to take stock of the current status of GLib, and to highlight some of the changes that will impact GLib-based code.

  • Meson – Thanks to the ongoing work of Nirbheek Chauhan and Xavier Claessens, the Meson build has been constantly improving, to the point that we can start switching to it as the default build system. The plan—as outlined on the mailing list—is to release GLib 2.58 using Meson, while keeping the Autotools build in tree and available in the release archive; then, we’ll drop the Autotools build during the following development cycle, and release GLib 2.60 without Autotools support. Linux distributors are very much welcome to start testing the Meson build in their builders; we’ve been running the Meson build as part of our CI process for a while, now, but more exposure will bring out eventual regressions that we missed; additionally, it would be stellar if people with different toolchains than GCC/Clang/MSVC would start trying the Meson build and report bugs. In the meantime, if you’re using GLib on macOS and Windows, we already recommend you switch to Meson to build GLib, as it’s easier and better integrated with those platforms than Autotools
  • Reliability and portability – GLib switched to GitLab alongside the rest of GNOME, which meant being able to run continuous integration outside of the GNOME Continuous builds. Now we run CI on multiple toolchains, multiple build systems, and multiple platforms for every commit and merge request, which significantly reduces the chances of a broken build. We’ve also improved the code coverage in the test suite. Of course, we could always do better; for instance, we don’t have a CI runner for macOS and the Solaris family of OSes, and more runners for the *BSD family would be greatly appreciated. We’ve issued a call for help, if you have a spare machine and some bandwidth that you can donate
  • File monitoring on *BSD – Apropos the *BSD family, the kqueue backend for file monitoring in GIO has been completely overhauled by Martin Pieuchot and Ting-Wei Lan; the new code is simpler, more robust, and passes all the tests
  • Use posix_spawn() for efficient process launching — Thanks to Daniel Drake, GLib now can use posix_spawn() under specific circumstances, if the platform’s C library supports it; this allows hitting fast paths in the kernel, compared to manually calling fork() + exec(); those fast paths are especially beneficial when running on memory constrained platforms
  • Reference counting types and allocations — GLib uses reference counting as a memory management and garbage collection mechanism in many of its types, but lacks the public API to allow other people to implement the same semantics in their own data structures; this leads to much copy-pasting and re-implementations, and typically to things like undefined behavior when it comes to saturation and thread safety. GLib 2.58 has a grefcount and a gatomicrefcount types, alongside their API, to reduce this duplication. Additionally, taking a cue from other languages like Rust, GLib provides a way to add reference counting semantics on memory allocations, by adding a low level API that allows you to allocate structures that do not have a reference count field, and automatically add reference counting semantics to them
  • Deprecations – A few soft deprecations have become real deprecations in this last development cycle:
      • g_type_class_add_private() has finally been deprecated, five years after we introduced the instance private data macros; if you’re still using that function in your class initialization, please switch to G_DEFINE_TYPE_WITH_PRIVATE or G_ADD_PRIVATE
      • g_main_context_wait() is officially deprecated, but you should have already seen run time warnings about its use
      • gtester, the GTest harness provided by GLib, is deprecated; if you’re using Autotools, you should use the TAP harness that comes with Automake

There have been lots of contributions in GLib, in this past cycle, thanks to the tireless efforts of Philip Withnall; he’s been instrumental in reviewing patches, triaging bugs, and implementing changes in the development process of the project. The switch over to GitLab has also improved the contribution process, with many more developers opening merge requests:

  • 2.54.0..c182cd68: 968 changesets from 143 developers, up from 412 changesets and 68 developers during the 2.53 development cycle
  • A total of 31851 lines added, 27976 removed (delta: +3875)
Developers with the most changesets
Philip Withnall 303 31.3%
Xavier Claessens 79 8.2%
Emmanuele Bassi 69 7.1%
Christoph Reiter 42 4.3%
Ting-Wei Lan 21 2.2%
Chun-wei Fan 21 2.2%
Nirbheek Chauhan 21 2.2%
Ondrej Holy 20 2.1%
Руслан Ижбулатов 20 2.1%
Mikhail Zabaluev 20 2.1%
Simon McVittie 15 1.5%
Matthias Clasen 14 1.4%
Christian Hergert 13 1.3%
Iñigo Martínez 12 1.2%
Bastien Nocera 10 1.0%
Rafal Luzynski 9 0.9%
Michael Catanzaro 9 0.9%
Will Thompson 8 0.8%
Allison Lortie 8 0.8%
Daniel Boles 8 0.8%

Make sure to test your code with GLib 2.57.2, the next development snapshot towards the 2.58.0 stable release.

GTK+ 3.92

Yesterday, we released GTK+ 3.92.1, 重庆市. Since it has been a while since the last 3.91 release, here is a brief look at the major changes.

This release is another milestone on our way towards GTK+ 4. And while a lot still needs to be done, this release allows a first glimpse at some of the things we hope to achieve in GTK+ 4.

GSK

Much of the work since the last release has gone into GSK. The Vulkan renderer is now close to complete, as far as avoiding cairo fallbacks goes. The only missing piece are blurred shadows (admittedly, an important piece).

One major step forward since the 3.91.2 release is that we no longer use cairo fallbacks for all text. Instead, text (in labels and entries, sadly not in text views yet) gets translated into text nodes. Each text node contains a PangoGlyphString and a PangoFont. The Vulkan renderer uses a glyph cache to avoid re-rendering the glyphs for each frame.

The internal logic of the Vulkan renderer has been reworked to use textures instead of cairo surfaces for intermediate results and thus avoid more cairo fallbacks.

Other node types that have gained support in the Vulkan renderer include blurs, repeat nodes, blend modes and cross-fades. In some cases, the shaders we use are very naive implementations. Help with improving them would be more than welcome!

As a first example of what we can do with render nodes, we have implemented a blur-under feature for GtkOverlay. This works by capturing the ‘main child’ of the overlay as a render node, and then reusing it several times, with the right clipping, and sometimes with a blur node.

Inspector

To help you explore GSK, the inspector now shows Vulkan information and the recorder shows a lot more information about render nodes.

Input

On the input side, events have gained accessors, and we are no longer accessing their fields directly. This is an intermediate step, cleaning up events is still a work in progress. We have moved the traditional widget signals for events (such as ::key-press-event) to an event controller, and most widgets inside GTK+ have stopped using them altogether.

Build System

We have switched over to using Meson exclusively for GTK+, and the 3.92.1 release is the first one done using Meson’s dist support. To get the release out the door, we also had to port the documentation, the test suite and the installed tests to using Meson.

There are still some rough edges (we don’t get all dependencies 100% right), but overall, Meson worked out well for us.

The rest

Of course, everybody loves Emoji, and the same color Emoji support that has landed in GTK+ 3.22 is also available in this release. Beyond that, font support in CSS has improved a bit with support for the CSS3 font-variant property.

Of course, this relies on fonts that have the corresponding features.

Try it out

With GTK+ 3.92.1, it should be easy to try some of these things out for yourself.

And if you’ve always wanted to get into GTK+ development but never found the right opportunity, now is a great time to get involved!

Logging and more

A while ago, GLib gained a new facility for ‘structured logging’. At the same time, it also gained support for writing logs to the systemd journal. Clearly, logging in GLib got a bit more complicated, and it can be a bit confusing.

This article is an attempt to clarify things.

Structured or not

The traditional GLib logging facilities are the g_message(), g_debug(), etc macros, which eventually call the g_log() function, which then uses the log handler that has been set with g_log_set_handler() to do the actual writing. You can put any information you like into your logs, but it has to all be formatted as a single string, the message.

g_debug ("You have %d eggs", 12 + 2);

g_log (G_LOG_DOMAIN,
       G_LOG_LEVEL_DEBUG,
       "You have %d eggs", 12 + 2);

With the new structured logging facilities, you call g_log_structured(), which then uses a log writer function to do the writing. So far, this is very similar to the older logging facility. The advantage of structured logs is that you can put multiple fields into the log without resorting to formatting it all into a string. Instead, you pass an array of log fields, which are key-value pairs.

g_log_structured (G_LOG_DOMAIN,
                  G_LOG_LEVEL_DEBUG,
                  "CODE_FILE", "mysource.c",
                  "CODE_LINE", 312,
                  "MESSSAGE_ID", "06d4df59e6c24647bfe69d2c27ef0b4e",
                  "MESSAGE", "You have %d eggs", 12 + 2);

CODE_FILE, CODE_LINE and MESSAGE_ID here are just examples for “standard” fields. You can also invent your own fields. Note that you can still use printf-style formatting for the MESSAGE field.

So GLib has two separate logging facilities now. To make matters a bit more interesting, we allow you to redirect the g_message(), g_debug(), etc wrapper macros to use g_log_structured() instead of g_log() under the covers. To do so, define the G_LOG_USE_STRUCTURED macro before including glib.h.

Why is this useful? For one thing, it saves you the trouble of replacing all your g_debug() and still lets you take some advantage of the structured logging – when used in this fashion, the traditional macros use separate fields for the log domain, the code file and line and some other fields, which can be helpful for filtering and searching in the resulting logs, in particular with the systemd journal.

Another advantage is that you can use a single backend, the log writer function, to control where both old and new logging calls end up.

Where have all my logs gone ?

Structured logging is frequently associated with the systemd journal. So, it is not surprising that people expect the g_log_structured() output to go the journal. And that is a really useful thing for services, or when you are launching an application e.g. from a desktop icon. But if you run it from the terminal, you probably expect to see its output right there.

To satisfy these competing demands, the GLib default log writer function tries to be smart. If it detects that stderr is redirected to the journald socket, then it writes its structured output to the journal. Otherwise, it formats a message and writes it to stderr.

Both GNOME Shell and DBus arrange for stderr to be redirected to the journal when they start apps or services. A way to explicitly redirect stderr to the journal is to run your application under systemd-cat:

systemd-cat my-app-that-logs

If you are sure that you want your logs to always go to the journal, you can tell GLib to use a log writer that does that:

g_log_set_writer_func (g_log_writer_journald, NULL, NULL)

Beyond the default

Even though the log writer functions that GLib provides by default should serve many needs, you might need to write your own. In that case, GLib has a number of useful functions that can help you, such as g_log_writer_format_fields(), g_log_writer_is_journald() or g_log_writer_supports_color().

Happy logging!

References

Widget hierarchies in GTK+ 4.0

Today we’re going to have guest author Timm Bäder, maintainer of Corebird and a contributor of GTK+, talk about the changes in store for writing composite widgets in GTK+ 4.0.

(Note: Some of the information here is based on branches that have not been merged into master yet, but I’m confident that they will be in the near future)

In GTK+3, only GtkContainer subclasses can have child widgets. This makes a lot of sense for “public” container children like we know them, e.g. GtkBox — i.e. the developer can add, remove and reorder child widgets arbitrarily and the container just does layout.

However, there are more complicated widgets in GTK+3 which don’t inherit from GtkContainer, e.g. GtkSpinButton or GtkSwitch. These never have real GtkWidget children. Consider, for example, the two clickable areas in a GtkSpinButton. I’m not calling them “buttons” here for a reason, since in GTK+3, they are not actual GtkButton instances, as GtkSpinButton is not a GtkContainer. Instead, GtkSpinButton has to work around that fact, and create two GdkWindows for the up/down areas and then render two icons in there; care about hover and CSS states; various button up/down events; and the GdkWindow lifetime, etc. In order to work around the GtkContainer requirement, in GTK+3 we introduced gadgets (GtkCssGadget). On the styling side, a gadget corresponds to a CSS box and therefore represents one node in the CSS tree. On the widget side they were being used to have “widget-like”, CSS-stylable, children for non-container widgets.

GtkWidget Changes

Of course there were plenty of changes needed to support these use cases in GTK+4. I’m not going to list all of them here (in particular the more ugly ones like focus handling), but I think most of them are quire interesting and important for application developers and custom widget authors. Generally, we’re trying to get a away from special cases and go a more general way by re-using widgets wherever we can. So, instead of using a PangoLayout to display text, widgets should use a GtkLabel. If you have a clickable area with button-like semantics, try to use a GtkButton. If you want to lay out widgets in horizontal or vertical orientation, use a GtkBox. This way we have one widget tree that both input and rendering can operate on. In practice, this means mostly getting rid of all the gadgets used inside widgets, as well as standalone GtkCssNode instances.

Iterating over child widgets

While in GTK+3, every container had to implement GtkContainer::forall and one could easily iterate over all the child widgets using gtk_container_foreach() and gtk_container_forall(), in GTK+4, every GtkWidget can have child widgets, so every widget potentially has a list of children we need to draw, measure, size-allocate, etc. In the widget hierarchy, these children and sibling widgets can be accessed through:
  • gtk_widget_get_first_child()
  • gtk_widget_get_last_child()
  • gtk_widget_get_prev_sibling()
  • gtk_widget_get_next_sibling()
So, the easiest possible way of iterating over the child widgets of a given GtkWidget would be (in C):
GtkWidget *widget;
GtkWidget *child;
for (child = gtk_widget_get_first_child (widget);
     child != NULL;
     child = gtk_widget_get_next_sibling (child))
  {
    /* Do stuff with @child */
    g_assert (gtk_widget_get_parent (child) == widget);
  }

Adding widgets to a non-container parent

Every widget in GTK+ (both 3 and 4) saves a pointer to its parent widget. This parent can be set using gtk_widget_set_parent(), and all the GtkContainer::add implementations eventually had to use this function to set the parent of the given child widget.

In GTK+4, gtk_widget_set_parent() still works and adds the widget to the end of the child widget list of the parent. However, we clearly also want to manage the order of the child widgets, as well as where we add new children in the list, so we have:

  • gtk_widget_insert_before()
  • gtk_widget_insert_after()
to add new child widgets before or after a child widget that’s already in the parent’s list. These can also be used to reorder children child widgets by passing a child that already has the given parent set.
Since lots of widgets inside GTK+ currently use composite widget templates in XML form, GtkWidget now also has its very own GtkBuildable::add_child() implementation to support this use case. This is what e.g. GtkFileChooserWidget uses which is almost exclusively defined in XML.

Widget CSS names

Since we frequently need to use arbitrary CSS node names for arbitrary widgets, GtkWidget now has a construct-only property called GtkWidget:css-name that will be used as the css node name if specified. If not, the one passed to the widget’s gtk_widget_class_set_css_name() call will be used. If none of the 2 have been used, the CSS node will simply be called “widget”.

Examples of Converted Widgets

There are already a few widgets in current master (and side-branches) that were converted from using a variety of GtkCssGadgets, GdkWindows and PangoLayouts to using actual widgets. The end-goal being, of course, to re-use widgets as much as possible, therefore reducing code size and maintenance burden.

GtkSwitch

In GTK+3, GtkSwitch is a direct GtkWidget subclass (yay!) that uses a GdkWindow for the input (clicking on the switch will enable/disable it), one GtkCssGadget for the widget itself, two PangoLayouts for the ON/OFF text and another GtkCssGadget for the slider.
In GTK+ master, the switch still has its widget-level GtkCssGadget so it supports min-width/min-height CSS properties and CSS margins, but the slider gadget has been replaced by a GtkButton and the two PangoLayouts are replaced with GtkLabels. This way we can save roughly 300 lines in gtkswitch.c. Theoretically we also have more features and can e.g. use the limited support for the text-decoration CSS property that GtkLabel supports, but I’m just going to doubt that this is very useful.

GtkSpinButton

As noted earlier, GtkSpinButton could easily use actual GtkButtons for the up/down areas, and it does so in GTK+ master (which will become GTK+4 at some point). This gets rid of another 300 lines in gtkspinbutton.c. Through using GtkButtons, the old icon helper gadgets also become actual GtkImage instances. Unfortunately we have to implement some of the GtkGesture magic ourselves here since GtkSpinButton also supports middle and right clicks on its buttons while GtkButton::clicked only reacts to single, primary mouse button clicks.

GtkLevelBar

GtkLevelBar manages a set of blocks and assigns different style classes to them. In GTK+3, these blocks are all GtkCssGadget instances. All of them are “dumb” in the sense that they don’t do anything special — they are just CSS boxes and that’s it. That’s why converting it to use GtkWidgets for all blocks didn’t gain much of a reduction in file size.

 

GtkProgressBar

GtkProgressBar uses gadgets for the trough and the progress nodes. It also uses a PangoLayout to display the percentage or a user-given string.

In master, both trough and progress are widgets and the PangoLayout is a GtkLabel of course. Not having to listen to GtkWidget::style-changed (which gets done automatically for widgets) and not having to draw the PangoLayout ourselves (which the GtkLabel now takes care of) yields a nice code size saving of around 200 lines, however.

 

GtkExpander

GtkExpander is more complex than it looks like. In GTK+3, it consists of 2 GtkBoxGadgets (which is like a GtkBox, but not a widget…), one gadget for the arrow to the left of the title widget, the title widget and the actual content widget. In master, this is done using an actual GtkBox, and a GtkIcon (an internal widget) for the arrow. I’m not sure if this is the best way to express the GtkExpander functionality, e.g. we could also use a GtkButton for the arrow+title widget combination.
Since GtkBoxGadget is already almost a perfect GtkBox clone, the code savings here aren’t very interesting, but not having to listen to GtkWidget::direction-changed once again saves around 30 lines.

Accidental GtkBox & GtkButton subclasses

GTK+3 contains quit a lot of widgets that inherit from another widget for the sole purpose of looking and behaving like them. The problem here is that these widgets also inherit all the API of the parent class, which is only rarely wanted.
For GtkBox, almost all subclasses in GTK+3 are “accidental” in the sense that actually using them as a GtkBox doesn’t make any sense and people usually don’t do it, but they had to be GtkBox subclasses to satisfy GTK+’s GtkContainer requirement. One example of such a widget would be GtkFileChooserWidget. This is already one of the most complex widget to ever exist, but have you ever considered using gtk_container_add() or gtk_box_pack_{start,end}() to add widgets to it? It doesn’t make a lot of sense. It’s a closed entity with its own API. So, in GTK+4 it will be a direct GtkWidget subclass that contains a GtkBox. Or maybe not. That’s just an implementation detail you don’t have to care about. (Fun Fact on the side: GtkFileChooserButton is a GtkBox in GTK+3)
The same applies to GtkButton. In GTK+3, GtkButton has lots of subclasses that inherited all the GtkButton API without actually supporting it. What happens if you remove the child widget from a GtkLinkButton? What if you set the GtkButton:label property of a GtkFontButton? Again, these are closed entities that have their own API to set and get various data and change behavior and/or looks based on them, but that doesn’t mean they support all the GtkButton/GtkContainer shenanigans.

General Restructuring Rules and Future

For this refactoring work we try to keep the CSS node structure as it was in GTK+3, i.e. we try not to break the CSS node tests we currently have in testsuite/css/nodes.c.
A few of the more complex widgets inside GTK+ still heavily rely on gadgets and porting them away to use only actual widgets will be quite a lot of work. GtkRange is historically one of the most complex non-container widgets inside GTK+. It’s used both for scrollbars and scales so porting it to widgets might first need another round of refactoring.
Another interesting case is GtkNotebook, which combines gadget and widget usage. Here we could e.g. use a real GtkStack to switch between pages and effortlessly support page switching transitions.
Another exciting look into the future is of course Carlos’ wip/carlosg/event-delivery branch that gets rid of a ton of GdkWindow instances and makes widget input easier than ever before.

Versioning and long term stability promise in GTK+

The plans set out in this post supersede those that were publicised after the Toronto GTK+ hackfest in June 2016.

This month, the GTK+ team will publish the first in a series of long-term stable releases. This will make GTK+ more predictable and reliable, while not inhibiting future GTK+ improvements.

These plans are a result of discussions held with a variety of stakeholders since initial plans were made at the GTK+ hackfest in Toronto last June.

Background

GTK+ has followed a fairly straightforward versioning scheme since the 2.0 release in 2002:

  • major versions designate the general API version
  • minor versions designate development cycles (if odd) and stable cycles (if even)
  • micro versions designate bug fix updates

Any API introduced in GTK+ is guaranteed to exist until the next major version; API introduced during a development cycle is guaranteed to remain once the stable cycle begins. Stable cycles do not provide new features or new API.

This scheme served us well, but its problems have become increasingly clear during the 3.x series, especially when coupled with 6 month, time-based development cycles. During the 2.x cycle new features in GNOME applications were forced to appear in additional libraries because the toolkit was too complex or moving too slowly. Since 3.0, the development pace of GTK+ has been picking up. GTK+ has been placed front and center, with new widgets and new functionality being introduced every six months. In order to implement these new widgets and functionality, though, some of the toolkit’s internals have been in a state of flux.

From a GNOME perspective, GTK+ has been a reasonably stable, albeit moving target, because GNOME developers and GTK+ developers can share feedback and proposals and quickly keep up with internal changes. From the perspective of the community, on the other hand, following GTK+ development has been more painful. Application developers outside of the GNOME project have had a hard time finding out about what changes in the toolkit will impact their codebases. The GTK+ team has attempted to improve its communication channels, but blogging about changes during development cycles hasn’t been enough for many in the wider community of GTK+ users.

Long-term stable GTK+ releases

GTK+ has three primary stakeholders: application developers who want feature and API stable releases; desktop developers who want access to development versions of GTK+ in order to introduce new functionality at a rapid pace (this includes most of the GNOME project); and the GTK+ team itself, which needs the ability to iterate on the internals of the library during longer development cycles.

The introduction of long-term stable GTK+ releases is designed to ensure that GTK+ strikes a good balance between each of these audiences. In particular, application developers will have access to a stable platform which nonetheless provides access to new GTK+ features that have been developed during the 3.x series, such as CSS styling, touchscreen support, HiDPI displays support, Wayland support, new widgets, the GTK+ inspector, and more.

GTK+ will continue to publish major, minor and micro releases. New major versions will be released once new features have stabilised, which is expected to be roughly every 2-3 years. When bumping to a new major version deprecated API will be removed. After that, this API series will be considered stable. New minor releases may introduce new widgets, or update the implementation of windowing system protocols in the GDK backends, but no additional features or theme changes will be allowed. Whereas previously minor releases were published every six months, now they will be produced as and when necessary. We’ll also keep doing micro releases for bug fixes and security issues, for at least three years. Maintenance after this point may continue, depending on the amount of volunteer resources available. Distributors of operating systems with long term support release cycles that extend past three years may want to contact the GTK team to establish a policy for backports.

Updates within long-term stable series will be ABI stable. Alongside these stable series, GTK+ development will continue in semi-stable development series. These development releases will contain some API changes between minor versions, although changes will be limited wherever possible. This is the path that we expect GNOME applications to take, but other application developers may choose this option if they want access to the latest features, at the cost of some potential porting work for each minor release.

While the GTK+ team reserves the right to change API during development series, this does not mean that the whole GTK+ API will constantly break each release; only specific, and hopefully rarely used parts of the API may change, and if the changes are too extensive they will very likely be delayed to the next major development cycle. We’ll ensure that these changes are well-communicated in advance.

The new versioning scheme

The new GTK+ versioning scheme is a modification of the “semantic versioning” scheme that we have followed until now. Once a new major stable release has been published, the development cycle starts and we will:

  • update the pkg-config file to a new major version, to allow GNOME developers to target the new API during development
  • keep the existing major version at the same number
  • update the minor version to 90 to indicate a development release

For instance, after the 3.22.0 release and at the start of the new development cycle, the pkg-config file will be called gtk+-4.0 and the version in the configure.ac file will be set to 3.90.

Every six months a new even development version will be released, such as x.90, x.92, x.94, until the GTK+ team is confident that the new API and feature set are stable. Each of these minor versions will bump the soname of the shared library, to ensure that automated tools can pick up the eventual changes and notify distributors and maintainers. Once we reach the point where the API and feature set is stable enough for the wider community to use, we will release a new major version (x + 1).0 and declare the API stable.

Once this point-zero release has been made, a new stable branch will be created, and the master branch will be bumped to the next point-ninety release and begin the new development cycle. The point-ninenty releases will be parallel installable with the previous stable releases.

gtk-versioning-scheme

3.22 will be the last minor release of the 3.x series, with the new versioning scheme coming into effect with 3.90. The 3.22 release is irregular in the scheme, in that it is a long-term stable version but won’t receive further minor releases and doesn’t have a .0 version number. This is a necessary transition step.

What’s next

More details about these plans, including specifics for library developers and distribution packagers, will follow in subsequent blog posts. The GTK+ development blog will also continue to provide updates about technical changes in GTK+ itself, in order to provide information about which changes will arrive in each upcoming major release.

We’re excited about these plans and are hopeful that they will usher in a new era for GTK+, in which application authors can be more confident in our platform, while still allowing the rapid pace of development that we have seen during the 3.x series.

The plans set out in this post supersede those that were publicised after the Toronto GTK+ hackfest in June 2016.

Drawing in GTK+

The topic of how GTK+ draws the content of a window is a fairly complex one; it involves drilling down from GtkWidget, to GdkWindow, to Cairo, to the windowing system currently in use. This task can seem somewhat daunting, even for people that are familiar with the GTK+ API from an application development standpoint, so I decided to write down a quick introduction of how GTK+ draws, going from widgets, to windows, to surfaces, to native windowing resources.

How it starts

GTK+ always draws because something asked it to. This request may come from the windowing system — for instance, because the window manager presented your application window to the user, or because the user resized it — but more often it’ll come from a widget updating its contents. Let’s say, a progress bar going from 50% to 60%; or a label, changing its text; or a spinner, doing a new iteration. This request invalidates the backing GdkWindow of the widget — which usually it’s the GdkWindow of the top-level GtkWindow that contains the widget. Each invalidation carries with itself the region of the window to be invalidated (the “damage”), so that when we get to actually drawing, we know which parts of the window need to be updated, and we can avoid drawing outside of the damaged areas.

Race the clock

The first invalidation will start the “frame clock”; this clock is an object that keeps track of each phase inside a frame, like painting windows, laying out widgets, or processing the event queue. This allows GTK+ to be synchronized to things like the windowing system compositor, and to avoid performing unnecessary work that won’t be seen by the user — for instance, drawing something at 1000 frames per second when your display can only run at 60 Hz.

Once the clock reaches the “paint” phase, we process all the scheduled updates on a window; this will cause a GDK_EXPOSE event to be emitted. The GDK_EXPOSE event contains the GdkWindow that needs to be updated, and the union of all the invalidated areas. It’s important to note that, by and large, only top level windows will receive a GDK_EXPOSE event; for historical reasons, though, some widgets may apply a particular event mask that will cause GDK_EXPOSE events to be delivered to them as well. You should not write code that depends on that, and if you have legacy code ported from older versions of GTK+ 2.x you should really consider dropping the GDK_EXPOSURE_MASK from the event mask.

Rendering

GTK+ takes the window and invalidated region out of the GDK_EXPOSE event and figures out which top level widget they belong to. Once that’s found, GTK+ will begin the actual rendering process. First of all, GTK+ will ask the GdkWindow to create a buffer where to draw the contents of the window; the buffer is going to be clipped to the region that needs to be drawn, and will be cleared with the background color of the window. GDK will create a “drawing context” — a transient object that keeps track of things like OpenGL and Cairo drawing. Then, GTK+ will ask the widget to draw itself using a Cairo context. For leaf widgets this means drawing themeselves on that context; for container widgets, this additionally means recursing through all their children. At the end of this process, GTK+ will end the frame by telling GDK to take the buffer that contains all the rendered widgets and use it to replace the current contents of the window. GDK will then ask the windowing system to present the window to the user, whenever it’s more appropriate.

Changing History

The process outlined above has various caveats, and the code that deals with invalidation and validation of windows inside GDK is fairly complex; it also has a long history, which means that its API is littered by the headstones of ages past.

Before GTK+ 3.0, for instance, you were supposed to handle the “expose” events yourself, and create a Cairo context to draw on a widget by using gdk_cairo_create(); this has long since been unnecessary, because the GtkWidget::draw virtual function already provides us with a Cairo context with which to draw. The gdk_cairo_create() function, though, has been deprecated in GTK+ 3.22, and should not be used in newly written code; if you need a Cairo context you should create a similar Cairo surface, call cairo_create() on it, and then use the surface as the source for the Cairo context that GTK+ provides to you when drawing a widget. On the other hand, if you were using gdk_cairo_create() to draw on a top-level, native GdkWindow in response to a GDK_EXPOSE event then you should use the newly added gdk_window_begin_draw_frame(), gdk_window_end_draw_frame(), and GdkDrawingContext API instead.

Shaping Future

The internals of the drawing code in GTK+ have been progressively updated over the years, to cope with things like new windowing systems, as well as other rendering API. It’s fairly certain that they will change again, especially when it comes to improving the rendering performance. Many of the changes that may seem arbitrary are, in reality, stepping stones towards reducing the time spent inside the toolkit in each frame, and leave more time to the application logic.

Controlling content sizes in GtkScrolledWindow

The GtkScrolledWindow widget is an old friend of Gtk+ application developers; its purpose is to allow big widgets to fit into small spaces through the use of scroll bars.

GtkScrolledWindow Example
A vertical GtkScrolledWindow in action

Since Gtk+ 3.0, GtkScrolledWindow has the ability to set the minimum content sizes (both width and height) through the GtkScrolledWindow:min-content-width and GtkScrolledWindow:min-content-height properties, and their related functions.

Starting from the next stable release, Gtk+ will also provide the maximum size counterparts of those properties.

What Do They Do?

The minimum sizes properties, as the name implies, define the minimum size, be it width or height, that the scrollable area will have – even if its child does not completely fill the available space.

scrolledwindow min-content-height
The scrolled window is allocated even when child widgets don’t fill the available space.

The maximum content sizes, on the other hand, define how much the scrollable area is allowed to grow before its contents will starts scrolling.

Lets see it in action:

scroll animation
Example demonstrating minimum and maximum content sizes. The scrolled window is never smaller than 110px, and never taller than 250px.
Where & How to Use Them

You want to use the new properties whenever you want to limit the size of the scrollable area. For example, GtkPopover always shrinks its children widgets to their minimum sizes. The following section exemplifies how to make the content grow to at most 300px, both width and height wise:

<template>
  <object class="GtkPopover">
    <child>
      <object class="GtkScrolledWindow">
        <property name="visible">True</property>
        <property name="max-content-width">300</property>
        <property name="max-content-height">300</property>
      </object>
    </child>
  </object>
</template>

Alternatively, you can call gtk_scrolled_window_set_max_content_width() and gtk_scrolled_window_set_max_content_height() if you want to achieve the same thing programmatically.

Cursors in GTK+

History

Cursors have traditionally been a big mess in Linux.

The X11 cursor font has been passed down to us from times immemorial, and given us gems such as gumby () or trek (). Unfortunately for us, this state of affairs was frozen into the GDK api with the GdkCursorType enumeration and the gdk_cursor_new() function.

Later on, the Xcursor library came around. It invented its own image format for storing cursors and brought us cursor themes, but didn’t do anything to answer the question “What cursors should my cursor theme provide ?”

Since there is no official list of recommended cursor names, cursor themes frequently provide all the variants of cursor names that have been spotted in the wild. As an example, here is the list of cursors included in the oxygen cursor theme. If you are wondering, the hex strings in this list are a clever trick of Xcursor to retrofit themed cursors underneath core X11 applications that use cursors from the cursor font mentioned above.

CSS to the rescue

About a year ago, we decided to finally improve the GTK+ cursor story. Thankfully, the CSS3 spec contains a decent list of cursor names that can be reasonably expected to be available across platforms.

Standard cursorsSince the GdkCursorType enumeration contains too much nonsense and is not easily extensible, we decided to make gdk_cursor_new_from_name() the recommended API for obtaining cursors. The documentation for this function now lists the CSS cursor names (follow the link above to see it), and the cursor handling code in the various GDK backends tries hard to give you meaningful cursors for all of these names.

On some platforms (such as X11 with a random cursor theme), we may have to fall back to the default arrow cursor if a certain cursor is not present in the theme. As part of this general overhaul of the cursor code, the Windows backend grew support for cursor themes.

GTK+ itself is now using gdk_cursor_new_from_name() exclusively, with the standard cursor names. And gtk3-demo includes a demo that shows all the standard cursors and lets you try them out. The screenshot above shows it.

The changes described here went into GTK+ 3.18, which was released about 9 months ago.

What you should do in your application

Most likely, you don’t have to do anything! GTK+ widgets use suitable cursors all by themselves, and you can benefit from that without any extra work.

If your application is creating its own cursors for whatever reason, you should check carefully if one of the standard cursors shown above is suitable for you. Using a standard cursor ensures that you will get a suitable cursor regardless of the platform your application is running on and regardless of the cursor theme the user has chosen.

Please use gdk_cursor_new_from_name() to generate your themed cursor, since this is now the preferred API for this task.