Adapt GhostInTheFirmware plugin to KeyEvent handlers

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

@ -20,115 +20,85 @@
#include <Kaleidoscope-LED-Stalker.h>
#include <Kaleidoscope-Macros.h>
// This sketch is set up to demonstrate the GhostInTheFirmware plugin. The left
// palm key will activate the plugin, virtually pressing each key on the bottom
// row in sequence, and lighting up the keys using the Stalker LED effect. It
// will type out the letters from A to N, but the right palm key can be used to
// toggle the custom EventDropper plugin to suppress USB output.
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, M(0),
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
___, ___, ___, ___,
___,
M(0),
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N,
___, ___, ___, ___,
___),
M(1)),
)
// *INDENT-ON*
class EventDropper_ : public kaleidoscope::Plugin {
public:
EventDropper_() {}
namespace kaleidoscope {
namespace plugin {
kaleidoscope::EventHandlerResult onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t key_state) {
return kaleidoscope::EventHandlerResult::EVENT_CONSUMED;
class EventDropper : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (active_)
return EventHandlerResult::EVENT_CONSUMED;
return EventHandlerResult::OK;
}
void toggle() {
active_ = !active_;
}
private:
bool active_ = false;
};
static EventDropper_ EventDropper;
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::EventDropper EventDropper;
const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) {
if (macro_index == 0 && keyToggledOn(key_state))
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_id == 0 && keyToggledOn(event.state))
GhostInTheFirmware.activate();
if (macro_id == 1 && keyToggledOn(event.state))
EventDropper.toggle();
return MACRO_NONE;
}
static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = {
{0, 6, 200, 50},
{0, 5, 200, 50},
{0, 4, 200, 50},
{0, 3, 200, 50},
{0, 2, 200, 50},
{0, 1, 200, 50},
{0, 0, 200, 50},
{1, 0, 200, 50},
{1, 1, 200, 50},
{1, 2, 200, 50},
{1, 3, 200, 50},
{1, 4, 200, 50},
{1, 5, 200, 50},
{1, 6, 200, 50},
{2, 6, 200, 50},
{2, 5, 200, 50},
{2, 4, 200, 50},
{2, 3, 200, 50},
{2, 2, 200, 50},
{2, 1, 200, 50},
{2, 0, 200, 50},
{3, 0, 200, 50},
{3, 1, 200, 50},
{3, 3, 200, 50},
{3, 4, 200, 50},
{3, 5, 200, 50},
{0, 7, 200, 50},
{1, 7, 200, 50},
{2, 7, 200, 50},
{3, 7, 200, 50},
{3, 6, 200, 50},
{3, 9, 200, 50},
{3, 8, 200, 50},
{2, 8, 200, 50},
{1, 8, 200, 50},
{0, 8, 200, 50},
{3, 10, 200, 50},
{3, 11, 200, 50},
{3, 12, 200, 50},
{3, 13, 200, 50},
{3, 14, 200, 50},
{3, 15, 200, 50},
{2, 15, 200, 50},
{2, 14, 200, 50},
{2, 13, 200, 50},
{2, 12, 200, 50},
{2, 11, 200, 50},
{2, 10, 200, 50},
{2, 9, 200, 50},
{1, 9, 200, 50},
{1, 10, 200, 50},
{1, 11, 200, 50},
{1, 12, 200, 50},
{1, 13, 200, 50},
{1, 14, 200, 50},
{1, 15, 200, 50},
{0, 15, 200, 50},
{0, 14, 200, 50},
{0, 13, 200, 50},
{0, 12, 200, 50},
{0, 11, 200, 50},
{0, 10, 200, 50},
{0, 9, 200, 50},
{0, 0, 0, 0}
{KeyAddr(3, 0), 200, 50},
{KeyAddr(3, 1), 200, 50},
{KeyAddr(3, 2), 200, 50},
{KeyAddr(3, 3), 200, 50},
{KeyAddr(3, 4), 200, 50},
{KeyAddr(3, 5), 200, 50},
{KeyAddr(2, 6), 200, 50},
{KeyAddr(2, 9), 200, 50},
{KeyAddr(3, 10), 200, 50},
{KeyAddr(3, 11), 200, 50},
{KeyAddr(3, 12), 200, 50},
{KeyAddr(3, 13), 200, 50},
{KeyAddr(3, 14), 200, 50},
{KeyAddr(3, 15), 200, 50},
{KeyAddr::none(), 0, 0}
};
KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
LEDControl,
StalkerEffect,
Macros,
EventDropper);

@ -22,16 +22,16 @@ that.
#include <Kaleidoscope-GhostInTheFirmware.h>
#include <Kaleidoscope-Macros.h>
const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) {
if (macro_index == 0 && keyToggledOn(key_state))
const macro_t *macroAction(uint8_t macro_id, KeyEvent& event) {
if (macro_id == 0 && keyToggledOn(event.state))
GhostInTheFirmware.activate();
return MACRO_NONE;
}
static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = {
{0, 0, 200, 50},
{0, 0, 0}
{KeyAddr(0, 0), 200, 50},
{KeyAddr::none(), 0, 0}
};
KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
@ -59,13 +59,14 @@ methods and properties:
### `.ghost_keys`
> Set this property to the sequence of keys to press, by assigning a sequence to
> this variable. Each element is a quartett of `row`, `column`, a `pressTime`,
> and a `delay`. Each of these will be pressed in different cycles, unlike
> macros which play back within a single cycle.
>
> The key at `row`, `column` will be held for `pressTime` milliseconds, and
> after an additional `delay` milliseconds, the plugin will move on to the next
> entry in the sequence.
> this variable. Each element is a `GhostKey` object, comprised of a `KeyAddr`
> (the location of a key on the keyboard), a duration of the key press (in
> milliseconds), and a delay after the key release until the next one is pressed
> (also in milliseconds).
> This `ghost_keys` array *MUST* end with the sentinal value of
> `{KeyAddr::none(), 0, 0}` to ensure that GhostInTheFirmware doesn't read past
> the end of the array.
>
> The sequence *MUST* reside in `PROGMEM`.

@ -18,54 +18,63 @@
#include "kaleidoscope/Runtime.h"
#include <Kaleidoscope-GhostInTheFirmware.h>
#include "kaleidoscope/keyswitch_state.h"
#include "kaleidoscope/progmem_helpers.h"
namespace kaleidoscope {
namespace plugin {
const GhostInTheFirmware::GhostKey *GhostInTheFirmware::ghost_keys;
bool GhostInTheFirmware::is_active_;
bool GhostInTheFirmware::is_pressed_;
uint16_t GhostInTheFirmware::current_pos_;
uint32_t GhostInTheFirmware::start_time_;
uint16_t GhostInTheFirmware::press_timeout_;
uint16_t GhostInTheFirmware::delay_timeout_;
bool GhostInTheFirmware::is_active_ = false;
uint16_t GhostInTheFirmware::current_pos_ = 0;
uint16_t GhostInTheFirmware::start_time_;
void GhostInTheFirmware::activate(void) {
is_active_ = true;
}
EventHandlerResult GhostInTheFirmware::beforeReportingState() {
EventHandlerResult GhostInTheFirmware::afterEachCycle() {
if (!is_active_)
return EventHandlerResult::OK;
if (press_timeout_ == 0) {
press_timeout_ = pgm_read_word(&(ghost_keys[current_pos_].pressTime));
delay_timeout_ = pgm_read_word(&(ghost_keys[current_pos_].delay));
// This stores the current GhostKey in the active sequence.
static GhostKey ghost_key{KeyAddr::none(), 0, 0};
if (press_timeout_ == 0) {
// When a ghost key has finished playing, it sets its delay to zero,
// indicating that it's time to read the next one from memory.
if (ghost_key.delay == 0) {
// Read the settings for the key from PROGMEM:
loadFromProgmem(ghost_keys[current_pos_], ghost_key);
// The end of the sequence is marked by a GhostKey with an invalid KeyAddr
// value (i.e. KeyAddr::none()). If we read this sentinel value, reset and
// deactivate.
if (!ghost_key.addr.isValid()) {
current_pos_ = 0;
ghost_key.delay = 0;
is_active_ = false;
return EventHandlerResult::OK;
}
is_pressed_ = true;
// If we're not at the end of the sequence, send the first keypress event,
// and start the timer.
Runtime.handleKeyEvent(KeyEvent(ghost_key.addr, IS_PRESSED));
start_time_ = Runtime.millisAtCycleStart();
} else {
if (is_pressed_ && Runtime.hasTimeExpired(start_time_, press_timeout_)) {
is_pressed_ = false;
start_time_ = Runtime.millisAtCycleStart();
byte row = pgm_read_byte(&(ghost_keys[current_pos_].row));
byte col = pgm_read_byte(&(ghost_keys[current_pos_].col));
handleKeyswitchEvent(Key_NoKey, KeyAddr(row, col), WAS_PRESSED);
} else if (is_pressed_) {
byte row = pgm_read_byte(&(ghost_keys[current_pos_].row));
byte col = pgm_read_byte(&(ghost_keys[current_pos_].col));
handleKeyswitchEvent(Key_NoKey, KeyAddr(row, col), IS_PRESSED);
} else if (Runtime.hasTimeExpired(start_time_, delay_timeout_)) {
current_pos_++;
press_timeout_ = 0;
} else if (ghost_key.addr.isValid()) {
// If the ghost key's address is still valid, that means that the virtual
// key is still being held.
if (Runtime.hasTimeExpired(start_time_, ghost_key.press_time)) {
// The key press has timed out, so we send the release event.
Runtime.handleKeyEvent(KeyEvent(ghost_key.addr, WAS_PRESSED));
// Next, we invalidate the ghost key's address to prevent checking the
// hold timeout again, then restart the timer for checking the delay.
ghost_key.addr.clear();
start_time_ = Runtime.millisAtCycleStart();
}
} else if (Runtime.hasTimeExpired(start_time_, ghost_key.delay)) {
// The ghost key has been (virtually) pressed and released, and its delay
// has now elapsed, so we set the delay to zero and increment the index
// value to indicate that the next key should be loaded in the next cycle.
ghost_key.delay = 0;
++current_pos_;
}
return EventHandlerResult::OK;

@ -23,29 +23,23 @@ namespace kaleidoscope {
namespace plugin {
class GhostInTheFirmware : public kaleidoscope::Plugin {
public:
typedef struct {
byte row;
byte col;
uint16_t pressTime;
struct GhostKey {
KeyAddr addr;
uint16_t press_time;
uint16_t delay;
} GhostKey;
};
static const GhostKey *ghost_keys;
GhostInTheFirmware(void) {}
static void activate(void);
EventHandlerResult beforeReportingState();
EventHandlerResult afterEachCycle();
private:
static bool is_active_;
static bool is_pressed_;
static uint16_t current_pos_;
static uint32_t start_time_;
static uint16_t press_timeout_;
static uint16_t delay_timeout_;
static void loopHook(bool is_post_clear);
static uint16_t start_time_;
};
}
}

Loading…
Cancel
Save