检测带死键的键盘布局中的按键释放事件
在我们的Tcl/Tk应用程序中,我们通过套接字向主机应用程序发送按键事件(<KeyPress> 和 <KeyRelease>),由后者处理实际的按键事件。
这通常工作得相当好,因为我们可以将任意 <KeyRelease> 事件关联到相应的 <KeyPress> 事件。举例来说,在我的德语键盘上,可以通过按下 AltGr+e 输入字符 €。这会产生以下事件:
| 事件 | %k |
%A |
%K |
%N |
|---|---|---|---|---|
KeyPress |
108 |
(空) | ISO_Level3_Shift |
65027 |
KeyPress |
26 |
€ |
€ |
8364 |
KeyRelease |
26 |
(空) | e |
101 |
KeyRelease |
108 |
(空) | ISO_Level3_Shift |
65027 |
如你所见,%A、%K 和 %N 在按下及释放 e 键时可能展开为不同的值,但 %k 是稳定的,因此我们可以用它来判断哪个松开事件对应哪个按下事件。
不幸的是,对于带死键的键盘布局,我们遇到了一堵难以逾越的墙:在这种情况下,我们无法将 <KeyRelease> 事件与 <KeyPress> 事件相关联。
例如,在带死键的德语键盘上,我可以通过按 ´-a 输入字母 á。
当我对某个日志函数进行 bind all <KeyPress> 与 bind all <KeyRelease> 的响应时,我会得到以下事件(按顺序):
| 事件 | %k |
%A |
%K |
%N |
|---|---|---|---|---|
KeyRelease |
21 |
(空) | ´ |
180 |
KeyPress |
0 |
á |
á |
225 |
KeyRelease |
38 |
(空) | a |
97 |
首先要注意的是,死键 ´ 不会产生 KeyPress 事件(只有一个 KeyRelease 事件)。
我们大概可以接受这一点,然而真正让人困惑的是,当按下 a 时,绑定变量之间没有对这两个事件给出明显的对应关系。
我如何知道产生字母 á 的按键何时被释放?这种方法要跨平台(Linux、Darwin、Windows)并适用于未知的键盘布局(我们曾收到来自法语、西班牙语和葡萄牙语用户的反馈,但社区实际上是全球性的)。
解决方案
潜在的问题在于,我们必须把按键按下传给一个库,该库将它们转换成要插入的文本(这可能比 á 复杂得多!),而我们只能从中得知用于 %A 替换的值。我们不能把按键释放传给这些库进行解码(除非在某些输入法的特定情形下;这东西相当复杂!),也不能用其他方式解码按键。我们拥有的是一种折中方案,能在大多数普通应用中做对的事,但你很可能应该完全忽略 <KeyRelease> 事件;尽管它们有 %A 替换,但它们并不太有用,且从按键码到字符序列的转换非常非平凡(并且也是有状态的)。
输入法是使用GUI时最让人头疼的事情之一。