commit
cfc2d65bdb
@ -0,0 +1,188 @@
|
|||||||
|
# Kaleidoscope-OneShot
|
||||||
|
|
||||||
|
One-shots are a new kind of behaviour for your standard modifier and momentary
|
||||||
|
layer keys: instead of having to hold them while pressing other keys, they can
|
||||||
|
be tapped and released, and will remain active until any other key is pressed.
|
||||||
|
In short, they turn `Shift, A` into `Shift+A`, and `Fn, 1` to `Fn+1`. The main
|
||||||
|
advantage is that this allows us to place the modifiers and layer keys to
|
||||||
|
positions that would otherwise be awkward when chording. Nevertheless, they
|
||||||
|
still act as normal when held, that behaviour is not lost.
|
||||||
|
|
||||||
|
Furthermore, if a one-shot key is tapped two times in quick succession, it
|
||||||
|
becomes sticky, and remains active until disabled with a third tap. This can be
|
||||||
|
useful when one needs to input a number of keys with the modifier or layer
|
||||||
|
active, and still does not wish to hold the key down. If this feature is
|
||||||
|
undesirable, unset the `OneShot.double_tap_sticky` property (see later).
|
||||||
|
|
||||||
|
To make multi-modifier, or multi-layer shortcuts possible, one-shot keys remain
|
||||||
|
active if another one-shot of the same type is tapped, so `Ctrl, Alt, b` becomes
|
||||||
|
`Ctrl+Alt+b`, and `L1, L2, c` is turned into `L1+L2+c`. Furthermore, modifiers
|
||||||
|
and other layer keys do not cancel the one-shot effect, either.
|
||||||
|
|
||||||
|
## Using One-Shot Keys
|
||||||
|
|
||||||
|
To enter one-shot mode, tap _quickly_ on a one-shot key. The next
|
||||||
|
normal (non-one-shot) key you press will have the modifier applied,
|
||||||
|
and then the modifier will automatically turn off. If the Shift key is
|
||||||
|
a one-shot modifier, then hitting `Shift, a, b` will give you `Ab`,
|
||||||
|
_if you hit shift quickly._
|
||||||
|
|
||||||
|
Longish keypresses do not activate one-shot mode. If you press `Shift,
|
||||||
|
a, b`, as above, but hold the Shift key a bit longer, you'll get `ab`.
|
||||||
|
|
||||||
|
To enter sticky mode, _tap twice quickly_ on a one-shot key. The
|
||||||
|
modifier will now stay on until you press it again. Continuing the
|
||||||
|
`Shift` example, tapping `Shift, Shift` _quickly_ and then `a, b, c,
|
||||||
|
Shift, d, e, f` will give you `ABCdef`.
|
||||||
|
|
||||||
|
This can be a bit tricky; combining this plugin with
|
||||||
|
[LED-ActiveModColor](LED-ActiveModColor).md
|
||||||
|
will help you understand what state your one-shot is in; when a
|
||||||
|
one-shot key is active, it will have a white LED highlight; when
|
||||||
|
sticky, a red highlight.
|
||||||
|
|
||||||
|
|
||||||
|
## Using the plugin
|
||||||
|
|
||||||
|
After adding one-shot keys to the keymap, all one needs to do, is enable the
|
||||||
|
plugin:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-OneShot.h>
|
||||||
|
#include <kaleidoscope/hid.h>
|
||||||
|
|
||||||
|
// somewhere in the keymap...
|
||||||
|
OSM(LeftControl), OSL(_FN)
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(OneShot);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keymap markup
|
||||||
|
|
||||||
|
There are two macros the plugin provides:
|
||||||
|
|
||||||
|
### `OSM(mod)`
|
||||||
|
|
||||||
|
> A macro that takes a single argument, the name of the modifier: `LeftControl`,
|
||||||
|
> `LeftShift`, `LeftAlt`, `LeftGui` or their right-side variant. When marked up
|
||||||
|
> with this macro, the modifier will act as a one-shot modifier.
|
||||||
|
|
||||||
|
### `OSL(layer)`
|
||||||
|
|
||||||
|
> Takes a layer number as argument, and sets up the key to act as a one-shot
|
||||||
|
> layer key.
|
||||||
|
>
|
||||||
|
> Please note that while `Kaleidoscope` supports more, one-shot layers are
|
||||||
|
> limited to 8 layers only.
|
||||||
|
|
||||||
|
## Plugin methods
|
||||||
|
|
||||||
|
The plugin provides one object, `OneShot`, which implements both one-shot
|
||||||
|
modifiers and one-shot layer keys. It has the following methods:
|
||||||
|
|
||||||
|
### `.isActive()`
|
||||||
|
|
||||||
|
> Returns if any one-shot key is in flight. This makes it possible to
|
||||||
|
> differentiate between having a modifier or layer active, versus having them
|
||||||
|
> active only until after the next key getting pressed. And this, in turn, is
|
||||||
|
> useful for macros that need to fiddle with either modifier or layer state: if
|
||||||
|
> one-shots are not active, they need not restore the original state.
|
||||||
|
|
||||||
|
### `.isPressed()`
|
||||||
|
|
||||||
|
> Returns true if any one-shot key is still held.
|
||||||
|
|
||||||
|
### `.isSticky(key)`
|
||||||
|
|
||||||
|
> Returns if the key is currently sticky.
|
||||||
|
|
||||||
|
### `.isModifierActive(key)`
|
||||||
|
|
||||||
|
> Returns if the modifier `key` has a one-shot state active. Use this together
|
||||||
|
> with `hid::isModifierKeyActive` to catch cases where a one-shot modifier is
|
||||||
|
> active, but not registered yet.
|
||||||
|
|
||||||
|
### `.cancel([with_stickies])`
|
||||||
|
|
||||||
|
> The `cancel()` method can be used to cancel any pending one-shot effects,
|
||||||
|
> useful when one changed their minds, and does not wish to wait for the
|
||||||
|
> timeout.
|
||||||
|
>
|
||||||
|
> The optional `with_stickies` argument, if set to `true`, will also cancel
|
||||||
|
> sticky one-shot effects. If omitted, it defaults to `false`, and not canceling
|
||||||
|
> stickies.
|
||||||
|
|
||||||
|
### `.inject(key, keyState)`
|
||||||
|
|
||||||
|
> Simulates a key event, specifically designed to inject one-shot keys into the
|
||||||
|
> event loop. The primary purpose of this method is to make it easier to trigger
|
||||||
|
> multiple one-shots at the same time.
|
||||||
|
>
|
||||||
|
> See the example sketch for more information about its use.
|
||||||
|
|
||||||
|
## Plugin properties
|
||||||
|
|
||||||
|
Along with the methods listed above, the `OneShot` object has the following
|
||||||
|
properties too:
|
||||||
|
|
||||||
|
### `.time_out`
|
||||||
|
|
||||||
|
> Set this property to the number of milliseconds to wait before timing out and
|
||||||
|
> cancelling the one-shot effect (unless interrupted or cancelled before by any
|
||||||
|
> other means).
|
||||||
|
>
|
||||||
|
> Defaults to 2500.
|
||||||
|
|
||||||
|
### `.hold_time_out`
|
||||||
|
|
||||||
|
> Set this property to the number of milliseconds to wait before considering a
|
||||||
|
> held one-shot key as intentionally held. In this case, the one-shot effect
|
||||||
|
> will not trigger when the key is released. In other words, holding a one-shot
|
||||||
|
> key at least this long, and then releasing it, will not trigger the one-shot
|
||||||
|
> effect.
|
||||||
|
>
|
||||||
|
> Defaults to 200.
|
||||||
|
|
||||||
|
### `.double_tap_sticky`
|
||||||
|
|
||||||
|
> Set this boolean property to make the plugin treat a double-tap of a one-shot
|
||||||
|
> key as making it sticky until a third tap. Setting it to `false` disables this
|
||||||
|
> behaviour, in which case double-tapping a one-shot modifier will just restart
|
||||||
|
> the timer.
|
||||||
|
>
|
||||||
|
> Defaults to `true`.
|
||||||
|
|
||||||
|
### `.double_tap_sticky_layers`
|
||||||
|
|
||||||
|
> The same as `.double_tap_sticky`, but only applies to layers. The two can be
|
||||||
|
> set separately.
|
||||||
|
>
|
||||||
|
> Defaults to `true`.
|
||||||
|
|
||||||
|
### `.double_tap_time_out`
|
||||||
|
|
||||||
|
> Set this property to the number of milliseconds within which a second
|
||||||
|
> uninterrupted tap of the same one-shot key will be treated as a sticky-tap.
|
||||||
|
> Only takes effect when `.double_tap_sticky` is set.
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> Setting the property to `-1` will make the double-tap timeout use `.time_out`
|
||||||
|
> for its calculations.
|
||||||
|
>
|
||||||
|
> Defaults to -1.
|
||||||
|
|
||||||
|
## 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/OneShot/OneShot.ino
|
@ -0,0 +1,88 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-OneShot -- One-shot modifiers and layers
|
||||||
|
* Copyright (C) 2016-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-Macros.h>
|
||||||
|
#include <Kaleidoscope-OneShot.h>
|
||||||
|
|
||||||
|
// Macros
|
||||||
|
enum {
|
||||||
|
OSMALTCTRL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// *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,
|
||||||
|
|
||||||
|
OSM(LeftControl), Key_Backspace, OSM(LeftGui), OSM(LeftShift),
|
||||||
|
M(OSMALTCTRL),
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
OSM(RightShift), OSM(RightAlt), Key_Spacebar, OSM(RightControl),
|
||||||
|
OSL(1)),
|
||||||
|
|
||||||
|
[1] = KEYMAP_STACKED
|
||||||
|
(
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
___, ___, ___, ___, ___, ___,
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
|
||||||
|
___, ___, ___, ___,
|
||||||
|
___,
|
||||||
|
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
Key_UpArrow, Key_DownArrow, Key_LeftArrow, Key_RightArrow,___, ___,
|
||||||
|
___, ___, ___, ___, ___, ___, ___,
|
||||||
|
|
||||||
|
___, ___, ___, ___,
|
||||||
|
___),
|
||||||
|
};
|
||||||
|
// *INDENT-ON*
|
||||||
|
|
||||||
|
void macroOneShotAltControl(uint8_t keyState) {
|
||||||
|
OneShot.inject(OSM(LeftAlt), keyState);
|
||||||
|
OneShot.inject(OSM(LeftControl), keyState);
|
||||||
|
}
|
||||||
|
|
||||||
|
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
|
||||||
|
if (macroIndex == OSMALTCTRL) {
|
||||||
|
macroOneShotAltControl(keyState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MACRO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(OneShot, Macros);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Kaleidoscope.loop();
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-OneShot -- One-shot modifiers and layers
|
||||||
|
* Copyright (C) 2016-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/plugin/OneShot.h>
|
@ -0,0 +1,274 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-OneShot -- One-shot modifiers and layers
|
||||||
|
* Copyright (C) 2016-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-OneShot.h>
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
// ---- state ---------
|
||||||
|
|
||||||
|
uint32_t OneShot::start_time_ = 0;
|
||||||
|
uint16_t OneShot::time_out = 2500;
|
||||||
|
uint16_t OneShot::hold_time_out = 250;
|
||||||
|
int16_t OneShot::double_tap_time_out = -1;
|
||||||
|
bool OneShot::double_tap_sticky = true;
|
||||||
|
bool OneShot::double_tap_sticky_layers = true;
|
||||||
|
OneShot::state_t OneShot::state_;
|
||||||
|
OneShot::state_t OneShot::sticky_state_;
|
||||||
|
OneShot::state_t OneShot::pressed_state_;
|
||||||
|
Key OneShot::prev_key_;
|
||||||
|
bool OneShot::should_cancel_ = false;
|
||||||
|
bool OneShot::should_cancel_stickies_ = false;
|
||||||
|
bool OneShot::should_mask_on_interrupt_ = false;
|
||||||
|
uint8_t OneShot::positions_[16];
|
||||||
|
|
||||||
|
// --- helper macros ------
|
||||||
|
|
||||||
|
#define isOneShotKey(key) (key.raw >= ranges::OS_FIRST && key.raw <= ranges::OS_LAST)
|
||||||
|
#define isModifier(key) (key.raw >= Key_LeftControl.raw && key.raw <= Key_RightGui.raw)
|
||||||
|
#define isLayerKey(key) (key.flags == (KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP) && key.keyCode >= LAYER_SHIFT_OFFSET && key.keyCode <= LAYER_SHIFT_OFFSET + 23)
|
||||||
|
|
||||||
|
#define isOneShot(idx) (bitRead (state_.all, (idx)))
|
||||||
|
#define setOneShot(idx) (bitWrite (state_.all, idx, 1))
|
||||||
|
#define clearOneShot(idx) (bitWrite (state_.all, idx, 0))
|
||||||
|
|
||||||
|
#define isSticky_(idx) (bitRead (sticky_state_.all, idx))
|
||||||
|
#define setSticky(idx) (bitWrite (sticky_state_.all, idx, 1))
|
||||||
|
#define clearSticky(idx) bitWrite (sticky_state_.all, idx, 0)
|
||||||
|
|
||||||
|
#define setPressed(idx) bitWrite(pressed_state_.all, idx, 1)
|
||||||
|
#define clearPressed(idx) bitWrite(pressed_state_.all, idx, 0)
|
||||||
|
#define isPressed(idx) bitRead (pressed_state_.all, idx)
|
||||||
|
|
||||||
|
#define isSameAsPrevious(key) (key.raw == prev_key_.raw)
|
||||||
|
#define saveAsPrevious(key) prev_key_.raw = key.raw
|
||||||
|
|
||||||
|
#define hasTimedOut() (millis () - start_time_ >= time_out)
|
||||||
|
|
||||||
|
void OneShot::positionToCoords(uint8_t pos, byte *row, byte *col) {
|
||||||
|
*col = pos % COLS;
|
||||||
|
*row = (pos - *col) / COLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- OneShot stuff ----
|
||||||
|
void OneShot::injectNormalKey(uint8_t idx, uint8_t key_state) {
|
||||||
|
Key key;
|
||||||
|
byte row, col;
|
||||||
|
|
||||||
|
if (idx < 8) {
|
||||||
|
key.flags = Key_LeftControl.flags;
|
||||||
|
key.keyCode = Key_LeftControl.keyCode + idx;
|
||||||
|
} else {
|
||||||
|
key.flags = KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP;
|
||||||
|
key.keyCode = LAYER_SHIFT_OFFSET + idx - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
positionToCoords(positions_[idx], &row, &col);
|
||||||
|
handleKeyswitchEvent(key, row, col, key_state | INJECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OneShot::activateOneShot(uint8_t idx) {
|
||||||
|
injectNormalKey(idx, IS_PRESSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OneShot::cancelOneShot(uint8_t idx) {
|
||||||
|
clearOneShot(idx);
|
||||||
|
injectNormalKey(idx, WAS_PRESSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResult OneShot::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState) {
|
||||||
|
uint8_t idx = mapped_key.raw - ranges::OS_FIRST;
|
||||||
|
|
||||||
|
if (keyState & INJECTED)
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (!state_.all) {
|
||||||
|
if (!isOneShotKey(mapped_key)) {
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyToggledOff(keyState)) {
|
||||||
|
clearPressed(idx);
|
||||||
|
|
||||||
|
if (mapped_key >= ranges::OSL_FIRST && mapped_key <= ranges::OSL_LAST) {
|
||||||
|
should_mask_on_interrupt_ = true;
|
||||||
|
}
|
||||||
|
} else if (keyToggledOn(keyState)) {
|
||||||
|
start_time_ = millis();
|
||||||
|
positions_[idx] = row * COLS + col;
|
||||||
|
setPressed(idx);
|
||||||
|
setOneShot(idx);
|
||||||
|
saveAsPrevious(mapped_key);
|
||||||
|
|
||||||
|
activateOneShot(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyIsPressed(keyState) && !keyWasPressed(keyState))
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (isOneShotKey(mapped_key)) {
|
||||||
|
if (isSticky_(idx)) {
|
||||||
|
if (keyToggledOn(keyState)) { // maybe on _off instead?
|
||||||
|
saveAsPrevious(mapped_key);
|
||||||
|
clearSticky(idx);
|
||||||
|
cancelOneShot(idx);
|
||||||
|
should_cancel_ = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (keyToggledOff(keyState)) {
|
||||||
|
clearPressed(idx);
|
||||||
|
if ((millis() - start_time_) >= hold_time_out) {
|
||||||
|
cancelOneShot(idx);
|
||||||
|
should_cancel_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyToggledOn(keyState)) {
|
||||||
|
setPressed(idx);
|
||||||
|
|
||||||
|
bool set_sticky = false;
|
||||||
|
if (isSameAsPrevious(mapped_key)) {
|
||||||
|
if (mapped_key >= ranges::OSM_FIRST && mapped_key <= ranges::OSM_LAST && double_tap_sticky)
|
||||||
|
set_sticky = true;
|
||||||
|
else if (mapped_key >= ranges::OSL_FIRST && mapped_key <= ranges::OSL_LAST && double_tap_sticky_layers)
|
||||||
|
set_sticky = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_sticky) {
|
||||||
|
if ((millis() - start_time_) <= ((double_tap_time_out == -1) ? time_out : double_tap_time_out)) {
|
||||||
|
setSticky(idx);
|
||||||
|
|
||||||
|
saveAsPrevious(mapped_key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
start_time_ = millis();
|
||||||
|
|
||||||
|
positions_[idx] = row * COLS + col;
|
||||||
|
setOneShot(idx);
|
||||||
|
saveAsPrevious(mapped_key);
|
||||||
|
|
||||||
|
activateOneShot(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ordinary key here, with some event
|
||||||
|
|
||||||
|
if (keyIsPressed(keyState)) {
|
||||||
|
saveAsPrevious(mapped_key);
|
||||||
|
if (!isModifier(mapped_key) && (mapped_key.flags != (KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP))) {
|
||||||
|
if (should_mask_on_interrupt_)
|
||||||
|
KeyboardHardware.maskKey(row, col);
|
||||||
|
should_cancel_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResult OneShot::beforeReportingState() {
|
||||||
|
if (!state_.all)
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if (isOneShot(i)) {
|
||||||
|
activateOneShot(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResult OneShot::afterEachCycle() {
|
||||||
|
if (!state_.all)
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (hasTimedOut())
|
||||||
|
cancel();
|
||||||
|
|
||||||
|
bool is_cancelled = false;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 32; i++) {
|
||||||
|
if (should_cancel_) {
|
||||||
|
if (isSticky_(i)) {
|
||||||
|
if (should_cancel_stickies_) {
|
||||||
|
is_cancelled = true;
|
||||||
|
clearSticky(i);
|
||||||
|
cancelOneShot(i);
|
||||||
|
clearPressed(i);
|
||||||
|
}
|
||||||
|
} else if (isOneShot(i) && !isPressed(i)) {
|
||||||
|
is_cancelled = true;
|
||||||
|
cancelOneShot(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cancelled) {
|
||||||
|
should_cancel_ = false;
|
||||||
|
should_cancel_stickies_ = false;
|
||||||
|
should_mask_on_interrupt_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OneShot::inject(Key mapped_key, uint8_t key_state) {
|
||||||
|
onKeyswitchEvent(mapped_key, UNKNOWN_KEYSWITCH_LOCATION, key_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- glue code ---
|
||||||
|
|
||||||
|
bool OneShot::isActive(void) {
|
||||||
|
return (state_.all && !hasTimedOut()) || (pressed_state_.all) || (sticky_state_.all);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OneShot::isActive(Key key) {
|
||||||
|
uint8_t idx = key.raw - ranges::OS_FIRST;
|
||||||
|
|
||||||
|
return (bitRead(state_.all, idx) && !hasTimedOut()) || (isPressed(idx)) || (isSticky_(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OneShot::isSticky(Key key) {
|
||||||
|
uint8_t idx = key.raw - ranges::OS_FIRST;
|
||||||
|
|
||||||
|
return (isSticky_(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OneShot::isModifierActive(Key key) {
|
||||||
|
if (key.raw < Key_LeftControl.raw || key.raw > Key_RightGui.raw)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return isOneShot(key.keyCode - Key_LeftControl.keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OneShot::cancel(bool with_stickies) {
|
||||||
|
should_cancel_ = true;
|
||||||
|
should_cancel_stickies_ = with_stickies;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kaleidoscope::plugin::OneShot OneShot;
|
@ -0,0 +1,87 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-OneShot -- One-shot modifiers and layers
|
||||||
|
* Copyright (C) 2016-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 OSM(kc) (Key) {.raw = kaleidoscope::ranges::OSM_FIRST + (Key_ ## kc).keyCode - Key_LeftControl.keyCode}
|
||||||
|
#define OSL(n) (Key) {.raw = kaleidoscope::ranges::OSL_FIRST + n}
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
class OneShot : public kaleidoscope::Plugin {
|
||||||
|
public:
|
||||||
|
OneShot(void) {}
|
||||||
|
|
||||||
|
static bool isOneShotKey(Key key) {
|
||||||
|
return (key.raw >= kaleidoscope::ranges::OS_FIRST && key.raw <= kaleidoscope::ranges::OS_LAST);
|
||||||
|
}
|
||||||
|
static bool isActive(void);
|
||||||
|
static bool isPressed() {
|
||||||
|
return !!pressed_state_.all;
|
||||||
|
}
|
||||||
|
static bool isActive(Key key);
|
||||||
|
static bool isSticky(Key key);
|
||||||
|
static void cancel(bool with_stickies);
|
||||||
|
static void cancel(void) {
|
||||||
|
cancel(false);
|
||||||
|
}
|
||||||
|
static uint16_t time_out;
|
||||||
|
static int16_t double_tap_time_out;
|
||||||
|
static uint16_t hold_time_out;
|
||||||
|
static bool double_tap_sticky;
|
||||||
|
static bool double_tap_sticky_layers;
|
||||||
|
|
||||||
|
static bool isModifierActive(Key key);
|
||||||
|
|
||||||
|
EventHandlerResult beforeReportingState();
|
||||||
|
EventHandlerResult afterEachCycle();
|
||||||
|
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t keyState);
|
||||||
|
|
||||||
|
void inject(Key mapped_key, uint8_t key_state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint8_t mods;
|
||||||
|
uint8_t layers;
|
||||||
|
};
|
||||||
|
uint16_t all;
|
||||||
|
} state_t;
|
||||||
|
static uint32_t start_time_;
|
||||||
|
static state_t state_;
|
||||||
|
static state_t sticky_state_;
|
||||||
|
static state_t pressed_state_;
|
||||||
|
static Key prev_key_;
|
||||||
|
static bool should_cancel_;
|
||||||
|
static bool should_cancel_stickies_;
|
||||||
|
static bool should_mask_on_interrupt_;
|
||||||
|
static uint8_t positions_[16];
|
||||||
|
|
||||||
|
static void positionToCoords(uint8_t pos, byte *row, byte *col);
|
||||||
|
|
||||||
|
static void injectNormalKey(uint8_t idx, uint8_t key_state);
|
||||||
|
static void activateOneShot(uint8_t idx);
|
||||||
|
static void cancelOneShot(uint8_t idx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern kaleidoscope::plugin::OneShot OneShot;
|
Loading…
Reference in new issue