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

1
.gitignore vendored

@ -2,3 +2,4 @@
*~ *~
/hardware/ /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: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 [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 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. 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 this is useful when you want to augment some mouse action with `Shift`, to
select text, for example. 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 [space-cadet]: https://en.wikipedia.org/wiki/Space-cadet_keyboard
## Using the plugin ## Using the plugin
@ -37,38 +46,78 @@ enabling the plugin:
#include <Kaleidoscope-SpaceCadet.h> #include <Kaleidoscope-SpaceCadet.h>
void setup() { void setup() {
Kaleidoscope.use(&SpaceCadetShift); Kaleidoscope.use(&SpaceCadet);
Kaleidoscope.setup(); Kaleidoscope.setup();
} }
``` ```
This assumes a US QWERTY layout on the host computer, and will use the `9` and This assumes a US QWERTY layout on the host computer, though the plugin sends
`0` keys for the left and right parens, respectively. To change these keys, use the correct keymap code for each symbol. Because the mapping is entirely
the `.opening_paren` and `.closing_paren` properties outlined below. 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: 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. > 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
> Defaults to `Key_0`. > 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` ### `.time_out`
> Set this property to the number of milliseconds to wait before considering a > 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 > 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. > Defaults to 1000.

@ -40,7 +40,24 @@ const Key keymaps[][ROWS][COLS] PROGMEM = {
}; };
void setup() { 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(); Kaleidoscope.setup();
} }

@ -1,6 +1,6 @@
/* -*- mode: c++ -*- /* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift * Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017 Gergely Nagy * Copyright (C) 2016, 2017 Gergely Nagy, Ben Gemperline
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -21,19 +21,46 @@
namespace kaleidoscope { namespace kaleidoscope {
uint8_t SpaceCadetShift::paren_needed_; //Constructor with input and output, and assume default timeout
uint32_t SpaceCadetShift::start_time_; SpaceCadet::KeyBinding::KeyBinding(Key input_, Key output_) {
uint16_t SpaceCadetShift::time_out = 1000; input = input_;
Key SpaceCadetShift::opening_paren = Key_9, SpaceCadetShift::closing_paren = Key_0; 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); 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 nothing happened, bail out fast.
if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) { if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) {
return mapped_key; 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 a key has been just toggled on...
if (keyToggledOn(key_state)) { if (keyToggledOn(key_state)) {
if (mapped_key.raw == Key_LeftShift.raw) { // if it is LShift, remember it
bitWrite(paren_needed_, 0, 1); //This will only set one key, and if it isn't in our map it clears everything
start_time_ = millis(); //for the non-pressed key
} else if (mapped_key.raw == Key_RightShift.raw) { // if it is RShift, remember it //Exit condition is if we reach the sentinal
bitWrite(paren_needed_, 1, 1); for (
start_time_ = millis(); uint8_t i = 0 ;
} else { // if it is something else, we do not need a paren at the end. !(
paren_needed_ = 0; map[i].input.raw == Key_NoKey.raw
start_time_ = 0; && 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. // 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, // 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. // 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; 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 if ((millis() - map[index].start_time) >= current_timeout) {
// need the parens in the end. // if we timed out, that means we need to keep pressing the mapped
if ((millis() - start_time_) >= time_out) { // key, but we won't need to send the alternative key in the end
paren_needed_ = 0; map[index].flagged = false;
map[index].start_time = 0;
return mapped_key; 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 // return. This can happen when another key is released, and that should not
// interrupt us. // interrupt us.
if (mapped_key.raw != Key_LeftShift.raw &&
mapped_key.raw != Key_RightShift.raw) if (!pressed_key_was_valid) {
return mapped_key; return mapped_key;
}
// if a key toggled off (and that must be one of the shifts at this point), // if a key toggled off (and that must be one of the mapped keys at this point),
// send the parens too (if we were interrupted, we bailed out earlier). // send the alternative key instead (if we were interrupted, we bailed out earlier).
if (keyToggledOff(key_state)) { if (keyToggledOff(key_state)) {
Key paren = opening_paren; Key alternate_key = map[index].output;
if (bitRead(paren_needed_, 1))
paren = closing_paren; //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
handleKeyswitchEvent(mapped_key, row, col, IS_PRESSED | INJECTED); //may want to even UNSET the originally pressed key (future
handleKeyswitchEvent(paren, row, col, IS_PRESSED | INJECTED); //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(); 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; 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++ -*- /* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift * Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017 Gergely Nagy * Copyright (C) 2016, 2017 Gergely Nagy, Ben Gemperline
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -20,23 +20,49 @@
#include <Kaleidoscope.h> #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: 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; void begin(void) final;
static uint16_t time_out; //Publically accessible variables
static Key opening_paren, closing_paren; static uint16_t time_out; // The global timeout in milliseconds
static SpaceCadet::KeyBinding * map; // The map of key bindings
private: 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); 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