Skip to content

Commit dad94c8

Browse files
committed
Escape-Oneshot: toggle OneShot_ActiveStickyKey
by pressing it again with no keys held. Signed-off-by: Peter Davis <[email protected]>
1 parent 1221ac0 commit dad94c8

File tree

11 files changed

+179
-9
lines changed

11 files changed

+179
-9
lines changed

docs/NEWS.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ automatically become a OneShot key when pressed, by applying modifier flags to
133133
#### Two new special OneShot keys
134134

135135
OneShot can now also turn _any_ key into a sticky key, using either of two
136-
special `Key` values that can be inserted in the keymap.
136+
special `Key` values that can be inserted in the keymap. Configure the
137+
`OneShotMetaKeys` plugin to use these.
137138

138139
##### `OneShot_MetaStickyKey`
139140

@@ -151,6 +152,14 @@ pressed. Press `X`, press `OneShot_ActiveStickyKey`, and release `X`, and `X`
151152
will be sticky until it is pressed again to deactivate it. Again, it works on
152153
any key value, so use with caution.
153154

155+
Additionally, when you add the `EscapeOneShot` plugin, this key will act as a
156+
toggle when you press it again while holding no other keys.
157+
158+
##### `Key_OneShotCancel`
159+
160+
When you configure the `EscapeOneShot` plugin, this key cancels all OneShot
161+
keys.
162+
154163
#### LED-ActiveModColor highlighting
155164

156165
With the updates to OneShot, LED-ActiveModColor now recognizes and highlights

plugins/Kaleidoscope-Escape-OneShot/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ effect - or act as the normal `Esc` key if none are active, or if any of them
55
are still held. For those times when one accidentally presses a one-shot key, or
66
change their minds.
77

8-
Additionally, the special `Key_OneShotCancel` key will also count as a oneshot
8+
This plugin also modifies the behavior of `OneShot_ActiveStickyKey`
9+
so that it acts like a toggle. Without holding any keys, pressing
10+
`OneShot_ActiveStickyKey` again will cancel any active sticky keys.
11+
This saves one from having to remember to deactivate each key individually.
12+
13+
Additionally, the special `Key_OneShotCancel` key will always count as a oneshot
914
cancel key, would one want a dedicated key for the purpose.
1015

1116
## Using the plugin

plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
#include "kaleidoscope/plugin/Escape-OneShot.h"
2424

25-
#include <Kaleidoscope-OneShot.h> // for OneShot
25+
#include <Kaleidoscope-OneShot.h> // for OneShot
26+
#include <Kaleidoscope-OneShotMetaKeys.h> // for OneShot_ActiveStickyKey
2627

