Add PrefixLayer plugin

Signed-off-by: iliana etaoin <iliana@buttslol.net>
pull/1187/head
iliana etaoin 2 years ago
parent 6f11d89152
commit 95b7a7b684

@ -0,0 +1,70 @@
// -*- mode: c++ -*-
/* This example demonstrates the Model 01 / Model 100 butterfly logo key as a
* tmux prefix key. When the key is held, Ctrl-B is pressed prior to the key
* you pressed.
*
* This example also demonstrates the purpose of using an entire layer for this
* plugin: the h/j/k/l keys in the TMUX layer are swapped for arrow keys to
* make switching between panes easier.
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-PrefixLayer.h>
enum {
PRIMARY,
TMUX,
}; // layers
/* Used in setup() below. */
static const kaleidoscope::plugin::PrefixLayer::Entry prefix_layers[] PROGMEM = {
kaleidoscope::plugin::PrefixLayer::Entry(TMUX, LCTRL(Key_B)),
};
// clang-format off
KEYMAPS(
[PRIMARY] = KEYMAP_STACKED
(XXX, Key_1, Key_2, Key_3, Key_4, Key_5, XXX,
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,
XXX,
XXX, Key_6, Key_7, Key_8, Key_9, Key_0, XXX,
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,
ShiftToLayer(TMUX), Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
XXX),
[TMUX] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___),
)
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(PrefixLayer);
void setup() {
Kaleidoscope.setup();
/* Configure the previously-defined prefix layers. */
PrefixLayer.prefix_layers = prefix_layers;
PrefixLayer.prefix_layers_length = 1;
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}

