From f89e63c6ccc0b97a14723584edcea6963ae63116 Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Tue, 4 Apr 2023 23:36:14 +0200 Subject: [PATCH 1/5] Make autoshift longpress configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit allows specifying the keys to produce on long press if not a shifted, but a totally different character is wanted. Such an explicit mapping takes precedence over shifting the key. That means if all alpanumeric characters are configured for AutoShift, but the ‘e’ key has an explicit mapping to produce ‘ë’, a long press on ‘e’ will result in ’ë’, not ‘E’. Signed-off-by: Marco Herrn --- .../src/kaleidoscope/plugin/AutoShift.cpp | 42 +++++++++++++++++-- .../src/kaleidoscope/plugin/AutoShift.h | 35 ++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp index 2427828545..fc0dd953d5 100644 --- a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp +++ b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp @@ -23,6 +23,7 @@ #include "kaleidoscope/Runtime.h" // for Runtime, Runtime_ #include "kaleidoscope/key_defs.h" // for Key, Key_0, Key_1, Key_A, Key_F1, Key_F12, Key... #include "kaleidoscope/keyswitch_state.h" // for keyToggledOn, keyIsInjected +#include "kaleidoscope/progmem_helpers.h" // for cloneFromProgmem // IWYU pragma: no_include "HIDAliases.h" @@ -57,6 +58,11 @@ bool AutoShift::enabledForKey(Key key) { if (!key.isKeyboardKey()) return false; + // Check whether we have an explicit mapping for that key + if (isExplicitlyMapped(key)) { + return true; + } + // We compare only the keycode, and disregard any modifier flags applied to // the key. This simplifies the comparison, and also allows AutoShift to // apply to keys like `RALT(Key_E)`. @@ -93,6 +99,19 @@ bool AutoShift::enabledForKey(Key key) { return false; } +bool AutoShift::isExplicitlyMapped(Key key) { + // Check whether the given key has an explicit mapping to a different one + for (uint8_t i{0}; i < explicitmappings_count_; ++i) { + LongPress mappedKey = cloneFromProgmem(explicitmappings_[i]); + if (mappedKey.key == key) { + return true; + } + } + + // If no matches were found, return false + return false; +} + // ============================================================================= // Event handler hook functions @@ -187,10 +206,25 @@ void AutoShift::flushEvent(bool is_long_press) { return; KeyEvent event = queue_.event(0); if (is_long_press) { - event.key = Runtime.lookupKey(event.addr); - uint8_t flags = event.key.getFlags(); - flags ^= SHIFT_HELD; - event.key.setFlags(flags); + event.key = Runtime.lookupKey(event.addr); + + // If we have an explicit mapping for that key, apply that. + bool mapped= false; + for (uint8_t i{0}; i < explicitmappings_count_; ++i) { + LongPress mappedKey = cloneFromProgmem(explicitmappings_[i]); + if (mappedKey.key == event.key) { + event.key = mappedKey.alternate_key; + mapped= true; + } + } + + // If there was no explicit mapping, just add the shift modifier + if (!mapped) { + // event.key = longpresses[event.key] + uint8_t flags = event.key.getFlags(); + flags ^= SHIFT_HELD; + event.key.setFlags(flags); + } } queue_.shift(); Runtime.handleKeyswitchEvent(event); diff --git a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h index 38d7c87911..d444f9b7c5 100644 --- a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h +++ b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h @@ -29,6 +29,22 @@ namespace kaleidoscope { namespace plugin { +struct LongPress { + // The key that should result in a different value on long press. + Key key; + // The alternate Key value that should be produced on long press. + Key alternate_key; + + // This is the constructor that should be used when creating a LongPress object in + // the PROGMEM array that will be used by explicit mappings (i.e. in the `AUTOSHIFT()` + // macro). + constexpr LongPress(Key key, Key alternate_key) + : key(key), alternate_key(alternate_key) {} + // This constructor is here so that we can create an empty LongPress object in RAM + // into which we can copy the values from a PROGMEM LongPress object. + LongPress() = default; +}; + // ============================================================================= /// Kaleidoscope plugin for long-press auto-shift keys /// @@ -232,6 +248,12 @@ class AutoShift : public Plugin { EventHandlerResult onKeyswitchEvent(KeyEvent &event); EventHandlerResult afterEachCycle(); + template + void configureLongPresses(LongPress const (&explicitmappings)[_explicitmappings_count]) { + explicitmappings_ = explicitmappings; + explicitmappings_count_ = _explicitmappings_count; + } + private: // --------------------------------------------------------------------------- /// A container for AutoShift configuration settings @@ -268,6 +290,12 @@ class AutoShift : public Plugin { /// The default function for `isAutoShiftable()` bool enabledForKey(Key key); + + bool isExplicitlyMapped(Key key); + + // An array of LongPress objects in PROGMEM. + LongPress const *explicitmappings_{nullptr}; + uint8_t explicitmappings_count_{0}; }; // ============================================================================= @@ -288,3 +316,10 @@ class AutoShiftConfig : public Plugin { extern kaleidoscope::plugin::AutoShift AutoShift; extern kaleidoscope::plugin::AutoShiftConfig AutoShiftConfig; + +#define AUTOSHIFT(longpress_defs...) \ + { \ + static kaleidoscope::plugin::LongPress const qk_table[] PROGMEM = { \ + longpress_defs}; \ + AutoShift.configureLongPresses(qk_table); \ + } From cd447ed76fb8203c04fde1836bf3792fa660cb04 Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Thu, 13 Apr 2023 08:57:23 +0200 Subject: [PATCH 2/5] Add tests for AutoShift explicit configuration Signed-off-by: Marco Herrn --- .../explicit-config/explicit-config.ino | 54 +++++++++++++++++++ .../AutoShift/explicit-config/sketch.json | 6 +++ .../AutoShift/explicit-config/sketch.yaml | 1 + .../AutoShift/explicit-config/test.ktest | 52 ++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 tests/plugins/AutoShift/explicit-config/explicit-config.ino create mode 100644 tests/plugins/AutoShift/explicit-config/sketch.json create mode 100644 tests/plugins/AutoShift/explicit-config/sketch.yaml create mode 100644 tests/plugins/AutoShift/explicit-config/test.ktest diff --git a/tests/plugins/AutoShift/explicit-config/explicit-config.ino b/tests/plugins/AutoShift/explicit-config/explicit-config.ino new file mode 100644 index 0000000000..9975a4aa59 --- /dev/null +++ b/tests/plugins/AutoShift/explicit-config/explicit-config.ino @@ -0,0 +1,54 @@ +/* -*- mode: c++ -*- + * Copyright (C) 2021 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 + +// *INDENT-OFF* +KEYMAPS( + [0] = KEYMAP_STACKED + ( + Key_LeftShift, Key_RightShift, ___, ___, ___, ___, ___, + Key_A, Key_B, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, + ___, + + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, + ___ + ), +) +// *INDENT-ON* + +KALEIDOSCOPE_INIT_PLUGINS(AutoShift); + +void setup() { + Kaleidoscope.setup(); + AutoShift.setTimeout(20); + + AUTOSHIFT( + kaleidoscope::plugin::LongPress(Key_A, Key_C), + ) +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/tests/plugins/AutoShift/explicit-config/sketch.json b/tests/plugins/AutoShift/explicit-config/sketch.json new file mode 100644 index 0000000000..8cc869221a --- /dev/null +++ b/tests/plugins/AutoShift/explicit-config/sketch.json @@ -0,0 +1,6 @@ +{ + "cpu": { + "fqbn": "keyboardio:virtual:model01", + "port": "" + } +} diff --git a/tests/plugins/AutoShift/explicit-config/sketch.yaml b/tests/plugins/AutoShift/explicit-config/sketch.yaml new file mode 100644 index 0000000000..4d94810065 --- /dev/null +++ b/tests/plugins/AutoShift/explicit-config/sketch.yaml @@ -0,0 +1 @@ +default_fqbn: keyboardio:virtual:model01 diff --git a/tests/plugins/AutoShift/explicit-config/test.ktest b/tests/plugins/AutoShift/explicit-config/test.ktest new file mode 100644 index 0000000000..4a433e6250 --- /dev/null +++ b/tests/plugins/AutoShift/explicit-config/test.ktest @@ -0,0 +1,52 @@ +VERSION 1 + +KEYSWITCH LSHIFT 0 0 +KEYSWITCH RSHIFT 0 1 +KEYSWITCH A 1 0 +KEYSWITCH B 1 1 + +# ============================================================================== +NAME AutoShift explicit config + +RUN 4 ms +PRESS A +RUN 1 cycle + +# Timeout is 20ms +RUN 20 ms +EXPECT keyboard-report Key_C # report: { 6 } + +RUN 4 ms +RELEASE A +RUN 1 cycle +EXPECT keyboard-report empty + +RUN 5 ms + +# ============================================================================== +NAME AutoShift explicit config shifted + +RUN 4 ms +PRESS LSHIFT +RUN 1 cycle +EXPECT keyboard-report Key_LeftShift # report: { e1 } + +RUN 4 ms +PRESS A +RUN 1 cycle + +# Timeout is 20ms +RUN 20 ms +EXPECT keyboard-report Key_LeftShift Key_C # report: { 6 e1 } + +RUN 4 ms +RELEASE A +RUN 1 cycle +EXPECT keyboard-report Key_LeftShift # report: { e1 } + +RUN 4 ms +RELEASE LSHIFT +RUN 1 cycle +EXPECT keyboard-report empty + +RUN 5 ms From aee2cf6ab68d8821779bde0949463ac1e2914f61 Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Thu, 13 Apr 2023 09:42:54 +0200 Subject: [PATCH 3/5] Add example for AutoShift explicit config Add an example ino-file and add a paragraph to the README about the usage. Signed-off-by: Marco Herrn --- examples/Keystrokes/AutoShift/AutoShift.ino | 4 ++++ plugins/Kaleidoscope-AutoShift/README.md | 24 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/examples/Keystrokes/AutoShift/AutoShift.ino b/examples/Keystrokes/AutoShift/AutoShift.ino index 4b6cbc353e..a0f7c8ad5f 100644 --- a/examples/Keystrokes/AutoShift/AutoShift.ino +++ b/examples/Keystrokes/AutoShift/AutoShift.ino @@ -65,6 +65,10 @@ void setup() { AutoShift.setEnabled(AutoShift.letterKeys() | AutoShift.numberKeys()); // Add symbol keys to the enabled categories: AutoShift.enable(AutoShift.symbolKeys()); + // instead of shifting, produce a backslash on long pressing slash + AUTOSHIFT( + kaleidoscope::plugin::LongPress(Key_Slash, Key_Backslash), + ) // Set the AutoShift long-press time to 150ms: AutoShift.setTimeout(150); // Start with AutoShift turned off: diff --git a/plugins/Kaleidoscope-AutoShift/README.md b/plugins/Kaleidoscope-AutoShift/README.md index f16b72f781..1987c44fd6 100644 --- a/plugins/Kaleidoscope-AutoShift/README.md +++ b/plugins/Kaleidoscope-AutoShift/README.md @@ -78,6 +78,30 @@ As you can see, this method takes a `Key` as its input and returns either `true` (for keys eligible to be auto-shifted) or `false` (for keys AutoShift will leave alone). +## Producing other characters than shifted variants of the keys + +It is possible to produce other characters than just shifted variants of the +pressed key by providing an explicit mapping between the pressed key and the +key that should be produced instead. + +Such a mapping must be defined in the `setup` method in your sketch: + +``` +AUTOSHIFT( + kaleidoscope::plugin::LongPress(Key_Slash, Key_Backslash), + kaleidoscope::plugin::LongPress(Key_Z, ShiftToLayer(SYMBOL)), +) +``` + +Such explicit mappings take precedence over shifting the key. That +means if all alphanumeric characters are configured for AutoShift, but +the ‘e’ key has an explicit mapping to produce ‘ë’, a long press on ‘e’ +will result in ’ë’, not ‘E’. + +As can be seen in the example above the resulting key does not necessarily need +to be a regular key, but can be any Key object, like the layer shift in the +example. Be aware however that key repeats are not suppressed. + ## Plugin compatibility If you're using AutoShift in a sketch that also includes the Qukeys and/or From b3fc45937c7314732639a9a8caacb17803f3ac34 Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Thu, 13 Apr 2023 23:32:00 +0200 Subject: [PATCH 4/5] Cache explicitly configured AutoShift key Avoid searching for the mapping again if it is already known. Signed-off-by: Marco Herrn --- .../src/kaleidoscope/plugin/AutoShift.cpp | 24 +++++++++---------- .../src/kaleidoscope/plugin/AutoShift.h | 4 ++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp index fc0dd953d5..6a58a8ad9b 100644 --- a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp +++ b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.cpp @@ -104,11 +104,16 @@ bool AutoShift::isExplicitlyMapped(Key key) { for (uint8_t i{0}; i < explicitmappings_count_; ++i) { LongPress mappedKey = cloneFromProgmem(explicitmappings_[i]); if (mappedKey.key == key) { - return true; + // cache the mapped key to not have to search it again + mapped_key_.key = mappedKey.key; + mapped_key_.alternate_key = mappedKey.alternate_key; + return true; } } - // If no matches were found, return false + // If no matches were found, clear mapped_key_ and return false + mapped_key_.key = Key_Transparent; + mapped_key_.alternate_key = Key_Transparent; return false; } @@ -209,17 +214,10 @@ void AutoShift::flushEvent(bool is_long_press) { event.key = Runtime.lookupKey(event.addr); // If we have an explicit mapping for that key, apply that. - bool mapped= false; - for (uint8_t i{0}; i < explicitmappings_count_; ++i) { - LongPress mappedKey = cloneFromProgmem(explicitmappings_[i]); - if (mappedKey.key == event.key) { - event.key = mappedKey.alternate_key; - mapped= true; - } - } - - // If there was no explicit mapping, just add the shift modifier - if (!mapped) { + if (mapped_key_.key != Key_Transparent) { + event.key = mapped_key_.alternate_key; + } else { + // If there was no explicit mapping, just add the shift modifier // event.key = longpresses[event.key] uint8_t flags = event.key.getFlags(); flags ^= SHIFT_HELD; diff --git a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h index d444f9b7c5..a87d1ed027 100644 --- a/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h +++ b/plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h @@ -296,6 +296,10 @@ class AutoShift : public Plugin { // An array of LongPress objects in PROGMEM. LongPress const *explicitmappings_{nullptr}; uint8_t explicitmappings_count_{0}; + + // A cache of the current explicit config key values, so we + // don't have to keep looking them up from PROGMEM. + LongPress mapped_key_ = {.key = Key_Transparent, .alternate_key = Key_Transparent}; }; // ============================================================================= From 16b9a9198ce8743b6371ed46b8c1bd6a070e9c06 Mon Sep 17 00:00:00 2001 From: Marco Herrn Date: Tue, 25 Apr 2023 10:24:35 +0200 Subject: [PATCH 5/5] Accept formatting changes from automatic formatter Signed-off-by: Marco Herrn --- examples/Keystrokes/AutoShift/AutoShift.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/Keystrokes/AutoShift/AutoShift.ino b/examples/Keystrokes/AutoShift/AutoShift.ino index a0f7c8ad5f..bb9c11bcf0 100644 --- a/examples/Keystrokes/AutoShift/AutoShift.ino +++ b/examples/Keystrokes/AutoShift/AutoShift.ino @@ -67,8 +67,7 @@ void setup() { AutoShift.enable(AutoShift.symbolKeys()); // instead of shifting, produce a backslash on long pressing slash AUTOSHIFT( - kaleidoscope::plugin::LongPress(Key_Slash, Key_Backslash), - ) + kaleidoscope::plugin::LongPress(Key_Slash, Key_Backslash), ) // Set the AutoShift long-press time to 150ms: AutoShift.setTimeout(150); // Start with AutoShift turned off: