13

What is the simplest way to (globally) bind a key combination (e.g. <Super>+A) to a function in a gnome shell extension?

Inspecting a couple of extensions, I ran into the following code:

global.display.add_keybinding('random-name',
                              new Gio.Settings({schema: 'org.gnome.shell.keybindings'}),
                              Meta.KeyBindingFlags.NONE,
                              function() { /* ... some code */ });

I understand that the key combination is specified by the schema parameter, and that it's possible to create an XML file describing the combination. Is there a simpler way to do this?

1
  • If you are creating a GNOME shell extension then you would probably get better responses on http://stackoverflow.com/. Flag for moderator attention and they should migrate your question. May 9, 2013 at 4:37

2 Answers 2

7

TL;DR

Here is a class:

KeyManager: new Lang.Class({
    Name: 'MyKeyManager',

    _init: function() {
        this.grabbers = new Map()

        global.display.connect(
            'accelerator-activated',
            Lang.bind(this, function(display, action, deviceId, timestamp){
                log('Accelerator Activated: [display={}, action={}, deviceId={}, timestamp={}]',
                    display, action, deviceId, timestamp)
                this._onAccelerator(action)
            }))
    },

    listenFor: function(accelerator, callback){
        log('Trying to listen for hot key [accelerator={}]', accelerator)
        let action = global.display.grab_accelerator(accelerator)

        if(action == Meta.KeyBindingAction.NONE) {
            log('Unable to grab accelerator [binding={}]', accelerator)
        } else {
            log('Grabbed accelerator [action={}]', action)
            let name = Meta.external_binding_name_for_action(action)
            log('Received binding name for action [name={}, action={}]',
                name, action)

            log('Requesting WM to allow binding [name={}]', name)
            Main.wm.allowKeybinding(name, Shell.ActionMode.ALL)

            this.grabbers.set(action, {
                name: name,
                accelerator: accelerator,
                callback: callback,
                action: action
            })
        }

    },

    _onAccelerator: function(action) {
        let grabber = this.grabbers.get(action)

        if(grabber) {
            this.grabbers.get(action).callback()
        } else {
            log('No listeners [action={}]', action)
        }
    }
})

And that's how you you use it:

let keyManager = new KeyManager()
keyManager.listenFor("<ctrl><shift>a", function(){
    log("Hot keys are working!!!")
})

You're going to need imports:

const Lang = imports.lang
const Meta = imports.gi.Meta
const Shell = imports.gi.Shell
const Main = imports.ui.main

To stop listening:

for (let it of keyManager.grabbers) {
    global.display.ungrab_accelerator(it[1].action)
    Main.wm.allowKeybinding(it[1].name, Shell.ActionMode.NONE)
}

Explanation

I might be terribly wrong, but that what I've figured out in last couple days.

First of all it is Mutter who is responsible for listening for hotkeys. Mutter is a framework for creating Window Managers, it is not an window manager itself. Gnome Shell has a class written in JS and called "Window Manager" - this is the real Window Manager which uses Mutter internally to do all low-level stuff. Mutter has an object MetaDisplay. This is object you use to request listening for a hotkey. But! But Mutter will require Window Manager to approve usage of this hotkey. So what happens when hotkey is pressed? - MetaDisplay generates event 'filter-keybinding'. - Window Manager in Gnome Shell checks if this hotkey allowed to be processed. - Window Manager returns appropriate value to MetaDisplay - If it is allowed to process this hotkey, MetaDisplay generates event 'accelerator-actived' - Your extension must listen for that event and figure out by action id which hotkey is activated.

7
  • I've tested this code only in Gnome 3.22
    – p2t2p
    Feb 26, 2017 at 9:22
  • This is a fantastic answer, thanks so much for sharing this. Sep 3, 2017 at 0:27
  • Where does global come from? Which library is it part of?
    – borizzzzz
    Apr 4, 2020 at 15:42
  • Thank you for this detailed answer! What is the license for the code? Would you be willing to license it BSD or MIT, by any chance? I appreciate your considering this request!
    – cxw
    Feb 9, 2021 at 14:03
  • 1
    @cxw I'm not sure what kind of weight it has here but sure I consider it to be public domain, in other words - do whatever you want with it.
    – p2t2p
    Feb 10, 2021 at 11:02
0

gconf-editor might do what you want: link

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .