summaryrefslogtreecommitdiff
path: root/seqmatcher.cpp
blob: 5858ea798b949213cb4e1d9513837bb2aad738c0 (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
#include "seqmatcher.h"
#include <stdexcept>
#include <cassert>


SeqMatcher::SeqMatcher(Display *dpy) : dpy{dpy} {}

SeqMatcher::SeqMatcher(Display *dpy, std::vector<SymSequence> seqs)
		: dpy{dpy} {
	for (const auto &seq : seqs) addSequence(seq.syms, seq.callback);
}

void SeqMatcher::addSequence(const std::vector<x::Keysym> &syms, Callback callback) {
	if (syms.empty()) {
		throw std::logic_error("Cannot register empty key sequence");
	}

	Node *current = &rootNode;
	for (x::Keysym sym : syms) {
		if (std::holds_alternative<Callback>(current->v)) {
			throw std::logic_error("Overlapping key sequences (second is longer)");
		} else {
			if (!std::holds_alternative<NodeMap>(current->v)) {
				current->v.emplace<NodeMap>();
			}
			NodeMap &map = std::get<NodeMap>(current->v);
			x::Keycode code = sym.toCode(dpy);
			auto it = map.find(code);
			if (it != map.end()) {
				current = it->second.get();
			} else {
				current = map.emplace(sym.toCode(dpy), std::make_unique<Node>()).first->second.get();
			}
		}
	}

	if (auto *map = std::get_if<NodeMap>(&current->v)) {
		if (!map->empty()) {
			throw std::logic_error("Overlapping key sequences (second is shorter)");
		}
	}
	if (std::holds_alternative<Callback>(current->v)) {
		throw std::logic_error("Overlapping key sequences (equally long)");
	}
	current->v.emplace<Callback>(callback);
}

std::optional<SeqMatcher::Callback> SeqMatcher::observe(const XKeyEvent &ev) {
	auto *map = std::get_if<NodeMap>(&curNode->v);
	assert(map);

	auto it = map->find(x::Keycode{ev.keycode});
	if (it == map->end()) {
		// Sequence not found
		reset();
		return [dpy = dpy]() { x::bel(dpy); };
	}

	curNode = it->second.get();

	if (auto *cb = std::get_if<Callback>(&curNode->v)) {
		// Sequence completed
		reset();
		return *cb;
	}

	// Need more keys
	return std::nullopt;
}

void SeqMatcher::reset() {
	curNode = &rootNode;
}