Merge pull request #4 from advisoray/master

Add support for dynamic mappings
pull/389/head
Jesse Vincent 7 years ago committed by GitHub
commit 634425d87f

3
.gitignore vendored

@ -1,4 +1,5 @@
.#*
*~
/hardware/
/output/
/output/
.DS_store

@ -9,7 +9,7 @@
[st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52
[st:experimental]: https://img.shields.io/badge/experimental----black.svg?style=flat&colorA=dfb317&colorB=494e52
[Space Cadet][space-cadet] Shift is a way to make it more convenient to input
[Space Cadet][space-cadet] is a way to make it more convenient to input
parens - those `(` and `)` things -, symbols that a lot of programming languages
use frequently. If you are working with Lisp, you are using these all the time.
@ -25,6 +25,15 @@ and it still would act as a `Shift`, without the parens inserted on release:
this is useful when you want to augment some mouse action with `Shift`, to
select text, for example.
After getting used to the Space Cadet style of typing, you may wish to enable
this sort of functionality on other keys, as well. Fortunately, the Space Cadet
plugin is configurable and extensible to support adding symbols to other keys.
Along with `(` on your left `Shift` key and `)` on your right `Shift` key,
you may wish to add other such programming mainstays as `{` to your left-side `cmd` key,
`}` to your right-side `alt` key, `[` to your left `Control` key, and `]` to your right
`Control` key. You can map the keys in whatever way you may wish to do, so feel free to
experiment with different combinations and discover what works best for you!
[space-cadet]: https://en.wikipedia.org/wiki/Space-cadet_keyboard
## Using the plugin
@ -37,38 +46,78 @@ enabling the plugin:
#include <Kaleidoscope-SpaceCadet.h>
void setup() {
Kaleidoscope.use(&SpaceCadetShift);
Kaleidoscope.use(&SpaceCadet);
Kaleidoscope.setup();
}
```
This assumes a US QWERTY layout on the host computer, and will use the `9` and
`0` keys for the left and right parens, respectively. To change these keys, use
the `.opening_paren` and `.closing_paren` properties outlined below.
This assumes a US QWERTY layout on the host computer, though the plugin sends
the correct keymap code for each symbol. Because the mapping is entirely
configurable, though, you may switch out keys at your leisure.
If you wish to enable additional modifier keys (or disable the default behavior
for the shift and parentheses combinations), configuration is as simple as
passing a new keymap into the SpaceCadet object, as shown below:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-SpaceCadet.h>
void setup() {
Kaleidoscope.use(&SpaceCadet);
//Set the keymap with a 250ms timeout per-key
//Setting is {KeyThatWasPressed, AlternativeKeyToSend, TimeoutInMS}
//Note: must end with the SPACECADET_MAP_END delimiter
static kaleidoscope::SpaceCadet::KeyBinding spacecadetmap[] = {
{Key_LeftShift, Key_LeftParen, 250}
, {Key_RightShift, Key_RightParen, 250}
, {Key_LeftGui, Key_LeftCurlyBracket, 250}
, {Key_RightAlt, Key_RightCurlyBracket, 250}
, {Key_LeftAlt, Key_RightCurlyBracket, 250}
, {Key_LeftControl, Key_LeftBracket, 250}
, {Key_RightControl, Key_RightBracket, 250}
, SPACECADET_MAP_END
};
//Set the map.
SpaceCadet.map = spacecadetmap;
## Plugin methods
Kaleidoscope.setup();
}
```
## Plugin methods
The plugin provides the `SpaceCadetShift` object, with the following methods and
The plugin provides the `SpaceCadet` object, with the following methods and
properties:
### `.opening_paren`
### `.map`
> Set this property to the key that - when shifted - will result in an opening paren.
> Set the key map. This takes an array of `kaleidoscope::SpaceCadet::KeyBinding`
> objects with the special `SPACECADET_MAP_END` sentinal to mark the end of the map.
> Each KeyBinding object takes, in order, the key that was pressed, the key that
> should be sent instead, and an optional per-key timeout override
>
> Defaults to `Key_9`.
> If not explicitly set, defaults to mapping left `shift` to `(` and right `shift`
> to `)`.
### `.closing_paren`
### `kaleidoscope::SpaceCadet::KeyBinding`
> Set this property to the key that - when shifted - will result in a closing paren.
>
> Defaults to `Key_0`.
> An object consisting of the key that is pressed, the key that should be sent
> in its place, and the timeout (in milliseconds) until the key press is
> considered to be a "held" key press. The third parameter, the timeout, is
> optional and may be set per-key or left out entirely (or set to `0`) to use
> the default timeout value.
### `.time_out`
> Set this property to the number of milliseconds to wait before considering a
> held key in isolation as its secondary role. That is, we'd have to hold a
> `Shift` key this long, by itself, to trigger the `Shift` role in itself.
> `Shift` key this long, by itself, to trigger the `Shift` role in itself. This
> timeout setting can be overridden by an individual key in the keymap, but if
> it is omitted or set to `0` in the key map, the global timeout will be used.
>
> Defaults to 1000.

@ -40,7 +40,24 @@ const Key keymaps[][ROWS][COLS] PROGMEM = {
};
void setup() {
Kaleidoscope.use(&SpaceCadetShift);
//Tell Kaleidoscope to use SpaceCadet
Kaleidoscope.use(&SpaceCadet);
//Set the SpaceCadet map
//Setting is {KeyThatWasPressed, AlternativeKeyToSend, TimeoutInMS}
//Note: must end with the SPACECADET_MAP_END delimiter
static kaleidoscope::SpaceCadet::KeyBinding spacecadetmap[] = {
{Key_LeftShift, Key_LeftParen, 250}
, {Key_RightShift, Key_RightParen, 250}
, {Key_LeftGui, Key_LeftCurlyBracket, 250}
, {Key_RightAlt, Key_RightCurlyBracket, 250}
, {Key_LeftAlt, Key_RightCurlyBracket, 250}
, {Key_LeftControl, Key_LeftBracket, 250}
, {Key_RightControl, Key_RightBracket, 250}
, SPACECADET_MAP_END
};
//Set the map.
SpaceCadet.map = spacecadetmap;
Kaleidoscope.setup();
}

@ -1,6 +1,6 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift
* Copyright (C) 2016, 2017 Gergely Nagy
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017 Gergely Nagy, Ben Gemperline
*
* 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
@ -21,19 +21,46 @@
namespace kaleidoscope {
uint8_t SpaceCadetShift::paren_needed_;
uint32_t SpaceCadetShift::start_time_;
uint16_t SpaceCadetShift::time_out = 1000;
Key SpaceCadetShift::opening_paren = Key_9, SpaceCadetShift::closing_paren = Key_0;
//Constructor with input and output, and assume default timeout
SpaceCadet::KeyBinding::KeyBinding(Key input_, Key output_) {
input = input_;
output = output_;
}
//Constructor with all three set
SpaceCadet::KeyBinding::KeyBinding(Key input_, Key output_, uint16_t timeout_) {
input = input_;
output = output_;
timeout = timeout_;
}
SpaceCadetShift::SpaceCadetShift() {
//Space Cadet
SpaceCadet::KeyBinding * SpaceCadet::map;
uint16_t SpaceCadet::time_out = 1000;
//Empty Constructor
SpaceCadet::SpaceCadet() {
SpaceCadet::KeyBinding initialmap[] = {
//By default, respect the default timeout
{Key_LeftShift, Key_LeftParen, 0}
, {Key_RightShift, Key_RightParen, 0}
//These may be uncommented, added, or set in the main sketch
/*,{Key_LeftGui,Key_LeftCurlyBracket, 250}
,{Key_RightAlt,Key_RightCurlyBracket, 250}
,{Key_LeftControl,Key_LeftBracket, 250}
,{Key_RightControl,Key_RightBracket, 250}*/
, SPACECADET_MAP_END
};
map = initialmap;
}
void SpaceCadetShift::begin() {
void SpaceCadet::begin() {
Kaleidoscope.useEventHandlerHook(eventHandlerHook);
}
Key SpaceCadetShift::eventHandlerHook(Key mapped_key, byte row, byte col, uint8_t key_state) {
Key SpaceCadet::eventHandlerHook(Key mapped_key, byte row, byte col, uint8_t key_state) {
// If nothing happened, bail out fast.
if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) {
return mapped_key;
@ -41,15 +68,29 @@ Key SpaceCadetShift::eventHandlerHook(Key mapped_key, byte row, byte col, uint8_
// If a key has been just toggled on...
if (keyToggledOn(key_state)) {
if (mapped_key.raw == Key_LeftShift.raw) { // if it is LShift, remember it
bitWrite(paren_needed_, 0, 1);
start_time_ = millis();
} else if (mapped_key.raw == Key_RightShift.raw) { // if it is RShift, remember it
bitWrite(paren_needed_, 1, 1);
start_time_ = millis();
} else { // if it is something else, we do not need a paren at the end.
paren_needed_ = 0;
start_time_ = 0;
//This will only set one key, and if it isn't in our map it clears everything
//for the non-pressed key
//Exit condition is if we reach the sentinal
for (
uint8_t i = 0 ;
!(
map[i].input.raw == Key_NoKey.raw
&& map[i].output.raw == Key_NoKey.raw
&& map[i].timeout == 0
) ;
++i
) {
if (mapped_key.raw == map[i].input.raw) {
//The keypress was valid and a match.
map[i].flagged = true;
map[i].start_time = millis();
} else {
//The keypress wasn't a match.
map[i].flagged = false;
map[i].start_time = 0;
}
}
// this is all we need to do on keypress, let the next handler do its thing too.
@ -58,35 +99,76 @@ Key SpaceCadetShift::eventHandlerHook(Key mapped_key, byte row, byte col, uint8_
// if the state is empty, that means that either the shifts weren't pressed,
// or we used another key in the interim. in both cases, nothing special to do.
if (!paren_needed_)
bool valid_key = false;
bool pressed_key_was_valid = false;
uint8_t index = 0;
//Look to see if any keys in our map are flagged.
//Exit condition is if we reach the sentinal
for (
uint8_t i = 0 ;
!(
map[i].input.raw == Key_NoKey.raw
&& map[i].output.raw == Key_NoKey.raw
&& map[i].timeout == 0
) ;
++i
) {
if (map[i].flagged) {
valid_key = true;
index = i;
}
if (map[i].input.raw == mapped_key.raw) {
pressed_key_was_valid = true;
}
}
//If no valid mapped keys were pressed, simply return the keycode
if (!valid_key) {
return mapped_key;
}
//use the map index to find the local timeout for this key
uint16_t current_timeout = map[index].timeout;
//If that isn't set, use the global timeout setting.
if (current_timeout == 0) {
current_timeout = time_out;
}
// if we timed out, that means we need to keep pressing shift, but won't
// need the parens in the end.
if ((millis() - start_time_) >= time_out) {
paren_needed_ = 0;
if ((millis() - map[index].start_time) >= current_timeout) {
// if we timed out, that means we need to keep pressing the mapped
// key, but we won't need to send the alternative key in the end
map[index].flagged = false;
map[index].start_time = 0;
return mapped_key;
}
// if we have a state, but the key in question is not either of the shifts,
// If the key that was pressed isn't one of our mapped keys, just
// return. This can happen when another key is released, and that should not
// interrupt us.
if (mapped_key.raw != Key_LeftShift.raw &&
mapped_key.raw != Key_RightShift.raw)
if (!pressed_key_was_valid) {
return mapped_key;
}
// if a key toggled off (and that must be one of the shifts at this point),
// send the parens too (if we were interrupted, we bailed out earlier).
// if a key toggled off (and that must be one of the mapped keys at this point),
// send the alternative key instead (if we were interrupted, we bailed out earlier).
if (keyToggledOff(key_state)) {
Key paren = opening_paren;
if (bitRead(paren_needed_, 1))
paren = closing_paren;
handleKeyswitchEvent(mapped_key, row, col, IS_PRESSED | INJECTED);
handleKeyswitchEvent(paren, row, col, IS_PRESSED | INJECTED);
Key alternate_key = map[index].output;
//Since we are sending the actual key (no need for shift, etc),
//only need to send that key and not the original key. In fact, we
//may want to even UNSET the originally pressed key (future
//enhanacement?). This might also mean we don't need to return the
//key that was pressed, though I haven't confirmed that.
handleKeyswitchEvent(alternate_key, row, col, IS_PRESSED | INJECTED);
hid::sendKeyboardReport();
paren_needed_ = 0;
//Unflag the key so we don't try this again.
map[index].flagged = false;
map[index].start_time = 0;
}
return mapped_key;
@ -94,4 +176,4 @@ Key SpaceCadetShift::eventHandlerHook(Key mapped_key, byte row, byte col, uint8_
}
kaleidoscope::SpaceCadetShift SpaceCadetShift;
kaleidoscope::SpaceCadet SpaceCadet;

@ -1,6 +1,6 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift
* Copyright (C) 2016, 2017 Gergely Nagy
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017 Gergely Nagy, Ben Gemperline
*
* 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
@ -20,23 +20,49 @@
#include <Kaleidoscope.h>
namespace kaleidoscope {
#ifndef SPACECADET_MAP_END
#define SPACECADET_MAP_END (kaleidoscope::SpaceCadet::KeyBinding) { Key_NoKey, Key_NoKey, 0 }
#endif
class SpaceCadetShift : public KaleidoscopePlugin {
namespace kaleidoscope {
//Declaration for the method (implementing KaleidoscopePlugin)
class SpaceCadet : public KaleidoscopePlugin {
public:
SpaceCadetShift(void);
//Internal Class
//Declarations for the modifier key mapping
class KeyBinding {
public:
//Empty constructor; set the vars separately
KeyBinding(void) {}
//Constructor with input and output
KeyBinding(Key input_, Key output_);
//Constructor with all three set
KeyBinding(Key input_, Key output_, uint16_t timeout_);
//The key that is pressed
Key input;
//the key that is sent
Key output;
//The timeout (default to global timeout)
uint16_t timeout = 0;
//The flag (set to 0)
bool flagged = false;
//the start time for this key press
uint32_t start_time = 0;
};
//Empty constructor
SpaceCadet(void);
//Methods
void begin(void) final;
static uint16_t time_out;
static Key opening_paren, closing_paren;
//Publically accessible variables
static uint16_t time_out; // The global timeout in milliseconds
static SpaceCadet::KeyBinding * map; // The map of key bindings
private:
static uint8_t paren_needed_;
static uint32_t start_time_;
static Key eventHandlerHook(Key mapped_key, byte row, byte col, uint8_t key_state);
};
};
extern kaleidoscope::SpaceCadetShift SpaceCadetShift;
extern kaleidoscope::SpaceCadet SpaceCadet;

Loading…
Cancel
Save