Skip to content

Instantly share code, notes, and snippets.

@unxed
Created December 5, 2021 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unxed/4ae473e78bfc30b7bf493a23eaeca077 to your computer and use it in GitHub Desktop.
Save unxed/4ae473e78bfc30b7bf493a23eaeca077 to your computer and use it in GitHub Desktop.
// xkbcat: Logs X11 keypresses, globally.
#include <X11/XKBlib.h>
#include <X11/extensions/XInput2.h>
#include <xkbcommon/xkbcommon.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
unsigned char XKeyCodeToVirtualKeyCode[] = {
/* 0 */ 0x00,
/* 1 */ 0x00,
/* 2 */ 0x00,
/* 3 */ 0x00,
/* 4 */ 0x00,
/* 5 */ 0x00,
/* 6 */ 0x00,
/* 7 */ 0x00,
/* 8 */ 0x00,
/* 9 */ 0x1b, // Esc
/* 10 */ 0x31, // 1
/* 11 */ 0x32, // 2
/* 12 */ 0x33, // 3
/* 13 */ 0x34, // 4
/* 14 */ 0x35, // 5
/* 15 */ 0x36, // 6
/* 16 */ 0x37, // 7
/* 17 */ 0x38, // 8
/* 18 */ 0x39, // 9
/* 19 */ 0x30, // 0
/* 20 */ 0xbd, // -
/* 21 */ 0xbb, // =
/* 22 */ 0x08, // Backspace
/* 23 */ 0x09, // Tab
/* 24 */ 0x51, // Q
/* 25 */ 0x57, // W
/* 26 */ 0x45, // E
/* 27 */ 0x52, // R
/* 28 */ 0x54, // T
/* 29 */ 0x59, // Y
/* 30 */ 0x55, // U
/* 31 */ 0x49, // I
/* 32 */ 0x4F, // O
/* 33 */ 0x50, // P
/* 34 */ 0xdb, // [
/* 35 */ 0xdd, // ]
/* 36 */ 0x0d, // Return
/* 37 */ 0xa2, // Ctrl Left
/* 38 */ 0x41, // A
/* 39 */ 0x53, // S
/* 40 */ 0x44, // D
/* 41 */ 0x46, // F
/* 42 */ 0x47, // G
/* 43 */ 0x48, // H
/* 44 */ 0x4a, // J
/* 45 */ 0x4b, // K
/* 46 */ 0x4c, // L
/* 47 */ 0xba, // ;
/* 48 */ 0xde, // '
/* 49 */ 0xc0, // `
/* 50 */ 0xa0, // Shift Left
/* 51 */ 0xdc, // \
/* 52 */ 0x5a, // Z
/* 53 */ 0x58, // X
/* 54 */ 0x43, // C
/* 55 */ 0x56, // V
/* 56 */ 0x42, // B
/* 57 */ 0x4e, // N
/* 58 */ 0x4d, // M
/* 59 */ 0xbc, // ,
/* 60 */ 0xbe, // .
/* 61 */ 0xbf, // /
/* 62 */ 0xa1, // Shift Right
/* 63 */ 0x6a, // KP *
/* 64 */ 0xa4, // Alt Left (-> Command)
/* 65 */ 0x20, // Space
/* 66 */ 0x14, // Caps Lock
/* 67 */ 0x70, // F1
/* 68 */ 0x71, // F2
/* 69 */ 0x72, // F3
/* 70 */ 0x73, // F4
/* 71 */ 0x74, // F5
/* 72 */ 0x75, // F6
/* 73 */ 0x76, // F7
/* 74 */ 0x77, // F8
/* 75 */ 0x78, // F9
/* 76 */ 0x79, // F10
/* 77 */ 0x90, // Num Lock
/* 78 */ 0x91, // Scroll Lock
/* 79 */ 0x67, // KP 7
/* 80 */ 0x68, // KP 8
/* 81 */ 0x69, // KP 9
/* 82 */ 0x6d, // KP -
/* 83 */ 0x64, // KP 4
/* 84 */ 0x65, // KP 5
/* 85 */ 0x66, // KP 6
/* 86 */ 0x6b, // KP +
/* 87 */ 0x61, // KP 1
/* 88 */ 0x62, // KP 2
/* 89 */ 0x63, // KP 3
/* 90 */ 0x60, // KP 0
/* 91 */ 0x6c, // KP .
/* 92 */ 0x00,
/* 93 */ 0x00,
/* 94 */ 0x00, // International // ???
/* 95 */ 0x7a, // F11
/* 96 */ 0x7b, // F12
// the part above is from https://gist.github.com/rickyzhang82/8581a762c9f9fc6ddb8390872552c250
// the part below is written by unxed using experiments and some data from /usr/share/X11/xkb/keycodes/evdev
/* 97 */ 0x00,
/* 98 */ 0x00,
/* 99 */ 0x00,
/* 100 */ 0x00,
/* 101 */ 0x00,
/* 102 */ 0x00,
/* 103 */ 0x00,
/* 104 */ 0x0d, // Return # !!! Keypad info lost during translation
/* 105 */ 0xa3, // Ctrl Right
/* 106 */ 0x6f, // KP /
/* 107 */ 0x2c, // PrintScrn
/* 108 */ 0xa5, // Alt Right (-> Command)
/* 109 */ 0x00,
/* 110 */ 0x24, // Home
/* 111 */ 0x26, // Cursor Up
/* 112 */ 0x21, // Page Up
/* 113 */ 0x25, // Cursor Left
/* 114 */ 0x27, // Cursor Right
/* 115 */ 0x23, // End
/* 116 */ 0x28, // Cursor Down
/* 117 */ 0x22, // Page Down
/* 118 */ 0x2d, // Insert
/* 119 */ 0x2e, // Delete
/* 120 */ 0x00,
/* 121 */ 0x00,
/* 122 */ 0x00,
/* 123 */ 0x00,
/* 124 */ 0x00,
/* 125 */ 0x00,
/* 126 */ 0x00,
/* 127 */ 0x13, // Pause
/* 128 */ 0x00,
/* 129 */ 0x00,
/* 130 */ 0x00,
/* 131 */ 0x00,
/* 132 */ 0x00,
/* 133 */ 0x5b, // Logo Left (-> Option)
/* 134 */ 0x5c, // Logo Right (-> Option)
/* 135 */ 0x5d // Menu (-> International)
};
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));
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);
}
char buffer[32];
unsigned vkc;
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: {
XIRawEvent *ev = cookie->data;
XkbStateRec xkbState;
XkbGetState(disp, XkbUseCoreKbd, &xkbState);
// Ask X what it calls that key; skip if it doesn't know
KeySym s = XkbKeycodeToKeysym(disp, ev->detail,
xkbState.group /*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("X11 Key Name: %s\n", str);
memset(buffer, 0, 32);
xkb_keysym_to_utf8(s, buffer, 32);
printf("Unicode char: %s\n", buffer);
printf("X11 KeySym: %ld\n", s);
printf("X11 Key Code: %d\n", ev->detail);
vkc = XKeyCodeToVirtualKeyCode[ev->detail];
printf("Corresponding Windows Virtual Key Code: %#x\n", vkc);
// todo: determine extended key flag value from keycode
// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
// The extended-key flag indicates whether the keystroke message originated
// from one of the additional keys on the enhanced keyboard. The extended keys
// consist of the ALT and CTRL keys on the right-hand side of the keyboard;
// the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters
// to the left of the numeric keypad; the NUM LOCK key; the BREAK (CTRL+PAUSE) key;
// the PRINT SCRN key; and the divide (/) and ENTER keys in the numeric keypad.
// The extended-key flag is set if the key is an extended key.
// todo: take in mind num lock state
// todo: fire Alt/Ctrl/Shift events for every L/R Alt/Ctrl/Shift event
// this is for testing translation table.
// from here: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
printf("Windows key info: ");
if (vkc == 0x01) printf("Left mouse button\n");
if (vkc == 0x02) printf("Right mouse button\n");
if (vkc == 0x03) printf("Control-break processing\n");
if (vkc == 0x04) printf("Middle mouse button (three-button mouse)\n");
if (vkc == 0x05) printf("X1 mouse button\n");
if (vkc == 0x06) printf("X2 mouse button\n");
if (vkc == 0x07) printf("Undefined\n");
if (vkc == 0x08) printf("BACKSPACE key\n");
if (vkc == 0x09) printf("TAB key\n");
//if (vkc == 0x0A-0B) printf("Reserved\n");
if (vkc == 0x0C) printf("CLEAR key\n");
if (vkc == 0x0D) printf("ENTER key\n");
//if (vkc == 0x0E-0F) printf("Undefined\n");
if (vkc == 0x10) printf("SHIFT key\n");
if (vkc == 0x11) printf("CTRL key\n");
if (vkc == 0x12) printf("ALT key\n");
if (vkc == 0x13) printf("PAUSE key\n");
if (vkc == 0x14) printf("CAPS LOCK key\n");
if (vkc == 0x15) printf("IME Kana mode\n");
if (vkc == 0x15) printf("IME Hanguel mode (maintained for compatibility; use VK_HANGUL)\n");
if (vkc == 0x15) printf("IME Hangul mode\n");
if (vkc == 0x16) printf("IME On\n");
if (vkc == 0x17) printf("IME Junja mode\n");
if (vkc == 0x18) printf("IME final mode\n");
if (vkc == 0x19) printf("IME Hanja mode\n");
if (vkc == 0x19) printf("IME Kanji mode\n");
if (vkc == 0x1A) printf("IME Off\n");
if (vkc == 0x1B) printf("ESC key\n");
if (vkc == 0x1C) printf("IME convert\n");
if (vkc == 0x1D) printf("IME nonconvert\n");
if (vkc == 0x1E) printf("IME accept\n");
if (vkc == 0x1F) printf("IME mode change request\n");
if (vkc == 0x20) printf("SPACEBAR\n");
if (vkc == 0x21) printf("PAGE UP key\n");
if (vkc == 0x22) printf("PAGE DOWN key\n");
if (vkc == 0x23) printf("END key\n");
if (vkc == 0x24) printf("HOME key\n");
if (vkc == 0x25) printf("LEFT ARROW key\n");
if (vkc == 0x26) printf("UP ARROW key\n");
if (vkc == 0x27) printf("RIGHT ARROW key\n");
if (vkc == 0x28) printf("DOWN ARROW key\n");
if (vkc == 0x29) printf("SELECT key\n");
if (vkc == 0x2A) printf("PRINT key\n");
if (vkc == 0x2B) printf("EXECUTE key\n");
if (vkc == 0x2C) printf("PRINT SCREEN key\n");
if (vkc == 0x2D) printf("INS key\n");
if (vkc == 0x2E) printf("DEL key\n");
if (vkc == 0x2F) printf("HELP key\n");
if (vkc == 0x30) printf("0 key\n");
if (vkc == 0x31) printf("1 key\n");
if (vkc == 0x32) printf("2 key\n");
if (vkc == 0x33) printf("3 key\n");
if (vkc == 0x34) printf("4 key\n");
if (vkc == 0x35) printf("5 key\n");
if (vkc == 0x36) printf("6 key\n");
if (vkc == 0x37) printf("7 key\n");
if (vkc == 0x38) printf("8 key\n");
if (vkc == 0x39) printf("9 key\n");
//if (vkc == 0x3A-40) printf("Undefined\n");
if (vkc == 0x41) printf("A key\n");
if (vkc == 0x42) printf("B key\n");
if (vkc == 0x43) printf("C key\n");
if (vkc == 0x44) printf("D key\n");
if (vkc == 0x45) printf("E key\n");
if (vkc == 0x46) printf("F key\n");
if (vkc == 0x47) printf("G key\n");
if (vkc == 0x48) printf("H key\n");
if (vkc == 0x49) printf("I key\n");
if (vkc == 0x4A) printf("J key\n");
if (vkc == 0x4B) printf("K key\n");
if (vkc == 0x4C) printf("L key\n");
if (vkc == 0x4D) printf("M key\n");
if (vkc == 0x4E) printf("N key\n");
if (vkc == 0x4F) printf("O key\n");
if (vkc == 0x50) printf("P key\n");
if (vkc == 0x51) printf("Q key\n");
if (vkc == 0x52) printf("R key\n");
if (vkc == 0x53) printf("S key\n");
if (vkc == 0x54) printf("T key\n");
if (vkc == 0x55) printf("U key\n");
if (vkc == 0x56) printf("V key\n");
if (vkc == 0x57) printf("W key\n");
if (vkc == 0x58) printf("X key\n");
if (vkc == 0x59) printf("Y key\n");
if (vkc == 0x5A) printf("Z key\n");
if (vkc == 0x5B) printf("Left Windows key (Natural keyboard)\n");
if (vkc == 0x5C) printf("Right Windows key (Natural keyboard)\n");
if (vkc == 0x5D) printf("Applications key (Natural keyboard)\n");
if (vkc == 0x5E) printf("Reserved\n");
if (vkc == 0x5F) printf("Computer Sleep key\n");
if (vkc == 0x60) printf("Numeric keypad 0 key\n");
if (vkc == 0x61) printf("Numeric keypad 1 key\n");
if (vkc == 0x62) printf("Numeric keypad 2 key\n");
if (vkc == 0x63) printf("Numeric keypad 3 key\n");
if (vkc == 0x64) printf("Numeric keypad 4 key\n");
if (vkc == 0x65) printf("Numeric keypad 5 key\n");
if (vkc == 0x66) printf("Numeric keypad 6 key\n");
if (vkc == 0x67) printf("Numeric keypad 7 key\n");
if (vkc == 0x68) printf("Numeric keypad 8 key\n");
if (vkc == 0x69) printf("Numeric keypad 9 key\n");
if (vkc == 0x6A) printf("Multiply key\n");
if (vkc == 0x6B) printf("Add key\n");
if (vkc == 0x6C) printf("Separator key\n");
if (vkc == 0x6D) printf("Subtract key\n");
if (vkc == 0x6E) printf("Decimal key\n");
if (vkc == 0x6F) printf("Divide key\n");
if (vkc == 0x70) printf("F1 key\n");
if (vkc == 0x71) printf("F2 key\n");
if (vkc == 0x72) printf("F3 key\n");
if (vkc == 0x73) printf("F4 key\n");
if (vkc == 0x74) printf("F5 key\n");
if (vkc == 0x75) printf("F6 key\n");
if (vkc == 0x76) printf("F7 key\n");
if (vkc == 0x77) printf("F8 key\n");
if (vkc == 0x78) printf("F9 key\n");
if (vkc == 0x79) printf("F10 key\n");
if (vkc == 0x7A) printf("F11 key\n");
if (vkc == 0x7B) printf("F12 key\n");
if (vkc == 0x7C) printf("F13 key\n");
if (vkc == 0x7D) printf("F14 key\n");
if (vkc == 0x7E) printf("F15 key\n");
if (vkc == 0x7F) printf("F16 key\n");
if (vkc == 0x80) printf("F17 key\n");
if (vkc == 0x81) printf("F18 key\n");
if (vkc == 0x82) printf("F19 key\n");
if (vkc == 0x83) printf("F20 key\n");
if (vkc == 0x84) printf("F21 key\n");
if (vkc == 0x85) printf("F22 key\n");
if (vkc == 0x86) printf("F23 key\n");
if (vkc == 0x87) printf("F24 key\n");
//if (vkc == 0x88-8F) printf("Unassigned\n");
if (vkc == 0x90) printf("NUM LOCK key\n");
if (vkc == 0x91) printf("SCROLL LOCK key\n");
//if (vkc == 0x92-96) printf("OEM specific\n");
//if (vkc == 0x97-9F) printf("Unassigned\n");
if (vkc == 0xA0) printf("Left SHIFT key\n");
if (vkc == 0xA1) printf("Right SHIFT key\n");
if (vkc == 0xA2) printf("Left CONTROL key\n");
if (vkc == 0xA3) printf("Right CONTROL key\n");
if (vkc == 0xA4) printf("Left MENU key\n");
if (vkc == 0xA5) printf("Right MENU key\n");
if (vkc == 0xA6) printf("Browser Back key\n");
if (vkc == 0xA7) printf("Browser Forward key\n");
if (vkc == 0xA8) printf("Browser Refresh key\n");
if (vkc == 0xA9) printf("Browser Stop key\n");
if (vkc == 0xAA) printf("Browser Search key\n");
if (vkc == 0xAB) printf("Browser Favorites key\n");
if (vkc == 0xAC) printf("Browser Start and Home key\n");
if (vkc == 0xAD) printf("Volume Mute key\n");
if (vkc == 0xAE) printf("Volume Down key\n");
if (vkc == 0xAF) printf("Volume Up key\n");
if (vkc == 0xB0) printf("Next Track key\n");
if (vkc == 0xB1) printf("Previous Track key\n");
if (vkc == 0xB2) printf("Stop Media key\n");
if (vkc == 0xB3) printf("Play/Pause Media key\n");
if (vkc == 0xB4) printf("Start Mail key\n");
if (vkc == 0xB5) printf("Select Media key\n");
if (vkc == 0xB6) printf("Start Application 1 key\n");
if (vkc == 0xB7) printf("Start Application 2 key\n");
//if (vkc == 0xB8-B9) printf("Reserved\n");
if (vkc == 0xBA) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key\n");
if (vkc == 0xBB) printf("For any country/region, the '+' key\n");
if (vkc == 0xBC) printf("For any country/region, the ',' key\n");
if (vkc == 0xBD) printf("For any country/region, the '-' key\n");
if (vkc == 0xBE) printf("For any country/region, the '.' key\n");
if (vkc == 0xBF) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key\n");
if (vkc == 0xC0) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key\n");
//if (vkc == 0xC1-D7) printf("Reserved\n");
//if (vkc == 0xD8-DA) printf("Unassigned\n");
if (vkc == 0xDB) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key\n");
if (vkc == 0xDC) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\\|' key\n");
if (vkc == 0xDD) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key\n");
if (vkc == 0xDE) printf("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key\n");
if (vkc == 0xDF) printf("Used for miscellaneous characters; it can vary by keyboard.\n");
if (vkc == 0xE0) printf("Reserved\n");
if (vkc == 0xE1) printf("OEM specific\n");
if (vkc == 0xE2) printf("Either the angle bracket key or the backslash key on the RT 102-key keyboard\n");
//if (vkc == 0xE3-E4) printf("OEM specific\n");
if (vkc == 0xE5) printf("IME PROCESS key\n");
if (vkc == 0xE6) printf("OEM specific\n");
if (vkc == 0xE7) printf("Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP\n");
if (vkc == 0xE8) printf("Unassigned\n");
//if (vkc == 0xE9-F5) printf("OEM specific\n");
if (vkc == 0xF6) printf("Attn key\n");
if (vkc == 0xF7) printf("CrSel key\n");
if (vkc == 0xF8) printf("ExSel key\n");
if (vkc == 0xF9) printf("Erase EOF key\n");
if (vkc == 0xFA) printf("Play key\n");
if (vkc == 0xFB) printf("Zoom key\n");
if (vkc == 0xFC) printf("Reserved\n");
if (vkc == 0xFD) printf("PA1 key\n");
if (vkc == 0xFE) printf("Clear key\n");
printf("\n");
fflush(stdout);
break;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment