在开发辅助功能应用程序时,我遇到了一种行为,它与我目前所能接触到的所有 Xlib 文档集所规定的行为有所不同:
事件MappingNotify
应该在键盘布局更改时触发。据我了解,在安装了多种语言的情况下切换输入语言会导致(在幕后)键盘布局更改。然而,与此理解相反,使用命令行记录 X 事件,xev
我可以看到MappingNotify
触发的事件仅有的当我更改键盘快捷键时通过 Gnome 调整工具。
通过 gnome 菜单栏小部件或指定的键盘快捷键切换输入语言时,此事件根本不会触发。
可以说这是一个系统开发问题,而不是常见的使用场景。
切换输入语言在所有 Windows 中均有效 ― 在已安装的输入语言之间切换后,按键会立即按照预期遵循所选输入语言的布局。但是,该MappingNotify
事件似乎没有触发或到达xev
,我自己的 Xlib 代码也没有监听 X 事件。
很高兴知道这是否也为您重现了,或者任何指向正确的开发者论坛的指针可以进行咨询。
编辑:
我确实在 xev 中获得了KeyRelease
事件(但不是 KeyPress 事件)ISO_Next_Group
,这很可能如果是 Arch Linux,则用于切换布局的 X 键盘扩展事件和 Ubuntu 对此共享。因此,我可以大胆猜测,gnome 实际上可能正在抓取 X11MappingNotify
事件,阻止它到达 X 客户端,而是向它们发送此 KeyPress 事件的按键释放。
使用命令行时setxkbmap
例如
setxkbmap us
然后MappingNotify
确实会为客户端触发!但此后它会导致 gnome 语言切换无法操作。此后 Windows 将简单地忽略 Gnome 对当前输入语言的看法。
答案1
在我的系统上,MappingNotify
X 的键盘布局事件通常不会触发。当通过 Gnome Tweaks 工具修改键盘快捷键时,它们会从该工具内部触发,但其他情况下不会触发,因此实际上,它们似乎对 X 客户端没有用处,并且通常不可用(因为在 Tweak 工具中调整快捷键的那种边缘情况不是我们期望获得该通知的关键用例)。
在我的系统上(18.04,以及升级到 20.04 后),X KEYBOARD 扩展至 X11 处于活动状态。此扩展旨在为标准 X 键盘处理添加各种灵活性。
因此,具体到键盘布局更改,由于此 X11 扩展处于活动状态,MappingNotify
在正常情况下不会触发,而是此扩展会将不同的事件插入客户端队列:
API调用XkbQueryExtension
应该向 X 键盘扩展查询 X 键盘扩展使用的“全局”事件代码,然后当在 X11 队列上检测到此类事件时,XkbStateNotify
将使用事件中包含的子类型来通知客户端键盘布局已更改!
该流程与它所属的更大流程一起在 X 键盘规范中进行了描述,并以 C 代码给出这里。该代码稍微整理后的版本如下:
#include <stdio.h>
#include <X11/XKBlib.h>
int main(int argc, char **argv)
{
XEvent e;
Display *d;
if (!(d = XOpenDisplay(NULL))) {
fprintf(stderr, "cannot open display\n");
return 1;
}
// initialize the XKB protocol and obtain the integer event code that will be used by it
int xkbEventType;
XkbQueryExtension(d, 0, &xkbEventType, 0, 0, 0); // assigns the integer code for XKB to xkbEventType
printf("%d\n", xkbEventType);
XkbSelectEvents(d, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);
XSync(d, False);
while (1) {
XNextEvent(d, &e);
if (e.type == xkbEventType) {
XkbEvent* xkbEvent = (XkbEvent*) &e;
if (xkbEvent->any.xkb_type == XkbStateNotify) {
printf("keyboard layout change, or mouse click");
fflush(stdout);
}
}
}
return(0);
}
请注意,任何鼠标单击也会触发相同的事件...我相信我们可以过滤掉一些东西,例如,XkbSelectEvents
如果我们在系统上映射了设备,则可以通过按设备类型调整我们的参数来过滤掉它(还没有尝试过)。