Single instance
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
// execution), M3 (config watching + lifecycle), M4 (D-Bus GUI channel) and
|
||||
// M5 (hot-plug, repeat/chord decoding, error reporting to the GUI) are done.
|
||||
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
@@ -81,8 +82,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Runs the mapping of G<index+1> (0-based). A real keypress and the D-Bus
|
||||
// TestMapping call both go through here, so a test fires exactly the same
|
||||
// path. Returns whether there was an action to run.
|
||||
auto run_mapping = [&](int index, bool test) -> bool {
|
||||
// path. `hold` is set for a real press, where a "keyhold" mapping keeps its
|
||||
// keys down until the G key is released; a test always taps. Returns whether
|
||||
// there was an action to run.
|
||||
auto run_mapping = [&](int index, bool test, bool hold) -> bool {
|
||||
if (index < 0 || index >= static_cast<int>(mappings.keys.size())) {
|
||||
return false;
|
||||
}
|
||||
@@ -94,7 +97,11 @@ int main(int argc, char** argv) {
|
||||
test ? " (test)" : "");
|
||||
std::fflush(stdout);
|
||||
if (usable) {
|
||||
actions.run(mapping);
|
||||
if (hold && mapping.type == gkeymapper::ActionType::KeyHold) {
|
||||
actions.begin_hold(index, mapping);
|
||||
} else {
|
||||
actions.run(mapping);
|
||||
}
|
||||
}
|
||||
return usable;
|
||||
};
|
||||
@@ -110,7 +117,7 @@ int main(int argc, char** argv) {
|
||||
return profile != nullptr ? std::string(profile->name) : std::string();
|
||||
});
|
||||
service.set_test([&](int index, std::string& action) {
|
||||
if (!run_mapping(index, true)) {
|
||||
if (!run_mapping(index, true, false)) {
|
||||
return false;
|
||||
}
|
||||
const gkeymapper::Mapping& mapping = mappings.keys[index];
|
||||
@@ -194,6 +201,8 @@ int main(int argc, char** argv) {
|
||||
// Lost the keyboard (unplugged, or the kernel took the interface
|
||||
// back). Report it and keep looking; the daemon stays up.
|
||||
std::fprintf(stderr, "gkeymapper-daemon: read error (%d), device lost\n", n);
|
||||
// A held key would stay down forever without its release report.
|
||||
actions.release_held();
|
||||
service.notify_device_changed(false, profile != nullptr ? profile->name : "");
|
||||
device.close();
|
||||
profile = nullptr;
|
||||
@@ -207,19 +216,29 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
uint32_t state = 0;
|
||||
if (gkeymapper::daemon::decode_gkey_state(*profile, buffer, n, state)) {
|
||||
// Only a key that was not pressed before fires: a held key repeats its
|
||||
// report, and a chord arrives as one report with several bits set, so
|
||||
// every key of it fires exactly once.
|
||||
for (size_t i = 0; i < mappings.keys.size() && i < gkeymapper::kNumKeys; ++i) {
|
||||
// Only a *change* of a bit acts: a held key repeats its report, and a
|
||||
// chord arrives as one report with several bits set, so every key of
|
||||
// it fires exactly once. The falling edge matters for "keyhold",
|
||||
// which lets its keys go when the G key comes up.
|
||||
const size_t count =
|
||||
std::min(mappings.keys.size(), static_cast<size_t>(gkeymapper::kNumKeys));
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const uint32_t bit = 1u << i;
|
||||
if ((state & bit) == 0 || (pressed & bit) != 0) {
|
||||
const bool down = (state & bit) != 0;
|
||||
const bool was_down = (pressed & bit) != 0;
|
||||
if (down == was_down) {
|
||||
continue;
|
||||
}
|
||||
if (!down) {
|
||||
// No-op unless this G key holds a "keyhold" mapping.
|
||||
actions.end_hold(static_cast<int>(i));
|
||||
continue;
|
||||
}
|
||||
const gkeymapper::Mapping& mapping = mappings.keys[i];
|
||||
const std::string action =
|
||||
std::string(gkeymapper::action_type_token(mapping.type)) + ":" +
|
||||
mapping.target;
|
||||
run_mapping(static_cast<int>(i), false);
|
||||
run_mapping(static_cast<int>(i), false, true);
|
||||
service.notify_key_pressed(static_cast<int>(i), action);
|
||||
}
|
||||
pressed = state;
|
||||
@@ -247,6 +266,9 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// Shutting down: a "keyhold" key that is still down belongs to the session,
|
||||
// not to the daemon, so let it go before the virtual keyboard disappears.
|
||||
actions.release_held();
|
||||
service.notify_device_changed(false, profile != nullptr ? profile->name : "");
|
||||
service.pump();
|
||||
device.close();
|
||||
|
||||
Reference in New Issue
Block a user