summaryrefslogtreecommitdiff
path: root/seqmatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'seqmatcher.cpp')
-rw-r--r--seqmatcher.cpp72
1 files changed, 72 insertions, 0 deletions
diff --git a/seqmatcher.cpp b/seqmatcher.cpp
new file mode 100644
index 0000000..f0abedc
--- /dev/null
+++ b/seqmatcher.cpp
@@ -0,0 +1,72 @@
+#include "seqmatcher.h"
+#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;
+}