2

I followed this https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial for Overwriting a function.

For example, I want to override the function _setupKeyboard() on the Keyboard class, but my override isn't invoked. The specific portion I want to change is this, to remove the if guard:

if (Meta.is_wayland_compositor()) {
    this._connectSignal(this._keyboardController, 'emoji-visible',
        this._onEmojiKeyVisible.bind(this));
}

I copied the function from the source, removed the part I didn't want, then set the replacement function like this:

const Keyboard = imports.ui.keyboard;

Keyboard.Keyboard.prototype._setupKeyboard = myOverride;

Why isn't my override being invoked and how can I achieve this?

andy.holmes
  • 3,383
  • 17
  • 28
PRATAP
  • 127
  • 3
  • 17
  • How have you confirmed your function is not being called? I see two versions of this function; one in `workspace.js` and one in `workspaceThumbnail.js`. Are you sure you're overriding the correct function? – andy.holmes Aug 16 '20 at 19:30
  • That function is called during `_init()` in GNOME Shell's initialization, so I don't see what overriding it will do. Can you post a code example of what you're actually doing so I can compose an answer to it? – andy.holmes Aug 17 '20 at 18:32

1 Answers1

2

There are two common reasons an override won't be invoked. If the method is invoked before your override is applied, or if the function is a callback set with Function.prototype.bind() which creates a new closure.

In this case, the function _setupKeyboard() is called before your override is applied. When GNOME Shell starts up, it creates an instance of Keyboard.KeyboardManager here:

// main.js, line #204
keyboard = new Keyboard.KeyboardManager();

By the time the keyboard variable has been assigned to the instance, a default Keyboard.Keyboard class has been created and the function _setupKeyboard() has already been called in Keyboard._init(), which is much sooner than your extension is loaded.

Since there's no way to easily fix that, your best option is to just re-create the one part of the code you want to run:

const Meta = imports.gi.Meta;
const Main = imports.ui.main;
const Keyboard = imports.ui.keyboard.Keyboard;


const originalSetup = Keyboard.prototype._setupKeyboard;

const modifiedSetup = function () {
    originalSetup.call(this);

    if (!Meta.is_wayland_compositor()) {
        this._connectSignal(this._keyboardController, 'emoji-visible',
            this._onEmojiKeyVisible.bind(this));
    }
        
    this._relayout();
};


function init() {
}

// Your extension's enable function (might be a class method)
function enable() {
    let kbd = Main.keyboard.keyboardActor;

    if (kbd !== null) {
        if (!Meta.is_wayland_compositor()) {
            kbd.__mySignalId = kbd._connectSignal(kbd._keyboardController, 'emoji-visible',
                kbd._onEmojiKeyVisible.bind(kbd));
        }
    }

    Keyboard.prototype._setupKeyboard = modifiedSetup;
}

function disable() {
    let kbd = Main.keyboard.keyboardActor;

    if (kbd !== null && kbd.__mySignalId) {
        kbd.disconnect(kbd.__mySignalId);
        kbd.__mySignalId = 0;
    }

    Keyboard.prototype._setupKeyboard = originalSetup;
}

This is not very pretty, but that is often the price of patching private code. I can also not guarantee that the code will do what you want, because I suspect the emoji key is hidden on X11 for a reason.

andy.holmes
  • 3,383
  • 17
  • 28