从Linux的输入设备访问键 [英] Accessing Keys from Linux Input Device

查看:222
本文介绍了从Linux的输入设备访问键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我所试图做的

所以,我一直在试图访问Linux的键盘输入。具体来说,我需要能够访问修改键presses的没有其他键在pressed。此外,我希望能够做到这一点的没有的X系统上运行。

因此​​,简而言之,我的要求是这些:


  • 在Linux

  • 并不需要X11

  • 可以检索修改键preSS的没有其他任何按键被pressed

    • 这包括以下键:


      • 控制

      • 替代


    • 所有我需要的是一个简单的 0 =未pressed 1 =当前pressed 让我知道,如果
      按键被按下时,键盘被选中


我的电脑设置

我的正常的Linux机器上对我的新公寓的卡车;所以,我只有一台MacBook Air与现在的工作。因此,我在虚拟机上运行Linux测试了这一点。

在VirtualBox中的虚拟机


  • 操作系统:Linux Mint的16

  • 桌面环境:XFCE

下面在此环境中做一切。我都试过,x的和其他的ttys之一。

我的想法

我要改变这一点,如果有人能指正。

我所做的阅读,实现更高层次的库不提供这种功能公平一点。辅助按键与其他按键用来提供一个备用键code。通过在Linux中的高级别访问库修改密钥本身是不容易。或者说,我还没有发现这个Linux上的一个高层次的API。

我想 libtermkey 将是答案,但它似乎不支持换档键修改任何比普通按键检索更好。我也不能肯定它是否工作没有X。

虽然有libtermkey工作(我才意识到它并没有得到类似的情况下Shift +回车切换),我正打算写,将运行收集键盘事件的守护程序。守护程序运行副本只会键盘数据管道请求,以接收键盘数据。我可以使用此设置有东西一直在后台运行,如果我不能在特定的时间检查重点code状态(必须是获得关键codeS,因为它们发生)。

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

尝试#1

我曾尝试直接访问键盘设备,但我遇到的问题。我曾尝试建议这里是在另一个堆栈溢出线程。它给了我一个分段错误;所以,我改变了它的fopen从打开:

  // ...INT的fd;
FD =打开(的/ dev /输入/按路径/平台的i8042,里奥 - 0 - 事件骨节病,O_RDONLY);烧焦key_map [KEY_MAX / 8 + 1];memset的(key_map,0,sizeof的(key_map));
的ioctl(FD,EVIOCGKEY(sizeof的key_map),key_map);// ...

虽然没有分段错误,没有任何按键preSS(不只是修改键)的指标。我测试了这个使用:

  ./富和放大器;&安培;回声TRUE||回声假

我用,要测试成功返回codeS由命令了不少;所以,我知道这很好。我还输出的键(通常为0)和掩码(0100)来检查。它只是似乎没有任何检测

尝试#2

从这里,我想我会尝试一个稍微不同的方法。我想弄清楚我在做什么错。继这个页面提供了一个展示片段打印出关键codeS,我是捆绑到一个程序:

 的#include<&stdio.h中GT;
#包括LT&;&stdint.h GT;
#包括LT&;&string.h中GT;
#包括LT&;&fcntl.h GT;
#包括LT&; Linux的/ input.h>INT主(INT ARGC,字符** argv的){
    uint8_t有按键[128];
    INT的fd;    FD =打开(的/ dev /输入/按路径/平台的i8042,里奥 - 事件骨节病,O_RDONLY);
    为(;;){
        memset的(数字键0,128);
        的ioctl(FD,EVIOCGKEY(sizeof的键)键);        INT I,J;
        对于(i = 0; I< sizeof的钥匙;我++)
            为(J = 0; J&下; 8; J ++)
                如果(键[1] - 放大器;(1 <<;&所述; j)条)
                    的printf(键code%d个\\ N,(我* 8)+ J);
    }    返回0;
}

previously,我有大小为16字节而不是128字节。我老老实实应该花更多的时间了解和读写控制EVIOCGKEY。我只知道,它理应位映射到特定的键表示presses,或者类似的东西(纠正我,如果我错了,请!)。

