【发布时间】:2016-02-08 10:34:21
【问题描述】:
有没有办法在 Linux 上从终端窗口运行的简单 C 程序中捕获 while (1) 循环中的 KEY PRESSED 和 KEY RELEASED 事件。
kbhit() 将返回true,如果一个键被按下,getch() 返回被按下的字符。
如何捕捉 RELEASE 事件?
【问题讨论】:
标签: keyboard c interrupt events
有没有办法在 Linux 上从终端窗口运行的简单 C 程序中捕获 while (1) 循环中的 KEY PRESSED 和 KEY RELEASED 事件。
kbhit() 将返回true,如果一个键被按下,getch() 返回被按下的字符。
如何捕捉 RELEASE 事件?
【问题讨论】:
标签: keyboard c interrupt events
您不能以可移植的方式执行此操作。终端(以及诸如xterm 之类的模拟器)只为您提供按下的键,而不是释放事件。图形用户界面通常提供接收单独的新闻和发布事件的能力。
在图形环境中运行的终端模拟器将这些事件组合成单个字符。如在图形环境中所读,这些是键符号,其中可能包含字符。除了按键事件本身的按下和释放事件之外,您还可以拥有 修饰符,例如 shift-、control- 和 meta-modifiers,它们是单独的事件。如果你运行xev,你可以看到这些单独的事件。
在将这些事件组合成一个字符后,终端仿真器可能会将其作为一系列数据字节(例如 UTF-8 编码)发送到您的应用程序。当您使用getch() 时,ncurses 库会读取这些字节,并将它们再次组合成一个字符。在这两者之间(终端仿真器和应用程序)是伪终端和它的翻译,终端仿真器和应用程序都必须操作它们。
如果您不在图形环境中运行,则(并非总是)有除图形应用程序之外的其他方式,例如 xev 可以直接读取按键/释放事件。 Linux 控制台支持这一点。例如,请参阅 Receiving key press and key release events in Linux terminal applications?
【讨论】:
$WINDOWID 上使用XISelectEvents()(对于X11 模拟器)?就像xinput test-xi2 --root 在根窗口上所做的那样?
这适用于 xterm 和 openbox 窗口管理器:
xinput test-xi2 --root |
awk -v id="$(
xwininfo -children -id "$WINDOWID" |
awk '$1 == "Parent" {print $4}'
)" '
$1 == "EVENT"{e = $NF}
$1 == "detail:" {k = $2}
$6 == "child" && $7 == id && e ~ /Key/ {print e, k}'
输出类似:
(KeyRelease) 36
(KeyPress) 38
a(KeyRelease) 38
s(KeyPress) 39
d(KeyPress) 40
(KeyRelease) 39
(KeyRelease) 40
(那些a、s、d 是xterm 在处理这些 KeyPress 事件时写入伪终端的主端的字符的本地回显,而不是输出的一部分) .
xinput test-xi2 --root 将报告当前$DISPLAY 的每个 X Window 事件,包括但不限于 keypress 和 keyrelease 事件,格式类似于:
EVENT type 2 (KeyPress)
device: 14 (14)
detail: 54
flags:
root: 795.06/645.66
event: 795.06/645.66
buttons:
modifiers: locked 0 latched 0 base 0x4 effective: 0x4
group: locked 0 latched 0 base 0 effective: 0
valuators:
windows: root 0x252 event 0x252 child 0x1c0015b
从我有限的测试中,我发现那里的 child 字段似乎与接收那些事件。我怀疑窗口 id 有点属于窗口管理器。
因此,在这里,我们从 xwininfo -children 命令输出中检索该 id,并使用它来过滤 xinput 报告的事件。
要将这些关键代码转换为关键标签,请查看this other answer。
现在,您需要检查 xinput 的源代码或使用 ltrace(如果在 Linux 上)直接在 C 中执行相同的操作,而无需这些实用程序的帮助。
【讨论】: