Merge remote-tracking branch 'plugin/SpaceCadet/f/monorepo' into f/monorepo-stage2

pull/389/head
Gergely Nagy 6 years ago
commit 237ab5e286
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -0,0 +1,164 @@
# Kaleidoscope-SpaceCadet
[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.
What it does, is that it turns your left and right `Shift` keys into parens if
you tap and release them, without pressing any other key while holding them.
Therefore, to input, say, `(print foo)`, you don't need to press `Shift`, hold
it, and press `9` to get a `(`, you simply press and release `Shift`, and
continue writing. You use it as if you had a dedicated key for parens!
But if you wish to write capital letters, you hold it, as usual, and you will
not see any parens when you release it. You can also hold it for a longer time,
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
Using the plugin with its defaults is as simple as including the header, and
enabling the plugin:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-SpaceCadet.h>
KALEIDOSCOPE_INIT_PLUGINS(SpaceCadet);
void setup() {
Kaleidoscope.setup();
}
```
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>
KALEIDOSCOPE_INIT_PLUGINS(SpaceCadet);
void setup() {
Kaleidoscope.setup();
//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::plugin::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
The plugin provides the `SpaceCadet` object, with the following methods and
properties:
### `.map`
> Set the key map. This takes an array of
> `kaleidoscope::plugin::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
>
> If not explicitly set, defaults to mapping left `shift` to `(` and right `shift`
> to `)`.
### `kaleidoscope::plugin::SpaceCadet::KeyBinding`
> 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. 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.
### `.enable()`
> This method enables the SpaceCadet plugin. This is useful for interfacing
> with other plugins or macros, especially where SpaceCadet functionality isn't
> always desired.
>
> The default behavior is `enabled`.
### `.disable()`
> This method disables the SpaceCadet behavior. This is useful for interfacing
> with other plugins or macros, especially where SpaceCadet functionality isn't
> always desired.
### `.active()`
> This method returns `true` if SpaceCadet is enabled and `false` if SpaceCadet
> is disabled. This is useful for interfacing with other plugins or macros,
> especially where SpaceCadet functionality isn't always desired.
### `Key_SpaceCadetEnable`
> This provides a key for placing on a keymap for enabling the SpaceCadet
> behavior. This is only triggered on initial downpress, and does not
> trigger again if held down or when the key is released.
### `Key_SpaceCadetDisable`
> This provides a key for placing on a keymap for disabling the SpaceCadet
> behavior. This is only triggered on initial downpress, and does not
> trigger again if held down or when the key is released.
## Dependencies
* [Kaleidoscope-Ranges](Ranges.md)
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: ../../examples/SpaceCadet/SpaceCadet.ino
## Upgrading
Previous versions of `SpaceCadet` used
`kaleidoscope::SpaceCadet::KeyBinding` as a type for defining keybindings. In newer versions, this is
`kaleidoscope::plugin::SpaceCadet::KeyBinding`. The old name still works,
but will be removed by 2019-01-14.

@ -0,0 +1,67 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift
* Copyright (C) 2016, 2017, 2018 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-SpaceCadet.h>
// *INDENT-OFF*
const Key keymaps[][ROWS][COLS] PROGMEM = {
[0] = KEYMAP_STACKED
(
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_SpaceCadetEnable,
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,
Key_skip,
Key_SpaceCadetDisable, 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,
Key_skip),
};
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(SpaceCadet);
void setup() {
Kaleidoscope.setup();
//Set the SpaceCadet map
//Setting is {KeyThatWasPressed, AlternativeKeyToSend, TimeoutInMS}
//Note: must end with the SPACECADET_MAP_END delimiter
static kaleidoscope::plugin::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;
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,20 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift
* Copyright (C) 2016, 2017 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/SpaceCadet.h>

@ -0,0 +1,250 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, 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 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-SpaceCadet.h>
#include <kaleidoscope/hid.h>
namespace kaleidoscope {
namespace plugin {
//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_;
}
//Space Cadet
SpaceCadet::KeyBinding * SpaceCadet::map;
uint16_t SpaceCadet::time_out = 1000;
bool SpaceCadet::disabled = false;
SpaceCadet::SpaceCadet() {
static 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;
}
//Function to enable SpaceCadet behavior
void SpaceCadet::enable() {
disabled = false;
}
//Function to disable SpaceCadet behavior
void SpaceCadet::disable() {
disabled = true;
}
//Function to determine whether SpaceCadet is active (useful for Macros and other plugins)
bool SpaceCadet::active() {
return !disabled;
}
EventHandlerResult SpaceCadet::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
//Handle our synthetic keys for enabling and disabling functionality
if (mapped_key.raw >= kaleidoscope::ranges::SC_FIRST &&
mapped_key.raw <= kaleidoscope::ranges::SC_LAST) {
//Only fire the activate / deactivate on the initial press (not held or release)
if (keyToggledOn(key_state)) {
if (mapped_key == Key_SpaceCadetEnable) {
enable();
} else if (mapped_key == Key_SpaceCadetDisable) {
disable();
}
}
return EventHandlerResult::EVENT_CONSUMED;
}
//if SpaceCadet is disabled, this was an injected key, it was NoKey,
//or if they key somehow came here without being either pressed or released,
//return the mapped key and just get out of here.
if (
disabled
|| (key_state & INJECTED)
|| mapped_key == Key_NoKey
|| (!keyIsPressed(key_state) && !keyWasPressed(key_state))
) {
return EventHandlerResult::OK;
}
// If a key has been just toggled on...
if (keyToggledOn(key_state)) {
//check to see if we found a valid key. Assume not valid.
bool valid_key = false;
//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 special SPACECADET_MAP_END sentinel
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. Mark it as flagged and reset the counter
map[i].flagged = true;
map[i].start_time = millis();
//yes, we found a valid key
valid_key = true;
} else {
//If the key entry we're looking at was flagged previously, add it to the
//report before we do anything else (this handles the situation where we
//hit another key after this -- if it's a modifier, we want the modifier
//key to be added to the report, for things like ctrl, alt, shift, etc)
if (map[i].flagged) {
handleKeyswitchEvent(map[i].input, row, col, IS_PRESSED | INJECTED);
}
//The keypress wasn't a match, so we need to mark it as not flagged and
//reset the timer for it to disable everything.
map[i].flagged = false;
map[i].start_time = 0;
}
}
//If we found a valid key in our map, we don't actually want to send anything.
//This gets around an issue in Windows if we map a SpaceCadet function on top
//of Alt -- sending Alt by itself activates the menubar. We don't want to send
//anything until we know that we're either sending the alternate key or we
//know for sure that we want to send the originally pressed key.
if (valid_key) {
return EventHandlerResult::EVENT_CONSUMED;
}
//this is all we need to do on keypress, let the next handler do its thing too.
//This case assumes we weren't a valid key that we were watching, so we don't
//need to do anything else.
return EventHandlerResult::OK;
}
// if the state is empty, that means that either an activating key wasn't pressed,
// or we used another key in the interim. in both cases, nothing special to do.
bool valid_key = false;
bool pressed_key_was_valid = false;
uint8_t index = 0;
//Look to see if any keys in our map are currently flagged.
//Exit condition is if we reach the special SPACECADET_MAP_END sentinel
for (
uint8_t i = 0 ;
!(
map[i].input.raw == Key_NoKey.raw
&& map[i].output.raw == Key_NoKey.raw
&& map[i].timeout == 0
);
++i
) {
//The key we're looking at was previously flagged (so perform action)
if (map[i].flagged) {
valid_key = true;
index = i;
}
//the key we're looking at was valid (in the map)
if (map[i].input.raw == mapped_key.raw) {
pressed_key_was_valid = true;
}
}
//If no valid mapped keys were pressed, simply return the key that
//was originally passed in.
if (!valid_key) {
return EventHandlerResult::OK;
}
//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;
}
//Check to determine if we have surpassed our timeout for holding this key
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;
//Just return this key itself (we won't run alternative keys check)
return EventHandlerResult::OK;
}
// 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 (!pressed_key_was_valid) {
return EventHandlerResult::OK;
}
// 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 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.
//inject our new key
handleKeyswitchEvent(alternate_key, row, col, IS_PRESSED | INJECTED);
//Unflag the key so we don't try this again.
map[index].flagged = false;
map[index].start_time = 0;
}
//Special case here for if we had a valid key that's continuing to be held.
//If it's a valid key, and it's continuing to be held, return NoKey.
//This prevents us from accidentally triggering a keypress that we didn't
//mean to handle.
if (valid_key) {
return EventHandlerResult::EVENT_CONSUMED;
}
//Finally, as a final sanity check, simply return the passed-in key as-is.
return EventHandlerResult::OK;
}
}
}
kaleidoscope::plugin::SpaceCadet SpaceCadet;

@ -0,0 +1,80 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, 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 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.h>
#include <Kaleidoscope-Ranges.h>
#ifndef SPACECADET_MAP_END
#define SPACECADET_MAP_END (kaleidoscope::SpaceCadet::KeyBinding) { Key_NoKey, Key_NoKey, 0 }
#endif
#define Key_SpaceCadetEnable (Key) { .raw = kaleidoscope::ranges::SC_FIRST }
#define Key_SpaceCadetDisable (Key) { .raw = kaleidoscope::ranges::SC_LAST }
namespace kaleidoscope {
namespace plugin {
class SpaceCadet : public kaleidoscope::Plugin {
public:
//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;
};
SpaceCadet(void);
//Methods
static void enable(void);
static void disable(void);
static bool active(void);
//Publically accessible variables
static uint16_t time_out; // The global timeout in milliseconds
static SpaceCadet::KeyBinding * map; // The map of key bindings
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
private:
static bool disabled;
};
}
// Backwards compatibility
typedef plugin::SpaceCadet SpaceCadet;
}
extern kaleidoscope::plugin::SpaceCadet SpaceCadet;
Loading…
Cancel
Save