Versioning and long term stability promise in GTK+

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.

This Week in GTK+ – 1

What’s up with GTK+ for the week of 9 May 2016 – 15 May 2016

In this last week, GTK+ has seen 51 commits, with 5375 lines added and 4970 lines removed.

Planning and Status

The 3.22 development cycle is picking up pace while we go through the items of the Roadmap.

Notable changes
  • Olivier Fourdan fixed scroll event handling in GtkMenu, to ensure that it behaves consistently on X11 and Wayland with regards to smooth and discrete scrolling
  • The list of available protocols in the Connect to Server help popover is now populated using the list of supported schemes in GVFS, thanks to Georges Basile Stavracas Neto
  • Benjamin Otte has pushed a commit that lets GtkWidget emit the style-updated signal on unrealized widgets instead of delaying until realization; the original behavior was the result of an older optimization to avoid too many invalidations during construction, but the style system has improved over the years.
  • Timm Bäder pushed various clean up commits over various widgets, like GtkListBox, GtkStack, and GtkToolbar.
Bugs fixed
  • Bug 766166 key bindings in gtk.css are ignored
  • Bug 766207 Fix build on pre-C99 compilers
  • Bug 765939 [Wayland] very slow scrolling in GtkMenu using the touchpad
  • Bug 756570 gtkplacesview no longer provides guidance on address formats
  • Bug 766120 Scale draw_value() align changed from centre/right (H/V) to left, causing significant visual regression
  • Bug 766233 Crash when server does not support XI2
  • Bug 766175 Translation of quotes may misinterpreted by GTK sidebar
  • Bug 765700 GtkPaned use causes “How does the code know the size to allocate?”
  • Bug 682080 Gtk:ERROR:gtktoolbar.c:2271:logical_to_physical: assertion failed: (logical == 0)
  • Bug 766458 widget: fix GtkLabelAccessible NULL links.
Get Involved

Interested in working on GTK+? Look at the list of bugs for newcomers and join the IRC channel #gtk+ on irc.gnome.org.