我正在尝试包裹菜单在某些脚本中为其提供输入并对其进行操作,但是当按 Return 键选择某些 dmenu 输入时,KeyRelease 事件会发送到 dmenu 关闭时聚焦的任何窗口。如果该应用程序对其做出反应(在我的例子中,它发生在 Firefox 中的 javascript 中),则可能会发生不需要的事情。
但这并不特定于我的脚本甚至 dmenu。我测试的其他对话框风格的 X 应用程序也会发生这种情况,特别是 ssh-askpass、ksshaskpass、gpg 密码对话框等。
一个简单的测试如下:
- 将 ssh-askpass 绑定到密钥(组合)。我使用 i3-wm 配置来实现这一点。
在终端中运行以下命令:
xev | grep -EA2 --line-buffered '^Key(Press|Release)' | sed -n 's/^.*\(Press\|Release\|keysym[^)]*\).*$/\1/p'
- 按绑定的组合键。
按回车键,观察终端中的以下输出:
Release keysym 0xff0d, Return
- 不观察以前
Press
的Return
为什么这些应用程序不使用Release
X 事件队列中的事件?我认为这是一个错误 - 但在 X 中还是在应用程序中?
当使用(python?)脚本包装这样的应用程序时,我该如何解决这个问题?
答案1
已处理事件的应用程序KeyPress
通常不会处理后续KeyRelease
事件。如果用户按下一个键,然后切换到另一个应用程序,然后释放该键,则焦点更改预计会在请求时发生,不应延迟到用户释放该键为止。
X11 处理输入事件的架构非常简单:要么事件已被客户端抓取,在这种情况下客户端接收事件,要么事件尚未被抓取,在这种情况下当前拥有焦点的客户端接收事件。没有规定KeyRelease
根据哪个客户端接收到KeyPress
事件来单独调度事件。
一个使用修饰符将KeyRelease
事件重新路由到接收事件的客户端显然是错误的示例。KeyPress
在焦点更改时,如果按下修改器,它将保持按下状态。KeyRelease
当不再按下修饰键时发送该事件。虽然KeyRelease
在焦点丢失时发送所有按下的修饰符的事件并将相应的KeyPress
事件发送到新聚焦的窗口可能是有意义的,但这不会是实际用户输入的准确报告,这对于之间的焦点变化尤其具有破坏性。同一应用程序的小部件。
X11 行为显然是正确的,而您重新路由按键事件的建议是错误的另一个例子是同一应用程序的小部件之间的焦点变化。如果应用程序关心这个特定的键而不关心哪个小部件被聚焦,那么发送虚假事件将是错误的。但如果应用程序确实关心哪个小部件接收该KeyRelease
事件,那么该事件肯定应该发送到当时具有焦点的小部件。
大多数应用程序的操作是由按键事件触发的,而不是由按键释放事件触发的。当事件导致按钮被按下时,按钮的效果预计在按下时触发,而不是在释放时触发。如果导致窗口关闭的事件表现不同,这将是一种奇怪的用户体验。
当按键导致窗口关闭时,让焦点转移到另一个窗口显然是正确的一个示例,即按住该Esc键退出多个对话框。如果应用程序需要释放Esc要释放的键来关闭窗口,这将是破坏性的。
在您描述的场景中,显然是 Firefox/JavaScript 应用程序出了问题。大多数键盘界面都是基于按键,而不是按键释放。特别是,它是紧迫 Enter这应该会导致某些事情发生,而不是释放它。如果应用程序对按键释放事件做出反应,则它有责任处理接收到事件而未以合理方式KeyRelease
接收到匹配的情况。KeyPress
鼠标单击有些不同,因为在许多情况下,界面需要对释放事件做出反应,例如拖放或取决于单击持续时间的事件。即使如此,如果应用程序ButtonRelease
没有获得相应的ButtonPress
.