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

时间:2016-02-23 05:29:49

标签: linux layout keyboard

现在,我正在开发Linux上的应用程序,并希望捕获键盘布局更改事件(通过UI /窗口小部件/ shell /编程等更改键盘布局)并获取/设置新的键盘布局信息以进一步处理。我想这不是一个新问题,但我一次又一次地从stackoverflow中搜索,但没有答案。希望我能在这里得到正确答案!

我想要学习的主要解决方案在这里描述。在Windows中,WM_INPUTLANGCHANGE窗口消息可以在WinProc中捕获,其中包含键盘布局信息。我们可以使用GetKeyboardLayout()API来获取当前的键盘布局信息。最后,如果我想使用我首选的键盘布局,我可以使用ActivateKeyboardLayout()来激活键盘布局。

总之,我希望在Linux中找到通知消息以及如何在代码中捕获消息(最好是给我看一个示例),在Linux中获取键盘布局API和Set键盘布局API。开发语言也是C / C ++。

提前致谢。

3 个答案:

答案 0 :(得分:2)

回答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

答案 1 :(得分:2)

另一个答案对我不起作用。它编译并运行,但切换布局时不会发生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]会运行。我有2个布局,xmappingnotify输出“1”用于一个布局,“0”用于另一个布局。

答案 2 :(得分:0)

我使用@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 更改没有被通知,这在以前是可能的。