Adapt TapDance plugin to KeyEvent handlers

Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
pull/1024/head
Michael Richters 4 years ago
parent 0bf128be09
commit f931a59efc
No known key found for this signature in database
GPG Key ID: 1288FD13E4EEF0C0

@ -19,6 +19,7 @@
#include <Kaleidoscope-FocusSerial.h>
#include "kaleidoscope/keyswitch_state.h"
#include "kaleidoscope/layers.h"
#include "kaleidoscope/KeyEventTracker.h"
namespace kaleidoscope {
namespace plugin {
@ -26,153 +27,124 @@ namespace plugin {
// --- config ---
uint16_t TapDance::time_out = 200;
KeyAddr TapDance::release_addr_ = KeyAddr{KeyAddr::invalid_state};
uint8_t TapDance::tap_count_ = 0;
KeyEventTracker TapDance::event_tracker_;
// --- api ---
void TapDance::actionKeys(uint8_t tap_count,
ActionType tap_dance_action,
ActionType action,
uint8_t max_keys,
const Key tap_keys[]) {
if (event_queue_.isEmpty())
return;
if (tap_count > max_keys)
tap_count = max_keys;
Key key = tap_keys[tap_count - 1].readFromProgmem();
KeyEvent event = event_queue_.event(0);
event.key = tap_keys[tap_count - 1].readFromProgmem();
switch (tap_dance_action) {
case Tap:
break;
case Interrupt:
case Timeout:
if (event_queue_.isEmpty())
break;
{
KeyAddr td_addr = event_queue_.addr(0);
bool key_released = (live_keys[td_addr] == Key_Transparent);
handleKeyswitchEvent(key, td_addr, IS_PRESSED | INJECTED);
if (key_released)
release_addr_ = td_addr;
}
break;
case Hold:
case Release:
break;
if (action == Interrupt || action == Timeout) {
event_queue_.shift();
Runtime.handleKeyswitchEvent(event);
} else if (action == Tap && tap_count == max_keys) {
tap_count_ = 0;
event_queue_.clear();
Runtime.handleKeyswitchEvent(event);
}
}
void TapDance::flushQueue(KeyAddr ignored_addr) {
while (! event_queue_.isEmpty()) {
KeyEvent queued_event = event_queue_.event(0);
event_queue_.shift();
if (queued_event.addr != ignored_addr)
Runtime.handleKeyswitchEvent(queued_event);
}
}
// --- hooks ---
EventHandlerResult TapDance::onNameQuery() {
return ::Focus.sendName(F("TapDance"));
}
EventHandlerResult TapDance::onKeyswitchEvent(Key &key,
KeyAddr key_addr,
uint8_t key_state) {
if (key_state & INJECTED)
EventHandlerResult TapDance::onKeyswitchEvent(KeyEvent &event) {
// If the plugin has already processed and released this event, ignore it.
// There's no need to update the event tracker explicitly.
if (event_tracker_.shouldIgnore(event)) {
// We should never get an event that's in our queue here, but just in case
// some other plugin sends one, abort.
if (event_queue_.shouldAbort(event))
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
if (event_queue_.isEmpty()) {
if (keyToggledOn(key_state) && isTapDanceKey(key)) {
// Begin a new TapDance sequence:
uint8_t td_id = key.getRaw() - ranges::TD_FIRST;
tapDanceAction(td_id, key_addr, 1, Tap);
event_queue_.append(key_addr, key_state);
return EventHandlerResult::EVENT_CONSUMED;
// If event.addr is not a physical key, ignore it; some other plugin injected it.
if (! event.addr.isValid() || (event.state & INJECTED) != 0) {
return EventHandlerResult::OK;
}
if (keyToggledOff(event.state)) {
if (event_queue_.isEmpty())
return EventHandlerResult::OK;
event_queue_.append(event);
return EventHandlerResult::ABORT;
}
uint8_t td_count = event_queue_.length();
if (event_queue_.isEmpty() && !isTapDanceKey(event.key))
return EventHandlerResult::OK;
KeyAddr td_addr = event_queue_.addr(0);
Key td_key = Layer.lookup(td_addr);
Key td_key = Layer.lookupOnActiveLayer(td_addr);
uint8_t td_id = td_key.getRaw() - ranges::TD_FIRST;
if (keyToggledOn(key_state)) {
if (key_addr == td_addr) {
// The same TapDance key was pressed again; continue the sequence:
tapDanceAction(td_id, td_addr, ++td_count, Tap);
} else {
// A different key was pressed; interrupt the sequeunce:
tapDanceAction(td_id, td_addr, td_count, Interrupt);
event_queue_.clear();
// If the sequence was interrupted by another TapDance key, start the new
// sequence:
if (isTapDanceKey(Layer.lookup(key_addr))) {
td_id = key.getRaw() - ranges::TD_FIRST;
tapDanceAction(td_id, key_addr, 1, Tap);
}
}
// Any key that toggles on while a TapDance sequence is live gets added to
// the queue. If it interrupted the queue, we need to hold off on processing
// that event until the next cycle to guarantee that the events appear in
// order on the host.
event_queue_.append(key_addr, key_state);
if (isTapDanceKey(key))
return EventHandlerResult::EVENT_CONSUMED;
return EventHandlerResult::ABORT;
} else if (keyIsPressed(key_state)) {
// Until a key press event has been released from the queue, its "hold
// event" must be suppressed every cycle.
for (uint8_t i{0}; i < event_queue_.length(); ++i) {
if (event_queue_.addr(i) == key_addr) {
return EventHandlerResult::ABORT;
}
}
}
// We always indicate that other plugins don't need to handle TapDance keys,
// but we do allow them to show up as active keys when they're held. This way,
// when one times out, if it's not being held any longer, we can send the
// release event.
if (isTapDanceKey(key))
return EventHandlerResult::EVENT_CONSUMED;
// This key is being held, but is not in the queue, or it toggled off, but is
// not (currently) a TapDance key.
if (! event_queue_.isEmpty() &&
event.addr != event_queue_.addr(0)) {
// Interrupt: Call `tapDanceAction()` first, so it will have access to the
// TapDance key press event that needs to be sent, then flush the queue.
tapDanceAction(td_id, td_addr, tap_count_, Interrupt);
flushQueue();
tap_count_ = 0;
// If the event isn't another TapDance key, let it proceed. If it is, fall
// through to the next block, which handles "Tap" actions.
if (! isTapDanceKey(event.key))
return EventHandlerResult::OK;
}
EventHandlerResult TapDance::afterEachCycle() {
if (release_addr_.isValid()) {
handleKeyswitchEvent(Key_NoKey, release_addr_, WAS_PRESSED | INJECTED);
release_addr_ = KeyAddr{KeyAddr::invalid_state};
}
Key event_key;
// Purge any non-TapDance key events from the front of the queue.
while (! event_queue_.isEmpty()) {
KeyAddr event_addr = event_queue_.addr(0);
event_key = Layer.lookup(event_addr);
if (isTapDanceKey(event_key)) {
break;
}
handleKeyswitchEvent(event_key, event_addr, IS_PRESSED | INJECTED);
event_queue_.shift();
// Tap: First flush the queue, ignoring the previous press and release events
// for the TapDance key, then add the new tap to the queue (it becomes the
// first entry).
flushQueue(event.addr);
event_queue_.append(event);
tapDanceAction(td_id, td_addr, ++tap_count_, Tap);
return EventHandlerResult::ABORT;
}
EventHandlerResult TapDance::afterEachCycle() {
// If there's no active TapDance sequence, there's nothing to do.
if (event_queue_.isEmpty())
return EventHandlerResult::OK;
// The first event in the queue is now guaranteed to be a TapDance key.
uint8_t td_id = event_key.getRaw() - ranges::TD_FIRST;
KeyAddr td_addr = event_queue_.addr(0);
Key td_key = Layer.lookupOnActiveLayer(td_addr);
uint8_t td_id = td_key.getRaw() - ranges::TD_FIRST;
// Check for timeout
uint8_t td_count = event_queue_.length();
uint16_t start_time = event_queue_.timestamp(td_count - 1);
uint16_t start_time = event_queue_.timestamp(0);
if (Runtime.hasTimeExpired(start_time, time_out)) {
tapDanceAction(td_id, td_addr, td_count, Timeout);
event_queue_.clear();
// There's still a race condition here, but it's a minor one. If a TapDance
// sequence times out in the `afterEachCycle()` handler, then another key
// toggles on in the following scan cycle, and that key is handled first,
// the two events could show up out of order on the host. The probability of
// this happening is low, and event-driven Kaleidoscope will fix it
// completely, so I'm willing to accept the risk for now.
tapDanceAction(td_id, td_addr, tap_count_, Timeout);
flushQueue();
tap_count_ = 0;
}
return EventHandlerResult::OK;
}
}
}
} // namespace plugin
} // namespace kaleidoscope
__attribute__((weak)) void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count,
kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {

@ -20,7 +20,9 @@
#include "kaleidoscope/Runtime.h"
#include "kaleidoscope/LiveKeys.h"
#include <Kaleidoscope-Ranges.h>
#include "kaleidoscope/KeyAddr.h"
#include "kaleidoscope/KeyAddrEventQueue.h"
#include "kaleidoscope/KeyEventTracker.h"
#define TD(n) Key(kaleidoscope::ranges::TD_FIRST + n)
@ -49,7 +51,7 @@ class TapDance : public kaleidoscope::Plugin {
void actionKeys(uint8_t tap_count, ActionType tap_dance_action, uint8_t max_keys, const Key tap_keys[]);
EventHandlerResult onNameQuery();
EventHandlerResult onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t keyState);
EventHandlerResult onKeyswitchEvent(KeyEvent &event);
EventHandlerResult afterEachCycle();
static constexpr bool isTapDanceKey(Key key) {
@ -64,12 +66,17 @@ class TapDance : public kaleidoscope::Plugin {
// The event queue stores a series of press and release events.
KeyAddrEventQueue<queue_capacity_> event_queue_;
static KeyAddr release_addr_;
static KeyEventTracker event_tracker_;
// The number of taps in the current TapDance sequence.
static uint8_t tap_count_;
void flushQueue(KeyAddr ignored_addr = KeyAddr::none());
};
}
}
} // namespace plugin
} // namespace kaleidoscope
void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count,
kaleidoscope::plugin::TapDance::ActionType tap_dance_action);

Loading…
Cancel
Save