summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorGravatar Antti Korpimäki2021-12-07 19:41:49 +0100
committerGravatar Antti Korpimäki2021-12-07 19:51:21 +0100
commitdc5d4e0919b2f1fd18aee16468a23a6631c26251 (patch)
tree6cefa912b1d0e216c722919d125273a24f2278fc
parent7e38d0a27a8ae8feb3443262743da6eadecf1482 (diff)
downloadxkbcat-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.c58
1 files changed, 47 insertions, 11 deletions
diff --git a/xkbcat.c b/xkbcat.c
index b339914..c56cce0 100644
--- a/xkbcat.c
+++ b/xkbcat.c
@@ -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;
+ }
}
}
}