diff --git a/doc/plugin/GhostInTheFirmware.md b/doc/plugin/GhostInTheFirmware.md new file mode 100644 index 00000000..33df6769 --- /dev/null +++ b/doc/plugin/GhostInTheFirmware.md @@ -0,0 +1,84 @@ +# Kaleidoscope-GhostInTheFirmware + +Born out of the desire to demo LED effects on the keyboard without having to +touch it by hand (which would obstruct the video), the `GhostInTheFirmware` +plugin allows one to inject events at various delays, by telling it which keys +to press. Unlike macros, these press keys at given positions, as if they were +pressed by someone typing on it - the firmware will not see the difference. + +Given a sequence (with press- and delay times), the plugin will walk through it +once activated, and hold the key for the specified amount, release it, and move +on to the next after the delay time. + +## Using the plugin + +To use the plugin, one needs to include the header, and configure it with a list +of key coordinates, a press time, and a delay time quartett. One also needs a +way to trigger starting the sequence, and a macro is the most convenient way for +that. + +```c++ +#include +#include +#include + +const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { + if (macro_index == 0 && keyToggledOn(key_state)) + GhostInTheFirmware.activate(); + + return MACRO_NONE; +} + +static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = { + {0, 0, 200, 50}, + {0, 0, 0} +}; + +KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware, + Macros); + +void setup() { + Kaleidoscope.setup (); + + GhostInTheFirmware.ghost_keys = ghost_keys; +} +``` + +The plugin won't be doing anything until its `activate()` method is called - +hence the macro. + +## Plugin methods + +The plugin provides the `GhostInTheFirmware` object, which has the following +methods and properties: + +### `.activate()` + +> Start playing back the sequence. Best called from a macro. + +### `.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. +> +> The sequence *MUST* reside in `PROGMEM`. + +## Further reading + +Starting from the [example][plugin:example] is the recommended way of getting +started with the plugin. + + [plugin:example]: ../../examples/GhostInTheFirmware/GhostInTheFirmware.ino + +## Upgrading + +Previous versions of `GhostInTheFirmware` used +`kaleidoscope::GhostInTheFirmware::GhostKey` as a type for defining keys. In +newer versions, this is `kaleidoscope::plugin::GhostInTheFirmware::GhostKey`. +The old name still works, but will be removed by 2019-01-14. diff --git a/examples/GhostInTheFirmware/GhostInTheFirmware.ino b/examples/GhostInTheFirmware/GhostInTheFirmware.ino new file mode 100644 index 00000000..cae21b04 --- /dev/null +++ b/examples/GhostInTheFirmware/GhostInTheFirmware.ino @@ -0,0 +1,145 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-GhostInTheFirmware -- Let the keyboard write for you! + * Copyright (C) 2017, 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include + +// *INDENT-OFF* +const Key keymaps[][ROWS][COLS] PROGMEM = { + [0] = KEYMAP_STACKED + (___, ___, ___, ___, ___, ___, M(0), + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + + ___, ___, ___, ___, + ___, + + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + + ___, ___, ___, ___, + ___), +}; +// *INDENT-ON* + +class EventDropper_ : public kaleidoscope::Plugin { + public: + EventDropper_() {} + + kaleidoscope::EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) { + return kaleidoscope::EventHandlerResult::EVENT_CONSUMED; + } +}; + +static EventDropper_ EventDropper; + +const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { + if (macro_index == 0 && keyToggledOn(key_state)) + GhostInTheFirmware.activate(); + + 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} +}; + +KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware, + StalkerEffect, + Macros, + EventDropper); + +void setup() { + Kaleidoscope.setup(); + + StalkerEffect.variant = STALKER(BlazingTrail); + GhostInTheFirmware.ghost_keys = ghost_keys; +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/Kaleidoscope-GhostInTheFirmware.h b/src/Kaleidoscope-GhostInTheFirmware.h new file mode 100644 index 00000000..1e904541 --- /dev/null +++ b/src/Kaleidoscope-GhostInTheFirmware.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-GhostInTheFirmware -- Let the keyboard write for you! + * Copyright (C) 2017 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include diff --git a/src/kaleidoscope/plugin/GhostInTheFirmware.cpp b/src/kaleidoscope/plugin/GhostInTheFirmware.cpp new file mode 100644 index 00000000..14566f19 --- /dev/null +++ b/src/kaleidoscope/plugin/GhostInTheFirmware.cpp @@ -0,0 +1,76 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-GhostInTheFirmware -- Let the keyboard write for you! + * Copyright (C) 2017 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include + +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_; + +void GhostInTheFirmware::activate(void) { + is_active_ = true; +} + +EventHandlerResult GhostInTheFirmware::beforeReportingState() { + 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)); + + if (press_timeout_ == 0) { + current_pos_ = 0; + is_active_ = false; + return EventHandlerResult::OK; + } + is_pressed_ = true; + start_time_ = millis(); + } else { + if (is_pressed_ && ((millis() - start_time_) > press_timeout_)) { + is_pressed_ = false; + start_time_ = millis(); + + byte row = pgm_read_byte(&(ghost_keys[current_pos_].row)); + byte col = pgm_read_byte(&(ghost_keys[current_pos_].col)); + + handleKeyswitchEvent(Key_NoKey, 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, row, col, IS_PRESSED); + } else if ((millis() - start_time_) > delay_timeout_) { + current_pos_++; + press_timeout_ = 0; + } + } + + return EventHandlerResult::OK; +} + +} +} + +kaleidoscope::plugin::GhostInTheFirmware GhostInTheFirmware; diff --git a/src/kaleidoscope/plugin/GhostInTheFirmware.h b/src/kaleidoscope/plugin/GhostInTheFirmware.h new file mode 100644 index 00000000..d7eb28d2 --- /dev/null +++ b/src/kaleidoscope/plugin/GhostInTheFirmware.h @@ -0,0 +1,56 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-GhostInTheFirmware -- Let the keyboard write for you! + * Copyright (C) 2017, 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include + +namespace kaleidoscope { +namespace plugin { +class GhostInTheFirmware : public kaleidoscope::Plugin { + public: + typedef struct { + byte row; + byte col; + uint16_t pressTime; + uint16_t delay; + } GhostKey; + static const GhostKey *ghost_keys; + + GhostInTheFirmware(void) {} + + static void activate(void); + + EventHandlerResult beforeReportingState(); + + 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); +}; +} + +// For backwards compatibility +typedef kaleidoscope::plugin::GhostInTheFirmware GhostInTheFirmware; +} + +extern kaleidoscope::plugin::GhostInTheFirmware GhostInTheFirmware;