我不确定这是否最终是问这个问题的正确位置,但我正在开发一个需要用户实时输入的程序。那是;它需要能够实时读取按下的按键(以及可选的按住和释放)。让这个问题有点麻烦的是我绝对不能为此使用任何类型的库;甚至连标准 C 库都没有。因此,这需要使用原始内核系统调用和int 0x80
.
fcntl
我想我应该使用和将 stdin 设置为非阻塞模式,O_NONBLOCK
然后从 stdin 读取输入数据。然而,令我惊讶的是我的代码似乎无法正常工作。现在看来输入是从 stdin 读取的后正在按下 Enter 键。棘手的是,我发现很难弄清楚这是否是以下原因造成的:
- 我的代码行为不正确(最有可能)
- 我的终端/用户/进程(无论处理什么?)没有对标准输入的非阻塞写入访问(不可能)。
- 我错误地认为我可以从标准输入直接读取键盘输入。
所以我的问题是阅读直接键盘交互的最佳/常用方式是什么如果不通过将标准输入设置为非阻塞模式并从那里读取键盘输入数据?或者我是正确的并且应该修复我的代码? :)
(好奇的是,不,它不是键盘记录器。它适用于大小非常有限(可执行文件大小 <= 256 字节)的 demoscene 产品,需要用户能够从介绍中 ESC 出来。)
答案1
如果您要阅读stdin
,您应该阅读 的联机帮助页tcsetattr
,特别是有关“规范和非规范模式”的部分 ( ICANON
)。除非您禁用ICANON
on stdin
,否则输入是面向行的(在按下 Enter 之前您什么也得不到)。即使对于 也是如此O_NONBLOCK
。
如果有疑问,strace -v stty raw >&log; stty sane; grep TCSETSW log
看看它会做什么stdin
。您可以使用具有相同参数的相同系统调用来取消终端。请注意,除非您在退出时恢复设置,否则程序终止后终端将毫无用处。 (这就是我stty sane
之后这样做的原因stty raw
)
使用原始硬件 I/O 可能是非常坏主意,除非你绝对地确定您的目标硬件,和/或您希望预测现有的每种类型的键盘。
哦,尽管它是“原始”的,你仍然从键盘上读取一些经过处理的输入:你不会得到扫描码,你会得到 ASCII(或者很可能是 UTF-8)。因此 Escape 是十进制 27,而不是 1(如原始 IBM PC 键盘上的那样)。
需要超级用户访问权限的另一种建议:阅读事件设备,打开正确的设备并从中读取 Linux 事件结构。此方法返回原始按下/释放事件和按键(不过,它们是内核的内部按键代码,而不是硬件扫描代码)。/usr/include/linux/input.h
具体情况请查看。eventX
当然,在 256 字节的代码中找到正确的设备来打开完全是另一回事。
答案2
您需要将 tty 设置为原始模式,请参阅man stty
。