首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从Linux输入设备访问密钥

从Linux输入设备访问密钥
EN

Stack Overflow用户
提问于 2014-01-06 05:07:07
回答 1查看 47.4K关注 0票数 37

我想做的是

因此,我一直在尝试访问Linux中的键盘输入。具体来说,我需要能够访问修饰符键按下,而不按下其他键。此外,我希望能够在没有运行X系统的情况下完成这个

因此,简言之,我的要求是:

  • 在Linux上工作
  • 不需要X11
  • 可以在没有的情况下检索修饰符键按,任何其他键按下
    • 这包括以下键:
      • 移位
      • 控制
      • 丙氨酸氨基转移酶

代码语言:javascript
复制
- All I need is a simple `0 = not pressed`, `1 = currently pressed` to let me know if the key is being held down when the keyboard is checked

我的电脑设置

我的普通Linux机器是在一辆卡车上驶向我的新公寓;所以,我现在只有一台Macbook Air可以使用。因此,我在VM中运行Linux来测试这个问题。

虚拟机在VirtualBox中的应用

  • 操作系统: Linux 16
  • 桌面环境: XFCE

下面的一切都是在这种环境下完成的。我试过用X跑,也试过其他一种。

我的思想

如果有人能纠正我,我会改变的。

为了认识到高级库不提供这种功能,我做了大量的阅读工作。修饰符键与其他键一起使用,以提供备用密钥代码。通过Linux中的高级库访问修饰符本身并不是那么容易。或者说,我还没有在Linux上找到一个高级别的API。

我认为白蚁是答案,但它似乎不支持移位修饰符键比正常的击键检索更好。我也不确定在没有X的情况下它是否有效。

在使用libtermkey时(在我意识到在像shift -before这样的情况下它没有换档),我计划编写一个运行以收集键盘事件的守护进程。运行守护进程程序的副本将简单地传递对键盘数据的请求,并接收相应的键盘数据。我可以使用这个设置让一些东西总是在后台运行,以防我无法在特定时间检查关键代码状态(必须在发生时接收密钥代码)。

下面是我两次尝试编写一个可以从Linux键盘设备读取的程序。我还包括了我的小支票,以确保我有正确的设备。

企图#1

我曾试图直接访问键盘设备,但遇到了一些问题。我已经尝试了另一个堆栈溢出线程中的建议这里。它给了我一个分段错误;因此,我将它从fopen改为open:

代码语言:javascript
复制
// ...

int fd;
fd = open("/dev/input/by-path/platform-i8042-serio-0-event-kbd", O_RDONLY);

char key_map[KEY_MAX/8 + 1];

memset(key_map, 0, sizeof(key_map));
ioctl(fd, EVIOCGKEY(sizeof key_map), key_map);

// ...

虽然没有分割错误,但没有任何按键的指示(不仅仅是修饰符键)。我使用以下方法进行了测试:

代码语言:javascript
复制
./foo && echo "TRUE" || echo "FALSE"

我经常用它来测试命令中的成功返回代码;所以,我知道这很好。我还输出了键(总是0)和掩码(0100)来检查。它似乎什么也没发现。

第二次尝试

从现在开始,我想我应该尝试一种稍微不同的方法。我想弄清楚我做错了什么。在页面提供了演示打印关键代码的片段之后,我将其捆绑到一个程序中:

代码语言:javascript
复制
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <linux/input.h>

int main(int argc, char** argv) {
    uint8_t keys[128];
    int fd;

    fd = open("/dev/input/by-path/platform-i8042-serio-event-kbd", O_RDONLY);
    for (;;) {
        memset(keys, 0, 128);
        ioctl (fd, EVIOCGKEY(sizeof keys), keys);

        int i, j;
        for (i = 0; i < sizeof keys; i++)
            for (j = 0; j < 8; j++)
                if (keys[i] & (1 << j))
                    printf ("key code %d\n", (i*8) + j);
    }

    return 0;
}

以前,我的大小是16字节,而不是128字节。老实说,我应该花更多的时间来理解ioctl和EVIOCGKEY。我只知道它应该将位映射到特定的键以指示按下,或者类似的东西(如果我错了,请纠正我!!)。

我最初也没有一个循环,我只会按住各种键,看看是否出现了密钥代码。我什么也没收到;所以,我认为循环可能会使检查更容易测试,以防遗漏了什么东西。

我怎么知道输入设备是正确的

我通过在输入设备上运行cat来测试它。具体地说:

代码语言:javascript
复制
$ sudo cat /dev/input/by-path/platform-i8042-serio-0-event-kbd

当我使用cat开始输出时,垃圾ASCII被发送到我的终端,按下并释放事件,从返回(enter)键开始。我还知道,在运行Linux的Macbook上,修改键,如shift、control、Functionand甚至Apple的命令键似乎都能很好地工作。当按下键时会出现输出,从按住键发送的后续信号开始迅速显示,并在释放密钥时输出更多的数据。

因此,虽然我的方法可能不是正确的(我愿意听到任何的替代方案),但设备似乎提供了我所需要的。

此外,我知道这个设备只是一个链接,指向运行中的/dev/input/event2:

代码语言:javascript
复制
$ ls -l /dev/input/by-path/platform-i8042-serio-0-event-kbd

我已经用/dev/ data /event2尝试了上面的两个程序,但是没有收到任何数据。在/dev/ cat /event2 2上运行cat提供了与链接相同的输出。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-01-06 08:48:45

打开输入装置,

代码语言:javascript
复制
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>

static const char *const evval[3] = {
    "RELEASED",
    "PRESSED ",
    "REPEATED"
};

int main(void)
{
    const char *dev = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
    struct input_event ev;
    ssize_t n;
    int fd;

    fd = open(dev, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
        return EXIT_FAILURE;
    }

然后从设备读取键盘事件:

代码语言:javascript
复制
    while (1) {
        n = read(fd, &ev, sizeof ev);
        if (n == (ssize_t)-1) {
            if (errno == EINTR)
                continue;
            else
                break;
        } else
        if (n != sizeof ev) {
            errno = EIO;
            break;
        }

如果发生任何错误,或者如果用户空间只接收到部分事件结构(不应该发生,但可能在将来的/ but内核中),那么上面的代码片段就会从循环中释放出来。您可能希望使用更健壮的read循环;我个人会满意的是,用break替换最后一个continue,从而忽略部分事件结构。

然后,您可以检查ev事件结构以查看发生了什么,并完成该程序:

代码语言:javascript
复制
        if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2)
            printf("%s 0x%04x (%d)\n", evval[ev.value], (int)ev.code, (int)ev.code);

    }
    fflush(stdout);
    fprintf(stderr, "%s.\n", strerror(errno));
    return EXIT_FAILURE;
}

按下键,

  • ev.time:事件的时间(struct timeval类型)
  • ev.typeEV_KEY
  • ev.codeKEY_*,键标识符;参见/usr/include/linux/input.h中的完整列表
  • ev.value0 if键释放,1 if键按下,2 if自动重复键按

有关更多细节,请参见Linux内核源代码中的文件/输入/输入

/usr/include/linux/input.h中的命名常量是相当稳定的,因为它是一个内核-用户空间接口,内核开发人员非常努力地维护兼容性。(也就是说,您可以预期不时会出现新代码,但现有代码很少更改。)

票数 58
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20943322

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档