@ -0,0 +1,60 @@
# PrefixLayer
The `PrefixLayer` plugin allows you to easily create a keyboard layer designed
for use with programs that use a prefix key, such as tmux or screen. When a key
in a prefix layer is pressed, the prefix is injected first, then the key in
that layer is pressed.
## Using the plugin
You will need to define a keymap layer and configure the plugin to use that
layer with a prefix key. You can then include the plugin's header and set the
`.prefix_layers` property.
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-PrefixLayer.h>
enum {
PRIMARY,
TMUX,
}; // layers
static const kaleidoscope::plugin::PrefixLayer::Entry prefix_layers[] PROGMEM = {
kaleidoscope::plugin::PrefixLayer::Entry(TMUX, LCTRL(Key_B)),
};
KALEIDOSCOPE_INIT_PLUGINS(LEDControl, PrefixLayer);
void setup() {
Kaleidoscope.setup();
PrefixLayer.prefix_layers = prefix_layers;
PrefixLayer.prefix_layers_length = 1;
}
```
## Plugin methods
The plugin provides a `PrefixLayer` object, which has the following methods
and properties:
### `.prefix_layers`
> A `kaleidoscope::plugin::PrefixLayer::Entry` array that maps layers to prefix
> keys. The `Entry` constructor accepts `Entry(layer_number, prefix_key)`. This
> array must be stored in `PROGMEM` as shown above.
>
> Defaults to an empty array.
### `.prefix_layers_length`
> Length of the `prefix_layers` array.
>
> Defaults to *0*
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: /examples/Keystrokes/PrefixLayer/PrefixLayer.ino

@ -0,0 +1,7 @@
name=Kaleidoscope-PrefixLayer
version=0.0.0
sentence=Sends a prefix key for every key in a layer.
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
url=https://github.com/keyboardio/Kaleidoscope
author=iliana etaoin, James Cash
paragraph=

@ -0,0 +1,21 @@
/* -*- mode: c++ -*-
* Kaleidoscope-PrefixLayer -- Sends a prefix key for every key in a layer.
* Copyright (C) 2017, 2022 iliana etaoin <iliana@buttslol.net>
* Copyright (C) 2017 James Cash
*
* 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/PrefixLayer.h" // IWYU pragma: export

@ -0,0 +1,77 @@
/* -*- mode: c++ -*-
* Kaleidoscope-PrefixLayer -- Sends a prefix key for every key in a layer.
* Copyright (C) 2017, 2022 iliana etaoin <iliana@buttslol.net>
* Copyright (C) 2017 James Cash
*
* 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/plugin/PrefixLayer.h"
#include <Arduino.h> // for PROGMEM
#include <stdint.h> // for uint8_t
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
#include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/keyswitch_state.h" // for keyToggledOn
#include "kaleidoscope/layers.h" // for Layer
namespace kaleidoscope {
namespace plugin {
static const PrefixLayer::Entry prefix_layers_default_[] PROGMEM = {};
const PrefixLayer::Entry *PrefixLayer::prefix_layers = prefix_layers_default_;
uint8_t PrefixLayer::prefix_layers_length = 0;
bool PrefixLayer::clear_modifiers_ = false;
EventHandlerResult PrefixLayer::onKeyEvent(KeyEvent &event) {
if (event.state & INJECTED)
return EventHandlerResult::OK;
if (!keyToggledOn(event.state))
return EventHandlerResult::OK;
if (event.key == Key_NoKey)
return EventHandlerResult::OK;
if (!event.key.isKeyboardKey())
return EventHandlerResult::OK;
if (event.key.isKeyboardModifier())
return EventHandlerResult::OK;
for (uint8_t i = 0; i < prefix_layers_length; i++) {
if (Layer.isActive(prefix_layers[i].layer)) {
clear_modifiers_ = true;
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), IS_PRESSED | INJECTED, prefix_layers[i].prefix});
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), WAS_PRESSED | INJECTED, prefix_layers[i].prefix});
clear_modifiers_ = false;
}
}
return EventHandlerResult::OK;
}
EventHandlerResult PrefixLayer::beforeReportingState(const KeyEvent &event) {
if (clear_modifiers_) {
for (uint8_t i = HID_KEYBOARD_FIRST_MODIFIER; i <= HID_KEYBOARD_LAST_MODIFIER; i++) {
Runtime.hid().keyboard().releaseKey(Key(i, KEY_FLAGS));
}
Runtime.hid().keyboard().pressModifiers(event.key);
}
return EventHandlerResult::OK;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::PrefixLayer PrefixLayer;

@ -0,0 +1,54 @@
/* -*- mode: c++ -*-
* Kaleidoscope-PrefixLayer -- Sends a prefix key for every key in a layer.
* Copyright (C) 2017, 2022 iliana etaoin <iliana@buttslol.net>
* Copyright (C) 2017 James Cash
*
* 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 <stdint.h> // for uint8_t
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/plugin.h" // for Plugin
namespace kaleidoscope {
namespace plugin {
class PrefixLayer : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult beforeReportingState(const KeyEvent &event);
struct Entry {
uint8_t layer;
Key prefix;
constexpr Entry(uint8_t layer, Key prefix)
: layer(layer), prefix(prefix) {}
};
static const Entry *prefix_layers;
static uint8_t prefix_layers_length;
private:
static bool clear_modifiers_;
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::PrefixLayer PrefixLayer;

@ -0,0 +1,68 @@
/* -*- mode: c++ -*-
* Copyright (C) 2022 iliana etaoin <iliana@buttslol.net>
*
* 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-PrefixLayer.h>
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(ShiftToLayer(1), ___, ___, ___, ___, ___, ___,
Key_LeftControl, Key_LeftShift, ___, ___, ___, ___, ___,
Key_H, Key_J, Key_K, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___),
[1] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, Key_DownArrow, XXX, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___),
)
// clang-format on
static const kaleidoscope::plugin::PrefixLayer::Entry prefix_layers[] PROGMEM = {
kaleidoscope::plugin::PrefixLayer::Entry(1, LCTRL(Key_B)),
};
KALEIDOSCOPE_INIT_PLUGINS(PrefixLayer);
void setup() {
Kaleidoscope.setup();
PrefixLayer.prefix_layers = prefix_layers;
PrefixLayer.prefix_layers_length = 1;
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:virtual:model01",
"port": ""
}
}

@ -0,0 +1,231 @@
VERSION 1
KEYSWITCH PREFIX_B 0 0
KEYSWITCH CTRL 1 0
KEYSWITCH SHIFT 1 1
KEYSWITCH H 2 0
KEYSWITCH J 2 1
KEYSWITCH K 2 2
# ==============================================================================
NAME Prefix layer passthrough
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS H
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # press Ctrl
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report empty # release Ctrl
EXPECT keyboard-report Key_H # press H
RUN 4 ms
RELEASE H
RUN 1 cycle
EXPECT keyboard-report empty # release H
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
# ==============================================================================
NAME Prefix layer explicit
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS J
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # press Ctrl
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report empty # release Ctrl
EXPECT keyboard-report Key_DownArrow # press J
RUN 4 ms
RELEASE J
RUN 1 cycle
EXPECT keyboard-report empty # release J
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
# ==============================================================================
NAME Prefix layer masked
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS K
RUN 1 cycle
EXPECT no keyboard-report # press K (masked)
RUN 4 ms
RELEASE K
RUN 1 cycle
EXPECT no keyboard-report # release K (masked)
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
# ==============================================================================
NAME Prefix layer same modifier first
RUN 4 ms
PRESS CTRL
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # press Ctrl
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS H
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report Key_LCtrl Key_H # press H
RUN 4 ms
RELEASE H
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # release H
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
RUN 4 ms
RELEASE CTRL
RUN 1 cycle
EXPECT keyboard-report empty # release Ctrl
# ==============================================================================
NAME Prefix layer same modifier second
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS CTRL
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # press Ctrl
RUN 4 ms
PRESS H
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report Key_LCtrl Key_H # press H
RUN 4 ms
RELEASE H
RUN 1 cycle
EXPECT keyboard-report Key_LCtrl # release H
RUN 4 ms
RELEASE CTRL
RUN 1 cycle
EXPECT keyboard-report empty # release Ctrl
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
# ==============================================================================
NAME Prefix layer different modifier first
RUN 4 ms
PRESS SHIFT
RUN 1 cycle
EXPECT keyboard-report Key_LShift # press Shift
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS H
RUN 1 cycle
EXPECT keyboard-report Key_LShift Key_LCtrl # press Ctrl
EXPECT keyboard-report Key_LCtrl # release pressed shift
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report Key_LShift # restoring pressed Shift
EXPECT keyboard-report Key_LShift Key_H # press H
RUN 4 ms
RELEASE H
RUN 1 cycle
EXPECT keyboard-report Key_LShift # release H
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
RUN 4 ms
RELEASE SHIFT
RUN 1 cycle
EXPECT keyboard-report empty # release Ctrl
# ==============================================================================
NAME Prefix layer different modifier second
RUN 4 ms
PRESS PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after pressing PREFIX_B
RUN 4 ms
PRESS SHIFT
RUN 1 cycle
EXPECT keyboard-report Key_LShift # press Shift
RUN 4 ms
PRESS H
RUN 1 cycle
EXPECT keyboard-report Key_LShift Key_LCtrl # press Ctrl
EXPECT keyboard-report Key_LCtrl # release pressed shift
EXPECT keyboard-report Key_LCtrl Key_B # press B, Ctrl held
EXPECT keyboard-report Key_LCtrl # release B, Ctrl held
EXPECT keyboard-report Key_LShift # restoring pressed Shift
EXPECT keyboard-report Key_LShift Key_H # press H
RUN 4 ms
RELEASE H
RUN 1 cycle
EXPECT keyboard-report Key_LShift # release H
RUN 4 ms
RELEASE SHIFT
RUN 1 cycle
EXPECT keyboard-report empty # release Ctrl
RUN 4 ms
RELEASE PREFIX_B
RUN 1 cycle
EXPECT no keyboard-report # no report after releasing PREFIX_B
Loading…
Cancel
Save