Skip to content

Instantly share code, notes, and snippets.

@ardnew
Created January 10, 2025 23:39
Show Gist options
  • Save ardnew/cceffbb9596337031abd86fa6122007b to your computer and use it in GitHub Desktop.
Save ardnew/cceffbb9596337031abd86fa6122007b to your computer and use it in GitHub Desktop.
C command-line utility to control/query the state of Caps-Lock in X11
// capslock.c - Andrew Shultzabarger (9 Jan 2025)
//
// Command-line utility to control/query the state of Caps-Lock in X11.
//
// To compile:
// gcc -g -Wall -std=gnu99 -o capslock -lX11 capslock.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
void usage()
{
printf("Usage: capslock [-v] [[on|1]|[off|0]|state]\n");
}
Bool state(Display *display, XkbStateRec *xkb_state)
{
Status status = XkbGetState(display, XkbUseCoreKbd, xkb_state);
if (status) {
fprintf(stderr, "XkbGetState returned %d\n", status);
}
return status == 0;
}
// If the --verbose|-v flag is provided in any mode,
// the keyboard state is first changed/queried,
// and then it is printed with all detail shown here.
void debug(XkbStateRec xkb_state)
{
printf("state.group=%02x\n", xkb_state.group);
printf("state.locked_group=%02x\n", xkb_state.locked_group);
printf("state.base_group=%02x\n", xkb_state.base_group);
printf("state.latched_group=%02x\n", xkb_state.latched_group);
printf("state.mods=%02x\n", xkb_state.mods);
printf("state.base_mods=%02x\n", xkb_state.base_mods);
printf("state.latched_mods=%02x\n", xkb_state.latched_mods);
printf("state.locked_mods=%02x\n", xkb_state.locked_mods);
printf("state.compat_state=%02x\n", xkb_state.compat_state);
printf("state.grab_mods=%02x\n", xkb_state.grab_mods);
printf("state.compat_grab_mods=%02x\n", xkb_state.compat_grab_mods);
printf("state.lookup_mods=%02x\n", xkb_state.lookup_mods);
printf("state.compat_lookup_mods=%02x\n", xkb_state.compat_lookup_mods);
printf("state.ptr_buttons=%02x\n", xkb_state.ptr_buttons);
}
int main(int argc, char *argv[])
{
// Parse argv for the following flags:
// --help|-h
// --verbose|-v
// And the following arguments:
// on|1
// off|0
// state
// If no arguments, print usage and exit.
if (argc < 2) {
usage();
return 0;
}
// Use --verbose or -v to print the state of the keyboard.
Bool verbose = False;
Bool chlock = False;
// If any argument is --help or -h, print usage and exit.
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
usage();
return 0;
}
if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
verbose = True;
}
}
// If the last argument is on|1, send the LockMask.
// If the last argument is off|0, send 0.
// If the last argument is state, print the state of CapsLock.
// If the last argument is anything else, print usage and exit.
unsigned int SetMask = 0;
if (strcmp(argv[argc - 1], "on") == 0 ||
strcmp(argv[argc - 1], "1") == 0) {
SetMask = LockMask;
chlock = True;
} else if (strcmp(argv[argc - 1], "off") == 0 ||
strcmp(argv[argc - 1], "0") == 0) {
chlock = True;
} else if (strcmp(argv[argc - 1], "state") == 0) {
;
} else {
usage();
return 1;
}
// Open the display defined in the DISPLAY environment variable.
const char *name = getenv("DISPLAY");
if (name == NULL) {
fprintf(stderr, "DISPLAY environment variable not set\n");
return 3;
}
Display *display = XOpenDisplay(name);
if (display == NULL) {
fprintf(stderr, "Couldn't open display\n");
return 2;
}
if (chlock) {
Bool sent = XkbLockModifiers(display, XkbUseCoreKbd, LockMask, SetMask);
if (!sent) {
fprintf(stderr, "Couldn't send LatchLockState\n");
return 1;
}
}
XkbStateRec xkb_state;
if (state(display, &xkb_state)) {
if (verbose) {
debug(xkb_state);
}
if (!chlock) { // true iff "state" requested
fprintf(
stdout,
verbose ? "CapsLockEnabled=%x\n" : "%x\n",
(xkb_state.locked_mods & LockMask) != 0
);
}
}
int err = XCloseDisplay(display);
if (err) {
fprintf(stderr, "XCloseDisplay returned %d\n", err);
return 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment