Search code examples
gtkcentos7gnome-shellgnome-shell-extensions

GNOME Shell extension that waits for a new window to be opened


I'm writing a GNOME Shell extension in JavaScript that modifies all application windows.

As an exercise, I want to start off by getting the application name of just one open window and displaying it in the panel.

Right now, in Looking Glass, I can get all of the open windows by typing

>>> global.screen.get_active_workspace().list_windows()
r(0) = [object instance proxy GType:MetaWindowX11 ...], ...

and I can get the name of the first window in the list by typing

>>> Shell.WindowTracker.get_default().get_window_app(r(0)[0]).get_name()
r(1) = <name of application>

However, when I try to do this in my extension's extension.js file and then restart GNOME Shell, I can't get the name of the open window because the result of global.screen.get_active_workspace().list_windows() is undefined.

I thought that this might be happening because my extension was being executed before the windows were created, so I started looking into how to wait until a window is created before performing operations on windows.

This is where I've gotten really stuck.

In my extension's init() function, I am trying to add an event listener that runs my function update() when it receives the window-created signal.

The window-created signal is from the MetaDisplay object.

Here is what my code looks like so far:

let display = global.display;
display.connect('window-created', Lang.bind(this, this.update));

The code compiles without errors, but my function update() is not being called when new windows are created.

Does anyone know what is happening here? Is my syntax wrong? Should I be using a different signal?

Any help would be appreciated.

Full extension.js file

const St = imports.gi.St;
const Main = imports.ui.main;
const Lang = imports.lang;
const Shell = imports.gi.Shell;

let text, button;

function init() {
    button = new St.Bin({ style_class: 'panel-button', 
                          reactive: true, 
                          can_focus: true, 
                          x_fill: true, 
                          y_fill: false, 
                          track_hover: true });

    let display = global.display;
    display.connect('window-created', Lang.bind(this, this.update));
}

function update() {
    let window = global.screen.get_active_workspace().list_windows()[0];
    let name = Shell.WindowTracker.get_default().get_window_app(window).get_name();

    text = new St.Label({ text: name });
    button.set_child(text);
}

function enable() {
    Main.panel._rightBox.insert_child_at_index(button, 0);
}

function disable() {
    Main.panel._rightBox.remove_child(button);
}

Solution

  • I ended up figuring out how to solve my problem. The issues were in which signals I was listening for and where I was listening for them. After a lot of research, I also noticed that most extensions are object-oriented, so I changed my code to reflect that. Below is my new, working, object-oriented code.

    const St = imports.gi.St;
    const Main = imports.ui.main;
    const Lang = imports.lang;
    const Shell = imports.gi.Shell;
    
    let button;
    
    function MyApplication() {
        this._init();
    }
    
    MyApplication.prototype = {
        _init: function() {
            button = new St.Bin({ style_class: 'panel-button', 
                                  reactive: true, 
                                  can_focus: true, 
                                  x_fill: true, 
                                  y_fill: false, 
                                  track_hover: true });
        },
    
        _update: function() {
            text = new St.Label({ text: Shell.WindowTracker.get_default().focus_app.get_name() });
            button.set_child(text);
        },
    
        enable: function() {
            Main.panel._rightBox.insert_child_at_index(button, 0);
            let callbackID = global.display.connect('notify::focus-window', Lang.bind(this, this._update));
        },
    
        disable: function() {
            Main.panel._rightBox.remove_child(button);
        }
    };
    
    function init() {
        return new MyApplication();
    }