This implements an extension to the `TapDance` plugin, allowing us to store `tapDanceActionKeys()`-esque lists in Storage. The core idea here is very similar to that of `DynamicMacros`: we dump/restore the full list via Focus, build a cached index on setup and any updates, and play back the selected key when need be. Unlike `DynamicMacros`,this plugin is built on top of `TapDance` and cannot function without it. Fixes #730. Signed-off-by: Gergely Nagy <algernon@keyboard.io>pull/734/head
parent
11a749b5f5
commit
b415a85950
@ -0,0 +1,101 @@
|
|||||||
|
# Kaleidoscope-DynamicTapDance
|
||||||
|
|
||||||
|
The `DynamicTapDance` plugin allows one to set up [TapDance][plugin:tapdance] keys
|
||||||
|
without the need to compile and flash new firmware: one can change dynamic
|
||||||
|
dances via [Focus][plugin:focus], using a tool like [Chrysalis][chrysalis].
|
||||||
|
|
||||||
|
[plugin:tapdance]: TapDance.md
|
||||||
|
[plugin:focus]: FocusSerial.md
|
||||||
|
[chrysalis]: https://github.com/keyboardio/Chrysalis
|
||||||
|
|
||||||
|
Dynamic dances come with certain limitations, however: unlike the built-in ones,
|
||||||
|
dynamic ones do not support running custom code. They can only choose a key from
|
||||||
|
a list of possibilities. Given a list of keys, the plugin will choose the one
|
||||||
|
corresponding to the number of taps on the key, just like `TapDance` itself does.
|
||||||
|
|
||||||
|
Basically, this plugin allows us to store `tapDanceActionKeys` key lists in the
|
||||||
|
on-board memory of our keyboard.
|
||||||
|
|
||||||
|
You can define up to 16 dynamic dances, there is no limit on their length,
|
||||||
|
except the amount of storage available on the keyboard. You can even mix them
|
||||||
|
with built-in dances! But the total number of tap-dances is 16.
|
||||||
|
|
||||||
|
## Using the plugin
|
||||||
|
|
||||||
|
To use the plugin, we need to include the header, tell the firmware to `use` the
|
||||||
|
plugin, and reserve storage space for the dances. This is best illustrated with
|
||||||
|
an example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-EEPROMSettings.h>
|
||||||
|
#include <Kaleidoscope-FocusSerial.h>
|
||||||
|
#include <Kaleidoscope-TapDance.h>
|
||||||
|
#include <Kaleidoscope-DynamicTapDance.h>
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(
|
||||||
|
EEPROMSettings,
|
||||||
|
Focus,
|
||||||
|
TapDance,
|
||||||
|
DynamicTapDance
|
||||||
|
);
|
||||||
|
|
||||||
|
void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {
|
||||||
|
DynamicTapDance.dance(tap_dance_index, key_addr, tap_count, tap_dance_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
|
||||||
|
// 0 is the amount of built-in dances we have.
|
||||||
|
// 128 is how much space (in bytes) we reserve for dances.
|
||||||
|
DynamicTapDance.setup(0, 128);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin methods
|
||||||
|
|
||||||
|
The plugin provides a `DynamicTapDance` object, with the following methods and properties available:
|
||||||
|
|
||||||
|
### `.setup(builtin_dances, size)`
|
||||||
|
|
||||||
|
> Reserves `size` bytes of storage for dynamic dances. This must be called from
|
||||||
|
> the `setup()` method of your sketch, otherwise dynamic tap-dances will not
|
||||||
|
> function.
|
||||||
|
>
|
||||||
|
> The `builtin_dances` argument tells the plugin how many built-in dances there
|
||||||
|
> are.
|
||||||
|
|
||||||
|
### `.dance(index, key_addr, tap_count, tap_dance_action)`
|
||||||
|
|
||||||
|
> Performs a given dance (`index`) made on the key at `key_addr` address, which
|
||||||
|
> has been tapped `tap_count` times, and the action to perform is
|
||||||
|
> `tap_dance_action`.
|
||||||
|
>
|
||||||
|
> This mirrors the overrideable `tapDanceAction()` method of
|
||||||
|
> [TapDance][plugin:tapdance], and is intended to be called from therein.
|
||||||
|
|
||||||
|
## Focus commands
|
||||||
|
|
||||||
|
The plugin provides one Focus command: `tapdance.ap`.
|
||||||
|
|
||||||
|
### `tapdance.map [dances...]`
|
||||||
|
|
||||||
|
> Without arguments, displays all the stored dances. Each dance is terminated by
|
||||||
|
> an end marker (`0`, aka `Key_NoKey`), and the last dance is followed by an
|
||||||
|
> additional marker. The plugin will send back the entire dynamic tap-dance
|
||||||
|
> storage space, even data after the final marker.
|
||||||
|
>
|
||||||
|
> With arguments, it replaces the current set of dynamic dances with the newly
|
||||||
|
> given ones. Dances are terminated by an end marker, and the last macro must be
|
||||||
|
> terminated by an additional one. It is up to the caller to make sure these
|
||||||
|
> rules are obeyed.
|
||||||
|
>
|
||||||
|
> In both cases, the data sent or expected is a sequence of 16-bit values, a
|
||||||
|
> memory dump.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* [Kaleidoscope-EEPROM-Settings](EEPROM-Settings.md)
|
||||||
|
* [Kaleidoscope-FocusSerial](FocusSerial.md)
|
||||||
|
* [Kaleidoscope-TapDance](TapDance.md)
|
@ -0,0 +1,73 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* DynamicTapDance -- Dynamic TapDance support for Kaleidoscope
|
||||||
|
* Copyright (C) 2019 Keyboard.io, Inc
|
||||||
|
* Copyright (C) 2019 Dygma Lab S.L.
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||||
|
#include <Kaleidoscope-FocusSerial.h>
|
||||||
|
#include <Kaleidoscope-TapDance.h>
|
||||||
|
#include <Kaleidoscope-DynamicTapDance.h>
|
||||||
|
|
||||||
|
// *INDENT-OFF*
|
||||||
|
KEYMAPS(
|
||||||
|
[0] = KEYMAP_STACKED
|
||||||
|
(
|
||||||
|
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||||
|
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||||
|
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||||
|
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||||
|
|
||||||
|
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||||
|
TD(0),
|
||||||
|
|
||||||
|
Key_skip, 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_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||||
|
|
||||||
|
Key_RightShift, Key_RightAlt, Key_Spacebar, TD(2),
|
||||||
|
TD(1)),
|
||||||
|
)
|
||||||
|
// *INDENT-ON*
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TD_TAB_ESC,
|
||||||
|
TD_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {
|
||||||
|
switch (tap_dance_index) {
|
||||||
|
case TD_TAB_ESC:
|
||||||
|
return tapDanceActionKeys(tap_count, tap_dance_action, Key_A, Key_B);
|
||||||
|
default:
|
||||||
|
DynamicTapDance.dance(tap_dance_index, key_addr, tap_count, tap_dance_action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings,
|
||||||
|
Focus,
|
||||||
|
TapDance,
|
||||||
|
DynamicTapDance);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
DynamicTapDance.setup(TD_LAST, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Kaleidoscope.loop();
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/* DynamicTapDance -- Dynamic TapDance support for Kaleidoscope
|
||||||
|
* Copyright (C) 2019 Keyboard.io, Inc
|
||||||
|
* Copyright (C) 2019 Dygma Lab S.L.
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kaleidoscope/plugin/DynamicTapDance.h"
|
@ -0,0 +1,132 @@
|
|||||||
|
/* DynamicTapDance -- Dynamic TapDance support for Kaleidoscope
|
||||||
|
* Copyright (C) 2019 Keyboard.io, Inc
|
||||||
|
* Copyright (C) 2019 Dygma Lab S.L.
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Kaleidoscope-DynamicTapDance.h"
|
||||||
|
|
||||||
|
#include "kaleidoscope/hid.h"
|
||||||
|
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||||
|
#include "Kaleidoscope-FocusSerial.h"
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
uint16_t DynamicTapDance::storage_base_;
|
||||||
|
uint16_t DynamicTapDance::storage_size_;
|
||||||
|
uint16_t DynamicTapDance::map_[];
|
||||||
|
uint8_t DynamicTapDance::offset_;
|
||||||
|
uint8_t DynamicTapDance::dance_count_;
|
||||||
|
constexpr uint8_t DynamicTapDance::reserved_tap_dance_key_count_;
|
||||||
|
|
||||||
|
void DynamicTapDance::updateDynamicTapDanceCache() {
|
||||||
|
uint16_t pos = storage_base_;
|
||||||
|
uint8_t current_id = 0;
|
||||||
|
bool previous_dance_ended = false;
|
||||||
|
|
||||||
|
dance_count_ = 0;
|
||||||
|
map_[0] = 0;
|
||||||
|
|
||||||
|
while (pos < storage_base_ + storage_size_) {
|
||||||
|
uint16_t raw_key = Kaleidoscope.storage().read(pos);
|
||||||
|
pos += 2;
|
||||||
|
Key key(raw_key);
|
||||||
|
|
||||||
|
if (key == Key_NoKey) {
|
||||||
|
map_[++current_id] = pos - storage_base_;
|
||||||
|
|
||||||
|
if (previous_dance_ended)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dance_count_++;
|
||||||
|
previous_dance_ended = true;
|
||||||
|
} else {
|
||||||
|
previous_dance_ended = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DynamicTapDance::dance(uint8_t tap_dance_index, KeyAddr key_addr,
|
||||||
|
uint8_t tap_count, TapDance::ActionType tap_dance_action) {
|
||||||
|
uint16_t pos = map_[tap_dance_index - offset_] + ((tap_count - 1) * 2);
|
||||||
|
uint16_t next_pos = map_[tap_dance_index - offset_ + 1];
|
||||||
|
if (next_pos <= pos || (tap_dance_index > offset_ + dance_count_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Key key;
|
||||||
|
Kaleidoscope.storage().get(storage_base_ + pos, key);
|
||||||
|
|
||||||
|
switch (tap_dance_action) {
|
||||||
|
case TapDance::Tap:
|
||||||
|
break;
|
||||||
|
case TapDance::Interrupt:
|
||||||
|
case TapDance::Timeout:
|
||||||
|
handleKeyswitchEvent(key, key_addr, IS_PRESSED | INJECTED);
|
||||||
|
break;
|
||||||
|
case TapDance::Hold:
|
||||||
|
handleKeyswitchEvent(key, key_addr, IS_PRESSED | WAS_PRESSED | INJECTED);
|
||||||
|
break;
|
||||||
|
case TapDance::Release:
|
||||||
|
hid::sendKeyboardReport();
|
||||||
|
handleKeyswitchEvent(key, key_addr, WAS_PRESSED | INJECTED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResult DynamicTapDance::onFocusEvent(const char *command) {
|
||||||
|
if (::Focus.handleHelp(command, PSTR("tapdance.map")))
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (strncmp_P(command, PSTR("tapdance."), 9) != 0)
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (strcmp_P(command + 9, PSTR("map")) == 0) {
|
||||||
|
if (::Focus.isEOL()) {
|
||||||
|
for (uint16_t i = 0; i < storage_size_; i += 2) {
|
||||||
|
Key k;
|
||||||
|
Kaleidoscope.storage().get(storage_base_ + i, k);
|
||||||
|
::Focus.send(k);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t pos = 0;
|
||||||
|
|
||||||
|
while (!::Focus.isEOL()) {
|
||||||
|
Key k;
|
||||||
|
::Focus.read(k);
|
||||||
|
|
||||||
|
Kaleidoscope.storage().put(storage_base_ + pos, k);
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
Kaleidoscope.storage().commit();
|
||||||
|
updateDynamicTapDanceCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynamicTapDance::setup(uint8_t dynamic_offset, uint16_t size) {
|
||||||
|
storage_base_ = ::EEPROMSettings.requestSlice(size);
|
||||||
|
storage_size_ = size;
|
||||||
|
offset_ = dynamic_offset;
|
||||||
|
updateDynamicTapDanceCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kaleidoscope::plugin::DynamicTapDance DynamicTapDance;
|
@ -0,0 +1,50 @@
|
|||||||
|
/* DynamicTapDance -- Dynamic TapDance support for Kaleidoscope
|
||||||
|
* Copyright (C) 2019 Keyboard.io, Inc
|
||||||
|
* Copyright (C) 2019 Dygma Lab S.L.
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-TapDance.h>
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
class DynamicTapDance: public kaleidoscope::Plugin {
|
||||||
|
public:
|
||||||
|
DynamicTapDance() {}
|
||||||
|
|
||||||
|
EventHandlerResult onFocusEvent(const char *command);
|
||||||
|
|
||||||
|
static void setup(uint8_t dynamic_offset, uint16_t size);
|
||||||
|
|
||||||
|
static bool dance(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count,
|
||||||
|
TapDance::ActionType tap_dance_action);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static uint16_t storage_base_;
|
||||||
|
static uint16_t storage_size_;
|
||||||
|
static constexpr uint8_t reserved_tap_dance_key_count_ = ranges::TD_LAST - ranges::TD_FIRST + 1;
|
||||||
|
static uint16_t map_[reserved_tap_dance_key_count_];
|
||||||
|
static uint8_t dance_count_;
|
||||||
|
static uint8_t offset_;
|
||||||
|
static void updateDynamicTapDanceCache();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern kaleidoscope::plugin::DynamicTapDance DynamicTapDance;
|
Loading…
Reference in new issue