简体   繁体   English

Gnome Shell 扩展键绑定

[英]Gnome Shell Extension Key Binding

What is the simplest way to (globally) bind a key combination (eg <Super>+A ) to a function in a gnome shell extension? (全局)将组合键(例如<Super>+A )绑定到 gnome shell 扩展中的函数的最简单方法是什么?

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.我知道组合键是由模式参数指定的,并且可以创建一个描述组合的 XML 文件。 Is there a simpler way to do this?有没有更简单的方法来做到这一点?

Following is a copy of my answer here I've only tested this in Gnome 3.22以下是我在这里的答案的副本我只在 Gnome 3.22 中测试过

TL;DR TL; 博士

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

    },

    _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

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 负责监听热键。 Mutter is a framework for creating Window Managers, it is not an window manager itself. Mutter 是一个用于创建窗口管理器的框架,它本身并不是一个窗口管理器。 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. Gnome Shell 有一个用 JS 编写的类,称为“窗口管理器”——这是真正的窗口管理器,它在内部使用 Mutter 来做所有低级的事情。 Mutter has an object MetaDisplay. Mutter 有一个对象 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.但是 Mutter 将需要 Window Manager 批准使用此热键。 So what happens when hotkey is pressed?那么当按下热键时会发生什么? - MetaDisplay generates event 'filter-keybinding'. - MetaDisplay 生成事件“过滤器键绑定”。 - Window Manager in Gnome Shell checks if this hotkey allowed to be processed. - Gnome Shell 中的窗口管理器检查是否允许处理此热键。 - 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. - 窗口管理器向 MetaDisplay 返回适当的值 - 如果允许处理此热键,则 MetaDisplay 生成事件 'accelerator-actived' - 您的扩展程序必须侦听该事件并通过操作 ID 确定哪个热键被激活。

The question is old, but I just implemented that for Gnome Shell 40. So here is how I did it.这个问题很老,但我刚刚为 Gnome Shell 40 实现了它。所以我是这样做的。

The key is defined in your normal schema file that you use for the settings of the extension.密钥在用于扩展设置的普通架构文件中定义。 So it looks like this:所以它看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
    <schema id="org.gnome.shell.extensions.mycoolstuff" path="/org/gnome/shell/extensions/mycoolstuff/">
        <key name="cool-hotkey" type="as">
            <default><![CDATA[['<Ctrl><Super>T']]]></default>
            <summary>Hotkey to open the cool stuff.</summary>
        </key>
        
        ... other config options

    </schema>
</schemalist>

The key type is a "Array of String", so you can configure multiple key-combinations for the action.键类型是“字符串数组”,因此您可以为操作配置多个键组合。

In your code you use it like this:在您的代码中,您可以这样使用它:

const Main = imports.ui.main;
const ExtensionUtils = imports.misc.extensionUtils;

...

let my_settings = ExtensionUtils.getSettings("org.gnome.shell.extensions.mycoolstuff");

Main.wm.addKeybinding("cool-hotkey", my_settings,
    Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
    Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW
    this._hotkeyActionMethod.bind(this));

I would recommend to remove the key binding when the extension gets disabled.我建议在扩展程序被禁用时删除键绑定。 Don't know what happens if you don't do this.如果你不这样做,不知道会发生什么。

Main.wm.removeKeybinding("cool-hotkey");

BTW: Changes to the settings (via dconf editor, gsettings or your extensions preferences) are active immediately.顺便说一句:对设置的更改(通过 dconf 编辑器、gsettings 或您的扩展首选项)会立即生效。

Same as that of @p2t2p but recast using ES5 class.与@p2t2p 相同,但使用 ES5 类重铸。 This is also using my logger class but you can replace that with log().这也是使用我的记录器类,但您可以用 log() 替换它。

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

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();

const Logger = Me.imports.logger.Logger;

var KeyboardShortcuts = class KeyboardShortcuts {
  constructor(settings) {
    this._grabbers = {};

    this.logger = new Logger('kt kbshortcuts', settings);

    global.display.connect('accelerator-activated', (display, action, deviceId, timestamp) => {
      this.logger.debug("Accelerator Activated: [display=%s, action=%s, deviceId=%s, timestamp=%s]",
        display, action, deviceId, timestamp)
      this._onAccelerator(action)
    });
  }

  listenFor(accelerator, callback) {
    this.logger.debug('Trying to listen for hot key [accelerator=%s]', accelerator);
    let action = global.display.grab_accelerator(accelerator, 0);

    if (action == Meta.KeyBindingAction.NONE) {
      this.logger.error('Unable to grab accelerator [%s]', accelerator);
      return;
    }

    this.logger.debug('Grabbed accelerator [action={}]', action);
    let name = Meta.external_binding_name_for_action(action);
    this.logger.debug('Received binding name for action [name=%s, action=%s]',
        name, action)

    this.logger.debug('Requesting WM to allow binding [name=%s]', name)
    Main.wm.allowKeybinding(name, Shell.ActionMode.ALL)

    this._grabbers[action]={
      name: name,
      accelerator: accelerator,
      callback: callback
    };
  }

  _onAccelerator(action) {
    let grabber = this._grabbers[action];

    if (grabber) {
      grabber.callback();
    } else {
      this.logger.debug('No listeners [action=%s]', action);
    }
  }
}

and use it like,并使用它,

      this.accel = new KeyboardShortcuts(this.settings);
      this.accel.listenFor("<ctrl><super>T", () => {
        this.logger.debug("Toggling show endtime");
        this._timers.settings.show_endtime = !this._timers.settings.show_endtime;
      });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM