如何在Linux上捕获键盘布局更改事件并获取当前新的键盘布局?

8
现在,我正在Linux上开发应用程序,并希望捕获键盘布局更改事件(通过UI / widget / shell /编程等更改键盘布局),并获取/设置新的键盘布局信息以进一步处理。我认为这不是一个新问题,但我一次又一次地从stackoverflow搜索,却没有找到答案。希望我能在这里得到正确的答案!
主要解决方案如下所述。在Windows中,可以在WinProc中捕获WM_INPUTLANGCHANGE窗口消息,其中包含键盘布局信息。我们可以使用GetKeyboardLayout() API获取当前键盘布局信息。最后,如果我想使用我的首选键盘布局,我可以使用ActivateKeyboardLayout()来激活键盘布局。
总之,我希望在Linux中找到通知消息以及如何在代码中捕获消息(最好向我展示一个示例),以及Linux中的Get键盘布局API和Set键盘布局API。开发语言是C / C ++。
谢谢提前。
3个回答

8

另一个答案对我没用。虽然编译和运行正常,但是当我切换布局时,MappingNotify事件不会发生。这里有一个适合我的修改版本。

#include <stdio.h>
#include <X11/Xutil.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;
        }

        XKeysymToKeycode(d, XK_F1);

    int xkbEventType;
    XkbQueryExtension(d, 0, &xkbEventType, 0, 0, 0);
    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) {
                int lang = xkbEvent->state.group;
                if (lang == 1) {
                    fprintf(stdout, "1\n");
                    fflush(stdout);
                } else {
                    fprintf(stdout, "0\n");
                    fflush(stdout);
                }
            }
        }
    }

    return(0);
}

使用以下命令进行编译:

gcc -Wall -O2 xmappingnotify.c -o xmappingnotify -lX11

我这样使用它:
xmappingnotify | xargs -I {} my-custom-command {} &

当我切换布局时,my-custom-command [number_of_the_layout] 会运行。我有两个布局,xmappingnotify 输出一个布局的值为 "1",另一个布局的值为 "0"。


在我的设置中(Ubuntu 16.04,两种不同的语言/布局),这总是打印出0。 - MikaelF
似乎事件可能会因为相同的布局更改而触发多次。X键盘协议规范可能会对此进行解释。 - matanster
当启用X键盘扩展时,就必须监听此答案代码中演示的事件,而不是另一个答案中的事件。 - matanster

3

X11的解答:

通过MappingNotify事件检测更改

通过setxkbmap更改或查询布局

以下是检测MappingNotify事件的基本xlib示例:

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

int main(int argc, char **argv)
{
        XEvent event;
        Display *dpy;

        if (!(dpy = XOpenDisplay(NULL))) {
                fprintf(stderr, "cannot open display\n");
                return 1;
        }

        /**
         * Note: We might never get a MappingNotify event if the
         * modifier and keymap information was never cached in Xlib.
         * The next line makes sure that this happens initially.
         */
        XKeysymToKeycode(dpy, XK_F1);

        while (1)
        {
                XNextEvent(dpy, &event);
                if (event.type == MappingNotify) {
                        XMappingEvent *e = (XMappingEvent *) &event;
                        if (e->request == MappingKeyboard) {
                                fprintf(stderr, "The keyboard mapping was changed!\n");
                        }
                        XRefreshKeyboardMapping(e);
                }
        }

        return(0);
}

构建命令:

gcc -Wall -O2 xmappingnotify.c -o xmappingnotify -lX11

有 MappingNotify 的任何代码示例吗?我找不到它的代码示例。 - zhangyang01123
你使用哪个工具包?(Qt,Gtk,xlib)? - gollum

2
我将@ gvlasov的答案用作Suckless dwm栏中带有每个窗口键盘布局补丁的键盘布局通知。它在结果方面是正确的,但它产生了太多的事件(ctrl,mod,shift键按下/释放和鼠标滚轮移动输出布局;正确的布局,但不需要,只有当布局更改时才应返回布局)。这可能就是@matanster评论中提到的内容。因此,我进行了更改,停止了来自鼠标滚轮移动的输出,尽管仍会产生来自ctrl、mod、shift键按下/释放的输出。这并不理想,但比以前好一点:

更改了以下行:

XkbSelectEvents(d, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);

使用这个:
XkbSelectEventDetails(d, XkbUseCoreKbd, XkbMapNotifyMask, XkbAllEventsMask, XkbAllEventsMask);

如果有人知道如何仅在键盘布局更改时获取结果/输出,而不是ctrl、mod、shift键的按下释放,则欢迎提供意见。
更新,使用以下内容:
XkbSelectEventDetails(d, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);

我得到了预期的结果。唯一通知的事件是键盘布局更改,既没有按键也没有释放键。与我的先前行相比,它唯一的缺点是不能通知caps更改,这在以前是可能的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接