我想要做什么
所以,我一直在尝试在 Linux 中访问键盘输入。具体来说,我需要能够在不按下其他键的情况下访问修饰键。此外,我希望能够在没有运行 X 系统的情况下执行此操作。
所以,简而言之,我的要求是:
- 在 Linux 上工作
- 不需要 X11
- 可以在不按下任何其他键的
情况下检索修饰键按下
- 这包括以下键:
- 转移
- 控制
- Alt
- 我只需要一个简单的
0 = not pressed
,1 = currently pressed
让我知道在检查键盘时是否按下了键
- 这包括以下键:
我的电脑设置
我的普通 Linux 机器正在开往我的新公寓的卡车上;所以,我现在只有一台 Macbook Air 可以使用。因此,我在虚拟机中运行 Linux 来测试这一点。
VirtualBox 中的虚拟机
- 操作系统:Linux Mint 16
- 桌面环境:XFCE
下面的一切都是在这种环境下完成的。我已经尝试过运行 X 和其他 tty 之一。
我的想法
如果有人可以纠正我,我会改变这个。
我做了很多阅读,意识到更高级别的库不提供这种功能。修饰键与其他键一起使用以提供备用键代码。通过 Linux 中的高级库访问修饰键本身并不容易。或者,更确切地说,我还没有在 Linux 上找到用于此的高级 API。
我认为libtermkey将是答案,但它似乎并不比普通的击键检索更好地支持 Shift 修饰键。我也不确定它是否在没有 X 的情况下工作。
在使用 libtermkey 时(在我意识到它在 Shift-Return 之类的情况下不会发生转变之前),我打算编写一个可以运行以收集键盘事件的守护程序。运行守护程序的副本将简单地通过管道传输对键盘数据的请求并接收键盘数据作为响应。我可以使用此设置让某些东西始终在后台运行,以防我无法在特定时间检查键码状态(必须在它们发生时接收键码)。
以下是我编写可以从 Linux 键盘设备读取的程序的两次尝试。我还附上了我的小支票,以确保我拥有正确的设备。
尝试#1
我曾尝试直接访问键盘设备,但遇到了问题。我在这里尝试了另一个 Stack Overflow 线程中的建议。它给了我一个分段错误;所以,我把它从 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 的命令键)配合得很好。输出在按键被按下时出现,从随后按键发出的信号开始迅速出现,在按键松开时输出更多的数据。
因此,虽然我的方法可能不是正确的(我愿意听到任何替代方案),但该设备似乎提供了我需要的东西。
此外,我知道这个设备只是一个指向 /dev/input/event2 运行的链接:
$ ls -l /dev/input/by-path/platform-i8042-serio-0-event-kbd
我已经使用 /dev/input/event2 尝试了上述两个程序,但没有收到任何数据。在 /dev/input/event2 上运行 cat 提供与链接相同的输出。