diff options
| author | 2021-12-07 19:41:49 +0100 | |
|---|---|---|
| committer | 2021-12-07 19:51:21 +0100 | |
| commit | dc5d4e0919b2f1fd18aee16468a23a6631c26251 (patch) | |
| tree | 6cefa912b1d0e216c722919d125273a24f2278fc | |
| parent | 7e38d0a27a8ae8feb3443262743da6eadecf1482 (diff) | |
| download | xkbcat-dc5d4e0919b2f1fd18aee16468a23a6631c26251.tar.gz xkbcat-dc5d4e0919b2f1fd18aee16468a23a6631c26251.zip | |
Support switching keyboard layouts
As noted in issue #5, xkbcat would previously not react at all to
keyboard layout changes; it would continue to print keysyms according to
the first keyboard layout X was started with.
Keyboard keysym group changes (i.e. keyboard layout changes) are
listened to as separate events, so if you only use 1 layout, this causes
no performance overhead.
Closes #5.
Thanks to @unxed for very helpful research.
| -rw-r--r-- | xkbcat.c | 58 |
1 files changed, 47 insertions, 11 deletions
@@ -75,6 +75,8 @@ int main(int argc, char * argv[]) { m.deviceid = XIAllMasterDevices; m.mask_len = XIMaskLen(XI_LASTEVENT); m.mask = calloc(m.mask_len, sizeof(char)); + // Raw key presses correspond to physical key-presses, without + // processing steps such as auto-repeat. XISetMask(m.mask, XI_RawKeyPress); if (printKeyUps) XISetMask(m.mask, XI_RawKeyRelease); XISelectEvents(disp, root, &m, 1 /*number of masks*/); @@ -82,22 +84,47 @@ int main(int argc, char * argv[]) { free(m.mask); } + int xkbOpcode, xkbEventCode; + { // Test for Xkb extension + int queryError, majorVersion, minorVersion; + if (! XkbQueryExtension(disp, &xkbOpcode, &xkbEventCode, &queryError, + &majorVersion, &minorVersion)) { + fprintf(stderr, "Xkb extension not available\n"); + exit(2); + } + } + // Register to receive events when the keyboard's keysym group changes. + // Keysym groups are normally used to switch keyboard layouts. The + // keyboard continues to send the same keycodes (numeric identifiers of + // keys) either way, but the active keysym group determines how those map + // to keysyms (textual names of keys). + XkbSelectEventDetails(disp, XkbUseCoreKbd, XkbStateNotify, + XkbGroupStateMask, XkbGroupStateMask); + int group; + { // Determine initial keysym group + XkbStateRec state; + XkbGetState(disp, XkbUseCoreKbd, &state); + group = state.group; + } + while ("forever") { XEvent event; XGenericEventCookie *cookie = (XGenericEventCookie*)&event.xcookie; XNextEvent(disp, &event); - if (XGetEventData(disp, cookie) && - cookie->type == GenericEvent && - cookie->extension == xiOpcode) { - switch (cookie->evtype) { - case XI_RawKeyRelease: - case XI_RawKeyPress: { + if (XGetEventData(disp, cookie)) { + // Handle key press and release events + if (cookie->type == GenericEvent + && cookie->extension == xiOpcode) { + if (cookie->evtype == XI_RawKeyRelease + || cookie->evtype == XI_RawKeyPress) { XIRawEvent *ev = cookie->data; - // Ask X what it calls that key; skip if it doesn't know - KeySym s = XkbKeycodeToKeysym(disp, ev->detail, - 0 /*group*/, 0 /*shift level*/); + // Ask X what it calls that key; skip if unknown. + // Ignore shift-level argument, to show the "basic" key + // regardless of what modifiers are held down. + KeySym s = XkbKeycodeToKeysym( + disp, ev->detail, group, 0 /*shift level*/); if (NoSymbol == s) continue; char *str = XKeysymToString(s); if (NULL == str) continue; @@ -107,8 +134,17 @@ int main(int argc, char * argv[]) { cookie->evtype == XI_RawKeyPress ? "+" : "-"); printf("%s\n", str); fflush(stdout); - break; - } + } + } + // Release memory associated with event data + XFreeEventData(disp, cookie); + } else { // No extra data to release; `event` contains everything. + // Handle keysym group change events + if (event.type == xkbEventCode) { + XkbEvent *xkbEvent = (XkbEvent*)&event; + if (xkbEvent->any.xkb_type == XkbStateNotify) { + group = xkbEvent->state.group; + } } } } |
