Process for iterating developing an extension on Wayland

Hi,

Thank you for the GNOME extensions part of the platform! It seems that thanks to existing documentation and threads ChatGPT knows stuff about this, but it was unable to consistently guide me in this.

I’m developing an extension or two on Wayland for Wayland only, on a GNOME 46 system (Ubuntu 24.04). This is for a proof of concept for now.

I followed the getting started page and started a nested shell, which appears like a desktop window on my display, I guess this is the nested shell.

When I click to open a terminal session inside that nested shell, it opens outside of that window, like any other window I’d open in the parent real non-nested desktop. Is this normal?

As I assume it is normal, then I’m not sure whether my extension would really work on that window as it looks as if part of my parent real shell. my extension is supposed to highlight the active window with some visual effect. I don’t see that happening, but not sure how to really debug my extension ― what options do I have for tracing my extension code line by line or for creating side effects like debug prints to some ergonomic destination?

Further lets say I wanted to apply the extension to my real desktop, at times, not the nested one, what’s the minimal series of steps for this with Wayland? I’ve seen various ways mentioned on the web and while chatting with AI, and am wondering what’s the really most minimal and robust way.

Can you kindly provide some advice?

Matan

Answering my own question ― there is no hot reloading of extension code for Wayland at least. That is unless you want to tinker with “a nested GNOME Shell” which didn’t work out as expected for my case. On Wayland one should use something like this and then login back, unless you want to tinker with virtual machines and more complexity.

gnome-session-quit --logout --no-prompt

At least, no need to run gnome-extension commands to re-enable the extension after that.

Then look at journalctl -f -o cat /usr/bin/gnome-shell where all extension loading goes as well as all javascript log outputs from the extension code as far as I understand.

gnome-extensions info <extension-name> just gives you the high level status (ERROR when loading the extension has failed such as due to a javascript syntax error).

My best practice is to make a soft link from the extension directory under ~/.local/share/gnome-shell/extensions/ (which you one time create when starting work on a new extension) to my project directory for it to always use the latest code after loging in.

This is sure a bit tedious, but that’s how it is.

Also one should look at the official GNOME extensions (Github) repo to avoid the wrong language level of javascript (ESx) if aiming at modern versions of GNOME as the runtime.

A normal state after logging back in should be:
Enabled: Yes
State: ACTIVE

And not something like INITIALIZED, which you get before you ever first activated your new extension.

Here’s an approach kind of solving the iterative development workflow for an extension, using a script which launches a nested GNOME shell while enabling the extension being developed.

This script demonstrates the minimal working core, further ergonomic (but harder to read ones) can be retrieved through the link at the bottom.

This was tested on an untweaked Ubuntu 24.04 machine, meaning one using the default GNOME that 24.04 comes with, and Wayland.

#!/usr/bin/env bash
# starts a disposable nested GNOME Shell with our extension already enabled. Created in a ChatGPT o3 model discussion.
# ─ by starting a nested GNOME Shell, propagating the Wayland socket to D-Bus / user-systemd, and enabling our extension.
# this script should be run from the root of the extension's directory.
Extension_UUID="" # make same as in your metadata.json
DISPLAY_NAME="wayland-nested-$RANDOM"
WORKDIR="$(pwd)"                              

dbus-run-session -- bash -euxc "
  # 1.  Tell every client in *this* session which socket to use
  export WAYLAND_DISPLAY=$DISPLAY_NAME
  dbus-update-activation-environment --systemd WAYLAND_DISPLAY
  systemctl --user import-environment WAYLAND_DISPLAY

  # 2.  Point the shell at the work-tree copy of the code
  export GNOME_SHELL_EXTENSIONS_PATH='$WORKDIR'

  # 3.  Developer niceties
  export MUTTER_DEBUG_DUMMY_MODE_SPECS=1920x1080     # 1080 p virtual monitor
  export SHELL_DEBUG=all                             # JS stack traces
  # Make it simulate two fake monitors 
  # Add export MUTTER_DEBUG_NUM_DUMMY_MONITORS=2

  # 4.  Start the compositor in the background
  gnome-shell --nested --wayland-display=\$WAYLAND_DISPLAY & 
  SHELL_PID=\$!

  # 5.  Wait until the shell is on D-Bus, then enable the extension
  until gdbus call --session --dest org.gnome.Shell \
          --object-path /org/gnome/Shell \
          --method org.gnome.Shell.Eval '""' >/dev/null 2>&1; do
      sleep 0.3
  done
  gnome-extensions enable '$Extension_UUIDUID'
  cd '$GNOME_SHELL_EXTENSIONS_PATH'

  # 6.  Hand over the terminal to the compositor process
  wait \$SHELL_PID
"


More details and a fuller implementation can be seen at ChatGPT - Nested GNOME Shell extension dev workflow.

I believe it’s normal that the first application launches within the nested shell are much slower than in the parent shell (like, the terminal app, or file viewer app) and potential speedups are discussed as well in the above linked conversation.