Single instance
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "action.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
@@ -19,17 +20,28 @@ bool ActionRunner::init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActionRunner::resolve_shortcut(const std::string& target,
|
||||
std::vector<gkeymapper::ShortcutStep>& steps) {
|
||||
// Resolve key names through the active keyboard layout.
|
||||
gkeymapper::KeyResolver resolve = [this](const std::string& name) {
|
||||
return keymap_.key_code(name);
|
||||
};
|
||||
if (!gkeymapper::parse_shortcut(target, steps, resolve)) {
|
||||
std::fprintf(stderr, "gkeymapper-daemon: invalid shortcut '%s'\n", target.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActionRunner::run(const gkeymapper::Mapping& mapping) {
|
||||
switch (mapping.type) {
|
||||
case gkeymapper::ActionType::Key: {
|
||||
case gkeymapper::ActionType::Key:
|
||||
case gkeymapper::ActionType::KeyHold: {
|
||||
// A test (TestMapping) and any other caller that cannot wait for a
|
||||
// key release fires a hold mapping as a tap. A real G-key press goes
|
||||
// through begin_hold/end_hold instead.
|
||||
std::vector<gkeymapper::ShortcutStep> steps;
|
||||
// Resolve key names through the active keyboard layout.
|
||||
gkeymapper::KeyResolver resolve = [this](const std::string& name) {
|
||||
return keymap_.key_code(name);
|
||||
};
|
||||
if (!gkeymapper::parse_shortcut(mapping.target, steps, resolve)) {
|
||||
std::fprintf(stderr, "gkeymapper-daemon: invalid shortcut '%s'\n",
|
||||
mapping.target.c_str());
|
||||
if (!resolve_shortcut(mapping.target, steps)) {
|
||||
return;
|
||||
}
|
||||
keyboard_.play(steps);
|
||||
@@ -44,6 +56,82 @@ void ActionRunner::run(const gkeymapper::Mapping& mapping) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionRunner::begin_hold(int index, const gkeymapper::Mapping& mapping) {
|
||||
if (index < 0 || index >= static_cast<int>(held_.size())) {
|
||||
return false;
|
||||
}
|
||||
end_hold(index); // a repeated press must not stack codes
|
||||
|
||||
std::vector<gkeymapper::ShortcutStep> steps;
|
||||
if (!resolve_shortcut(mapping.target, steps)) {
|
||||
return false;
|
||||
}
|
||||
if (steps.size() != 1) {
|
||||
// Holding only means something for a single step (that is where the
|
||||
// "same duration as the key press" idea comes from, see README). A
|
||||
// sequence would have to hold several chords at once, so it is replayed
|
||||
// as a tap instead - exactly like a "key" mapping.
|
||||
std::fprintf(stderr,
|
||||
"gkeymapper-daemon: 'keyhold:%s' is not a single step, replaying it "
|
||||
"as a tap\n",
|
||||
mapping.target.c_str());
|
||||
keyboard_.play(steps);
|
||||
return false;
|
||||
}
|
||||
if (!keyboard_.is_open()) {
|
||||
return false; // no virtual keyboard: nothing to hold
|
||||
}
|
||||
|
||||
const std::vector<unsigned short> codes = step_key_codes(steps.front());
|
||||
if (codes.empty()) {
|
||||
return false;
|
||||
}
|
||||
held_[static_cast<size_t>(index)] = codes;
|
||||
keyboard_.press(codes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActionRunner::end_hold(int index) {
|
||||
if (index < 0 || index >= static_cast<int>(held_.size())) {
|
||||
return;
|
||||
}
|
||||
std::vector<unsigned short>& codes = held_[static_cast<size_t>(index)];
|
||||
if (codes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release in press order; UinputKeyboard::release() turns that into the
|
||||
// reverse order, so the key goes up first and the modifiers go up last. A
|
||||
// code another G key also holds stays down until *its* G key is released.
|
||||
std::vector<unsigned short> up;
|
||||
for (unsigned short code : codes) {
|
||||
bool still_held = false;
|
||||
for (size_t other = 0; other < held_.size(); ++other) {
|
||||
if (other == static_cast<size_t>(index)) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(held_[other].begin(), held_[other].end(), code) !=
|
||||
held_[other].end()) {
|
||||
still_held = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!still_held) {
|
||||
up.push_back(code);
|
||||
}
|
||||
}
|
||||
codes.clear();
|
||||
if (!up.empty()) {
|
||||
keyboard_.release(up);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionRunner::release_held() {
|
||||
for (size_t i = 0; i < held_.size(); ++i) {
|
||||
end_hold(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionRunner::tap_key(unsigned short code) {
|
||||
keyboard_.tap(code);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user