-
Notifications
You must be signed in to change notification settings - Fork 16
/
xkbcat.c
164 lines (149 loc) · 6.12 KB
/
xkbcat.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// xkbcat: Logs X11 keypresses, globally.
#include <X11/XKBlib.h>
#include <X11/extensions/XInput2.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
const char * DEFAULT_DISPLAY = ":0";
const bool DEFAULT_PRINT_UP = false;
int printUsage() {
printf("\
USAGE: xkbcat [-display <display>] [-up]\n\
display target X display (default %s)\n\
up also print key-ups (default %s)\n",
DEFAULT_DISPLAY, (DEFAULT_PRINT_UP ? "yes" : "no") );
exit(0);
}
int main(int argc, char * argv[]) {
const char * xDisplayName = DEFAULT_DISPLAY;
bool printKeyUps = DEFAULT_PRINT_UP;
// Get arguments
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-help")) printUsage();
else if (!strcmp(argv[i], "-up")) printKeyUps = true;
else if (!strcmp(argv[i], "-display")) {
// Read next entry to find value
++i;
if (i >= argc) {
fprintf(stderr, "No value given to option `-display`\n");
printUsage();
exit(5);
}
xDisplayName = argv[i];
}
else { printf("Unexpected argument `%s`\n", argv[i]); printUsage(); }
}
// Connect to X display
Display * disp = XOpenDisplay(xDisplayName);
if (NULL == disp) {
fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName);
exit(1);
}
int xiOpcode;
{ // Test for XInput 2 extension
int queryEvent, queryError;
if (! XQueryExtension(disp, "XInputExtension", &xiOpcode,
&queryEvent, &queryError)) {
fprintf(stderr, "X Input extension not available\n");
exit(2);
}
}
{ // Request XInput 2.0, to guard against changes in future versions
int major = 2, minor = 0;
int queryResult = XIQueryVersion(disp, &major, &minor);
if (queryResult == BadRequest) {
fprintf(stderr, "Need XI 2.0 support (got %d.%d)\n", major, minor);
exit(3);
} else if (queryResult != Success) {
fprintf(stderr, "XIQueryVersion failed!\n");
exit(4);
}
}
{ // Register to receive XInput events
Window root = DefaultRootWindow(disp);
XIEventMask m;
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*/);
XSync(disp, false);
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)) {
// 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 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*/);
// Non-zero keysym groups are "overlays" on the base (`0`)
// group. If the current group has no keysym for this
// keycode, defer to the base group instead. (This usually
// happens with common shared keys like Return, Backspace,
// or numeric keypad keys.)
if (NoSymbol == s) {
if (group == 0) continue;
else {
s = XkbKeycodeToKeysym(disp, ev->detail,
0 /* base group */, 0 /*shift level*/);
if (NoSymbol == s) continue;
}
}
char *str = XKeysymToString(s);
if (NULL == str) continue;
// Output line
if (printKeyUps) printf("%s",
cookie->evtype == XI_RawKeyPress ? "+" : "-");
printf("%s\n", str);
fflush(stdout);
}
}
// 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;
}
}
}
}
}