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-LED-Stalker.h>
#include <Kaleidoscope-Macros.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* // *INDENT-OFF*
KEYMAPS( KEYMAPS(
[0] = KEYMAP_STACKED [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* // *INDENT-ON*
class EventDropper_ : public kaleidoscope::Plugin { namespace kaleidoscope {
public: namespace plugin {
EventDropper_() {}
kaleidoscope::EventHandlerResult onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t key_state) { class EventDropper : public Plugin {
return kaleidoscope::EventHandlerResult::EVENT_CONSUMED; 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) { const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_index == 0 && keyToggledOn(key_state)) if (macro_id == 0 && keyToggledOn(event.state))
GhostInTheFirmware.activate(); GhostInTheFirmware.activate();
if (macro_id == 1 && keyToggledOn(event.state))
EventDropper.toggle();
return MACRO_NONE; return MACRO_NONE;
} }
static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = { static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = {
{0, 6, 200, 50}, {KeyAddr(3, 0), 200, 50},
{0, 5, 200, 50}, {KeyAddr(3, 1), 200, 50},
{0, 4, 200, 50}, {KeyAddr(3, 2), 200, 50},
{0, 3, 200, 50}, {KeyAddr(3, 3), 200, 50},
{0, 2, 200, 50}, {KeyAddr(3, 4), 200, 50},
{0, 1, 200, 50}, {KeyAddr(3, 5), 200, 50},
{0, 0, 200, 50}, {KeyAddr(2, 6), 200, 50},
{1, 0, 200, 50}, {KeyAddr(2, 9), 200, 50},
{1, 1, 200, 50}, {KeyAddr(3, 10), 200, 50},
{1, 2, 200, 50}, {KeyAddr(3, 11), 200, 50},
{1, 3, 200, 50}, {KeyAddr(3, 12), 200, 50},
{1, 4, 200, 50}, {KeyAddr(3, 13), 200, 50},
{1, 5, 200, 50}, {KeyAddr(3, 14), 200, 50},
{1, 6, 200, 50}, {KeyAddr(3, 15), 200, 50},
{2, 6, 200, 50},
{2, 5, 200, 50}, {KeyAddr::none(), 0, 0}
{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}
}; };
KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware, KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
LEDControl,
StalkerEffect, StalkerEffect,
Macros, Macros,
EventDropper); EventDropper);

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

@ -18,54 +18,63 @@
#include "kaleidoscope/Runtime.h" #include "kaleidoscope/Runtime.h"
#include <Kaleidoscope-GhostInTheFirmware.h> #include <Kaleidoscope-GhostInTheFirmware.h>
#include "kaleidoscope/keyswitch_state.h" #include "kaleidoscope/keyswitch_state.h"
#include "kaleidoscope/progmem_helpers.h"
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
const GhostInTheFirmware::GhostKey *GhostInTheFirmware::ghost_keys; const GhostInTheFirmware::GhostKey *GhostInTheFirmware::ghost_keys;
bool GhostInTheFirmware::is_active_; bool GhostInTheFirmware::is_active_ = false;
bool GhostInTheFirmware::is_pressed_; uint16_t GhostInTheFirmware::current_pos_ = 0;
uint16_t GhostInTheFirmware::current_pos_; uint16_t GhostInTheFirmware::start_time_;
uint32_t GhostInTheFirmware::start_time_;
uint16_t GhostInTheFirmware::press_timeout_;
uint16_t GhostInTheFirmware::delay_timeout_;
void GhostInTheFirmware::activate(void) { void GhostInTheFirmware::activate(void) {
is_active_ = true; is_active_ = true;
} }
EventHandlerResult GhostInTheFirmware::beforeReportingState() { EventHandlerResult GhostInTheFirmware::afterEachCycle() {
if (!is_active_) if (!is_active_)
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (press_timeout_ == 0) { // This stores the current GhostKey in the active sequence.
press_timeout_ = pgm_read_word(&(ghost_keys[current_pos_].pressTime)); static GhostKey ghost_key{KeyAddr::none(), 0, 0};
delay_timeout_ = pgm_read_word(&(ghost_keys[current_pos_].delay));
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; current_pos_ = 0;
ghost_key.delay = 0;
is_active_ = false; is_active_ = false;
return EventHandlerResult::OK; 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(); 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 (ghost_key.addr.isValid()) {
} else if (Runtime.hasTimeExpired(start_time_, delay_timeout_)) { // If the ghost key's address is still valid, that means that the virtual
current_pos_++; // key is still being held.
press_timeout_ = 0; 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; return EventHandlerResult::OK;

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

Loading…
Cancel
Save