Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autoshift explicit config #1340

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/Keystrokes/AutoShift/AutoShift.ino
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ 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:
Expand Down
24 changes: 24 additions & 0 deletions plugins/Kaleidoscope-AutoShift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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)`.
Expand Down Expand Up @@ -93,6 +99,24 @@ 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) {
// 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, clear mapped_key_ and return false
mapped_key_.key = Key_Transparent;
mapped_key_.alternate_key = Key_Transparent;
return false;
}

// =============================================================================
// Event handler hook functions

Expand Down Expand Up @@ -187,10 +211,18 @@ 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.
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;
event.key.setFlags(flags);
}
}
queue_.shift();
Runtime.handleKeyswitchEvent(event);
Expand Down
39 changes: 39 additions & 0 deletions plugins/Kaleidoscope-AutoShift/src/kaleidoscope/plugin/AutoShift.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -232,6 +248,12 @@ class AutoShift : public Plugin {
EventHandlerResult onKeyswitchEvent(KeyEvent &event);
EventHandlerResult afterEachCycle();

template<uint8_t _explicitmappings_count>
void configureLongPresses(LongPress const (&explicitmappings)[_explicitmappings_count]) {
explicitmappings_ = explicitmappings;
explicitmappings_count_ = _explicitmappings_count;
}

private:
// ---------------------------------------------------------------------------
/// A container for AutoShift configuration settings
Expand Down Expand Up @@ -268,6 +290,16 @@ 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};

// 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};
};

// =============================================================================
Expand All @@ -288,3 +320,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); \
}
54 changes: 54 additions & 0 deletions tests/plugins/AutoShift/explicit-config/explicit-config.ino
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#include <Kaleidoscope.h>
#include <Kaleidoscope-AutoShift.h>

// *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();
}
6 changes: 6 additions & 0 deletions tests/plugins/AutoShift/explicit-config/sketch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:virtual:model01",
"port": ""
}
}
1 change: 1 addition & 0 deletions tests/plugins/AutoShift/explicit-config/sketch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_fqbn: keyboardio:virtual:model01
52 changes: 52 additions & 0 deletions tests/plugins/AutoShift/explicit-config/test.ktest
Original file line number Diff line number Diff line change
@@ -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
Loading