Updating GNOME Shell Extensions To Work With GNOME 3.2

This particular post will contain the knowledge that I accumulate of the next few weeks about updating GNOME 3.0 Shell extensions so that they work with the GNOME 3.2 Shell. It will be a living document for a period of about 2 months so check back frequently for updates. When the GNOME 3.4 Shell is released, it will be covered in a separate post.

As you are probably aware, GNOME Shell comes with an interactive GNOME Shell extension creator called gnome-shell-extension-tool. The utility is actually a simple Python script. It generates a simple Hello World extension using:

gnome-shell-extension-tool --create-extension

After asking you 3 questions, the utility generates three files which are placed in $HOME/.local/share/gnome-shell/extensions/<EXTENSION_UUID>/.

Thanks to the seemingly tireless Jasper St. Pierre, here is a slightly more advanced version of a GNOME 3.0 HelloWorld Shell extension which was backported from the code generated by the same utility on Fedora 16 beta.

This extension simply adds a button to the right hand side of the (top) panel and displays a Hello World message for a number of seconds. I am going to assume that you are familiar with JavaScript and GNOME Shell extensions in general if you are reading this post – so I am not going to explain the code.

In GNOME 3.0, all Shell extensions are enabled by default unless the Shell extension is in a blacklist, i.e. org.gnome.shell.disabled-extensions. Per-user and systemwide Shell extensions can be disabled with this GSettings key. Thus, this Shell extension is enabled by default and loaded when you restart the Shell using Alt+F2 R, gnome-shell –replace or some other means.

To gain an understanding of what changes may be required in a 3.0 Shell extension in order for the extension to work in GNOME 3.2, we examine the code generated by this utility for a 3.2 Shell extension. By the way, with Fedora 16 Beta you may get the following error when you run this utility:

As you can see it is quite different than the backported GNOME 3.0 HelloWorld Shell extension. The basic functionality of the Shell extension is the same. There is no longer a main function; it has been replaced by init. Also two functions or methods named enable and disable are mandatory. More about these later.

In GNOME 3.2, a Shell extension is not available to you by default when the Shell is restarted because, in GNOME 3.2, Shell extensions are no longer enabled by default. The Shell extension is loaded but not enabled. Looking Glass will show the Shell extension in the Extensions pane but it will display as disabled. Any errors in the Shell extension code will be displayed in the Errors pane.

To become enabled, a Shell extension must be specifically added to a whitelist, i.e. the GSettings org.gnome.shell.enabled-extensions key. Then the enable method or function in the Shell extension must be called. Typically this automatically happens – more about this later.

Suppose a Shell extension is named example32@example.com. One way to enable the Shell extension is to use the gsettings utility:

A copy of this new version of gnome-shell-extension-tool is available here. Note that it does not check the validity of extension names!

By the way, while it is not specifically stated in the GNOME 3.2 Release Notes, from looking at the GNOME 3.2 Shell source code, it would appear that the org.gnome.shell.disabled-extensions key is no longer used to blacklist Shell extensions.

If you compare the 3.0 and 3.2 versions of the Shell extension code, the main function in 3.0 is replaced by three functions in 3.2, i.e. init, enable and disable. The init function is mandatory just like main was in 3.0. It does everything the main function usually does, except it cannot change any Shell state. Its purpose is to setup whatever needs to be setup, such as labels, text, icons or actors, prior to enabling the extension. It is only called once per shell session. The enable and disable functions are mandatory and are intended to be used to make a Shell extension’s UI visible (enable) or invisible (disable) to the user.

Note that while enable and disable can be empty functions and init can contain the code to enable the UI, it goes against what the GNOME Shell developers are trying to achieve, i.e. provide a way to enable or disable a Shell extension without having to reload the GNOME Shell. Remember, in GNOME Shell 3.0, every time you wished to do something with an extension you had to reload the GNOME Shell.

This design is not enforced; it cannot be. All the Shell loadExtension function, which was extensively modified in 3.2 (see extensionSystem.js), can do is to check for the existence of init, enable and disable. This is all the Shell can do to support the intent of the new design without extensive code being added to the Shell. It is up to writers of Shell extensions to comply with the spirit and intent of the design. When the proposed extensions.gnome.org materializes, a tool could probably be written to check that enable and disable actually enable or disable the Shell extension UI as part of the acceptable criteria for repository.

