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);
}
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();
}