我想做的是
因此,我一直在尝试访问Linux中的键盘输入。具体来说,我需要能够访问修饰符键按下,而不按下其他键。此外,我希望能够在没有运行X系统的情况下完成这个。
因此,简言之,我的要求是:
- 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中的应用
下面的一切都是在这种环境下完成的。我试过用X跑,也试过其他一种。
我的思想
如果有人能纠正我,我会改变的。
为了认识到高级库不提供这种功能,我做了大量的阅读工作。修饰符键与其他键一起使用,以提供备用密钥代码。通过Linux中的高级库访问修饰符本身并不是那么容易。或者说,我还没有在Linux上找到一个高级别的API。
我认为白蚁是答案,但它似乎不支持移位修饰符键比正常的击键检索更好。我也不确定在没有X的情况下它是否有效。
在使用libtermkey时(在我意识到在像shift -before这样的情况下它没有换档),我计划编写一个运行以收集键盘事件的守护进程。运行守护进程程序的副本将简单地传递对键盘数据的请求,并接收相应的键盘数据。我可以使用这个设置让一些东西总是在后台运行,以防我无法在特定时间检查关键代码状态(必须在发生时接收密钥代码)。
下面是我两次尝试编写一个可以从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)来检查。它似乎什么也没发现。
第二次尝试
从现在开始,我想我应该尝试一种稍微不同的方法。我想弄清楚我做错了什么。在这页面提供了演示打印关键代码的片段之后,我将其捆绑到一个程序中:
#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被发送到我的终端,按下并释放事件,从返回(enter)键开始。我还知道,在运行Linux的Macbook上,修改键,如shift、control、Functionand甚至Apple的命令键似乎都能很好地工作。当按下键时会出现输出,从按住键发送的后续信号开始迅速显示,并在释放密钥时输出更多的数据。
因此,虽然我的方法可能不是正确的(我愿意听到任何的替代方案),但设备似乎提供了我所需要的。
此外,我知道这个设备只是一个链接,指向运行中的/dev/input/event2:
$ ls -l /dev/input/by-path/platform-i8042-serio-0-event-kbd我已经用/dev/ data /event2尝试了上面的两个程序,但是没有收到任何数据。在/dev/ cat /event2 2上运行cat提供了与链接相同的输出。
发布于 2014-01-06 08:48:45
打开输入装置,
#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;
}如果发生任何错误,或者如果用户空间只接收到部分事件结构(不应该发生,但可能在将来的/ but内核中),那么上面的代码片段就会从循环中释放出来。您可能希望使用更健壮的read循环;我个人会满意的是,用break替换最后一个continue,从而忽略部分事件结构。
然后,您可以检查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.type:EV_KEYev.code:KEY_*,键标识符;参见/usr/include/linux/input.h中的完整列表ev.value:0 if键释放,1 if键按下,2 if自动重复键按有关更多细节,请参见Linux内核源代码中的文件/输入/输入。
/usr/include/linux/input.h中的命名常量是相当稳定的,因为它是一个内核-用户空间接口,内核开发人员非常努力地维护兼容性。(也就是说,您可以预期不时会出现新代码,但现有代码很少更改。)
https://stackoverflow.com/questions/20943322
复制相似问题