【问题标题】:create Wayland desktop environment that simply binds keyboard shortcuts创建简单绑定键盘快捷键的 Wayland 桌面环境
【发布时间】:2018-11-12 12:06:09
【问题描述】:

这里有很多问题询问如何在 Wayland 环境中创建全局键盘绑定。通常,答案是“使用您的桌面环境”——对于大多数提出问题的人来说,这个答案几乎毫无用处。

那么,为了得出一个更有用的答案,请问如何创建一个可以绑定快捷键的最小Wayland桌面环境?

我让 Mutter 作为我的 WM 运行,并且我使用 GNOME Do 作为启动器。这几乎正​​是我想要的桌面环境,只是我无法绑定热键。

我不在乎是否必须编写一个 10k 行的 C 应用程序来完成这项工作。我只想知道如何进行。 GNOME 如何在 Wayland 中绑定键盘快捷键?代码在哪里? Wayland/Mutter 的相应文档在哪里?

【问题讨论】:

    标签: keyboard-shortcuts wayland


    【解决方案1】:

    经过大量研究和实验,看起来libevdev 可能是正确的工具。我开发了下面的程序作为概念验证来绑定Alt+X 以启动xterm

    不幸的是,它必须以 root 身份运行,所以我想我需要以某种方式将它与本地桌面会话联系起来。就我的目的而言,使用 root 用户 setuid 并收工可能就足够了。

    我也不相信我的键盘检测启发式方法非常好。我本质上是在寻找任何具有按键和重复率的设备,在我的系统上只匹配我的键盘。

    #include <errno.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <string.h>
    #include <dirent.h>
    #include <fcntl.h>
    #include <libevdev/libevdev.h>
    
    #define DEVROOT     "/dev/input/"
    #define DEVROOT_LEN 12
    #define PATH_LEN    (DEVROOT_LEN + NAME_MAX)
    
    int outerr(int, const char*);
    struct libevdev* open_device(int);
    bool kblike(struct libevdev*);
    
    int main(int argc, char* argv[]) {
        DIR* dir;
        struct dirent* entry;
        char path[PATH_LEN];
        int fd, err;
        struct libevdev* dev = NULL;
        struct input_event ev;
        bool key, rep, alt;
    
        if (!(dir = opendir("/dev/input"))) {
            return outerr(errno, "cannot enumerate devices");
        }
    
        // look for keyboard device
        while (entry = readdir(dir)) {
            if (DT_CHR == entry->d_type) {
                sprintf(path, "/dev/input/%s", entry->d_name);
    
                if (-1 == (fd = open(path, O_RDONLY|O_NONBLOCK))) {
                    return outerr(errno, "cannot read device");
                }
    
                if (dev = open_device(fd)) {
                    if (kblike(dev)) break;
                    libevdev_free(dev);
                    dev = NULL;
                }
            }
        }
    
        closedir(dir);
    
        // check if keyboard was found
        if (dev == NULL) {
            return outerr(ENODEV, "could not detect keyboard");
        } else do {
            err = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
    
            if (err == 0 && ev.type == EV_KEY) switch (ev.code) {
                case KEY_LEFTALT:
                    alt = ev.value == 1;
                    break;
                case KEY_X:
                    if (ev.value == 1 && alt) system("xterm");
                    break;
            }
        } while (err == 1 || err == 0 || err == -EAGAIN);
    
        return 0;
    }
    
    int outerr(int errnum, const char* msg) {
        fprintf(stderr, "%s (%s)\n", msg, strerror(errnum));
        return errnum;
    }
    
    bool kblike(struct libevdev* dev) {
        return libevdev_has_event_type(dev, EV_KEY)
            && libevdev_has_event_type(dev, EV_REP);
    }
    
    struct libevdev* open_device(int fd) {
        struct libevdev* dev = libevdev_new();
        int err;
    
        if (dev == NULL) {
            errno = ENOMEM;
        } else if (0 > (err = libevdev_set_fd(dev, fd))) {
            libevdev_free(dev);
            dev = NULL;
            errno = -err;
        }
    
        return dev;
    }
    

    【讨论】:

    • 当我尝试 hkd 时,我通过将自己添加到 input 组来避免 setuid。