The Shell listens for a GSettingschanged signal that the org.gnome.shell.enabled_extensions key has changed and invokes onEnabledExtensionsChanged which gets the list of enabled Shell extensions, compares it to the current in-memory list and enables or disables Shell extensions accordingly by calling init followed by enable for any new Shell extension added to the whitelist, enable for Shell extensions previously initialized but disabled, and disable for Shell extensions that are no longer on the whitelist.

Changes to the Shell UI by a Shell extension are controlled by whatever code is in the Shell extension’s enable and disable methods or functions. Typically, the Shell will call init, and then enable if the Shell extension is enabled, i.e. is in the Shell extension whitelist when the Shell starts up. The Shell may call disable and enable again during the same session. However, the Shell does not call enable or disable if a Shell extension is disabled at startup. Currently, the Shell calls init for all Shell extension including disabled Shell extensions at startup.

You can code your Shell extension so that init returns a JavaScript object with two methods called enable and disable. The object type returned does not matter. The Shell does not touch that object except to invoke either the enable or disable methods. If init does not return anything or returns a falsey (something which evaluates to FALSE) object, the Shell then assumes that enable and disable are functions defined within extension.js and acts accordingly.

Here is a modified version of the extension.js generated by the 3.2 version of gnome-shell-extension-tool in which init returns a simple object with enable and disable methods.

The key line is return new HelloWorldExtension. This is the initialized object that is passed back to the Shell. It contains the enable method which inserts the button on the panel and the disable method which removes the button from the panel.

From the above, it should be obvious that you can modify your 3.0 Shell extension in one of three ways to enable it to work with GNOME Shell 3.2.

BAD – You can rename the main function to init, and add empty enable and disable functions. The UI is displayed when the extension is loaded and cannot be enabled or disabled.

GOOD – You can rename the main function to init, and move any code that adds actors, or other UI components, to the Shell into a new enable function and add a new disable function that removes such UI components.

BETTER – You can rename the main function to init and modify the code in init to return an object that has enable and disable functions.

In theory, if you want your Shell extension to work with both GNOME 3.2 and GNOME 3.0, you could add a simple main function that calls init followed by enable. In practice, I have not found this to be workable unless the Shell extension is extremely simple because of the large number of differences in the names of internal variables and the workings of functions between GNOME 3.0 and GNOME 3.2.

For example, here is the GNOME 3.0 version of extension.js for my noa11y Shell extension. The purpose of that extension is to remove the accessibility (AKA a11y) button from the right-hand side of the panel.

Most of the new code is in support of the enable and disable extension UI functionality. As you can see, supporting such functionality significantly increases the number of lines of code and complexity of the extension. I could easily make a valid argument that you need even more detailed knowledge of the internals of the 3.2 Shell than you needed for the 3.0 Shell to write Shell extensions that support enable and disable functionality.

Here is how the Linux Mint developers implemented their noa11y Shell extension:

By the way, their MGSE (Mint GNOME Shell Extensions) repository is well worth checking out.

Monkey patching does not work the same in the 3.2 Shell as it did in the 3.0 Shell. Consider the differences between the 3.0 version of my Shell extension that replaces the SYMBOLIC icons on the panel right-hand side with FULLCOLOR icons (much nicer!)

Not all of the work that you need to do to enable your Shell Extension to work with GNOME 3.2 is in extension.js. You also need to modify metadata.json to match the required version of the GNOME Shell (shell-version) and possibly also the GJS version (js-version). See the versionCheck function in extensionSystem.js. To find out the version of GNOME Shell that you are running, enter gnome-shell –version, or cat /usr/share/gnome-shell/js/misc/config.js. Currently, I set the shell-version to 3.2 in metadata.json.

By the way, if you run into trouble getting your extension to work, try the following command to invoke the GNOME Shell in debug mode:

env GJS_DEBUG_OUTPUT=stderr gnome-shell --replace

Also remember that Looking Glass is your friend. Get familiar with Looking Glass and you can use it to solve many a coding error. Note however that only the properties and methods of introspected objects that have been used before LG is invoked will show up.

It is nice to see that the Shell extensions got a modicum of real love in GNOME 3.2. Remember that it is not so long ago that some people were adamant that Shell extensions and themes were an evil aberration, were detracting from the purity of the one single cross-distribution GNOME Shell experience, and should be totally unsupported! With the release of GNOME 3.2, this is no longer the case. The user base won the battle. Hundreds of extensions, some useful, some quirky, had been developed since the release of GNOME 3.0 earlier this year.

My expectation is that disabled extensions don’t effect the other extensions but when checking out the movehotcorner extension with the MGSE extensions the right hot corner is active & set disabled. If I enable it I get an overview icon “button” & I have to click on it, disaled I get that nice bump the corner to show overview behavior.

During the early testing I would activate/deactivate extensions by compressing them & deleting the directory. This recent experience indicates that if the extension exists it does load (as indicated by lg) & in some cases still affects the desktop even when disabled.

please. I need the extension autohidetopbar, I’m from south america, my english is not very good. I wish express to the extension is the most important for me because my monitor is really small, thanks for listening and notify to me the actualization.

Hi.
I’m using the colorstatusbutton extension in Ubuntu Oneiric (started from a minimal installation).
When I activate it the size of the icons is enormous.
How can I fix it?
The simply modification of “icon-size: 1.34em;” in stylesheet.css file doesn’t work (nothing happen also after the refresh of the shell).

The problem is the interaction between line 19 of extension.js file of activitiesbutton extnsion:
this._themeContext = St.ThemeContext.get_for_stage(global.stage);
and the same file of mgse extension at line 221 222:
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
But I really don’t understand javascript yet.

Thank you! I have (ab)used your idea to make a little extension (see in my blog) to change the font size and padding in the top panel. It’s a shameless copy :-), so I haven’t published it in extensions.gnome.org — unless the original author authorizes it.

Hello, the information about GTK3 theming is pretty scarce so I thought of asking here:
1
Why don’t custom themes that use unico load colors properly, especially dark variations? The common symptom are wrong selection color or gray font with darkish themes. The only workaround that works for me is logging out of gnome and back in
2
Is adwaita engine really limited in its graphical capabilities compared to unico? Or are the differences minor?
3
Is theme test available in default Ubuntu Oneiric repositories?

Hi,
Allow me to introduce myself, I am Marshall Neill and recently installed you extension. Great, fantastic, just what I was looking for. I use it often and find it quite good.
I have one question, “How do you or can you change the popup of the different menu items?”
When moving from one category to the next, if you move the mouse over another category it immediately opens. I have been searching your code and found no time delay or timeout function. If it had just about 1/2 second delay for showing the next category that would be excellent. The way it is now I constantly have to go back and insure that I move the mouse horizontally to the menu item I want.
Again, your extension is great and thanks for all the work you have put into it.
I am beginning to see more and more extensions and it seems kinda funny to me that what I sorta wound up with was a Gnome 2 look within a Gnome 3 environment. The extensions make Gnome 3 that much better and, to me, much more usable.
Thank you again.
Marshall Neill

I’m new to extensions development, and I find it quiet tedious to start! Thanks for this blog post which helps a lot.
I’ve started my first extension, and would like to know if there’s any other way to update it than reloading all gnome-shell everytime ? (gnome-shell –replace)

First of all, thanks for these guides! I’ve wanted to write an extension since I started using Gnome 3 but there didn’t seem to be any good documentation on it.

I’m confused as to why you want to have init return a JavaScript object as opposed to simply using three separate functions. For one thing, it’s more confusing and less straightforward then the standard code generated. Also, it doesn’t work on my Fedora 16 (Gnome 3.2.1) system. The “Hello, world!” box appears up in the top left corner, making me believe the “this.text.set_position” line is failing. The box also doesn’t fade. I’ve tried poking around but it doesn’t seem to work no matter what I do.

Either way of coding works. I choose to code in an object-orientated manner. You do not – and that is fine. If you read the JavaScript sourcecode for the GNOME Shell, you will see that it is mostly written as object-orientated code. For more information on OO JavaScript, I suggest you read “JavaScript: The Good Parts” by Douglas Crockford.

As for you problems with your helloworld example, which helloworld example are you using? I just checked the helloworld exention at http://fpmurphy.com/gnome-shell-extensions and it is working as expected. The message appears in the middle of my screen and fades as expected. Without more details, there is not much I can do to help you.

Hi!
Are you planning to do a similar guide to help us migrating our extensions from 3.2 to 3.4?

It will be very useful!

I’m gettin a continued “extension is not compatible with current GNOME Shell and/or GJS version” and I don’t understand why.
Also, do you know where I can find some “the old VS the new” guidelines to write 3.4-compatible extensions?