Improving theme support for GTK 2 apps


#1

When working on the gtk-common-themes snap, we focused on GTK 3 themes because they represented what most new apps were using, and had the benefit of being safe declarative CSS.

However, there are still a fair number of apps relying on GTK 2 theming:

  • Electron 1.x apps.
  • Qt 4 apps
  • GTK 2 apps

So I’ve started looking at coming up with a solution for these apps too. This is taking the form of a new gtk2-common-themes snap, which is being maintained here:

https://gitlab.gnome.org/Community/Ubuntu/gtk2-common-themes

I’ve chosen not to integrate this into gtk-common-themes mainly because the native code theme engines mean it can’t be part of an architecture independent snap. As we’re not including icon themes in this snap, the snap is fairly slim, currently coming in at 280K on amd64.

We haven’t published the snap to the store yet but it should build fine with snapcraft cleanbuild.

Using the snap

Like with gtk-common-themes, an application snap needs some modifications. I realise the boilerplate is undesirable, and we plan to get rid of it when migrating snapcraft-desktop-helpers over to the new Snapcraft templates feature.

First up is the plug definitions:

plugs:
  gtk-2-engines:
    interface: content
    target: $SNAP/lib/gtk-2.0
    default-provider: gtk2-common-themes:gtk-2-engines
  gtk-2-themes:
    interface: content
    target: $SNAP/share/themes
    default-provider: gtk2-common-themes:gtk-2-themes
  icon-themes:
    interface: content
    target: $SNAP/share/icons
    default-provider: gtk-common-themes:icon-themes

There are two plugs for GTK 2: one for the theme engines, and the other for the theme definitions. And lastly we’re plugging icon themes from the main gtk-common-themes snap: this is needed to provide both icons and cursors.

The snap will also need to be modified to create the mount point directories:

  • lib/gtk-2.0
  • share/themes
  • share/icons

And in order to let GTK find the theme data, we need to set some environment variables:

environment:
  GTK_PATH: $SNAP/lib/gtk-2.0
  GTK_DATA_PREFIX: $SNAP
  XDG_DATA_DIRS: $SNAP/share:$XDG_DATA_DIRS

The XDG_DATA_DIRS variable allows the app to find icons/cursors. The GTK_DATA_PREFIX variable makes the theme definitions available (unfortunately GTK 2 doesn’t respect XDG_DATA_DIRS for this). The GTK_PATH variable is needed to find the engines.

Unfortunately, getting GTK_PATH to work correctly depends on a pending change to snapcraft-desktop-helpers:

Third party themes

I’ve configured the gtk-2-themes slot so that it will be possible for third party theme snaps to be plugged simultaneously with gtk2-common-themes. This is still mostly experimental though, since we still don’t have a facility to locate, install, and plug extra themes.

Unfortunately the layout of the GTK modules directory isn’t amenable to this, so only one provider will be able to be plugged at once. Given that theme engines are shared libraries that get dynamically linked into the application, I’m not sure it is a bad thing if we restrict engines to those provided by gtk2-common-themes.

At present, I’ve included just the engines needed by the common default themes:

  • adwaita
  • hcengine
  • murrine
  • pixmap

I’d be open to adding more on request.

What about base snaps?

As GTK 2 engines are native code, an open question is how compatible they are over different base snaps. GTK 2 itself is pretty much static these days, with the occasional 2.24.x point release for bug fixes but no new forward/backward incompatible changes.

The engines are C and only link to GTK libraries plus libc/libpthread, so I think there is a good chance that the libraries will work on core18, or anything else with a glibc at least as new as xenial. But perhaps it would be worth playing it safe, and have different -engines content slots for each base. I’m not sure what the best option is at present.


Request: Auto-connection of gtk-2-themes and gtk-2-engines interfaces
Help needed - app (qt4 + python-gtk2) in snap
#2

Yes, this sounds good. Thank you :slight_smile:


#3

We’ve now got demo snaps up on the store to test this, which can be installed with:

snap install --edge gtk2-common-themes
snap install --edge gtk2-demo
snap connect gtk2-demo:gtk-2-engines gtk2-common-themes:gtk-2-engines
snap connect gtk2-demo:gtk-2-themes gtk2-common-themes:gtk-2-themes

(manual connection of these two interfaces is necessary because we don’t have any store assertions to auto-connect them yet).

Once installed run gtk2-demo, which should display with your curren theme (provided it is one of the ones included in gtk2-common-themes) rather than the Raleigh default. For example, it shows with the Yaru theme on my Ubuntu 18.10 desktop:

The source for the demo snap can be found here:

The third part in the snapcraft.yaml will be able to go away once the snapcraft-desktop-helpers PR is merged. Everything else is as described in the initial post.


#4

And for comparison, here’s the same demo snap with all the content interfaces disconnected:

The theme has reverted due to the lack of theme data from gtk2-common-themes, and the stock icons have reverted to the compiled in defaults due to lack of icons from gtk-common-themes.


#5

After some discussions with @didrocks on IRC, I think it makes sense to change how we organise these snaps.

I had originally thought to keep this entirely separate from gtk-common-themes due to the use of native code. But the end result needed two content interface slots: one for theme data, and one for engines. There is no reason that these need to be provided by the same snap though: we could put the theme data in the architecture independent gtk-common-engines and ship only the engines separately.

In fact this would likely simplify things: the snapcraft.yaml files for the two theme snaps look remarkably similar since they are built from mostly the same sources: just one deletes the GTK 2 theme data and the other deletes the GTK 3 theme data. Shipping both together would also guarantee that we’re distributing a consistent set of themes.

This shouldn’t make much of a difference for application snaps: just a small alteration to the default-provider: stanzas in the plug definitions.


#6

I’m playing with this with a view to snapping a gtk2 app.

This line fails.

$ snap connect gtk2-demo:gtk-2-themes gtk2-common-themes:gtk-2-themes
error: snap "gtk2-common-themes" has no slot named "gtk-2-themes"
$ snap interfaces gtk2-common-themes
Slot                              Plug
gtk2-common-themes:gtk-2-engines  gtk2-demo

Is this related to your most recent comment? What’s the way to do this now?


#7

Sorry about the delay: as discussed earlier in the thread, I decided to move the theme data between snaps. The updated sample snaps are now published on the edge channel.

So you’ll need the edge channel of gtk-common-themes, and then run:

snap connect gtk2-demo:gtk-2-themes gtk-common-themes:gtk-2-themes

The gtk-2-engines slot is still on the gtk2-common-themes snap.