Skip to content

Instantly share code, notes, and snippets.

@dharmx
Created September 24, 2022 20:39
Show Gist options
  • Save dharmx/d11c1e93d20ebc9c79ffd025b1644f5e to your computer and use it in GitHub Desktop.
Save dharmx/d11c1e93d20ebc9c79ffd025b1644f5e to your computer and use it in GitHub Desktop.
* { all: unset; }
label {
font-family: Phosphor, Dosis;
font-size: 4em;
}
(defwindow window
:windowtype "normal"
:wm-ignore true
:geometry (geometry :anchor "center" :y 100 :width "10%" :height "10%")
(label :text {keymon}))
(deflisten keymon "./xkb -s  ")
/* cc -O3 --std=c11 -pedantic -Wall xkb -o xkb.c -lX11 -lXi */
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/XI2.h>
#include <X11/extensions/XInput2.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define HELP_MESSAGE "xkb -[p|l|h] ...\n"
#define BEGIN_XOP _setup();
#define END_XOP _finish();
Display* display = NULL;
typedef struct dump_t {
int xi_opcode;
int first_event;
int first_error;
int xkb_opcode;
int xkb_event;
int xkb_error;
int xkb_major_inout;
int xkb_minor_inout;
Status query_result;
Status xi_query_result;
Status xkb_query_result;
} dump_t;
void
_setup(void) {
display = XOpenDisplay(None);
}
void
_finish(void) {
if (display != NULL) {
XFlush(display);
XCloseDisplay(display);
}
}
void
list_extensions(char* delimiter) {
delimiter = delimiter ? delimiter : "\n";
int size;
char** extensions = XListExtensions(display, &size);
for (int index = 1; index < size; ++index) printf("%s%s", extensions[index], delimiter);
printf("%s", strcmp(delimiter, "\n") ? "\n" : "");
}
void init_logger(dump_t* dump, int version[2]) {
dump->query_result = XQueryExtension(display, "XInputExtension", &dump->xi_opcode, &dump->first_event, &dump->first_error);
dump->xi_query_result = XIQueryVersion(display, &version[0], &version[1]);
dump->xkb_query_result = XkbQueryExtension(display,
&dump->xkb_opcode,
&dump->xkb_event,
&dump->xkb_error,
&dump->xkb_major_inout,
&dump->xkb_minor_inout);
}
Bool
handle_rawkey(XIRawEvent* event, const char* prefix, Bool only_icon) {
KeySym keysym = XkbKeycodeToKeysym(display, event->detail, None, None);
if (keysym == NoSymbol) return False;
char* keystring = XKeysymToString(keysym);
if (keystring == NULL) return False;
printf("%s%s\n", prefix, only_icon ? "" : keystring);
fflush(stdout);
return True;
}
void
start_logger(const char* up, const char* down, Bool only_icon) {
int version[2] = {2, 0};
dump_t dump;
memset(&dump, None, sizeof(dump_t));
init_logger(&dump, version);
if (!dump.query_result) perror("ERROR: XInputExtension does not exist!");
if (dump.xi_query_result != Success) perror("ERROR: XI2 is not supported!");
if (!dump.xkb_query_result) perror("ERROR: XKBlib is not compatible!");
XIEventMask mask;
memset(&mask, None, sizeof(XIEventMask));
mask.deviceid = XIAllMasterDevices;
mask.mask_len = XIMaskLen(XI_LASTEVENT);
mask.mask = calloc(mask.mask_len, sizeof(char));
XISetMask(mask.mask, XI_RawKeyPress);
XISetMask(mask.mask, XI_RawKeyRelease);
Window root = DefaultRootWindow(display);
XISelectEvents(display, root, &mask, 1);
XFree(mask.mask);
XEvent event;
XGenericEventCookie* cookie;
memset(&event, None, sizeof(XEvent));
while (True) {
cookie = &event.xcookie;
XNextEvent(display, &event);
if (XGetEventData(display, cookie) && cookie->type == GenericEvent && cookie->extension == dump.xi_opcode) {
XIRawEvent* raw_event = cookie->data;
switch (cookie->evtype) {
case XI_RawKeyPress:
if (!handle_rawkey(raw_event, down, only_icon)) continue;
break;
case XI_RawKeyRelease:
if (!handle_rawkey(raw_event, up, only_icon)) continue;
break;
default: continue;
}
XFreeEventData(display, cookie);
}
}
}
int
main(int argc, char** argv) {
BEGIN_XOP
int optlog = 0;
int option;
while ((option = getopt(argc, argv, ":spl::h")) != -1) {
switch (option) {
case 'p': optlog = 1; break;
case 's': optlog = 2; break;
case 'l': list_extensions(optarg); break;
case 'h': fprintf(stdout, HELP_MESSAGE); break;
case '?': fprintf(stderr, HELP_MESSAGE); break;
default: perror("undefined"); break;
}
}
switch (optlog) {
case 1:
if (argc - optind == 2) start_logger(argv[optind], argv[optind + 1], False);
else start_logger("@", "~", False);
case 2:
if (argc - optind == 2) start_logger(argv[optind], argv[optind + 1], True);
else start_logger("@", "~", False);
}
END_XOP
return EXIT_SUCCESS;
}
@dharmx
Copy link
Author

dharmx commented Sep 24, 2022

keymon.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment