|
|
@ -23,7 +23,9 @@
|
|
|
|
#include <Kaleidoscope-FocusSerial.h>
|
|
|
|
#include <Kaleidoscope-FocusSerial.h>
|
|
|
|
#include "kaleidoscope/progmem_helpers.h"
|
|
|
|
#include "kaleidoscope/progmem_helpers.h"
|
|
|
|
#include "kaleidoscope/layers.h"
|
|
|
|
#include "kaleidoscope/layers.h"
|
|
|
|
|
|
|
|
#include "kaleidoscope/KeyEvent.h"
|
|
|
|
|
|
|
|
#include "kaleidoscope/KeyEventTracker.h"
|
|
|
|
|
|
|
|
#include "kaleidoscope/KeyAddrEventQueue.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace kaleidoscope {
|
|
|
|
namespace kaleidoscope {
|
|
|
|
namespace plugin {
|
|
|
|
namespace plugin {
|
|
|
@ -34,96 +36,46 @@ EventHandlerResult Qukeys::onNameQuery() {
|
|
|
|
|
|
|
|
|
|
|
|
// This is the event handler. It ignores certain events, but mostly just adds
|
|
|
|
// This is the event handler. It ignores certain events, but mostly just adds
|
|
|
|
// them to the Qukeys event queue.
|
|
|
|
// them to the Qukeys event queue.
|
|
|
|
EventHandlerResult Qukeys::onKeyswitchEvent(Key& key, KeyAddr k, uint8_t key_state) {
|
|
|
|
EventHandlerResult Qukeys::onKeyswitchEvent(KeyEvent &event) {
|
|
|
|
// If k is not a physical key, ignore it; some other plugin injected it.
|
|
|
|
// If the plugin has already processed and released this event, ignore it.
|
|
|
|
if (! k.isValid() || (key_state & INJECTED) != 0) {
|
|
|
|
// 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;
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the key was injected (from the queue being flushed), we need to ignore
|
|
|
|
// If event.addr is not a physical key, ignore it; some other plugin injected it.
|
|
|
|
// it.
|
|
|
|
if (! event.addr.isValid() || (event.state & INJECTED) != 0) {
|
|
|
|
if (flushing_queue_) {
|
|
|
|
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If Qukeys is turned off, continue to next plugin.
|
|
|
|
// If Qukeys is turned off, continue to next plugin.
|
|
|
|
if (! active_) {
|
|
|
|
if (! active_) {
|
|
|
|
if (isDualUseKey(key)) {
|
|
|
|
if (isDualUseKey(event.key)) {
|
|
|
|
key = queue_head_.primary_key;
|
|
|
|
event.key = queue_head_.primary_key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Deal with keyswitch state changes.
|
|
|
|
|
|
|
|
if (keyToggledOn(key_state) || keyToggledOff(key_state)) {
|
|
|
|
|
|
|
|
// If the user rolled over from a non-modifier key to a qukey, let the
|
|
|
|
|
|
|
|
// release event for that key skip the queue. This prevents unintended
|
|
|
|
|
|
|
|
// repeat characters for the tapped key, which would otherwise have its
|
|
|
|
|
|
|
|
// release event delayed.
|
|
|
|
|
|
|
|
if (keyToggledOff(key_state) && event_queue_.length() == 1 &&
|
|
|
|
|
|
|
|
k != event_queue_.addr(0) && !isModifierKey(key)) {
|
|
|
|
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we can't trivially ignore the event, just add it to the queue.
|
|
|
|
// If we can't trivially ignore the event, just add it to the queue.
|
|
|
|
event_queue_.append(k, key_state);
|
|
|
|
event_queue_.append(event);
|
|
|
|
// In order to prevent overflowing the queue, process it now.
|
|
|
|
// In order to prevent overflowing the queue, process it now.
|
|
|
|
if (event_queue_.isFull()) {
|
|
|
|
while (processQueue());
|
|
|
|
processQueue();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any event that gets added to the queue gets re-processed later, so we
|
|
|
|
// Any event that gets added to the queue gets re-processed later, so we
|
|
|
|
// need to abort processing now.
|
|
|
|
// need to abort processing now.
|
|
|
|
return EventHandlerResult::ABORT;
|
|
|
|
return EventHandlerResult::ABORT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The key is being held. We need to determine if we should block it because
|
|
|
|
|
|
|
|
// its key press event is still in the queue, waiting to be
|
|
|
|
|
|
|
|
// flushed. Therefore, we search the event queue for the same key. If the
|
|
|
|
|
|
|
|
// first event we find there is a key press, that means we need to suppress
|
|
|
|
|
|
|
|
// this hold, because it's still waiting on an earlier event.
|
|
|
|
|
|
|
|
for (uint8_t i{0}; i < event_queue_.length(); ++i) {
|
|
|
|
|
|
|
|
if (event_queue_.addr(i) == k) {
|
|
|
|
|
|
|
|
// If the first matching event is a release, we do not suppress it,
|
|
|
|
|
|
|
|
// because its press event has already been flushed.
|
|
|
|
|
|
|
|
if (event_queue_.isRelease(i)) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, the first matching event was a key press, so we need to
|
|
|
|
|
|
|
|
// suppress it for now.
|
|
|
|
|
|
|
|
return EventHandlerResult::ABORT;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Either this key doesn't have an event in the queue at all, or its first
|
|
|
|
|
|
|
|
// event in the queue is a release. We treat the key as a normal held key.
|
|
|
|
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This hook runs once each cycle, and checks to see if the first event in the
|
|
|
|
// This hook runs once each cycle, and checks to see if the first event in the
|
|
|
|
// queue is ready to be flushed. It only allows one event to be flushed per
|
|
|
|
// queue is ready to be flushed. It only allows one event to be flushed per
|
|
|
|
// cycle, because the keyboard HID report can't store all of the information
|
|
|
|
// cycle, because the keyboard HID report can't store all of the information
|
|
|
|
// necessary to correctly handle all of the rollover corner cases.
|
|
|
|
// necessary to correctly handle all of the rollover corner cases.
|
|
|
|
EventHandlerResult Qukeys::beforeReportingState() {
|
|
|
|
EventHandlerResult Qukeys::afterEachCycle() {
|
|
|
|
// For keys that have been physically released, but whose release events are
|
|
|
|
// If there hasn't been a keypress in a while, update the prior keypress
|
|
|
|
// still waiting to be flushed from the queue, we need to restore them,
|
|
|
|
|
|
|
|
// because `handleKeyswitchEvent()` didn't get called for those KeyAddrs.
|
|
|
|
|
|
|
|
for (uint8_t i{0}; i < event_queue_.length(); ++i) {
|
|
|
|
|
|
|
|
if (event_queue_.isRelease(i)) {
|
|
|
|
|
|
|
|
KeyAddr k = event_queue_.addr(i);
|
|
|
|
|
|
|
|
// Now for the tricky bit. Before "restoring" this key hold, we need to
|
|
|
|
|
|
|
|
// make sure that its key press event has already been flushed from the
|
|
|
|
|
|
|
|
// queue, so we need to search for a matching key press event preceding
|
|
|
|
|
|
|
|
// this release event. If we find one, we need to ignore it.
|
|
|
|
|
|
|
|
if (isKeyAddrInQueueBeforeIndex(k, i)) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
flushing_queue_ = true;
|
|
|
|
|
|
|
|
handleKeyswitchEvent(Key_NoKey, k, IS_PRESSED | WAS_PRESSED);
|
|
|
|
|
|
|
|
flushing_queue_ = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Next, if there hasn't been a keypress in a while, update the prior keypress
|
|
|
|
|
|
|
|
// timestamp to avoid integer overflow issues:
|
|
|
|
// timestamp to avoid integer overflow issues:
|
|
|
|
if (Runtime.hasTimeExpired(prior_keypress_timestamp_,
|
|
|
|
if (Runtime.hasTimeExpired(prior_keypress_timestamp_,
|
|
|
|
minimum_prior_interval_)) {
|
|
|
|
minimum_prior_interval_)) {
|
|
|
@ -131,12 +83,14 @@ EventHandlerResult Qukeys::beforeReportingState() {
|
|
|
|
Runtime.millisAtCycleStart() - (minimum_prior_interval_ + 1);
|
|
|
|
Runtime.millisAtCycleStart() - (minimum_prior_interval_ + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If any events get flushed from the queue, stop there; we can only safely
|
|
|
|
// If there's nothing in the queue, there's nothing more to do.
|
|
|
|
// send the one report per cycle.
|
|
|
|
if (event_queue_.isEmpty()) {
|
|
|
|
if (processQueue()) {
|
|
|
|
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
return EventHandlerResult::OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process as many events as we can from the queue.
|
|
|
|
|
|
|
|
while (processQueue());
|
|
|
|
|
|
|
|
|
|
|
|
// If we get here, that means that the first event in the queue is a qukey
|
|
|
|
// If we get here, that means that the first event in the queue is a qukey
|
|
|
|
// press. All that's left to do is to check if it's been held long enough that
|
|
|
|
// press. All that's left to do is to check if it's been held long enough that
|
|
|
|
// it has timed out.
|
|
|
|
// it has timed out.
|
|
|
@ -161,11 +115,9 @@ EventHandlerResult Qukeys::beforeReportingState() {
|
|
|
|
// overflow, but those are both rare cases, and should not cause any serious
|
|
|
|
// overflow, but those are both rare cases, and should not cause any serious
|
|
|
|
// problems even when they do come up.
|
|
|
|
// problems even when they do come up.
|
|
|
|
bool Qukeys::processQueue() {
|
|
|
|
bool Qukeys::processQueue() {
|
|
|
|
// If the queue is empty, signal that the beforeReportingState() process
|
|
|
|
// If there's nothing in the queue, abort.
|
|
|
|
// should abort before checking for a hold timeout (since there's nothing to
|
|
|
|
|
|
|
|
// do).
|
|
|
|
|
|
|
|
if (event_queue_.isEmpty()) {
|
|
|
|
if (event_queue_.isEmpty()) {
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// In other cases, we will want the KeyAddr of the first event in the queue.
|
|
|
|
// In other cases, we will want the KeyAddr of the first event in the queue.
|
|
|
@ -194,6 +146,7 @@ bool Qukeys::processQueue() {
|
|
|
|
// We now know that the first event is a key press. If it's not a qukey, or if
|
|
|
|
// We now know that the first event is a key press. If it's not a qukey, or if
|
|
|
|
// it's only there because the plugin was just turned off, we can flush it
|
|
|
|
// it's only there because the plugin was just turned off, we can flush it
|
|
|
|
// immediately.
|
|
|
|
// immediately.
|
|
|
|
|
|
|
|
// Should be able to remove the `active_` check once `deactivate()` gets updated
|
|
|
|
if (! isQukey(queue_head_addr) || ! active_) {
|
|
|
|
if (! isQukey(queue_head_addr) || ! active_) {
|
|
|
|
flushEvent(queue_head_.primary_key);
|
|
|
|
flushEvent(queue_head_.primary_key);
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
@ -211,8 +164,8 @@ bool Qukeys::processQueue() {
|
|
|
|
// key, so we don't need to do it repeatedly later.
|
|
|
|
// key, so we don't need to do it repeatedly later.
|
|
|
|
bool qukey_is_spacecadet = isModifierKey(queue_head_.primary_key);
|
|
|
|
bool qukey_is_spacecadet = isModifierKey(queue_head_.primary_key);
|
|
|
|
|
|
|
|
|
|
|
|
// If the qukey press is followed a printable key too closely, it's not
|
|
|
|
// If the qukey press followed a printable key too closely, it's not eligible
|
|
|
|
// eligible to take on its alternate value unless it's a SpaceCadet-type key.
|
|
|
|
// to take on its alternate value unless it's a SpaceCadet-type key.
|
|
|
|
if (!Runtime.hasTimeExpired(prior_keypress_timestamp_,
|
|
|
|
if (!Runtime.hasTimeExpired(prior_keypress_timestamp_,
|
|
|
|
minimum_prior_interval_) &&
|
|
|
|
minimum_prior_interval_) &&
|
|
|
|
!qukey_is_spacecadet) {
|
|
|
|
!qukey_is_spacecadet) {
|
|
|
@ -323,6 +276,7 @@ void Qukeys::flushEvent(Key event_key) {
|
|
|
|
// First we record the address and state of the event:
|
|
|
|
// First we record the address and state of the event:
|
|
|
|
KeyAddr queue_head_addr = event_queue_.addr(0);
|
|
|
|
KeyAddr queue_head_addr = event_queue_.addr(0);
|
|
|
|
uint8_t keyswitch_state = event_queue_.isRelease(0) ? WAS_PRESSED : IS_PRESSED;
|
|
|
|
uint8_t keyswitch_state = event_queue_.isRelease(0) ? WAS_PRESSED : IS_PRESSED;
|
|
|
|
|
|
|
|
KeyEvent event{queue_head_addr, keyswitch_state, event_key, event_queue_.id(0)};
|
|
|
|
|
|
|
|
|
|
|
|
// If the flushed event is a keypress of a printable symbol, record its
|
|
|
|
// If the flushed event is a keypress of a printable symbol, record its
|
|
|
|
// timestamp. This lets us suppress some unintended alternate values seen by
|
|
|
|
// timestamp. This lets us suppress some unintended alternate values seen by
|
|
|
@ -334,12 +288,11 @@ void Qukeys::flushEvent(Key event_key) {
|
|
|
|
prior_keypress_timestamp_ = event_queue_.timestamp(0);
|
|
|
|
prior_keypress_timestamp_ = event_queue_.timestamp(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Remove the head event from the queue:
|
|
|
|
// Remove the head event from the queue, then call `handleKeyswitchEvent()` to
|
|
|
|
|
|
|
|
// resume processing of the event. It's important to remove the event from the
|
|
|
|
|
|
|
|
// queue first; otherwise `onKeyswitchEvent()` will abort it.
|
|
|
|
event_queue_.shift();
|
|
|
|
event_queue_.shift();
|
|
|
|
// This ensures that the flushed event will be ignored by the event handler hook:
|
|
|
|
Runtime.handleKeyswitchEvent(event);
|
|
|
|
flushing_queue_ = true;
|
|
|
|
|
|
|
|
handleKeyswitchEvent(event_key, queue_head_addr, keyswitch_state);
|
|
|
|
|
|
|
|
flushing_queue_ = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -350,7 +303,7 @@ bool Qukeys::isQukey(KeyAddr k) {
|
|
|
|
// First, look up the value from the keymap. This value should be
|
|
|
|
// First, look up the value from the keymap. This value should be
|
|
|
|
// correct in the cache, even if there's been a layer change since
|
|
|
|
// correct in the cache, even if there's been a layer change since
|
|
|
|
// the key was pressed.
|
|
|
|
// the key was pressed.
|
|
|
|
Key key = Layer.lookup(k);
|
|
|
|
Key key = Runtime.lookupKey(k);
|
|
|
|
|
|
|
|
|
|
|
|
// Next, we check to see if this is a DualUse-type qukey (defined in the keymap)
|
|
|
|
// Next, we check to see if this is a DualUse-type qukey (defined in the keymap)
|
|
|
|
if (isDualUseKey(key)) {
|
|
|
|
if (isDualUseKey(key)) {
|
|
|
|