Skip to content

Commit

Permalink
Introduce new LongPress plugin
Browse files Browse the repository at this point in the history
This commit provides a new plugin “LongPress” that allows producing different
Keys when keys are held for a short time instead of only tapped. It is
based on the existing “AutoShift” plugin and contains its functionality,
but extends it for a broader area of application.

Signed-off-by: Marco Herrn <[email protected]>
  • Loading branch information
hupfdule authored and obra committed Nov 18, 2024
1 parent 1497782 commit 2e47e7b
Show file tree
Hide file tree
Showing 19 changed files with 1,576 additions and 0 deletions.
92 changes: 92 additions & 0 deletions examples/Keystrokes/LongPress/LongPress.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// -*- mode: c++ -*-

#include <Kaleidoscope.h>

#include <Kaleidoscope-LongPress.h>
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-EEPROM-Keymap.h>
#include <Kaleidoscope-FocusSerial.h>
#include <Kaleidoscope-Macros.h>

enum {
TOGGLE_LONGPRESS,
}; // macros

enum {
QWERTY,
}; // layers

// clang-format off
KEYMAPS(
[QWERTY] = 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,

Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
XXX,

M(TOGGLE_LONGPRESS), 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,

Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
XXX
),
)
// clang-format on

// Defining a macro (on the "any" key: see above) to turn LongPress on and off
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case TOGGLE_LONGPRESS:
if (keyToggledOn(event.state))
LongPress.toggle();
break;
}
return MACRO_NONE;
}

// This sketch uses the LongPressConfig plugin, which enables run-time
// configuration of LongPress configuration settings. All of the plugins marked
// "for LongPressConfig" are optional; LongPress itself will work without them.
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings, // for LongPressConfig
EEPROMKeymap, // for LongPressConfig
Focus, // for LongPressConfig
FocusEEPROMCommand, // for LongPressConfig
FocusSettingsCommand, // for LongPressConfig
LongPress,
LongPressConfig, // for LongPressConfig
Macros // for toggle LongPress Macro
);

void setup() {
// Enable AutoShift for letter keys and number keys only:
LongPress.setAutoshiftEnabled(LongPress.letterKeys() | LongPress.numberKeys());
// Add symbol keys to the enabled autoshift categories:
LongPress.enableAutoshift(LongPress.symbolKeys());

LONGPRESS(
// Long pressing the second key in the first row on the QWERTY layer
// produces a 0 instead of a 1 (and instead of Shift-1)
kaleidoscope::plugin::LongPressKey(QWERTY, KeyAddr(0, 1), Key_0),

// instead of shifting, produce a backslash on long pressing slash on
// all layers
kaleidoscope::plugin::LongPressKey(kaleidoscope::plugin::longpress::ALL_LAYERS, Key_Slash, Key_Backslash), )

// Set the LongPress trigger time to 150ms:
LongPress.setTimeout(150);
// Start with LongPress turned off:
LongPress.disable();

Kaleidoscope.setup();
}

void loop() {
Kaleidoscope.loop();
}
6 changes: 6 additions & 0 deletions examples/Keystrokes/LongPress/sketch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}
1 change: 1 addition & 0 deletions examples/Keystrokes/LongPress/sketch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_fqbn: keyboardio:avr:model01
148 changes: 148 additions & 0 deletions plugins/Kaleidoscope-LongPress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# LongPress

LongPress allows you to type different characters when long-pressing a key
rather than tapping it.

It is derived from the AutoShift plugin and integrates its functionality. When
using LongPress it supersedes the AutoShift plugin. They conflict with each
other and should not be used together.


## Setup

To use the plugin put the following into your .ino file:

```c++
#include <Kaleidoscope-LongPress.h>

KALEIDOSCOPE_INIT_PLUGINS(LongPress);
```
## Configuration
To do anything useful some configuration is necessary.
### Long-press mappings
To define the keys that should behave differently on long-press include
a definition like the following:
```c++
LONGPRESS(
kaleidoscope::plugin::LongPressKey(kaleidoscope::plugin::longpress::ALL_LAYERS,
KeyAddr(1, 1), Key_Q), // Long-press the key on QWERTY position of “q” to enter a “q” on all layers
kaleidoscope::plugin::LongPressKey(QWERTY,
KeyAddr(2, 4), LCTRL(Key_C)), // Long-press the key below the index finger to enter “Ctrl-C“ on the QWERTY layer
kaleidoscope::plugin::LongPressKey(kaleidoscope::plugin::longpress::ALL_LAYERS,
Key_T, RALT(Key_T)), // Long-press “t” to enter a “þ” on all layers
)
```

As can be seen in the example long-presses can be configured on either `KeyAddr`
or `Key`, even in combination. Which variant is preferred is based on the use
case.

