Added support for DualUse key definitions in keymap (#27)

With this change, Qukeys reads DualUse key defs from the keymap, and treats them as
Qukeys, within the limitations of normal DualUse keys. Primary keycodes can only be
unmodified, basic keys, and alternate keycodes can only be modifiers or layer-switch
keys.
pull/389/head
Michael Richters 7 years ago committed by GitHub
parent f6f5f9ae3b
commit 0fa876f912
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,6 @@
--style=google
--unpad-paren
--pad-header
--pad-oper
--indent-classes
--indent=spaces=2

@ -58,6 +58,15 @@ likely to generate errors and out-of-order events.
[example](https://github.com/gedankenlab/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino)
for a way to turn `Qukeys` on and off, using Kaleidoscope-Macros
### DualUse key definitions
In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats
DualUse keys in the keymap as `Qukeys`. See [the Kaleidoscope-DualUse
documentation](https://github.com/keyboardio/Kaleidoscope-DualUse#keymap-markup)
for a thorough description of how to define DualUse keys. This makes `Qukeys` a
drop-in replacement for the `DualUse` plugin, without the need to edit the
keymap.
## Design & Implementation

@ -16,15 +16,35 @@ KEYMAPS(
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
___,
Key_Q,
M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_H, SFT_T(J), CTL_T(K), ALT_T(L), GUI_T(Semicolon), Key_Quote,
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
___),
LT(1,E)
),
[1] = KEYMAP_STACKED
(
___, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_1, Key_2, Key_3, Key_4,
___,
___, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
Key_1, Key_2, Key_3, Key_4,
___
),
)
// *INDENT-ON*
@ -47,7 +67,8 @@ void setup() {
kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd
kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt
kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl
kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift
kaleidoscope::Qukey(0, 2, 4, Key_LeftShift), // F/shift
kaleidoscope::Qukey(0, 3, 6, ShiftToLayer(1)) // Q/layer-shift (on `fn`)
)
Qukeys.setTimeout(200);

@ -20,6 +20,8 @@
#include <Kaleidoscope-Qukeys.h>
#include <kaleidoscope/hid.h>
#include <MultiReport/Keyboard.h>
#include <Kaleidoscope-Ranges.h>
#include <key_defs_keymaps.h>
#ifdef ARDUINO_VIRTUAL
#define debug_print(...) printf(__VA_ARGS__)
@ -30,6 +32,39 @@
namespace kaleidoscope {
bool isDualUse(Key k) {
if (k.raw < ranges::DU_FIRST || k.raw > ranges::DU_LAST)
return false;
return true;
}
Key getDualUsePrimaryKey(Key k) {
if (k.raw >= ranges::DUM_FIRST && k.raw <= ranges::DUM_LAST) {
k.raw -= ranges::DUM_FIRST;
k.flags = 0;
} else if (k.raw >= ranges::DUL_FIRST && k.raw <= ranges::DUL_LAST) {
k.raw -= ranges::DUL_FIRST;
k.flags = 0;
}
return k;
}
Key getDualUseAlternateKey(Key k) {
if (k.raw >= ranges::DUM_FIRST && k.raw <= ranges::DUM_LAST) {
k.raw -= ranges::DUM_FIRST;
k.raw = (k.raw >> 8) + Key_LeftControl.keyCode;
} else if (k.raw >= ranges::DUL_FIRST && k.raw <= ranges::DUL_LAST) {
k.raw -= ranges::DUL_FIRST;
byte layer = k.flags;
// Should be `ShiftToLayer(layer)`, but that gives "narrowing conversion"
// warnings that I can't figure out how to resolve
k.keyCode = layer + LAYER_SHIFT_OFFSET;
k.flags = KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP;
}
return k;
}
Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) {
this->layer = layer;
this->addr = addr::addr(row, col);
@ -89,15 +124,21 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) {
void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) {
addr::unmask(key_queue_[0].addr);
int8_t qukey_index = lookupQukey(key_queue_[0].addr);
if (qukey_index != QUKEY_NOT_FOUND) {
setQukeyState(key_queue_[0].addr, qukey_state);
}
bool is_qukey = (qukey_index != QUKEY_NOT_FOUND);
byte row = addr::row(key_queue_[0].addr);
byte col = addr::col(key_queue_[0].addr);
bool is_dual_use = isDualUse(Layer.lookup(row, col));
Key keycode = Key_NoKey;
if (qukey_state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) {
if (is_qukey || is_dual_use) {
setQukeyState(key_queue_[0].addr, qukey_state);
if (qukey_state == QUKEY_STATE_ALTERNATE) {
if (is_dual_use) {
keycode = getDualUseAlternateKey(keycode);
} else { // is_qukey
keycode = qukeys[qukey_index].alt_keycode;
}
}
}
// Before calling handleKeyswitchEvent() below, make sure Qukeys knows not to handle
// these events:
@ -125,7 +166,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) {
memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report));
// Last, if the key is still down, add its code back in
if (! keyToggledOn(keyswitch_state))
if (keyswitch_state | IS_PRESSED)
handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED);
// Now that we're done sending the report(s), Qukeys can process events again:
@ -166,28 +207,42 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) {
// If Qukeys is turned off, continue to next plugin
if (!active_)
return mapped_key;
return getDualUsePrimaryKey(mapped_key);
// get key addr & qukey (if any)
uint8_t key_addr = addr::addr(row, col);
int8_t qukey_index = lookupQukey(key_addr);
// If the key was injected (from the queue being flushed), continue to next plugin
if (flushing_queue_)
// If the key was injected (from the queue being flushed)
if (flushing_queue_) {
// If it's a DualUse key, we still need to update its keycode
if (isDualUse(mapped_key)) {
if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) {
return getDualUseAlternateKey(mapped_key);
} else {
return getDualUsePrimaryKey(mapped_key);
}
}
// ...otherwise, just continue to the next plugin
return mapped_key;
}
// If the key isn't active, and didn't just toggle off, continue to next plugin
if (!keyIsPressed(key_state) && !keyWasPressed(key_state))
return mapped_key;
// get key addr & qukey (if any)
uint8_t key_addr = addr::addr(row, col);
int8_t qukey_index = lookupQukey(key_addr);
return getDualUsePrimaryKey(mapped_key);
// If the key was just pressed:
if (keyToggledOn(key_state)) {
// If the queue is empty and the key isn't a qukey, proceed:
if (key_queue_length_ == 0 &&
qukey_index == QUKEY_NOT_FOUND)
! isDualUse(mapped_key) &&
qukey_index == QUKEY_NOT_FOUND) {
return mapped_key;
}
// Otherwise, queue the key and stop processing:
enqueue(key_addr);
// flushQueue() has already handled this key release
return Key_NoKey;
}
@ -199,21 +254,25 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) {
// If the key isn't in the key_queue, proceed
if (queue_index == QUKEY_NOT_FOUND) {
// If a qukey was released while in its alternate state, change its keycode
if (qukey_index != QUKEY_NOT_FOUND &&
getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) {
if (isDualUse(mapped_key)) {
if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE)
return getDualUseAlternateKey(mapped_key);
return getDualUsePrimaryKey(mapped_key);
} else if (qukey_index != QUKEY_NOT_FOUND) {
if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE)
return qukeys[qukey_index].alt_keycode;
}
return mapped_key;
}
}
flushQueue(queue_index);
// flushQueue() has already handled this key release
return Key_NoKey;
}
// Otherwise, the key is still pressed
// If the key is not a qukey:
if (qukey_index == QUKEY_NOT_FOUND) {
if (qukey_index == QUKEY_NOT_FOUND &&
! isDualUse(mapped_key)) {
// If the key was pressed before the keys in the queue, proceed:
if (queue_index == QUKEY_NOT_FOUND) {
return mapped_key;
@ -226,9 +285,11 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) {
// If the qukey is not in the queue, check its state
if (queue_index == QUKEY_NOT_FOUND) {
if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) {
if (isDualUse(mapped_key))
return getDualUseAlternateKey(mapped_key);
return qukeys[qukey_index].alt_keycode;
} else { // qukey_state == QUKEY_STATE_PRIMARY
return mapped_key;
return getDualUsePrimaryKey(mapped_key);
}
}
// else state is undetermined; block. I could check timeouts here,
@ -241,13 +302,19 @@ void Qukeys::preReportHook(void) {
// state to the alternate keycode and add it to the report
uint16_t current_time = (uint16_t)millis();
while (key_queue_length_ > 0) {
if (lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) {
flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED);
} else if ((current_time - key_queue_[0].start_time) > time_limit_) {
byte row = addr::row(key_queue_[0].addr);
byte col = addr::col(key_queue_[0].addr);
Key keycode = Layer.lookup(row, col);
bool is_dual_use = isDualUse(keycode);
if (lookupQukey(key_queue_[0].addr) != QUKEY_NOT_FOUND || is_dual_use) {
if ((current_time - key_queue_[0].start_time) > time_limit_) {
flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED);
} else {
break;
}
} else {
flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED);
}
}
}

@ -20,6 +20,7 @@
#include <Kaleidoscope.h>
#include <addr.h>
#include <Kaleidoscope-Ranges.h>
// Maximum length of the pending queue
#define QUKEYS_QUEUE_MAX 8
@ -41,6 +42,16 @@
// Wildcard value; this matches any layer
#define QUKEY_ALL_LAYERS -1
#define MT(mod, key) (Key) { \
.raw = kaleidoscope::ranges::DUM_FIRST + \
(((Key_ ## mod).keyCode - Key_LeftControl.keyCode) << 8) + (Key_ ## key).keyCode }
#define SFT_T(key) MT(LeftShift, key)
#define CTL_T(key) MT(LeftControl, key)
#define ALT_T(key) MT(LeftAlt, key)
#define GUI_T(key) MT(LeftGui, key)
#define LT(layer, key) (Key) { .raw = kaleidoscope::ranges::DUL_FIRST + (layer << 8) + (Key_ ## key).keyCode }
namespace kaleidoscope {
// Data structure for an individual qukey

Loading…
Cancel
Save