Created
January 10, 2025 23:39
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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