summaryrefslogtreecommitdiff
path: root/main.cpp
blob: a3679abb762269479a869dbaee034db1b6c2c9bf (plain)
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <vector>
#include <string>
#include <optional>
#include <utility>
#include <cstring>
#include "xutil.h"
#include "keysym_table.h"


namespace {
	void usage(const char *argv0) {
		std::cerr
			<< "Usage:\n"
			<< "  " << argv0 << " --which\n"
			<< "  " << argv0 << " <key>\n"
			<< "  " << argv0 << " [<key> <output> | s:<keysym> <output> | c:<keycode> <output>]*\n"
			<< "\n"
			<< "Keys are specified with the X11 keysym name, its keysym ID (prefixed with 's:'),\n"
			<< "or its keycode (prefixed with 'c:'). The keycode is typically non-portable\n"
			<< "because it depends on the particular keyboard hardware.\n"
			<< "xev(1) prints the keysym name and the keysym ID (in hexadecimal).\n"
			<< "Keysym IDs and keycodes can be given in decimal and in hexadecimal (with 0x).\n"
			<< "Examples:\n"
			<< "- KP_1 and s:0xffb1 are valid key values for the '1' key on the numpad.\n"
			<< "- XF86Launch7 and s:0x1008ff47 are valid key values for F16 on an Apple keyboard.\n"
			<< "The values can also be found in /usr/include/xkbcommon/xkbcommon-keysyms.h .\n"
			<< "\n"
			<< "If a single key is given, the program grabs that key and exits when the key is\n"
			<< "pressed. If multiple keys are given, the output string corresponding to the\n"
			<< "pressed key will be printed before exiting.\n"
			<< "\n"
			<< "In --which mode, the program will grab the entire keyboard, wait until the first\n"
			<< "pressed key, and print its keycode for use with 'c:'.\n"
			;
	}

	std::vector<std::pair<x::Keycode, std::string>> parse_watchlist(Display *dpy, int argc, char **argv) {
		std::vector<std::pair<x::Keycode, std::string>> watchlist;

		if (argc <= 1) {
			usage(argv[0]);
			exit(1);
		}

		for (int i = 1; i < argc; i++) {
			if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
				usage(argv[0]);
				exit(0);
			}

			std::optional<x::Keycode> mcode;

			if ((argv[i][0] == 's' || argv[i][0] == 'c') && argv[i][1] == ':') {
				unsigned number;
				if (argv[i][2] == '0' && argv[i][3] == 'x') {
					char *endp;
					number = strtol(argv[i] + 2, &endp, 16);
					if (argv[i][2] == '\0' || *endp != '\0') {
						std::cerr << "Invalid hexadecimal number in '" << argv[i] << "'\n";
						exit(1);
					}
				} else {
					char *endp;
					number = strtol(argv[i] + 2, &endp, 10);
					if (argv[i][2] == '\0' || *endp != '\0') {
						std::cerr << "Invalid decimal number in '" << argv[i] << "'\n";
						exit(1);
					}
				}
				if (argv[i][0] == 's') mcode = x::Keysym{number}.toCode(dpy);
				else mcode = x::Keycode{number};
			}

			if (!mcode) {
				for (size_t j = 0; j < keysym_table.size(); j++) {
					if (strcmp(argv[i], keysym_table[j].first) == 0) {
						mcode = keysym_table[j].second.toCode(dpy);
						break;
					}
				}
			}

			if (!mcode) {
				std::cerr << "Unknown key name '" << argv[i] << "'\n";
				exit(1);
			}

			std::string output;
			if (i == 1 && argc == 2) {
				// empty output
			} else if (i >= argc - 1) {
				std::cerr << "Missing output for key '" << argv[i] << "'\n";
				exit(1);
			} else {
				output = argv[i+1];
				i++;  // skip output
			}

			watchlist.emplace_back(*mcode, std::move(output));
		}

		return watchlist;
	}
}

int main(int argc, char **argv) {
	auto dpypair = x::XOpenDisplayRAII(nullptr);
	Display *dpy = dpypair.first;

	if (argc >= 2 && strcmp(argv[1], "--which") == 0) {
		x::globalKeyboardGrab(dpy, [](const XKeyEvent &ev) -> bool {
			std::cout << "c:" << ev.keycode << std::endl;
			return true;
		});
		return 0;
	}

	std::vector<std::pair<x::Keycode, std::string>> watchlist = parse_watchlist(dpy, argc, argv);

	Window root = DefaultRootWindow(dpy);

	std::vector<x::UponExit<std::function<void()>>> cleaners;
	cleaners.reserve(watchlist.size());

	for (const auto &p : watchlist) {
		cleaners.push_back(x::XGrabKeyRAII(dpy, p.first, AnyModifier, root));
	}

	XSelectInput(dpy, root, KeyPressMask);
	while (true) {
		XEvent ev;
		XNextEvent(dpy, &ev);
		if (ev.type != KeyPress) continue;
		for (const auto &p : watchlist) {
			if (ev.xkey.keycode == (unsigned int)p.first) {
				if (p.second.size() > 0) std::cout << p.second << std::endl;
				return 0;
			}
		}
	}
}