11

(全局)将键组合(例如<Super>+A)绑定到 gnome shell 扩展中的函数的最简单方法是什么?

检查几个扩展,我遇到了以下代码:

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

我知道组合键是由 schema 参数指定的,并且可以创建一个描述组合的 XML 文件。有没有更简单的方法来做到这一点?

4

3 回答 3

3

以下是我的答案的副本,我 在 Gnome 3.22 中测试过

TL;博士

这是一个类:

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

这就是你使用它的方式:

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

您将需要导入:

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

解释

我可能大错特错,但这是我最近几天发现的。

首先是 Mutter 负责监听热键。Mutter 是一个创建窗口管理器的框架,它本身并不是一个窗口管理器。Gnome Shell 有一个用 JS 编写的名为“窗口管理器”的类——这是真正的窗口管理器,它在内部使用 Mutter 来执行所有低级操作。Mutter 有一个对象 MetaDisplay。这是您用来请求监听热键的对象。但!但 Mutter 将要求窗口管理器批准此热键的使用。那么当按下热键时会发生什么?- MetaDisplay 生成事件“过滤键绑定”。- Gnome Shell 中的窗口管理器检查是否允许处理此热键。- 窗口管理器向 MetaDisplay 返回适当的值 - 如果允许处理此热键,MetaDisplay 将生成事件 'accelerator-actived'

于 2017-02-26T09:24:35.290 回答
3

这个问题很老,但我刚刚为 Gnome Shell 40 实现了它。所以这就是我的做法。

密钥在您用于扩展设置的普通模式文件中定义。所以它看起来像这样:

<?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>

键类型是“字符串数组”,因此您可以为操作配置多个组合键。

在您的代码中,您可以像这样使用它:

const Main = imports.ui.main;
const Meta = imports.gi.Meta
const Shell = imports.gi.Shell
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));

我建议在扩展程序被禁用时删除键绑定。不知道如果你不这样做会发生什么。

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

顺便说一句:对设置的更改(通过 dconf 编辑器、gsettings 或您的扩展首选项)会立即生效。

于 2021-06-16T17:18:28.777 回答
0

与@p2t2p 相同,但使用 ES5 类重铸。这也使用了我的记录器类,但您可以用 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);
    }
  }
}

并像使用它一样,

      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;
      });
于 2021-03-07T14:02:04.737 回答