summaryrefslogtreecommitdiff
path: root/main.cpp
blob: d5976290fc32e700e02a5bf37c095df21c64e7e7 (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
#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 << " <key>\n"
			<< "  " << argv0 << " [<key> <output>]*\n"
			<< "\n"
			<< "Keys are specified with the X11 keysym name or ID (in hexadecimal with \"0x\").\n"
			<< "Both are given by xev(1).\n"
			<< "Examples:\n"
			<< "- KP_1 and 0xffb1 are valid key values for the '1' key on the numpad.\n"
			<< "- XF86Launch7 and 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"
			;
	}

	std::vector<std::pair<x::Keysym, std::string>> parse_watchlist(int argc, char **argv) {
		std::vector<std::pair<x::Keysym, 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::Keysym> msym;

			if (argv[i][0] == '0' && argv[i][1] == 'x') {
				char *endp;
				unsigned code = strtol(argv[i] + 2, &endp, 16);
				if (argv[i][2] == '\0' || *endp != '\0') {
					std::cerr << "Invalid hexadecimal number in '" << argv[i] << "'\n";
					exit(1);
				}
				msym = x::Keysym{code};
			}

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

			if (!msym) {
				std::cerr << "Unknown keysym '" << 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 keysym '" << argv[i] << "'\n";
				exit(1);
			} else {
				output = argv[i+1];
				i++;  // skip output
			}

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

		return watchlist;
	}
}

int main(int argc, char **argv) {
	std::vector<std::pair<x::Keysym, std::string>> watchlist = parse_watchlist(argc, argv);

	auto dpypair = x::XOpenDisplayRAII(nullptr);
	Display *dpy = dpypair.first;

	Window root = DefaultRootWindow(dpy);

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

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

	XSelectInput(dpy, root, KeyPressMask);
	while (true) {
		XEvent ev;
		XNextEvent(dpy, &ev);
		if (ev.type != KeyPress) continue;
		for (size_t i = 0; i < watchlist.size(); i++) {
			if (ev.xkey.keycode == (unsigned int)keycodes[i]) {
				if (watchlist[i].second.size() > 0) std::cout << watchlist[i].second << std::endl;
				return 0;
			}
		}
	}
}