我也没有一个循环开始,并会只需按住各种按键,看是否有键code出现了。我收到什么;所以,我想一个循环可能使检查更容易的情况下,来测试错过了一些东西。

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

我运行输入设备上进行了测试。具体做法是:

  $ sudo的猫的/ dev /输入/按路径/平台的i8042,里奥 -  0  - 事件骨节病

垃圾ASCII被发送到我的终端上键preSS和释放开始回车(Enter)键的事件,当我使用的猫开始输出。我也知道,这似乎做工精细用组合键一样的转变,控制,功能,运行Linux VM上我的MacBook甚至苹果的命令键。输出出现时,关键是pressed,开始通过按住按键发送的后续信号迅速出现,当输出一键被释放更多的数据。

所以,虽然我的方法未必是正确的(我愿意听到的任何替代),该设备似乎提供我所需要的。

此外,我知道这个设备是一个刚刚从运行指向到/ dev /输入/ EVENT2链接:

  $ ls -l命令的/ dev /输入/按路径/平台的i8042,里奥 -  0  - 事件骨节病

我已经试过这两个程序上面的/ dev /输入/ EVENT2并没有收到任何数据。在/ dev /输入运行猫/ EVENT2提供相同的输出与链接。


解决方案

打开输入设备,

 的#include&LT;&stdlib.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&;&fcntl.h GT;
#包括LT&;&errno.h中GT;
#包括LT&; Linux的/ input.h&GT;
#包括LT&;&string.h中GT;
#包括LT&;&stdio.h中GT;静态常量char * const的evval [3] = {
    释放,
    preSSED
    重复
};INT主要(无效)
{
    为const char *开发=的/ dev /输入/按路径/平台的i8042,里奥 - 0 - 事件骨节病;
    结构input_event EV;
    ssiz​​e_t供N;
    INT的fd;    FD =开(DEV,O_RDONLY);
    如果(FD == -1){
        fprintf中(标准错误,无法打开%s%S \\ n,开发,字符串错误(错误));
        返回EXIT_FAILURE;
    }

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

 而(1){
        N =读取(FD,和放大器; EV,EV的sizeof);
        如果(N ==(ssize_t供)-1){
            如果(错误== EINTR)
                继续;
            其他
                打破;
        }其他
        如果(N!= sizeof的EV){
            错误号= EIO;
            打破;
        }

上面的代码从循环中爆发,如果发生任何错误,或者如果用户空间只接收部分事件结构(这不应该发生,但可能会在未来的某个/车内核)。您可能希望使用更可靠的读取循环;我个人会被替换最后一个继续,使部分事件的结构会被忽略。

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

 如果(ev.type == EV_KEY&放大器;&安培; ev.value&GT; = 0&放大器;&安培; ev.value&LT; = 2)
            的printf(%S为0x%04X(%D)\\ n,evval [ev.value](INT)EV code,(INT)EV code);    }
    fflush(标准输出);
    fprintf中(标准错误,%S \\ n,字符串错误(错误));
    返回EXIT_FAILURE;
}

有关的关键preSS,


  • ev.time :活动时间( timeval结构型)


  • ev.type EV_KEY


  • EV code KEY _ * 键标识;请参阅 /usr/include/linux/input.h

    完整列表

  • ev.value 0 如果键释放, 1 如果key preSS, 2 如果自动重复键preSS


请参阅文档/输入/ input.txt中在Linux内核源代码的进一步的细节。

/usr/include/linux/input.h 命名常量是相当稳定的,因为它是一个内核用户空间接口,内核开发人员非常努力维持兼容性。 (也就是说,你可以期望有是新的codeS飘飞,但现有的codeS很少改变。)

What I am trying to do

So, I have been trying to access keyboard input in Linux. Specifically, I need to be able to access modifier key presses without other keys being pressed. Furthermore, I want to be able to do this without an X system running.

So, in short, my requirements are these:

  • Works on Linux
  • Does not need X11
  • Can retrieve modifier key press without any other keys being pressed
    • This includes the following keys:
      • Shift
      • Control
      • Alt
    • 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

My computer setup

