当前位置: 首页 > 面试题库 >

从Linux输入设备访问键

单于耘豪
2023-03-14
问题内容

我要做什么

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

简而言之,我的要求是:

  • 在Linux上工作
  • 不需要X11
  • 无需 按下任何其他键 即可 检索修饰键
    • 这包括以下键:
    • 转移
    • 控制
    • Alt键
    • 我所需要的只是一个简单的方法0 = not pressed1 = currently pressed让我知道在检查键盘时是否按住了该键

我的电脑设定

我的普通Linux机器正在卡车上驶向我​​的新公寓。因此,我现在只能使用Macbook Air。因此,我正在VM中运行Linux进行测试。

VirtualBox中的虚拟机

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

下面的所有操作都是在此环境中完成的。我已经在运行X和其他tty之一中进行了尝试。

我的想法

如果有人可以纠正我,我将更改此设置。

我已经读了很多书,以了解更高级别的库不提供这种功能。修饰键与其他键一起使用以提供备用键代码。通过Linux中的高级库本身访问修饰键并不容易。或者,相反,我还没有在Linux上找到用于此目的的高级API。

我以为libtermkey将是答案,但是它似乎不比普通的击键检索更好地支持Shift修饰键。我也不确定如果没有X,它是否可以工作。

在使用libtermkey时(在意识到Shift-
Return之类的情况下没有发生移位之前),我正计划编写一个守护程序来运行以收集键盘事件。运行守护程序的副本将仅通过管道传送对键盘数据的请求并作为响应接收键盘数据。我可以使用此设置使某些内容始终在后台运行,以防万一我无法在特定时间检查键码状态(必须及时接收键码状态)。

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

尝试#1

我试图直接访问键盘设备,但是遇到问题。我在这里尝试了另一个堆栈溢出线程中的建议。这给了我分割错误。因此,我将其从fopen更改为open:

// ...

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

// ...

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

./foo && echo "TRUE" || echo "FALSE"

我已经用它来测试命令是否成功返回了很多代码。所以,我知道那很好。我还输出了密钥(始终为0)和掩码(0100)进行检查。它似乎什么也没检测到。

尝试#2

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

#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在输入设备上运行进行了测试。特别:

$ sudo cat /dev/input/by-path/platform-i8042-serio-0-event-kbd

当我使用cat开始输出时,在按键和释放事件(从回车(回车)开始)时,垃圾ASCII被发送到我的终端。我也知道,这在运行Linux
VM的Macbook上可以与诸如Shift,Control,Function甚至Apple的Command键之类的修饰键一起使用,似乎效果很好。按下键时出现输出,从按住该键发出的后续信号开始迅速出现,并在释放键时输出更多数据。

因此,尽管我的方法可能不正确(我愿意听到 其他 选择),但该设备似乎可以满足我的需求。

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

$ ls -l /dev/input/by-path/platform-i8042-serio-0-event-kbd

我已经使用/ dev / input / event2尝试了以上两个程序,但未收到任何数据。在/ dev / input /
event2上运行cat提供的输出与链接相同。


问题答案:

打开输入设备,

#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;
    }

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

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

如果发生任何错误,或者用户空间仅接收部分事件结构(不应发生,但可能在将来的/笨拙的内核中发生),则以上代码段将从循环中中断。您可能希望使用更强大的读取循环;我个人会被替换最后一个满足breakcontinue,所以,局部的事件结构被忽略。

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

        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如果释放按键12如果按键,如果自动重复按键

有关更多详细信息,请参见Linux内核资源中的Documentation / input /
input.txt

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



 类似资料:
  • 我打算通过RESTAPI将数据从嵌入式设备加载到BigQuery。 我的BigQuery初始化已经完成,我正在使用python库访问相同的初始化。 我的嵌入式设备使用这些与我的服务号JSON散列的参数创建JWT 我试图使用TCP终端应用程序测试相同的。请向我解释配方是否有任何问题。 我首先需要知道BigQuery是否允许这样的访问。 请告诉我JSON字符串中的子应该是什么。 如果我做错了,请告诉我

  • 问题内容: 我有一个UIAlertController,当用户在TouchID屏幕上选择“输入密码”时,系统会提示用户。在屏幕上显示此内容后,如何恢复用户的输入? 我知道“确定”按钮可能应该有一个处理程序,但是现在此代码实际上并没有执行任何操作,但是我想通过println显示文本字段的输出。对于我来说,这实际上只是一个测试应用程序,用于学习Swift和一些新的API知识。 问题答案: 我知道已经发

  • 类似基于 Chromium 的浏览器一样, Electron 也提供了通过 web API 访问设备硬件的方法。 大部分接口就像在浏览器调用的 API 一样,但有一些差异需要考虑到。 Electron和浏览器之间的主要区别是请求访问设备时发生的情况。 在浏览器中,用户可以在弹出窗口中允许访问单独的设备。 在 Electron API中,提供了可供开发者自动选择设备或提示用户通过开发者创建的接口选择

  • 26 输入设备 FFmpeg中的输入设备配置元素用来启用对附加到您的系统一个多媒体设备访问数据。 当编译时,默认会支持所有的输入设备。你可以通过在配置脚本执行时附加–list-indevs了解到支持的设备。 可以通过–disable-indevs在编译时禁用所有输入设备,也可以在此基础上通过–enable-indev=INDEV允许个别设备,或者在默认支持基础上通过–disable-indev=I

  • 我使用以下配置将lirc0绑定到容器中。 主机中/dev/lirc0的属性如下所示。 我使用lxc连接进入容器。容器中的属性与主机相同 但是无法打开。错误是“不允许操作”,我是容器中的主管(root)。 如何获得访问容器中的的权限?

  • 问题内容: 我试图使用带有以下代码的javax.smartcardio API加载智能卡终端: …并且我总是得到以下异常: 在Windows Vista / 7上,一切正常,但在Linux上无法正常运行。我正在使用Ubuntu 12.04 64位。 我使用以下命令安装了pcscd服务: 然后pcsc_scan命令输出: 因此,一切看起来都很好,但是smartcardio却无法正常工作。我正在尝试使