For example for mirroring the numbers in the number row (produce a “0” on long
pressing “1”, produce a “9” on long pressing “2”, etc.) the best approach is to
configure these on the `KeyAddr`. However to always generate an “ä” when “a” is
long-pressed, regardless of where the “a” is mapped on (and whether it is mapped
to different physical keys, probably on different layers), configuring it on the
`Key` may be preferable.

Be aware, however, that the order of the entries in LONGPRESS matters! Ealier
definitions take precedence over later ones. Usually it is best to define
long-presses on `KeyAddr` first and long-presses on `Key` afterwards as that is
the least surprising behaviour in case of conflicting mappings.

Another thing that can be seen in the example above is that long presses can be
restricted to a single layer (the second one is restricted to the QWERTY
layer). To apply the mapping to all layers, use the constant
`kaleidoscope::plugin::longpress::ALL_LAYERS`[^1] as can be seen the first and the
third mapping in the example above.

### Enabling and disabling LongPress

The following methods are provided for enabling / disabling the plugin altogether:

- `LongPress.enable()` to enable the plugin (after loading the plugin is enabled by default).
- `LongPress.disable()` to disable the plugin.
- `LongPress.toggle()` to switch the plugin between enabled and disabled state.
- `LongPress.enabled()` to check whether the plugin is currently enabled.

### Setting the long-press delay

To set the amount of time (in milliseconds) the LongPress plugin will wait
until it executes the long-press behaviour use `LongPress.setTimeout(timeout)`.

The default is 175.

### Auto-Shifting

One of the most common use cases for Long-Presses is auto-shifting of the
generated character. This use case has special support to avoid having to
configure every single key.

By default no auto-shifting behaviour is applied. To set this behaviour to some
certain sets of keys use one of the following methods:

- `LongPress.setAutoshiftEnabled(categories)` to activate auto-shifting for exactly the given categories.
To set multiple categories combine them using `|` (bitwise or), e.g.: `LongPress.setAutoshiftEnabled(LongPress.letterKeys() | LongPress.numberKeys())`.
- `LongPress.enableAutoshift(category)` to add a single category to be auto-shifted.
- `LongPress.disableAutoshift(category)` to remove a single category from the auto-shifted ones.
- `LongPress.isAutoshiftEnabled(category)` to check whether auto-shifting is enabled for the given category.
- `LongPress.enabledAutoShiftCategories()` to get an array of the categories for which auto-shifting is enabled.

These are the predefined categories for auto-shifting:

- `LongPress.noKeys()`: Can be used with `LongPress.setAutoshiftEnabled()` to remove all categories from being auto-shifted.
- `LongPress.letterKeys`: All letter keys.
- `LongPress.numberKeys`: All number keys (in the number row, not the numeric keypad).
- `LongPress.symbolKeys`: Other printable symbols.
- `LongPress.arrowKeys`: Navigational arrow keys.
- `LongPress.functionKeys`: All function keys (F1 – F24).
- `LongPress.printableKeys`: Letters, numbers and symbols.
- `LongPress.allKeys`: All non-modifier USB keyboard keys.

If the above categories are not sufficient for your auto-shifting needs, it is
possible to get even finer-grained control of which keys are affected by
auto-shifting, by overriding the `isAutoShiftable()` method in your sketch. For
example, to make LongPress only auto-shift keys `A` and `Z`, include the following
code in your sketch:

```c++
bool LongPress::isAutoShiftable(Key key) {
if (key == Key_A || key == key_Z)
return true;
return false;
}
```
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 to be left
alone).
In contrast to the explict configuration of long-presses via `LongPressKey`,
such auto-shift behaviour always applies to all layers.
## Conflicts with other plugins
Care should be taken when using the plugin together with the Qukeys, SpaceCadet
and Chords plugins. Most of the time they conflict with each other and when
using one of these plugins together with LongPress it should be avoided to
configure them on the same keys.
In any case the LongPress plugin should be defined as the last one of these.
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: /examples/Keystrokes/LongPress/LongPress.ino
[^1] The constant `kaleidoscope::plugin::longpress::ALL_LAYERS` is a bit long
and unwieldy. Before integrating this plugin there were some discussions about
whether that is acceptable. Therefore people using that plugin to apply
mappings to all layers are kindly requested to provide some feedback about
their usage and whether they are annoyed by that long constant name or not.
7 changes: 7 additions & 0 deletions plugins/Kaleidoscope-LongPress/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name=Kaleidoscope-LongPress
version=0.0.0
sentence=Provide different key strokes on long press
maintainer=Kaleidoscope's Developers <[email protected]>
url=https://github.com/keyboardio/Kaleidoscope
author=Marco Herrn <[email protected]>
paragraph=
20 changes: 20 additions & 0 deletions plugins/Kaleidoscope-LongPress/src/Kaleidoscope-LongPress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* -*- mode: c++ -*-
* Kaleidoscope-LongPress -- Provide different key strokes on long press
* Copyright (C) 2024 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/LongPress.h" // IWYU pragma: export
Loading

0 comments on commit 2e47e7b

Please sign in to comment.