I’ve been thinking about this a bit. I think we’ve categorically ruled out exposing the system themes directly, since that poses both a backward and forward compatibility problem. So we want some way for a snapped application to access a theme provided by a second snap.
We probably don’t want to model this relationship on the existing interface connection semantics because on multi-user systems different users could potentially have different themes. Rather, we probably want the app to say “I’m interested in gtk-3.20 themes”, and then be given access to all installed gtk-3.20 theme snaps. It’s also worth noting that an app will probably be interested in multiple types of theme resources. A typical GNOME app will want:
the GTK theme
a matching icon theme
a matching mouse cursor theme
So I think we probably want something similar to Flatpak’s extension point idea, which is kind of like our content interface, but in reverse. Something like the following:
application snap A says it is interested in the theme.gtk-3.20 extension point, and wants it exposed at $SNAP/themes
theme snaps B and C say they provide the theme.gtk-3.20 extension point at $SNAP/gtktheme
snap-confine configures A’s mount namespace by doing the equivalent of:
mount -t tmpfs none /snap/A/current/themes
mkdir /snap/A/current/gtktheme/B
mount --bind /snap/B/current/gtktheme /snap/A/current/themes/B
mkdir /snap/A/current/gtktheme/C
mount --bind /snap/C/current/gtktheme /snap/A/current/themes/C
Whenever new snaps providing the extension point are installed, A’s mount namespace is updated.
We could then rely on something like the desktop-launch script to update $XDG_DATA_DIRS to make themes under these extension point directories discoverable.
In the above example, I’m using the snap package names under the extension point. This would make it difficult to show up with a name like Ambiance, since we don’t allow upper case in package names. It would also be a problem if we wanted separate snaps providing the theme.gtk-2.0 and theme.gtk-3.20 extension points for that theme name.
Flatpak’s solution to this is to make the names of extension points the prefix of the names of the packages that provide that extension, and then use the suffix as the file system path. That guarantees that there isn’t any conflicts by passing the job on to the person managing the repo/store. It’s not clear that is the right option for us with our flat package naming scheme though.
Icon themes will probably add some more complication, since they have the concept of inheritance. For example, the default ubuntu-mono-dark theme says it inherits from Humanity-Dark, Adwaita, and hicolor. Would we expect the snapped version of the theme to be flattened to get rid of the dependencies, or do we need to expose that dependency metadata so that whatever component installs the theme snaps can follow them?
I’m sure there is some more to consider, but hopefully this is enough to start with.
For X apps, this is done via the xsettings protocol, which should work fine via the x11 interface.
For Wayland apps, GTK uses GSettings/dconf to detect the current theme. This can be accessed using the gsettings interface, but that gives read/write access to all the users’ settings. Ideally this wouldn’t be necessary.
security:
Some theme formats, like gtk-2.0 may require executable code, which will be running in the application’s security context. So if we do have some kind of extension point API, it might require assertions to allow a snap to provide an extension point.
Other theme formats like GTK 3 themes and icon themes should be safe though, so I imagine we could have automatic approval of most themes. Possibly in conjunction with some automated sanity checks to make sure the files being exported are of the expected type (e.g. CSS files for GTK themes, image files for icon themes, etc).
developer experience
Ideally, developers shouldn’t need to include much boiler plate to support standard features. One possible way to do this would be to add plugs and slots (and extension points?) sections to parts in the snapcraft.yaml file. Snapcraft would then copy these to the top level if there is nothing with the same name.
This way, the desktop-gnome-platform cloud part could ship the boilerplate so all snaps using it pick it up.
Isn’t that pretty much what content interfaces already do today? Their point is precisely to mount content across snaps, in a pretty similar way to what was described there.
The in-progress layouts feature will allow arbitrary remappings, but we don’t even have to wait for that. The content interface can already land the theme in a place the application can look for.
For details on which names to pick, the guidelines discussed around the gnome platform content interface seem to apply here as well, as there’s an implicit API that needs to be agreed on for the theme to work. I suggest reading the topic from the bottom up.
Then, we need to agree on some sort of convention so that something in the system, potentially snapd itself, can find out which theme would best match what’s available in the system, and then pull the snap in together with something that might make good use of it.
I won’t be around in the next couple of days, but would be happy to have a call or a more extensive conversation here next week, so we can evolve those ideas.
Lets say that we have a gtktheme snap containing the user’s selected theme exported via content interface, and a gtkapp snap that plugs into it. Now lets add a second user to the system, who goes ahead and changes to a different theme. What now?
Do we disconnect and reconnect the plugs on all installed apps each time someone logs in? (what about multi-seat when multiple users are logged in simultaneously?)
Do we require all available themes to be packaged in a single snap? (how do theme authors get their theme added to this snap?)
The extension point idea effectively changes from a many-to-one connection strategy to many-to-many. So all snaps that are interested in GTK themes would see data from all installed snaps that provide GTK themes.
This could potentially be modelled as a many-to-one relationship plus a one-to-many one through an intermediate snap (e.g. the GNOME platform snap), with the help of some kind of “reverse content interface” where the plug snaps provide content to the slot snap, but that has its own problems:
snaps have independent mount namespaces, so creating mounts within the GNOME platform snap won’t make content visible to other snaps that plug into it. We’d need some way to transmit information about the platform’s theme plugs to its app plugs in order to work out what to mount.
it introduces dependencies between snap connections. Plugging a new theme into the platform snap would require refreshing the connections of all the app snaps.
At that point, it’s not clear it is worth trying to squash this into the existing constraints.
There’s no designed constraint in the system that makes plugs/slots be one-to-many reationships and not many-to-many. We’ll likely hit bugs while trying to make that work since it was pretty much never exercised, but while designing the work with @zyga-snapd we at least consciously attempted to avoid closing major doors that would make that impossible.
So if you’d like to explore that, certainly sounds good to me.
As we discussed, this sounds like a good direction.
As a minor note, the syntax proposed is not just an extension to the current syntax. We have a bug today in the way that the content interface handles lists of paths with multiple entries, so that new syntax allows us to obsolete the old syntax in a backwards compatible way and fix the behavior, so that’d be a positive outcome even despite the theme-specific details.
I’ve split the content interface changes discussion into its own topic so we can discuss and track that work without polluting this one with issues not related to themes.
Due to API issues. Snaps work in a non-trivial number of Linux distributions, and each one of those distributions evolve at their own pace, and soon we’ll be supporting dozens of versions of dozens of distributions. Applications will depend on different versions of the graphic stack libraries, and those will support different versions of the theme API. It must all continue working over time.
The end goal of the on going work is that when someone installs a snap that supports themeing, the application will run with the right theme for the local system transparently, without any need for manual user actions.
The details are still being worked out, but the basic idea is that themes will be packed in snaps, and the correct snap will be selected based on the theme name. So it will be possible to select the proper theme even in those cases, where there’s a cross over between a desktop based on one toolkit is running an application from a different toolkit.