2728
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
2829
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::E...
@@ -32,19 +33,42 @@
3233
namespace kaleidoscope {
3334
namespace plugin {
3435

36+
EventHandlerResult EscapeOneShot::onKeyswitchEvent(KeyEvent &event) {
37+
if (!event.addr.isValid() || keyIsInjected(event.state))
38+
return EventHandlerResult::OK;
39+
// Track physically-held keys - for toggling OneShot_ActiveStickyKey below.
40+
// Needed because OneShot::onKeyEvent blocks release events.
41+
if (keyToggledOn(event.state)) {
42+
held_addrs_.set(event.addr);
43+
} else if (keyToggledOff(event.state)) {
44+
held_addrs_.clear(event.addr);
45+
}
46+
return EventHandlerResult::OK;
47+
}
48+
3549
EventHandlerResult EscapeOneShot::onKeyEvent(KeyEvent &event) {
3650
// We only act on an escape key (or `cancel_oneshot_key_`, if that has been
3751
// set) that has just been pressed, and not generated by some other
3852
// plugin. Also, only if at least one OneShot key is active and/or
39-
// sticky. Last, only if there are no OneShot keys currently being held.
53+
// sticky.
4054
//
41-
// `Key_OneShotCancel` will always count as an escape key, even if not
42-
// explicitly set so.
55+
// Two keys will always count as an escape key, even if not explicitly set:
56+
// - `Key_OneShotCancel` - explicit escape key
57+
// - `OneShot_ActiveStickyKey` - toggle off if no other keys are held
4358
if ((event.key == settings_.cancel_oneshot_key ||
44-
event.key == Key_OneShotCancel) &&
59+
event.key == Key_OneShotCancel ||
60+
event.key == OneShot_ActiveStickyKey) &&
4561
keyToggledOn(event.state) &&
4662
!keyIsInjected(event.state) &&
4763
::OneShot.isActive()) {
64+
if (event.key == OneShot_ActiveStickyKey) {
65+
for (KeyAddr held_addr : held_addrs_) {
66+
if (Runtime.lookupKey(held_addr) != OneShot_ActiveStickyKey) {
67+
// Keys are still physically held - don't cancel.
68+
return EventHandlerResult::OK;
69+
}
70+
}
71+
}
4872
// Cancel all OneShot keys
4973
::OneShot.cancel(true);
5074
// Change the cancellation key to a blank key, and signal that event

plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <Kaleidoscope-Ranges.h> // for OS_CANCEL
2626
#include <stdint.h> // for uint16_t
2727

28+
#include "kaleidoscope/KeyAddrBitfield.h" // for KeyAddrBitfield
2829
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
2930
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
3031
#include "kaleidoscope/key_defs.h" // for Key, Key_Escape
@@ -40,6 +41,7 @@ namespace plugin {
4041

4142
class EscapeOneShot : public kaleidoscope::Plugin {
4243
public:
44+
EventHandlerResult onKeyswitchEvent(KeyEvent &event);
4345
EventHandlerResult onKeyEvent(KeyEvent &event);
4446

4547
void setCancelKey(Key cancel_key) {
@@ -56,6 +58,7 @@ class EscapeOneShot : public kaleidoscope::Plugin {
5658
Key cancel_oneshot_key;
5759
};
5860
Settings settings_ = {.cancel_oneshot_key = Key_Escape};
61+
KeyAddrBitfield held_addrs_;
5962
};
6063

6164
class EscapeOneShotConfig : public Plugin {

plugins/Kaleidoscope-OneShotMetaKeys/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ to make any key on the keyboard (not just modifiers and layer shift keys)
77
These are both `Key` values that can be used as entries in your sketch's keymap.
88

99
Any keys made sticky in this way can be released just like OneShot modifier
10-
keys, by tapping them again to cancel the effect.
10+
keys, by tapping them again to cancel the effect. You can also use the
11+
`EscapeOneShot` plugin to cancel sticky keys without having to remember them.
1112

1213
## The `OneShot_MetaStickyKey`
1314

@@ -25,6 +26,10 @@ currently held (or otherwise active) sticky. Press (and hold) `X`, tap
2526
`OneShot_ActiveStickyKey`, then release `X`, and `X` will stay active until it
2627
is tapped again to deactivate it.
2728

29+
Additionally, if you add the `EscapeOneShot` plugin, then it acts as a toggle
30+
when no other keys are held. This saves having to remember which keys you've
31+
made sticky.
32+
2833
## Using the plugin
2934

3035
To use the plugin, just include one of the two special OneShot keys somewhere in

tests/plugins/Escape-OneShot/basic/test.ktest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ VERSION 1
22

33
KEYSWITCH OSM_0 0 0 # left shift
44
KEYSWITCH OSM_1 0 1 # left alt
5-
KEYSWITCH ESC 1 0
5+
KEYSWITCH ESC 1 0 # escape
66

77
# ==============================================================================
88
NAME EscapeOneShot cancel temporary
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// -*- mode: c++ -*-
2+
3+
/* Kaleidoscope - Firmware for computer input devices
4+
* Copyright (C) 2025 Keyboard.io, Inc.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it under
7+
* the terms of the GNU General Public License as published by the Free Software
8+
* Foundation, version 3.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13+
* details.
14+
*
15+
* You should have received a copy of the GNU General Public License along with
16+
* this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#include <cstdint>
22+
23+
namespace kaleidoscope {
24+
namespace testing {
25+
26+
} // namespace testing
27+
} // namespace kaleidoscope
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* -*- mode: c++ -*-
2+
* Copyright (C) 2025 Keyboard.io, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation, version 3.
7+
*
8+
* This program is distributed in the hope that it will be useful, but WITHOUT
9+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11+
* details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
17+
#include <Kaleidoscope.h>
18+
#include <Kaleidoscope-OneShot.h>
19+
#include <Kaleidoscope-OneShotMetaKeys.h>
20+
#include <Kaleidoscope-Escape-OneShot.h>
21+
22+
#include "./common.h"
23+
24+
// *INDENT-OFF*
25+
KEYMAPS(
26+
[0] = KEYMAP_STACKED
27+
(
28+
Key_A, Key_B, ___, ___, ___, ___, ___,
29+
OneShot_ActiveStickyKey, ___, ___, ___, ___, ___, ___,
30+
___, ___, ___, ___, ___, ___,
31+
___, ___, ___, ___, ___, ___, ___,
32+
___, ___, ___, ___,
33+
___,
34+
35+
___, ___, ___, ___, ___, ___, ___,
36+
___, ___, ___, ___, ___, ___, ___,
37+
___, ___, ___, ___, ___, ___,
38+
___, ___, ___, ___, ___, ___, ___,
39+
___, ___, ___, ___,
40+
___
41+
),
42+
)
43+
// *INDENT-ON*
44+
45+
KALEIDOSCOPE_INIT_PLUGINS(OneShot, OneShotMetaKeys, EscapeOneShot);
46+
47+
void setup() {
48+
Kaleidoscope.setup();
49+
OneShot.setTimeout(50);
50+
OneShot.setHoldTimeout(20);
51+
OneShot.setDoubleTapTimeout(20);
52+
}
53+
54+
void loop() {
55+
Kaleidoscope.loop();
56+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"cpu": {
3+
"fqbn": "keyboardio:virtual:model01",
4+
"port": ""
5+
}
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
default_fqbn: keyboardio:virtual:model01

0 commit comments

Comments
 (0)