GnomeShell Extension Guide

Estimated reading time:4 minutes 8 seconds

GnomeShell Extension Guide

Références/Sources:

Overview

Extensions can be installed

  • per-user in ~/.local/share/gnome-shell/extensions
  • systemwide in /usr/share/gnome-shell/extensions and /usr/local/share/gnome-shell/extensions

You should restart GNOME Shell after making changes to the code
For this reason, most extension development happens in Xorg/X11 sessions rather than Wayland, which requires you to logout and login to restart.

Something that's important to understand:

  • The code in extension.js is executed in the same process as gnome-shell
  • The code in prefs.js will be executed in a separate Gtk process

In extension.js you will have access to live code running in GNOME Shell, but fatal errors or mistakes in extension.js will affect the stablity of the desktop. It also means you will be using the Clutter and St toolkits, although you may still use utility functions and classes from Gtk.
The code is executed in the same process as gnome-shell so fatal programmer errors can crash GNOME Shell in a few situations.
If your extension crashes GNOME Shell as a result of the init() or enable() hooks being called, this can leave you unable to log into GNOME Shell.

In prefs.js you will not have access to, or the ability to modify code running in GNOME Shell, however fatal errors or mistakes in prefs.js will be contained within that process and won't affect the stability of the desktop. In this process you will be using the Gtk toolkit, although you may also use Clutter as well via Clutter-Gtk.

the following code is used to reference the current extension in its scripts

  const ExtensionUtils = imports.misc.extensionUtils;
  const Me = ExtensionUtils.getCurrentExtension();

Imports & Modules

A script placed in [email protected]/exampleLib.js is available as Me.imports.extensionLib
If it was in a subdirectory, such as [email protected]/modules/exampleLib.js, you would access it as Me.imports.modules.exampleLib

Many of the elements in GNOME Shell like panel buttons, popup menus and notifications are built from reusable classes and functions.
These common elements are the closest GNOME Shell has in terms of stable public API.
Here are a few links to some commonly used modules.

You can browse around in the js/ui/ folder or any other JavaScript file under js/ for more code to be reused.

Notice the path structure in the links above and how they compare to the imports below:

const ExtensionUtils = imports.misc.extensionUtils;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;

Clutter and Tweener

GNOME Shell's UI and extensions are written in GJS, which is JavaScript bindings for the GNOME C APIs.
This includes libraries like Gtk, GLib/Gio, Clutter, GStreamer and many others.

The GNOME Shell uses Clutter to lay out its graphical components.
The GNOME Shell has a custom Clutter-based toolkit called St (Shell Toolkit) that defines useful actors.
Some of these actors, such as St.BoxLayout and St.Bin implement various layout options.

The GNOME Shell does not use Clutter animation framework, but uses Tweener instead.

Important characteristics things in clutter are:

  • Stage: contain some actors (text, rectangles, etc). It is the window of the application. Also is an actor
  • Actor: GUI object
    • show(): you have to show each actor to be visible to the user
    • hide(): hide the actor

Basic Debugging

  • Logging
    When writing extensions, print() and printerr() are not particularly useful since we won't have easy access to gnome-shell's stdin and stderr pipes.
    You should generally use log() and logError() and watch the log in a new terminal with journalctl

    // Log a string, usually to `journalctl`
    log('a message');
    // Log an Error() with a stack trace and optional prefix
    try {
      throw new Error('An error occurred');
    } catch (e) {
      logError(e, 'ExtensionError');
    }
    // Print a message to stdout
    print('a message');
    // Print a message to stderr
    printerr('An error occured');

GSettings storage

Errors with GSettings are fatal in languages and will cause the application to crash!
Avoid "GNOME" in your application's name or ID.

  • Create a subdirectory for your settings schema and an empty schema file:
mkdir schemas/
code schemas/org.gnome.shell.extensions.example.gschema.xml
  • create content with xml format

    <?xml version="1.0" encoding="UTF-8"?>
    <schemalist>
    <schema id="org.gnome.shell.extensions.example" path="/org/gnome/shell/extensions/example/" >
      <key type="i" name="my-integer">
        <default>100</default>
        <summary>test integer</summary>
        <description>test integer description</description>
      </key>
    </schema>
    </schemalist>
  • compile the schema: glib-compile-schemas schemas/

Useful commands

  • restart GNOME Shell in X11: press Alt+F2 (to open the Run Dialog), type r and enter
  • launch looking glass: press Alt+F2 (to open the Run Dialog), type lg and enter. Use ESC for quit

  • enable the extension: gnome-extensions enable [email protected]
  • disable the extension: gnome-extensions disable [email protected]
  • create an extension skeleton: gnome-extensions create
    • Using -i it will walk you through picking a name, description and uuid before installing it to ~/.local/share/gnome-shell/extensions/ and opening extension.js in an editor
  • open the log to see log and error mesages: journalctl -f -o cat /usr/bin/gnome-shell
  • open the preferences dialog for your extension manually: gnome-extensions prefs [email protected]
  • open the GJS Console for live coding: gjs-console
    • not possible to access live code running in the gnome-shell process or import JS modules from GNOME Shell, since this a separate process
  • compile the Gsettings schema: glib-compile-schemas schemas/

Previous Post Next Post