Now, I am working on developing applications on Linux, and want to catch keyboard layout change event (changing keyboard layout through UI/widget/shell/programing, etc.) and get/set the new keyboard layout information to further process. This is not a new questions, I think, but I search from stackoverflow again and again, but no answer. Hope I could get right answer here!
The main solution I want to learn is described here. In windows, WM_INPUTLANGCHANGE windows message could be caught in WinProc, which contains keyboard layout information. And we could use GetKeyboardLayout() API to get current keyboard layout information. Finally, if I want to use my preferred keyboard layout, I could use ActivateKeyboardLayout() to activate keyboard layout.
In summary, I hope to find the notification messages and how to catch the message in code (it is better to show me an example) in Linux, the Get keyboard layout API and the Set keyboard layout API in Linux. The development language is C/C++, also.
Thanks in advance.
The other answer doesn't work for me. It compiles and runs, but MappingNotify
event doesn't happen when I switch layouts. Here is a modification that works for me.
#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);
}
Compile with:
gcc -Wall -O2 xmappingnotify.c -o xmappingnotify -lX11
I use it like this:
xmappingnotify | xargs -I {} my-custom-command {} &
so my-custom-command [number_of_the_layout]
runs when I change layouts. I have 2 layouts, xmappingnotify
outputs "1" for one layout and "0" for the other.
Answer for X11:
Detect changes via MappingNotify event
Change or query the layout via setxkbmap
Here is a basic xlib example for detecting MappingNotify events:
#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);
}
Built command:
gcc -Wall -O2 xmappingnotify.c -o xmappingnotify -lX11
I used @gvlasov answer as a keyboard layout notification in Suckless dwm bar with per-window keyboard layout patch. It works correctly regarding the result, but it produces too many events (ctrl, mod, shift keys presses-releases and mouse scroll wheel movement outputs the layout; the proper one, yet this is not needed, only when layout is changed should return layout). Probably it's what mentioned by @matanster 's comment. So I made a change which stopped output from mouse wheel movement, although still output from ctrl, mod, shift keys presses-releases is produced. It's not ideal, it's a bit better:
Changed the line:
XkbSelectEvents(d, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);
with this:
XkbSelectEventDetails(d, XkbUseCoreKbd, XkbMapNotifyMask, XkbAllEventsMask, XkbAllEventsMask);
If anybody has any idea how to get result/output only on keyboard layout change and not ctrl, mod, shift keys presses-releases, is welcomed.
Update, using this:
XkbSelectEventDetails(d, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);
I got the expected result. The only event notified is keyboard layout change, neither key-presses nor key-releases. It's only drawback compared to my previous line is that caps changes are not notified, which was possible before.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With