Hello,
Following this blog poston t he GNOME Extensions Rebooted initiative, I’d like to expose some of my workflow and the challenges I encounter while developing the Night Theme Switcher extension. This extension allows the user to automatically switch its GTK theme, Shell theme, icon theme, cursor theme, wallpapers and to run commands at sunrise and sunset. It reacts to Night Light, or uses location to calculate times, or the user can set a custom schedule, or add a button to switch whenever they want.
To start with some background, I am not a developer. I mainly work in artistic fields, and chose GNOME because it gets out of my way when I need to focus on a project. However, I learnt some programming by myself, mainly Javascript and Python, for my websites and to help me automate some tasks. As such, I don’t have a professional experience in programming, so my workflow and my design patterns may be flawed, I do what works best for me.
First steps
I started working on the extension almost one year ago. The initial goal was simple: switch the GTK theme to dark when Night Light turns on.
I used the Writing page of the GNOME wiki extensively as a reference to get started. As I didn’t know how to accomplish what I wanted, I looked at the source code of another program I knew did that: GNOME Builder.
The first version of the extension is basically a mix of a port of Builder C code to Javascript and the code of several other extensions that allowed to switch themes.
It worked for me, so I uploaded it to extensions.gnome.org in case it would be useful to anyone else.
Adding more functionalities
As time went by, users of the extension asked for more: switching Shell theme, remove the need to have Night Light enabled… At first I wrote another extension, but it quickly became a burden to maintain several extensions and synchronize changes between them, so I began modularising the code and eventually merge everything back into a single extension.
Some of these feature requests were a challenge, so I spent countless hours searching the GJS documentation and other projects’ source code to find a way of doing what I wanted.
For example, I recently added an item to the system menu to switch the theme variants. I hadn’t the slightest idea of how to do that, so I browsed GNOME Shell source code to find the appropriate methods, and compared with how other extensions had done.
Improving the code
I’d say most of the code improvements happened by chance, when I found an easier way of doing while looking at other projects’ source code. I’ll take one example for this section: my geolocation module.
The first implementation was, as with Builder code, a port of the GNOME Settings Daemon Night Light C code, with a big difference: they use GeoClue Simple, but when I looked at the official GeoClue documentation, it didn’t make any mention of it, so I used GeoClue the hard way with multiple DBus calls.
At the time I also didn’t know that I could get the interface XML with D-Feet and the Introspect()
method of the org.freedesktop.DBus.Introspectable
interface (and to be honest I didn’t have a clear idea of what a DBus interface really was), so I spent a lot of time searching for all over source codes. In the end I think I took it from another project that used GeoClue.
A few weeks ago, a user had an issue related to GeoClue. I took this opportunity to look at the code again. After some searching around, I found out that GNOME Weather also used GJS (and I learned that you could use GJS for desktop applications), so I looked at how they implemented the location. And i was surprised to discovered that they used const Geoclue = imports.gi.Geoclue;
. Which apparently allows using GeoClue Simple.
Unless I’m mistaken, this appears nowhere in the GJS documentation. But as I had the GeoClue Simple documentation, I could now write a much simpler code to get the location.
Most of my improvements to the code follow this pattern: first implementing it following the documentation, and discovering later while browsing other projects’ source code that there’s a much simpler way of doing it.
Bits of workflow
I use npm
to manage the packages I use in the project:
- AVA allows me to run some unit tests for all the theme variants, to make sure there’s no conflicts and that I cover all cases.
- ESLint to enforce the styling, I use GNOME Shell’s rules.
I also use a Makefile for often run actions: building the extension, running tests, updating the POT and PO files.
I have set up a Gitlab CI with a node container for the tests, and a Fedora container for building the extension. I wish there was a GNOME image so that I wouldn’t have to install GNOME Shell and all of its dependencies just to run gnome-extensions pack
.
While developing, I use VMs in GNOME Boxes: usually the latest Fedora under X11 (to restart the session without having to logout), an old Ubuntu 18.04 for GNOME 3.28 support, and a nightly Fedora to test for the next GNOME release.
What would have helped me
- I don’t think GNOME can do a lot about this, but some sort of convention for theme variants would be welcome. As you can see from the variant detection code, theme authors can get very creative when it comes to not naming their theme variants with simple names. I wish they’d all use a well-defined pattern like
<theme name>(-<size>)?(-<color>)?(-<light|dark>)?
. - A kind of simple API for basic functionalities: adding buttons to the Shell, adding a menu item to the system menu, sending notifications.
- A page explaining in simple terms what are GLib, Gio, Gtk, Gdk, St, etc., and how they relate to each other. It may be obvious for a seasoned GNOME developer, but for a hobbyist like me, they almost sound the same and I’m still not sure if I understand everything.
- Examples of common patterns: connecting to DBus, reading/writing properties and listening to signals, getting geolocation, adding interface elements, correctly using gettext for the localization, importing Glade files… Some of these examples already exist in various places, so it might just be a matter of centralizing them. I recently stumbled upon gjs.guide, which I didn’t know existed two weeks ago.
So here you are, a little insight at how I’m working on the extension. I’d like to thank the GNOME developers for empowering regular users like me. It’s not just the ability to tweak the Shell, it’s also about getting to know how everything works: I learned a lot about the inner workings of my system.