summaryrefslogtreecommitdiff
path: root/xlockkbd.c
blob: 2e970c52e7f754359801e1ae40964138e86a13cc (plain) (blame)
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
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>

struct named_mask {
	const char *name;
	unsigned int mask;
};

static const struct named_mask named_masks[] = {
	{ "Shift", ShiftMask },
	{ "Control", ControlMask },
	{ "Ctrl", ControlMask },
	{ "Alt", Mod1Mask },
	{ "Super", Mod4Mask },
	{ "Mod1", Mod1Mask },
	{ "Mod2", Mod2Mask },
	{ "Mod3", Mod3Mask },
	{ "Mod4", Mod4Mask },
	{ "Mod5", Mod5Mask }
};
#define len_named_masks (sizeof(named_masks) / sizeof(*named_masks))

static unsigned int str_to_mask(const char *s)
{
	for (size_t i = 0; i < len_named_masks; ++i) {
		const struct named_mask *m = named_masks + i;
		if (!strcmp(s, m->name))
			return m->mask;
	}

	return 0;
}

static void die(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);

	char *s;
	if (vasprintf(&s, fmt, ap) != -1)
		fprintf(stderr, "xlockkbd: %s\n", s);
	else
		fprintf(stderr, "xlockkbd: unknown error\n");

	va_end(ap);
	exit(1);
}

static bool grab_kbd(Display *d, int screen)
{
	Window w = RootWindow(d, screen);
	int res;

	// attempt to grab keyboard for 500ms if already grabbed.
	// thanks to slock code for the pointer.
	for (int i = 0;;) {
		res = XGrabKeyboard(d, w, True, GrabModeAsync, GrabModeAsync,
			CurrentTime);
		if (res != AlreadyGrabbed || ++i > 5)
			break;

		usleep(100000);
	}

	return (res == GrabSuccess);
}

static void wait_for_release(Display *d, KeySym key, unsigned int mask)
{
	XEvent ev;
	while (!XNextEvent(d, &ev)) {
		if (ev.type == KeyPress &&
				XLookupKeysym(&ev.xkey, 0) == key &&
				(mask & ev.xkey.state) == mask)
			break;
	}
}

int main(int argc, char **argv)
{
	unsigned int mask = 0, arg_mask;
	KeySym key = NoSymbol, arg_key;

	for (int i = 1; i < argc; ++i) {
		if ((arg_mask = str_to_mask(argv[i]))) {
			mask = mask | arg_mask;
		} else if (key != NoSymbol) {
			die("more than one non-modifier key provided");
		} else {
			if ((arg_key = XStringToKeysym(argv[i])) == NoSymbol)
				die("unknown key: %s", argv[i]);
			if (arg_key >= XK_A && arg_key <= XK_Z)
				die("use Shift modifier for uppercase key: %s",
					argv[i]);
			key = arg_key;
		}
	}

	if (key == NoSymbol) {
		if (mask)
			die("no non-modifier key provided");
		key = XK_Escape;
	}

	Display *d = XOpenDisplay(NULL);
	if (!d)
		die("cannot open display");

	for (int i = 0; i < ScreenCount(d); ++i) {
		if (!grab_kbd(d, i))
			die("failed to grab keyboard");
	}

	wait_for_release(d, key, mask);
	return 0;
}