From dad94c8d8e4dbdffccc93df862dc08b242847827 Mon Sep 17 00:00:00 2001 From: Peter Davis Date: Wed, 9 Apr 2025 22:20:22 -0700 Subject: [PATCH] Escape-Oneshot: toggle OneShot_ActiveStickyKey by pressing it again with no keys held. Signed-off-by: Peter Davis --- docs/NEWS.md | 11 +++- plugins/Kaleidoscope-Escape-OneShot/README.md | 7 ++- .../kaleidoscope/plugin/Escape-OneShot.cpp | 34 +++++++++-- .../src/kaleidoscope/plugin/Escape-OneShot.h | 3 + .../Kaleidoscope-OneShotMetaKeys/README.md | 7 ++- tests/plugins/Escape-OneShot/basic/test.ktest | 2 +- .../plugins/Escape-OneShot/meta-keys/common.h | 27 +++++++++ .../Escape-OneShot/meta-keys/meta-keys.ino | 56 +++++++++++++++++++ .../Escape-OneShot/meta-keys/sketch.json | 6 ++ .../Escape-OneShot/meta-keys/sketch.yaml | 1 + .../Escape-OneShot/meta-keys/test.ktest | 34 +++++++++++ 11 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 tests/plugins/Escape-OneShot/meta-keys/common.h create mode 100644 tests/plugins/Escape-OneShot/meta-keys/meta-keys.ino create mode 100644 tests/plugins/Escape-OneShot/meta-keys/sketch.json create mode 100644 tests/plugins/Escape-OneShot/meta-keys/sketch.yaml create mode 100644 tests/plugins/Escape-OneShot/meta-keys/test.ktest diff --git a/docs/NEWS.md b/docs/NEWS.md index 01aa6831ba..c13ffdec04 100644 --- a/docs/NEWS.md +++ b/docs/NEWS.md @@ -133,7 +133,8 @@ automatically become a OneShot key when pressed, by applying modifier flags to #### Two new special OneShot keys OneShot can now also turn _any_ key into a sticky key, using either of two -special `Key` values that can be inserted in the keymap. +special `Key` values that can be inserted in the keymap. Configure the +`OneShotMetaKeys` plugin to use these. ##### `OneShot_MetaStickyKey` @@ -151,6 +152,14 @@ pressed. Press `X`, press `OneShot_ActiveStickyKey`, and release `X`, and `X` will be sticky until it is pressed again to deactivate it. Again, it works on any key value, so use with caution. +Additionally, when you add the `EscapeOneShot` plugin, this key will act as a +toggle when you press it again while holding no other keys. + +##### `Key_OneShotCancel` + +When you configure the `EscapeOneShot` plugin, this key cancels all OneShot +keys. + #### LED-ActiveModColor highlighting With the updates to OneShot, LED-ActiveModColor now recognizes and highlights diff --git a/plugins/Kaleidoscope-Escape-OneShot/README.md b/plugins/Kaleidoscope-Escape-OneShot/README.md index 0a0c164ca4..91967f0054 100644 --- a/plugins/Kaleidoscope-Escape-OneShot/README.md +++ b/plugins/Kaleidoscope-Escape-OneShot/README.md @@ -5,7 +5,12 @@ effect - or act as the normal `Esc` key if none are active, or if any of them are still held. For those times when one accidentally presses a one-shot key, or change their minds. -Additionally, the special `Key_OneShotCancel` key will also count as a oneshot +This plugin also modifies the behavior of `OneShot_ActiveStickyKey` +so that it acts like a toggle. Without holding any keys, pressing +`OneShot_ActiveStickyKey` again will cancel any active sticky keys. +This saves one from having to remember to deactivate each key individually. + +Additionally, the special `Key_OneShotCancel` key will always count as a oneshot cancel key, would one want a dedicated key for the purpose. ## Using the plugin diff --git a/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.cpp b/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.cpp index 6c4af240ef..ab79dda440 100644 --- a/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.cpp +++ b/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.cpp @@ -22,7 +22,8 @@ #include "kaleidoscope/plugin/Escape-OneShot.h" -#include // for OneShot +#include // for OneShot +#include // for OneShot_ActiveStickyKey #include "kaleidoscope/KeyEvent.h" // for KeyEvent #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::E... @@ -32,19 +33,42 @@ namespace kaleidoscope { namespace plugin { +EventHandlerResult EscapeOneShot::onKeyswitchEvent(KeyEvent &event) { + if (!event.addr.isValid() || keyIsInjected(event.state)) + return EventHandlerResult::OK; + // Track physically-held keys - for toggling OneShot_ActiveStickyKey below. + // Needed because OneShot::onKeyEvent blocks release events. + if (keyToggledOn(event.state)) { + held_addrs_.set(event.addr); + } else if (keyToggledOff(event.state)) { + held_addrs_.clear(event.addr); + } + return EventHandlerResult::OK; +} + EventHandlerResult EscapeOneShot::onKeyEvent(KeyEvent &event) { // We only act on an escape key (or `cancel_oneshot_key_`, if that has been // set) that has just been pressed, and not generated by some other // plugin. Also, only if at least one OneShot key is active and/or - // sticky. Last, only if there are no OneShot keys currently being held. + // sticky. // - // `Key_OneShotCancel` will always count as an escape key, even if not - // explicitly set so. + // Two keys will always count as an escape key, even if not explicitly set: + // - `Key_OneShotCancel` - explicit escape key + // - `OneShot_ActiveStickyKey` - toggle off if no other keys are held if ((event.key == settings_.cancel_oneshot_key || - event.key == Key_OneShotCancel) && + event.key == Key_OneShotCancel || + event.key == OneShot_ActiveStickyKey) && keyToggledOn(event.state) && !keyIsInjected(event.state) && ::OneShot.isActive()) { + if (event.key == OneShot_ActiveStickyKey) { + for (KeyAddr held_addr : held_addrs_) { + if (Runtime.lookupKey(held_addr) != OneShot_ActiveStickyKey) { + // Keys are still physically held - don't cancel. + return EventHandlerResult::OK; + } + } + } // Cancel all OneShot keys ::OneShot.cancel(true); // Change the cancellation key to a blank key, and signal that event diff --git a/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.h b/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.h index 59e82a282d..9ddebb2707 100644 --- a/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.h +++ b/plugins/Kaleidoscope-Escape-OneShot/src/kaleidoscope/plugin/Escape-OneShot.h @@ -25,6 +25,7 @@ #include // for OS_CANCEL #include // for uint16_t +#include "kaleidoscope/KeyAddrBitfield.h" // for KeyAddrBitfield #include "kaleidoscope/KeyEvent.h" // for KeyEvent #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult #include "kaleidoscope/key_defs.h" // for Key, Key_Escape @@ -40,6 +41,7 @@ namespace plugin { class EscapeOneShot : public kaleidoscope::Plugin { public: + EventHandlerResult onKeyswitchEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event); void setCancelKey(Key cancel_key) { @@ -56,6 +58,7 @@ class EscapeOneShot : public kaleidoscope::Plugin { Key cancel_oneshot_key; }; Settings settings_ = {.cancel_oneshot_key = Key_Escape}; + KeyAddrBitfield held_addrs_; }; class EscapeOneShotConfig : public Plugin { diff --git a/plugins/Kaleidoscope-OneShotMetaKeys/README.md b/plugins/Kaleidoscope-OneShotMetaKeys/README.md index 9b192e8ec5..a9f0349401 100644 --- a/plugins/Kaleidoscope-OneShotMetaKeys/README.md +++ b/plugins/Kaleidoscope-OneShotMetaKeys/README.md @@ -7,7 +7,8 @@ to make any key on the keyboard (not just modifiers and layer shift keys) These are both `Key` values that can be used as entries in your sketch's keymap. Any keys made sticky in this way can be released just like OneShot modifier -keys, by tapping them again to cancel the effect. +keys, by tapping them again to cancel the effect. You can also use the +`EscapeOneShot` plugin to cancel sticky keys without having to remember them. ## The `OneShot_MetaStickyKey` @@ -25,6 +26,10 @@ currently held (or otherwise active) sticky. Press (and hold) `X`, tap `OneShot_ActiveStickyKey`, then release `X`, and `X` will stay active until it is tapped again to deactivate it. +Additionally, if you add the `EscapeOneShot` plugin, then it acts as a toggle +when no other keys are held. This saves having to remember which keys you've +made sticky. + ## Using the plugin To use the plugin, just include one of the two special OneShot keys somewhere in diff --git a/tests/plugins/Escape-OneShot/basic/test.ktest b/tests/plugins/Escape-OneShot/basic/test.ktest index 11822d25a1..748f8762b8 100644 --- a/tests/plugins/Escape-OneShot/basic/test.ktest +++ b/tests/plugins/Escape-OneShot/basic/test.ktest @@ -2,7 +2,7 @@ VERSION 1 KEYSWITCH OSM_0 0 0 # left shift KEYSWITCH OSM_1 0 1 # left alt -KEYSWITCH ESC 1 0 +KEYSWITCH ESC 1 0 # escape # ============================================================================== NAME EscapeOneShot cancel temporary diff --git a/tests/plugins/Escape-OneShot/meta-keys/common.h b/tests/plugins/Escape-OneShot/meta-keys/common.h new file mode 100644 index 0000000000..9b2478f39c --- /dev/null +++ b/tests/plugins/Escape-OneShot/meta-keys/common.h @@ -0,0 +1,27 @@ +// -*- mode: c++ -*- + +/* Kaleidoscope - Firmware for computer input devices + * Copyright (C) 2025 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 . + */ + +#pragma once + +#include + +namespace kaleidoscope { +namespace testing { + +} // namespace testing +} // namespace kaleidoscope diff --git a/tests/plugins/Escape-OneShot/meta-keys/meta-keys.ino b/tests/plugins/Escape-OneShot/meta-keys/meta-keys.ino new file mode 100644 index 0000000000..d5c2fb4de8 --- /dev/null +++ b/tests/plugins/Escape-OneShot/meta-keys/meta-keys.ino @@ -0,0 +1,56 @@ +/* -*- mode: c++ -*- + * Copyright (C) 2025 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 . + */ + +#include +#include +#include +#include + +#include "./common.h" + +// *INDENT-OFF* +KEYMAPS( + [0] = KEYMAP_STACKED + ( + Key_A, Key_B, ___, ___, ___, ___, ___, + OneShot_ActiveStickyKey, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, + ___, + + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, + ___ + ), +) +// *INDENT-ON* + +KALEIDOSCOPE_INIT_PLUGINS(OneShot, OneShotMetaKeys, EscapeOneShot); + +void setup() { + Kaleidoscope.setup(); + OneShot.setTimeout(50); + OneShot.setHoldTimeout(20); + OneShot.setDoubleTapTimeout(20); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/tests/plugins/Escape-OneShot/meta-keys/sketch.json b/tests/plugins/Escape-OneShot/meta-keys/sketch.json new file mode 100644 index 0000000000..43dc4c7e2d --- /dev/null +++ b/tests/plugins/Escape-OneShot/meta-keys/sketch.json @@ -0,0 +1,6 @@ +{ + "cpu": { + "fqbn": "keyboardio:virtual:model01", + "port": "" + } +} \ No newline at end of file diff --git a/tests/plugins/Escape-OneShot/meta-keys/sketch.yaml b/tests/plugins/Escape-OneShot/meta-keys/sketch.yaml new file mode 100644 index 0000000000..4d94810065 --- /dev/null +++ b/tests/plugins/Escape-OneShot/meta-keys/sketch.yaml @@ -0,0 +1 @@ +default_fqbn: keyboardio:virtual:model01 diff --git a/tests/plugins/Escape-OneShot/meta-keys/test.ktest b/tests/plugins/Escape-OneShot/meta-keys/test.ktest new file mode 100644 index 0000000000..0915d3f4d1 --- /dev/null +++ b/tests/plugins/Escape-OneShot/meta-keys/test.ktest @@ -0,0 +1,34 @@ +VERSION 1 + +KEYSWITCH KEY_A 0 0 +KEYSWITCH KEY_B 0 1 +KEYSWITCH OS_ASK 1 0 # OneShot_ActiveStickyKey + +# ============================================================================== +NAME EscapeOneShot cancel ActiveStickyKey + +RUN 4 ms +PRESS KEY_A +RUN 1 cycle +EXPECT keyboard-report Key_A +RUN 4 ms +PRESS KEY_B +RUN 1 cycle +EXPECT keyboard-report Key_A Key_B +RUN 4 ms +PRESS OS_ASK +RUN 4 ms +RELEASE KEY_A +RUN 4 ms +RELEASE KEY_B +RUN 4 ms +RELEASE OS_ASK +RUN 4 ms + +PRESS OS_ASK +RUN 1 cycle +EXPECT keyboard-report Key_B # Key_A gets cancelled first +EXPECT keyboard-report empty # Report should now be empty +RELEASE OS_ASK + +RUN 5 ms \ No newline at end of file