Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>pull/1040/head
parent
c4e6da0e40
commit
f7264f9f4d
@ -0,0 +1,66 @@
|
||||
# CharShift
|
||||
|
||||
CharShift allows you to independently assign symbols to shifted and unshifted
|
||||
positions of keymap entries. Either or both symbols can be ones that normally
|
||||
requires the `shift` modifier, and either or both symbols can be ones normally
|
||||
produced without it.
|
||||
|
||||
For example you can configure your keyboard so that a single key produces `,`
|
||||
when pressed unshifted, but `;` when pressed with `shift` held. Or `(`
|
||||
unshifted, and `[` shifted. Or `+`/`*` — all without changing your OS keyboard
|
||||
layout.
|
||||
|
||||
## Using the plugin
|
||||
|
||||
Using the plugin with its defaults is as simple as including the header, and
|
||||
enabling the plugin:
|
||||
|
||||
```c++
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-CharShift.h>
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(CharShift);
|
||||
```
|
||||
|
||||
Further configuration is required, of course; see below.
|
||||
|
||||
Note: CharShift should be registered in `KALEIDOSCOPE_INIT_PLUGINS()` after any
|
||||
plugin that changes the event's `Key` value to that of an CharShift key.
|
||||
|
||||
## Configuring CharShift keys
|
||||
|
||||
To use CharShift, we must first define `KeyPair` objects, which can then be
|
||||
referenced by entries in the keymap. This is easiest to do by using the
|
||||
`UNKEYS()` preprocessor macro in the sketch's `setup()` function, as follows:
|
||||
|
||||
```c++
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
UNKEYS(
|
||||
kaleidoscope::plugin::KeyPair(Key_Comma, Key_Semicolon), // `,`/`;`
|
||||
kaleidoscope::plugin::KeyPair(Key_Period, LSHIFT(Key_Semicolon)), // `.`/`:`
|
||||
kaleidoscope::plugin::KeyPair(LSHIFT(Key_9), Key_LeftBracket), // `(`/`[`
|
||||
kaleidoscope::plugin::KeyPair(LSHIFT(Key_Comma), LSHIFT(Key_LeftBracket)), // `<`/`{`
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The first argument to the `KeyPair()` constructor is the value for when the key is
|
||||
pressed without `shift` held, the second is what you'll get if a `shift`
|
||||
modifier is being held when the key toggles on. If that second ("upper") value
|
||||
doesn't have the `shift` modifier flag (i.e. `LSHIFT()`) applied to it, the held
|
||||
`shift` modifier will be suppressed when the key is pressed, allowing the
|
||||
"unshifted" symbol to be produced.
|
||||
|
||||
These `KeyPair`s can be referred to in the sketch's keymap by using the `UK()`
|
||||
preprocessor macro, which takes an integer argument, referring to items in the
|
||||
`UNKEYS()` array, starting with zero. With the example above, an entry of
|
||||
`UK(2)` will output `(` when pressed without `shift`, and `[` if `shift` is
|
||||
being held.
|
||||
|
||||
## Further reading
|
||||
|
||||
Starting from the [example][plugin:example] is the recommended way of getting
|
||||
started with the plugin.
|
||||
|
||||
[plugin:example]: /examples/Keystrokes/CharShift/CharShift.ino
|
@ -0,0 +1,7 @@
|
||||
name=Kaleidoscope-CharShift
|
||||
version=0.0.0
|
||||
sentence=Independent assignment of symbols to keys based on shift state
|
||||
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
|
||||
url=https://github.com/keyboardio/Kaleidoscope
|
||||
author=Michael Richters <gedankenexperimenter@gmail.com>
|
||||
paragraph=
|
@ -0,0 +1,20 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-CharShift -- Independently assign shifted and unshifted symbols
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kaleidoscope/plugin/CharShift.h>
|
@ -0,0 +1,149 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-CharShift -- Independently assign shifted and unshifted symbols
|
||||
* 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/plugin/CharShift.h"
|
||||
|
||||
#include <Kaleidoscope-Ranges.h>
|
||||
#include <Kaleidoscope-FocusSerial.h>
|
||||
|
||||
#include "kaleidoscope/KeyAddr.h"
|
||||
#include "kaleidoscope/key_defs.h"
|
||||
#include "kaleidoscope/KeyEvent.h"
|
||||
#include "kaleidoscope/keyswitch_state.h"
|
||||
#include "kaleidoscope/progmem_helpers.h"
|
||||
#include "kaleidoscope/Runtime.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
// =============================================================================
|
||||
// CharShift class variables
|
||||
CharShift::KeyPair const * CharShift::progmem_keypairs_{nullptr};
|
||||
uint8_t CharShift::num_keypairs_{0};
|
||||
|
||||
bool CharShift::reverse_shift_state_{false};
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Event handlers
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
EventHandlerResult CharShift::onNameQuery() {
|
||||
return ::Focus.sendName(F("CharShift"));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
EventHandlerResult CharShift::onKeyEvent(KeyEvent &event) {
|
||||
// If the event is for anything other than an CharShift key, we ignore it.
|
||||
if (!isCharShiftKey(event.key)) {
|
||||
// If this event is for a Keyboard key, we need to stop
|
||||
// `beforeReportingState()` from suppressing `shift` keys.
|
||||
if (event.key.isKeyboardKey())
|
||||
reverse_shift_state_ = false;
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
// Default to not suppressing `shift` modifiers.
|
||||
reverse_shift_state_ = false;
|
||||
|
||||
// It shouldn't be possible for an CharShift key to toggle off, because it
|
||||
// will get replaced by on of its `KeyPair` values when it toggles on, but just
|
||||
// in case, we exit early if that happens.
|
||||
if (keyToggledOff(event.state))
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
// Next, we get the `KeyPair` values corresponding to the event key.
|
||||
KeyPair keypair = decodeCharShiftKey(event.key);
|
||||
|
||||
// Determine if a shift key is being held.
|
||||
bool shift_held = false;
|
||||
for (Key key : live_keys.all()) {
|
||||
if (key.isKeyboardShift()) {
|
||||
shift_held = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shift_held) {
|
||||
// No shift key is held; just use the base value of the `KeyPair`.
|
||||
event.key = keypair.lower;
|
||||
} else {
|
||||
// At least one shift key is held; use the shifted value.
|
||||
event.key = keypair.upper;
|
||||
// Check for a shift modifier flag.
|
||||
if (event.key.isKeyboardKey() &&
|
||||
(event.key.getFlags() & SHIFT_HELD) == 0) {
|
||||
// The upper key doesn't have the `shift` modifier flag; we need to
|
||||
// suppress `shift` in `beforeReportingState()`.
|
||||
reverse_shift_state_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
EventHandlerResult CharShift::beforeReportingState(const KeyEvent &event) {
|
||||
// If `onKeyEvent()` has signalled that `shift` should be suppressed, this is
|
||||
// the time to do it. We can't do it in `onKeyEvent()`, because the new
|
||||
// Keyboard HID report hasn't been prepared yet there.
|
||||
if (reverse_shift_state_) {
|
||||
Runtime.hid().keyboard().releaseKey(Key_LeftShift);
|
||||
Runtime.hid().keyboard().releaseKey(Key_RightShift);
|
||||
}
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Support functions
|
||||
|
||||
bool CharShift::isCharShiftKey(Key key) {
|
||||
return (key >= ranges::CS_FIRST && key <= ranges::CS_LAST);
|
||||
}
|
||||
|
||||
CharShift::KeyPair CharShift::decodeCharShiftKey(Key key) {
|
||||
uint8_t i = key.getRaw() - ranges::CS_FIRST;
|
||||
if (i < numKeyPairs()) {
|
||||
return readKeyPair(i);
|
||||
}
|
||||
return {Key_NoKey, Key_NoKey};
|
||||
}
|
||||
|
||||
// This should be overridden if the KeyPairs array is stored in EEPROM
|
||||
__attribute__((weak))
|
||||
uint8_t CharShift::numKeyPairs() {
|
||||
return numProgmemKeyPairs();
|
||||
}
|
||||
|
||||
// This should be overridden if the KeyPairs array is stored in EEPROM
|
||||
__attribute__((weak))
|
||||
CharShift::KeyPair CharShift::readKeyPair(uint8_t n) {
|
||||
return readKeyPairFromProgmem(n);
|
||||
}
|
||||
|
||||
uint8_t CharShift::numProgmemKeyPairs() {
|
||||
return num_keypairs_;
|
||||
}
|
||||
|
||||
CharShift::KeyPair CharShift::readKeyPairFromProgmem(uint8_t n) {
|
||||
return cloneFromProgmem(progmem_keypairs_[n]);
|
||||
}
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace kaleidoscope
|
||||
|
||||
kaleidoscope::plugin::CharShift CharShift;
|
@ -0,0 +1,137 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-CharShift -- Independently assign shifted and unshifted symbols
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kaleidoscope/Runtime.h"
|
||||
|
||||
#include <Kaleidoscope-Ranges.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
// =============================================================================
|
||||
/// Kaleidoscope plugin for independently assigning shifted symbols
|
||||
///
|
||||
/// This plugin provides a way to define keys that, when `shift` is held, will
|
||||
/// produce different symbols than they normally would. Either, both, or
|
||||
/// neither of the symbols assigned to a key in this way can be ones that are
|
||||
/// normally produced with the `shift` modifier. Crucially, when `shift` is
|
||||
/// held, and the `Key` to be output does not have the `shift` modifier flag
|
||||
/// applied to it, the held `shift` modifier will be suppressed.
|
||||
class CharShift : public Plugin {
|
||||
|
||||
public:
|
||||
EventHandlerResult onNameQuery();
|
||||
EventHandlerResult onKeyEvent(KeyEvent &event);
|
||||
EventHandlerResult beforeReportingState(const KeyEvent &event);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/// A structure that stores CharShift key pair values
|
||||
///
|
||||
/// This structure stores two separate `Key` values: `lower` (to be used when
|
||||
/// a key is pressed without `shift` held), and `upper` (to be used when
|
||||
/// `shift` is held).
|
||||
struct KeyPair {
|
||||
Key lower;
|
||||
Key upper;
|
||||
|
||||
/// Constructor for use in PROGMEM
|
||||
///
|
||||
/// This constructor is used when defining an array of `KeyPair` objects in
|
||||
/// PROGMEM (though it can also be used elsewhere).
|
||||
constexpr KeyPair(Key lower, Key upper) : lower(lower), upper(upper) {}
|
||||
|
||||
/// Constructor for reading from PROGMEM
|
||||
///
|
||||
/// This constructor is used by the helper function that copies values from
|
||||
/// PROGMEM so that they can be used normally.
|
||||
KeyPair() = default;
|
||||
};
|
||||
|
||||
/// Configure the KeyPairs array in PROGMEM
|
||||
///
|
||||
/// This function configures the PROGMEM array of `KeyPair` objects,
|
||||
/// automatically setting the internal count variable from the size of the
|
||||
/// `keypairs` array given, which must be a fixed-sized array, not a pointer.
|
||||
/// Generally, it will be called via the `KEYPAIRS()` preprocessor macro, not
|
||||
/// directly by user code.
|
||||
template <uint8_t _num_keypairs>
|
||||
static void setProgmemKeyPairs(KeyPair const(&keypairs)[_num_keypairs]) {
|
||||
progmem_keypairs_ = keypairs;
|
||||
num_keypairs_ = _num_keypairs;
|
||||
}
|
||||
|
||||
private:
|
||||
// A pointer to an array of `KeyPair` objects in PROGMEM
|
||||
static KeyPair const * progmem_keypairs_;
|
||||
// The size of the PROGMEM array of `KeyPair` objects
|
||||
static uint8_t num_keypairs_;
|
||||
|
||||
// If a `shift` key needs to be suppressed in `beforeReportingState()`
|
||||
static bool reverse_shift_state_;
|
||||
|
||||
/// Test for keys that should be handled by CharShift
|
||||
static bool isCharShiftKey(Key key);
|
||||
|
||||
/// Look up the `KeyPair` specified by the given keymap entry
|
||||
static KeyPair decodeCharShiftKey(Key key);
|
||||
|
||||
/// Get the total number of KeyPairs defined
|
||||
///
|
||||
/// This function can be overridden in order to store the `KeyPair` array in
|
||||
/// EEPROM instead of PROGMEM.
|
||||
static uint8_t numKeyPairs();
|
||||
|
||||
/// Get the `KeyPair` at the specified index from the defined `KeyPair` array
|
||||
///
|
||||
/// This function can be overridden in order to store the `KeyPair` array in
|
||||
/// EEPROM instead of PROGMEM.
|
||||
static KeyPair readKeyPair(uint8_t n);
|
||||
|
||||
// Default for `keypairsCount()`: size of the PROGMEM array
|
||||
static uint8_t numProgmemKeyPairs();
|
||||
// Default for `readKeypair(i)`: fetch the value from PROGMEM
|
||||
static KeyPair readKeyPairFromProgmem(uint8_t n);
|
||||
};
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace kaleidoscope
|
||||
|
||||
extern kaleidoscope::plugin::CharShift CharShift;
|
||||
|
||||
/// Define an array of `KeyPair` objects in PROGMEM
|
||||
///
|
||||
/// This preprocessor macro takes a list of `KeyPair` objects as arguments, and
|
||||
/// defines them as an array in PROGMEM, and configures `CharShift` to use that
|
||||
/// array, automatically setting the count correctly to prevent out-of-bounds
|
||||
/// lookups.
|
||||
#define CS_KEYS(keypairs...) { \
|
||||
static kaleidoscope::plugin::CharShift::KeyPair const kp_table[] PROGMEM = { \
|
||||
keypairs \
|
||||
}; \
|
||||
CharShift.setProgmemKeyPairs(kp_table); \
|
||||
}
|
||||
|
||||
/// Define an `KeyPair` entry in a keymap
|
||||
///
|
||||
/// This defines a `Key` object that will be handled by the CharShift plugin.
|
||||
/// The argument `n` is the index number of the `KeyPair` in the array (starting
|
||||
/// at zero).
|
||||
constexpr kaleidoscope::Key CS(uint8_t n) {
|
||||
return kaleidoscope::Key(kaleidoscope::ranges::CS_FIRST + n);
|
||||
}
|
Loading…
Reference in new issue