Merge remote-tracking branch 'plugin/Leader/f/monorepo' into f/monorepo-stage2

pull/389/head
Gergely Nagy 6 years ago
commit c6e05848d8
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -0,0 +1,107 @@
# Kaleidoscope-Leader
Leader keys are a kind of key where when they are tapped, all following keys are
swallowed, until the plugin finds a matching sequence in the dictionary, it
times out, or fails to find any possibilities. When a sequence is found, the
corresponding action is executed, but the processing still continues. If any key
is pressed that is not the continuation of the existing sequence, processing
aborts, and the key is handled normally.
This behaviour is best described with an example. Suppose we want a behaviour
where `LEAD u` starts unicode input mode, and `LEAD u h e a r t` should result
in a heart symbol being input, and we want `LEAD u 0 0 e 9 SPC` to input `é`,
and any other hex code that follows `LEAD u`, should be handled as-is, and
passed to the host. Obviously, we can't have all of this in a dictionary.
So we put `LEAD u` and `LEAD u h e a r t` in the dictionary only. The first will
start unicode input mode, the second will type in the magic sequence that
results in the symbol, and then aborts the leader sequence processing. With this
setup, if we type `LEAD u 0`, then `LEAD u` will be handled first, and start
unicode input mode. Then, at the `0`, the plugin notices it is not part of any
sequence, so aborts leader processing, and passes the key on as-is, and it ends
up being sent to the host. Thus, we covered all the cases of our scenario!
## Using the plugin
To use the plugin, one needs to include the header, implement some actions,
create a dictionary, and configure the provided `Leader` object to use the
dictionary:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h>
static void leaderA(uint8_t seq_index) {
Serial.println("leaderA");
}
static void leaderTX(uint8_t seq_index) {
Serial.println("leaderTX");
}
static const kaleidoscope::Leader::dictionary_t leader_dictionary[] PROGMEM =
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_A), leaderA},
{LEADER_SEQ(LEAD(0), Key_T, Key_X), leaderTX});
KALEIDOSCOPE_INIT_PLUGINS(Leader);
void setup() {
Serial.begin(9600);
Kaleidoscope.setup();
Leader.dictionary = leader_dictionary;
}
```
The dictionary is made up of a list of keys, and an action callback. Using the
`LEADER_DICT` and `LEADER_SEQ` helpers is recommended. The dictionary *must* be
marked `PROGMEM`!
**Note** that we need to use the `Leader` object before any other that adds or
changes key behaviour! Failing to do so may result in unpredictable behaviour.
## Plugin methods
The plugin provides the `Leader` object, with the following methods and properties:
### `.dictionary`
> Set this property to the dictionary `Leader` should use. The dictionary is an
> array of `kaleidoscope::Leader::dictionary_t` elements. Each element is made
> up of two elements, the first being a list of keys, the second an action to
> perform when the sequence is found.i
>
> The dictionary *MUST* reside in `PROGMEM`.
### `.reset()`
> Finishes the leader sequence processing. This is best called from actions that
> are final actions, where one does not wish to continue the leader sequence
> further in the hopes of finding a longer match.
### `.time_out`
> The number of milliseconds to wait before a sequence times out. Once the
> sequence timed out, if there is a partial match with an action, that will be
> performed, otherwise the Leader sequence will simply reset.
>
> Defaults to 1000.
## Dependencies
* [Kaleidoscope-Ranges](Ranges.md)
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: ../../examples/Leader/Leader.ino
## Upgrading
Previous versions of `Leader` used `kaleidoscope::Leader::dictionary_t` as a
type for defining the dictionary. In newer versions, this is
`kaleidoscope::plugin::Leader::dictionary_t`. The old name still works, but will
be removed by 2019-01-14.

@ -0,0 +1,64 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Leader -- VIM-style leader keys
* Copyright (C) 2016, 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 <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h>
// *INDENT-OFF*
const Key keymaps[][ROWS][COLS] PROGMEM = {
[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,
LEAD(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, Key_RightControl,
LEAD(0)),
};
// *INDENT-ON*
static void leaderTestA(uint8_t seq_index) {
Serial.println(F("leaderTestA"));
}
static void leaderTestAA(uint8_t seq_index) {
Serial.println(F("leaderTestAA"));
}
static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM =
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_A), leaderTestA},
{LEADER_SEQ(LEAD(0), Key_A, Key_A), leaderTestAA});
KALEIDOSCOPE_INIT_PLUGINS(Leader);
void setup() {
Kaleidoscope.setup();
Leader.dictionary = leader_dictionary;
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,20 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Leader -- VIM-style leader keys
* Copyright (C) 2016, 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <kaleidoscope/plugin/Leader.h>

@ -0,0 +1,161 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Leader -- VIM-style leader keys
* Copyright (C) 2016, 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 <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope-Leader.h>
namespace kaleidoscope {
namespace plugin {
// --- state ---
Key Leader::sequence_[LEADER_MAX_SEQUENCE_LENGTH + 1];
uint8_t Leader::sequence_pos_;
uint32_t Leader::end_time_;
uint16_t Leader::time_out = 1000;
const Leader::dictionary_t *Leader::dictionary;
// --- helpers ---
#define PARTIAL_MATCH -1
#define NO_MATCH -2
#define isLeader(k) (k.raw >= ranges::LEAD_FIRST && k.raw <= ranges::LEAD_LAST)
#define isActive() (sequence_[0].raw != Key_NoKey.raw)
// --- actions ---
int8_t Leader::lookup(void) {
bool match;
for (uint8_t seq_index = 0; ; seq_index++) {
match = true;
if (pgm_read_word(&(dictionary[seq_index].sequence[0].raw)) == Key_NoKey.raw)
break;
Key seq_key;
for (uint8_t i = 0; i <= sequence_pos_; i++) {
seq_key.raw = pgm_read_word(&(dictionary[seq_index].sequence[i].raw));
if (sequence_[i].raw != seq_key.raw) {
match = false;
break;
}
}
if (!match)
continue;
seq_key.raw = pgm_read_word(&(dictionary[seq_index].sequence[sequence_pos_ + 1].raw));
if (seq_key.raw == Key_NoKey.raw) {
return seq_index;
} else {
return PARTIAL_MATCH;
}
}
return NO_MATCH;
}
// --- api ---
void Leader::reset(void) {
sequence_pos_ = 0;
sequence_[0].raw = Key_NoKey.raw;
}
void Leader::inject(Key key, uint8_t key_state) {
onKeyswitchEvent(key, UNKNOWN_KEYSWITCH_LOCATION, key_state);
}
// --- hooks ---
EventHandlerResult Leader::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState) {
if (keyState & INJECTED)
return EventHandlerResult::OK;
if (!keyIsPressed(keyState) && !keyWasPressed(keyState)) {
if (isLeader(mapped_key)) {
return EventHandlerResult::EVENT_CONSUMED;
}
return EventHandlerResult::OK;
}
if (!isActive() && !isLeader(mapped_key))
return EventHandlerResult::OK;
if (!isActive()) {
// Must be a leader key!
if (keyToggledOff(keyState)) {
// not active, but a leader key = start the sequence on key release!
end_time_ = millis() + time_out;
sequence_pos_ = 0;
sequence_[sequence_pos_].raw = mapped_key.raw;
}
// If the sequence was not active yet, ignore the key.
return EventHandlerResult::EVENT_CONSUMED;
}
// active
int8_t action_index = lookup();
if (keyToggledOn(keyState)) {
sequence_pos_++;
if (sequence_pos_ > LEADER_MAX_SEQUENCE_LENGTH) {
reset();
return EventHandlerResult::OK;
}
end_time_ = millis() + time_out;
sequence_[sequence_pos_].raw = mapped_key.raw;
action_index = lookup();
if (action_index >= 0) {
return EventHandlerResult::EVENT_CONSUMED;
}
} else if (keyIsPressed(keyState)) {
// held, no need for anything here.
return EventHandlerResult::EVENT_CONSUMED;
}
if (action_index == NO_MATCH) {
reset();
return EventHandlerResult::OK;
}
if (action_index == PARTIAL_MATCH) {
return EventHandlerResult::EVENT_CONSUMED;
}
action_t leaderAction = (action_t) pgm_read_ptr(&(dictionary[action_index].action));
(*leaderAction)(action_index);
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult Leader::afterEachCycle() {
if (!isActive())
return EventHandlerResult::OK;
if (millis() >= end_time_)
reset();
return EventHandlerResult::OK;
}
}
}
kaleidoscope::plugin::Leader Leader;

@ -0,0 +1,65 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Leader -- VIM-style leader keys
* Copyright (C) 2016, 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Kaleidoscope.h>
#include <Kaleidoscope-Ranges.h>
#define LEADER_MAX_SEQUENCE_LENGTH 4
#define LEAD(n) (Key) { .raw = kaleidoscope::ranges::LEAD_FIRST + n }
#define LEADER_SEQ(...) { __VA_ARGS__, Key_NoKey }
#define LEADER_DICT(...) { __VA_ARGS__, {{Key_NoKey}, NULL} }
namespace kaleidoscope {
namespace plugin {
class Leader : public kaleidoscope::Plugin {
public:
typedef void (*action_t)(uint8_t seq_index);
typedef struct {
Key sequence[LEADER_MAX_SEQUENCE_LENGTH + 1];
action_t action;
} dictionary_t;
Leader(void) {}
static const dictionary_t *dictionary;
static void reset(void);
static uint16_t time_out;
void inject(Key key, uint8_t key_state);
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState);
EventHandlerResult afterEachCycle();
private:
static Key sequence_[LEADER_MAX_SEQUENCE_LENGTH + 1];
static uint8_t sequence_pos_;
static uint32_t end_time_;
static int8_t lookup(void);
};
}
// Backwards compatibility
typedef plugin::Leader Leader;
}
extern kaleidoscope::plugin::Leader Leader;
Loading…
Cancel
Save