My normal Linux machine is on a truck towards my new apartment; so, I only have a Macbook Air to work with right now. Therefore, I am running Linux in a VM to test this out.

Virtual Machine in VirtualBox

  • OS: Linux Mint 16
  • Desktop Environment: XFCE

Everything below was done in this environment. I've tried both with X running and in one of the other ttys.

My Thoughts

I'll alter this if someone can correct me.

I've done a fair bit of reading to realize that higher-level libraries do not provide this kind of functionality. Modifier keys are used with other keys to provide an alternate key code. Accessing the modifier keys themselves through a high-level library in Linux isn't as easy. Or, rather, I haven't found a high-level API for this on Linux.

I thought libtermkey would be the answer, but it doesn't seem to support the Shift modifier key any better than normal keystroke retrieval. I'm also not sure if it works without X.

While working with libtermkey (before I realized it didn't get shift in cases like Shift-Return), I was planning to write a daemon that would run to gather keyboard events. Running copies of the daemon program would simply pipe requests for keyboard data and receive keyboard data in response. I could use this setup to have something always running in the background, in case I cannot check key code statuses at specific times (have to be receive key codes as they happen).

Below are my two attempts to write a program that can read from the Linux keyboard device. I've also included my small check to make sure I had the right device.

Attempt #1

I have tried to access the keyboard device directly, but am encountering issues. I have tried the suggestion here that is in another Stack Overflow thread. It gave me a segmentation fault; so, I changed it from fopen to 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);

// ...

While there was no segmentation fault, there was no indicator of any key press (not just modifier keys). I tested this using:

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

I've used that to test for successful return codes from commands quite a lot; so, I know that's fine. I've also outputted the key (always 0) and mask (0100) to check. It just doesn't seem to detect anything.

Attempt #2

From here, I thought I'd try a slightly different approach. I wanted to figure out what I was doing wrong. Following this page providing a snippet demonstrating printing out key codes, I bundled that into a program:

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

Previously, I had the size to 16 bytes instead of 128 bytes. I should honestly spend a bit more time understanding ioctl and EVIOCGKEY. I just know that it supposedly maps bits to specific keys to indicate presses, or something like that (correct me if I'm wrong, please!).

I also didn't have a loop initially and would just hold down various keys to see if a key code appeared. I received nothing; so, I thought a loop might make the check easier to test in case a missed something.

How I know the input device is the right one

I tested it by running cat on the input device. Specifically:

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

Garbage ASCII was sent to my terminal on key press and release events starting with the return (enter) key when I began the output using cat. I also know that this seems to work fine with modifier keys like shift, control, function, and even Apple's command key on my Macbook running a Linux VM. Output appeared when a key was pressed, began to appear rapidly from subsequent signals sent by holding the key down, and outputted more data when a key was released.

So, while my approach may not be the right one (I'm willing to hear any alternative), the device seems to provide what I need.

Furthermore, I know that this device is just a link pointing to /dev/input/event2 from running:

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

I've tried both programs above with /dev/input/event2 and received no data. Running cat on /dev/input/event2 provided the same output as with the link.

解决方案

Open the input device,

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

and then read keyboard events from the device:

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

The above snippet breaks out from the loop if any error occurs, or if the userspace receives only a partial event structure (which should not happen, but might in some future/buggy kernels). You might wish to use a more robust read loop; I personally would be satisfied by replacing the last break with continue, so that partial event structures are ignored.

You can then examine the ev event structure to see what occurred, and finish the program:

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

For a keypress,

  • ev.time: time of the event (struct timeval type)

  • ev.type: EV_KEY

  • ev.code: KEY_*, key identifier; see complete list in /usr/include/linux/input.h

  • ev.value: 0 if key release, 1 if key press, 2 if autorepeat keypress

See Documentation/input/input.txt in the Linux kernel sources for further details.

The named constants in /usr/include/linux/input.h are quite stable, because it is a kernel-userspace interface, and the kernel developers try very hard to maintain compatibility. (That is, you can expect there to be new codes every now and then, but existing codes rarely change.)

这篇关于从Linux的输入设备访问键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