从 Linux 输入设备访问密钥
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20943322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Accessing Keys from Linux Input Device
提问by Senkwich
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 withoutother keys being pressed. Furthermore, I want to be able to do this withoutan X system running.
所以,我一直在尝试在 Linux 中访问键盘输入。具体来说,我需要能够在不按下其他键的情况下访问修饰键。此外,我希望能够在不运行 X 系统的情况下执行此操作。
So, in short, my requirements are these:
所以,简而言之,我的要求是:
- Works on Linux
- Does not need X11
- Can retrieve modifier key press withoutany 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
- This includes the following keys:
- 适用于 Linux
- 不需要X11
- 可以在没有按下任何其他键的情况下检索修饰键按下
- 这包括以下键:
- 转移
- 控制
- 替代
- 我需要的只是一个简单的
0 = not pressed
,1 = currently pressed
让我知道在检查键盘时是否按下了键
- 这包括以下键:
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.
我的普通 Linux 机器在开往我新公寓的卡车上;所以,我现在只有一台 Macbook Air 可以使用。因此,我在 VM 中运行 Linux 来测试这一点。
Virtual Machine in VirtualBox
VirtualBox 中的虚拟机
- OS: Linux Mint 16
- Desktop Environment: XFCE
- 操作系统:Linux Mint 16
- 桌面环境:XFCE
Everything below was done in this environment. I've tried both with X running and in one of the other ttys.
下面的一切都是在这个环境中完成的。我已经尝试过 X 运行和其他 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.
我已经做了大量阅读以意识到更高级别的库不提供这种功能。修饰键与其他键一起使用以提供备用键代码。通过 Linux 中的高级库访问修饰键本身并不容易。或者,更确切地说,我还没有在 Linux 上找到用于此的高级 API。
I thought libtermkeywould 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.
我认为libtermkey将是答案,但它似乎并不比正常的击键检索更好地支持 Shift 修饰键。我也不确定它是否在没有 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).
在使用 libtermkey 时(在我意识到在像 Shift-Return 这样的情况下它不会发生转变之前),我计划编写一个守护进程来收集键盘事件。运行守护程序的副本将简单地传递对键盘数据的请求并接收键盘数据作为响应。我可以使用这个设置让某些东西始终在后台运行,以防我无法在特定时间检查关键代码状态(必须在它们发生时接收关键代码)。
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.
下面是我编写一个可以从 Linux 键盘设备读取的程序的两次尝试。我还附上了我的小支票,以确保我拥有正确的设备。
Attempt #1
尝试 #1
I have tried to access the keyboard device directly, but am encountering issues. I have tried the suggestion herethat is in another Stack Overflow thread. It gave me a segmentation fault; so, I changed it from fopen to open:
我曾尝试直接访问键盘设备,但遇到了问题。我在这里尝试了另一个 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);
// ...
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.
我已经用它来测试命令的成功返回码很多;所以,我知道这很好。我还输出了密钥(始终为 0)和掩码(0100)以进行检查。它似乎没有检测到任何东西。
Attempt #2
尝试#2
From here, I thought I'd try a slightly different approach. I wanted to figure out what I was doing wrong. Following thispage 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!).
以前,我的大小为 16 字节而不是 128 字节。老实说,我应该多花点时间了解 ioctl 和 EVIOCGKEY。我只知道它应该将位映射到特定键以指示按下或类似的东西(如果我错了,请纠正我!)。
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:
我通过cat
在输入设备上运行来测试它。具体来说:
$ 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.
当我使用 cat 开始输出时,垃圾 ASCII 在从返回(回车)键开始的按键按下和释放事件中发送到我的终端。我也知道这似乎适用于我的运行 Linux VM 的 Macbook 上的修饰键,例如 shift、control、function 甚至 Apple 的命令键。按下一个键时出现输出,通过按住键发送的后续信号开始快速出现,并在释放一个键时输出更多数据。
So, while my approach may not be the right one (I'm willing to hear anyalternative), 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:
此外,我知道这个设备只是一个指向 /dev/input/event2 运行的链接:
$ 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.
我已经用 /dev/input/event2 尝试了上面的两个程序,但没有收到任何数据。在 /dev/input/event2 上运行 cat 提供了与链接相同的输出。
采纳答案by Nominal Animal
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 readkeyboard 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.
如果发生任何错误,或者如果用户空间仅接收到部分事件结构(这不应该发生,但可能在某些未来/有问题的内核中发生),则上述代码段将从循环中跳出。您可能希望使用更健壮的读取循环;我个人会被替换最后一个满足break
用continue
,所以,局部的事件结构被忽略。
You can then examine the ev
event structure to see what occurred, and finish the program:
然后,您可以检查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;
}
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
ev.time
:事件的时间(struct timeval
类型)ev.type
:EV_KEY
ev.code
:KEY_*
,密钥标识符;查看完整列表/usr/include/linux/input.h
ev.value
:0
如果按键释放,1
如果按键,2
如果自动重复按键
See Documentation/input/input.txtin the Linux kernel sources for further details.
有关更多详细信息,请参阅Linux 内核源代码中的Documentation/input/input.txt。
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.)
in/usr/include/linux/input.h
中的命名常量相当稳定,因为它是内核-用户空间接口,内核开发人员非常努力地保持兼容性。(也就是说,您可以期望时不时有新代码,但现有代码很少更改。)