Insights from an extension author

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:

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.

2 Likes

We did have some discussions about putting in common patterns like for instance panels so that they are all done in one way. I don’t have much more to add, but someone like @andyholmes or @rockon999 would probably have a better answer for you.

Thanks for writing about your experience, it’s very helpful to get feedback like this.

The first thing I want to suggest to you, and everyone struggling with extension development, is to reach out for help! You can ask here on discourse, the extension channel on matrix or IRC and we even have tags on StackOverflow.

While it would be great to have GJS examples for things like DBus (here’s a long one), these can take a very long time to write and it’s unclear where to spend our limited resources. A fact we’re coming to terms with is that for a lot of extension authors, like yourself, this is the first exposure you’ve had to programming, Shell extensions, JavaScript, GJS, GNOME APIs and GObject.

That’s a lot of ground to cover and until very recently we’ve had no place to centralize GJS documentation or tutorials for these. It seems like gjs.guide (first started by @rockon999 as a GSoC project) may become our central portal for this, but I can’t give an official answer to that.

If you ever find documentation missing from gjs-docs.gnome.org you can open an issue in the repository where that’s hosted. We have some ongoing efforts to generate documentation for GNOME Shell’s JavaScript, but that may not be ready for a little bit.

Thanks again for your feedback!

1 Like

I remember that we have 2 technical writers. Can they assist us here ?

If they know GJS and these topics, that would be a great contribution. To be clear though, many of these topics really don’t have anything to do with GJS or extensions.

We might have to accept that a lot of these topics are just out of scope for the extensions documentation, and that developers should just be reviewing the standard library documentation.

1 Like

The Overview of the GNOME Platform answers two questions:

3 Likes

I think though a common set of computer language neutral documentation of the stack would be good - since it seems folks who write extensions - that would be their first introduction to the GNOME platform - meaning their entry into the GNOME ecosystem is not generally through C and so they are coming from somewhere else and do not generally find themselves on developer.gnome.org.

If we create a neutral language set of documentation of our platforms it can be copied to website that host bindings other than C.

1 Like

@rmnvgr Geoclue 2.0 has been added and will be up on gjs-docs.gnome.org once the service is rebuilt.

1 Like

Thank you all for your answers! Here’s some reactions:

That’s true, that’s a fault on my side. However, I find more rewarding for me to discover how to do things by myself! So far I’ve never been in a situation where I’ve been blocked and couldn’t implement a feature I had in mind, I always managed to find a way. It may not be the best way though.

I must admit I didn’t come across this page, my entry point to GNOME technologies was through the extensions writing wiki page. It would have been helpful, so it may be worth pointing to it from this page?

Great! However, how can I know if something is missing from the documentation? That is, how did GNOME Weather developers knew that they could import Geoclue directly?

Another thing that came to my mind: it might be helpful to have email notifications when a user posts a bug report on extensions.gnome.org. Right now I have to regularly check my extension’s page to make sure everything is fine, as some users post there and not on my issue tracker on Gitlab.

That’s totally your choice, but whether you’re reading documentation or another person’s code, you are getting help from someone. You may also discover there are a lot of other extension authors that can benefit from your help :slight_smile:

Any library that advertises GObject-Introspection bindings can be use in JavaScript, Python or any other language with support for GObject-Introspection. You will find you have many of these already installed in /usr/share/gir-1.0/.

So I guess the answer is that you typically go looking for a library that has functionality to solve a problem you have. Sometimes there’s more than one answer, so you have to either ask someone or try them all the find the best fit.

If a user uses the “Bug Report” button you will be sent an e-mail, but if they simply add a comment you won’t. There’s a rewrite of the website in progress, but since it’s just been one person maintaining the whole site it might take some time.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.