5

I can change my current workspace in various ways, e.g. by clicking on some switcher widget, with some key combination, with a command in a terminal.

Is there a way to detect that the workspace has been changed, to launch a bash script at that moment?

(I think, for example, of a monitorable signal on d-bus, something like that.)

Raffa
  • 34,963
Rodrigo
  • 153

2 Answers2

4

There is a more energy-efficient gnome-shell extension solution. It will actually "launch a bash script at that moment", without you having to start anything beforehand. Here is the installation procedure:

  1. Create the script you want to run at ~/script.sh
  2. Make sure it is executable
  3. Paste the correct version into ~/.local/share/gnome-shell/extensions/workspaceChangeDetect_1502368_1004020@askubuntu.com/metadata.json
  4. Paste the correct version into ~/.local/share/gnome-shell/extensions/workspaceChangeDetect_1502368_1004020@askubuntu.com/extension.js
  5. Relog
  6. Run gnome-extensions enable workspaceChangeDetect_1502368_1004020@askubuntu.com

I tested with a script.sh that just created a file. I tested Ctrl+Alt+/ and confirmed that the script runs at that time and not at other times such as login. Gnome-shell does not expose the signal to DBus, and the org.gnome.Shell.Eval DBus method is locked down. However, gnome-shell extensions can listen to the gobject signal WorkspaceManager::active-workspace-changed.

Gnome-shell ≤ 42 (22.04)

metadata.json

{
    "uuid": "workspaceChangeDetect_1502368_1004020@askubuntu.com",
    "name": "Workspace change detect script launcher",
    "description": "Detect that the workspace has been changed, to launch a bash script at that moment",
    "shell-version": [ "42" ],
    "url": "https://askubuntu.com/q/1502368/1004020"
}

extension.js

const {GLib} = imports.gi;
let handlerId;

function enable() { handlerId = global.workspaceManager.connect('active-workspace-changed', () => { // Put the script to launch at ~/script.sh , or change the string on the next line: GLib.spawn_command_line_async('./script.sh'); }); }

function disable() { global.workspaceManager.disconnect(handlerId); }

Gnome-shell ≥ 45 (23.10)

metadata.json

{
    "uuid": "workspaceChangeDetect_1502368_1004020@askubuntu.com",
    "name": "Workspace change detect script launcher",
    "description": "Detect that the workspace has been changed, to launch a bash script at that moment",
    "shell-version": [ "45" ],
    "url": "https://askubuntu.com/q/1502368/1004020"
}

extension.js

import GLib from 'gi://GLib';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';

export default class WorkspaceChangeDetectExtension extends Extension { enable() { global.workspaceManager.connectObject('active-workspace-changed', () => { // Put the script to launch at ~/script.sh , or change the string on the next line: GLib.spawn_command_line_async('./script.sh'); }, this); }

disable() {
    global.workspaceManager.disconnectObject(this);
}

}

Daniel T
  • 5,339
3

I did look into D-Bus and other Gnome DE specific methods that came to mind, but couldn't find a more reliable, sustainable and portable solution than what is described below ...

You can use xdotool (which also seems to work on Wayland for this purpose) in a shell loop like:

#!/bin/sh

while sleep 0.2 do v="$(xdotool get_desktop)" if [ -z "$ws" ] then ws="$v" elif [ "$ws" -ne "$v" ] then ws="$v" echo "changed to $ws" # Your command here fi done

... that tool still works on Wayland because it reads the _NET_CURRENT_DESKTOP property of the EWMHExtended Window Manager Hints which many windowing managers/systems still support and set by standards.

Needless to say that there are other tools which support reading that property along with builtin capability to continuously monitor it (If you don't like a continuous loop with a sleep call) like xprop and you can similarly use it like so:

#!/bin/bash

xprop -root -spy _NET_CURRENT_DESKTOP | while IFS=" " read -r p s n do if ! [[ "$ws" =~ ^[0-9]+$ ]] then ws="$n" elif [ "$n" -ne "$ws" ] then ws="$n" echo "changed to $ws" # Your command here fi done

... or even xev like so:

#!/bin/sh

xev -root -event property | grep --line-buffered -o '_NET_CURRENT_DESKTOP' | while read -r p do echo "Workspace changed." # Your command here done

... and of course you can, if you wish, alternatively implement your own app to read the _NET_CURRENT_DESKTOP property in any language C, Python ... etc. and not necessarily in a shell script using those tools.

Notice:

  • The above first loop with xdotool and the last one with xev is not Bash specific and can run with other POSIX shells with sh for example.

  • When using xprop on Wayland, as reported by @Daniel T (Thank you Daniel), then the workspace has to be switched at least once before xprop starts catching it and as a result your first switch between workspaces will be out of scope for the script ... So, if that is critical, use xdotool instead.

Notice as well that (goes without saying) those loops need to be run while a user graphical session is running i.e. after login and before logout ... and therefore the best implementation for either is as a startup application be it they are contained in script file or even laid out in the Command: field as a single shell command string like this:

 sh -c 'while sleep 0.2; do if [ -z "$ws" ]; then ws="$(xdotool get_desktop)"; elif [ "$ws" -ne "$(xdotool get_desktop)" ]; then ws="$(xdotool get_desktop)"; echo "changed to $ws"; fi done'

... or like this:

bash -c 'xprop -root -spy _NET_CURRENT_DESKTOP | while IFS=" " read -r p s n; do if ! [[ "$ws" =~ ^[0-9]+$ ]]; then ws="$n"; elif [ "$n" -ne "$ws" ]; then ws="$n"; echo "changet to $ws"; fi done'

... or like this:

sh -c 'xev -root -event property | grep --line-buffered -o '_NET_CURRENT_DESKTOP' | while read -r p; do  echo "Workspace changed."; done'

... your custom command in those command strings should go in-place of the echo "..." part.

Raffa
  • 34,963