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