commit
ae02d5d59d
After Width: | Height: | Size: 89 KiB |
@ -0,0 +1,182 @@
|
||||
# Kaleidoscope-MagicCombo
|
||||
|
||||
The `MagicCombo` extension provides a way to perform custom actions when a
|
||||
particular set of keys are held down together. The functionality assigned to
|
||||
these keys are not changed, and the custom action triggers as long as all keys
|
||||
within the set are pressed. The order in which they were pressed do not matter.
|
||||
|
||||
This can be used to tie complex actions to key chords.
|
||||
|
||||
## Using the extension
|
||||
|
||||
To use the extension, we must include the header, create actions for the magic
|
||||
combos we want to trigger, and set up a mapping:
|
||||
|
||||
```c++
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-Macros.h>
|
||||
#include <Kaleidoscope-MagicCombo.h>
|
||||
|
||||
enum { KIND_OF_MAGIC };
|
||||
|
||||
void kindOfMagic(uint8_t combo_index) {
|
||||
Macros.type(PSTR("It's a kind of magic!"));
|
||||
}
|
||||
|
||||
USE_MAGIC_COMBOS(
|
||||
[KIND_OF_MAGIC] = {
|
||||
.action = kindOfMagic,
|
||||
.keys = {R3C6, R3C9} // Left Fn + Right Fn
|
||||
});
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(MagicCombo, Macros);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
```
|
||||
|
||||
It is recommended to use the `RxCy` macros of the core firmware to set the keys
|
||||
that are part of a combination.
|
||||
|
||||
## Plugin properties
|
||||
|
||||
The extension provides a `MagicCombo` singleton object, with the following
|
||||
property:
|
||||
|
||||
### `.min_interval`
|
||||
|
||||
> Restrict the magic action to fire at most once every `min_interval`
|
||||
> milliseconds.
|
||||
>
|
||||
> Defaults to 500.
|
||||
|
||||
## Plugin callbacks
|
||||
|
||||
Whenever a combination is found to be held, the plugin will trigger the
|
||||
specified action, which is just a regular method with a single `uint8_t`
|
||||
argument: the index of the magic combo. This function will be called repeatedly
|
||||
(every `min_interval` milliseconds) while the combination is held.
|
||||
|
||||
## Further reading
|
||||
|
||||
Starting from the [example][plugin:example] is the recommended way of getting
|
||||
started with the plugin.
|
||||
|
||||
`RxCy` coordinates for a Model01:
|
||||
|
||||
![rxcy layout](../model01_coordinates.png)
|
||||
|
||||
[plugin:example]: ../../examples/MagicCombo/MagicCombo.ino
|
||||
|
||||
## Upgrading
|
||||
|
||||
To make `MagicCombo` more portable, and easier to use, we had to break the API
|
||||
previously provided, there was no way to maintain backwards compatibility. This
|
||||
document is an attempt at guiding you through the process of migrating from the
|
||||
earlier API to the current one.
|
||||
|
||||
Migration should be a straightforward process, but if you get stuck, please feel
|
||||
free to [open an issue][gh:issues], or start a thread on the [forums][forums],
|
||||
and we'll help you with it.
|
||||
|
||||
[gh:issues]: https://github.com/keyboardio/Kaleidoscope/issues
|
||||
[forums]: https://community.keyboard.io/
|
||||
|
||||
## The old API
|
||||
|
||||
```c++
|
||||
void magicComboActions(uint8_t combo_index, uint32_t left_hand, uint32_t right_hand) {
|
||||
switch (combo_index) {
|
||||
case 0:
|
||||
Macros.type(PSTR("It's a kind of magic!"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const kaleidoscope::MagicCombo::combo_t magic_combos[] PROGMEM = {
|
||||
{
|
||||
R3C6, // left palm key
|
||||
R3C9 // right palm key
|
||||
},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
|
||||
MagicCombo.magic_combos = magic_combos;
|
||||
}
|
||||
```
|
||||
|
||||
Previously, we used a global, overrideable function (`magicComboActions`) to run
|
||||
the actions of all magic combos, similar to how macros are set up to work.
|
||||
Unlike macros, magic combos can't be defined in the keymap, due to technical
|
||||
reasons, so we had to use a separate list - `magic_combos` in our example. We
|
||||
also needed to tell `MagicCombo` to use this list, which is what we've done in
|
||||
`setup()`.
|
||||
|
||||
## The new API
|
||||
|
||||
```c++
|
||||
void kindOfMagic(uint8_t combo_index) {
|
||||
Macros.type(PSTR("It's a kind of magic!"));
|
||||
}
|
||||
|
||||
USE_MAGIC_COMBOS({
|
||||
.action = kindOfMagic,
|
||||
.keys = {R3C6, R3C9} // Left Fn + Right Fn
|
||||
});
|
||||
```
|
||||
|
||||
The new API is much shorter, and is inspired by the way the [Leader][leader]
|
||||
plugin works: instead of having a list, and a dispatching function like
|
||||
`magicComboActions`, we include the action method in the list too!
|
||||
|
||||
[leader]: Leader.md
|
||||
|
||||
We also don't make a difference between left- and right-hand anymore, you can
|
||||
just list keys for either in the same list. This will be very handy for
|
||||
non-split keyboards.
|
||||
|
||||
## Migration
|
||||
|
||||
First of all, we'll need to split up `magicComboActions` into separate
|
||||
functions. Each function should have a unique name, but their shape is always
|
||||
the same:
|
||||
|
||||
```c++
|
||||
void someFunction(uint8_t combo_index) {
|
||||
// Do some action here
|
||||
}
|
||||
```
|
||||
|
||||
Copy the body of each `case` statement of `magicComboActions`, and copy them one
|
||||
by one into appropriately named functions of the above shape. You can name your
|
||||
functions anything you want, the only constraint is that they need to be valid
|
||||
C++ function names. The plugin itself does nothing with the name, we'll
|
||||
reference them later in the `USE_MAGIC_COMBOS` helper macro.
|
||||
|
||||
Once `magicComboActions` is split up, we need to migrate the `magic_combos` list
|
||||
to the new format. That list had to be terminated by a `{0, 0}` entry, the new
|
||||
method does not require such a sentinel at the end.
|
||||
|
||||
For each entry in `magic_combos`, add an entry to `USE_MAGIC_COMBOS`, with the
|
||||
following structure:
|
||||
|
||||
```c++
|
||||
{.action = theActionFunction,
|
||||
.keys = { /* list of keys */ }}
|
||||
```
|
||||
|
||||
The list of keys are the same `RxCy` constants you used for `magic_combos`, with
|
||||
the left- and right hands combined. The action, `theActionFunction`, is the
|
||||
function you extracted the magic combo action to. It's the function that has the
|
||||
same body as the `case` statement in `magicComboActions` had.
|
||||
|
||||
And this is all there is to it.
|
||||
|
||||
If your actions made use of the `left_hand` or `right_hand` arguments of
|
||||
`magicComboActions`, the same information is still available. But that's a bit
|
||||
more involved to get to, out of scope for this simple migration guide. Please
|
||||
open an issue, or ask for help on the forums, and we'll help you.
|
@ -0,0 +1,62 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-MagicCombo -- Magic combo framework
|
||||
* 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-Macros.h>
|
||||
#include <Kaleidoscope-MagicCombo.h>
|
||||
|
||||
enum {
|
||||
KIND_OF_MAGIC
|
||||
};
|
||||
|
||||
void kindOfMagic(uint8_t combo_index) {
|
||||
Macros.type(PSTR("It's a kind of magic!"));
|
||||
}
|
||||
|
||||
USE_MAGIC_COMBOS([KIND_OF_MAGIC] = {.action = kindOfMagic, .keys = {R3C6, R3C9}});
|
||||
|
||||
// *INDENT-OFF*
|
||||
const Key keymaps[][ROWS][COLS] PROGMEM = {
|
||||
[0] = 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,
|
||||
Key_NoKey,
|
||||
|
||||
Key_skip, 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_NoKey),
|
||||
};
|
||||
// *INDENT-ON*
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(MagicCombo, Macros);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-MagicCombo -- Magic combo framework
|
||||
* 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/MagicCombo.h>
|
@ -0,0 +1,59 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-MagicCombo -- Magic combo framework
|
||||
* 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-MagicCombo.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
uint16_t MagicCombo::min_interval = 500;
|
||||
uint32_t MagicCombo::end_time_;
|
||||
|
||||
EventHandlerResult MagicCombo::beforeReportingState() {
|
||||
for (byte i = 0; i < magiccombo::combos_length; i++) {
|
||||
bool match = true;
|
||||
byte j;
|
||||
|
||||
for (j = 0; j < MAX_COMBO_LENGTH; j++) {
|
||||
int8_t comboKey = pgm_read_byte(&(magiccombo::combos[i].keys[j]));
|
||||
|
||||
if (comboKey == 0)
|
||||
break;
|
||||
|
||||
match &= KeyboardHardware.isKeyswitchPressed(comboKey);
|
||||
if (!match)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j != KeyboardHardware.pressedKeyswitchCount())
|
||||
match = false;
|
||||
|
||||
if (match && (millis() >= end_time_)) {
|
||||
ComboAction action = (ComboAction) pgm_read_ptr(&(magiccombo::combos[i].action));
|
||||
|
||||
(*action)(i);
|
||||
end_time_ = millis() + min_interval;
|
||||
}
|
||||
}
|
||||
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
kaleidoscope::plugin::MagicCombo MagicCombo;
|
@ -0,0 +1,84 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-MagicCombo -- Magic combo framework
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
|
||||
#define MAX_COMBO_LENGTH 5
|
||||
|
||||
#define USE_MAGIC_COMBOS(...) \
|
||||
namespace kaleidoscope { \
|
||||
namespace plugin { \
|
||||
namespace magiccombo { \
|
||||
const kaleidoscope::plugin::MagicCombo::Combo combos[] PROGMEM = \
|
||||
{__VA_ARGS__}; \
|
||||
\
|
||||
const uint8_t combos_length = sizeof(combos) / sizeof(*combos); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define _MAGICCOMBO_API_CHANGE \
|
||||
"The MagicCombo API changed in an incompatible way, you will need to\n" \
|
||||
"upgrade.\n" \
|
||||
"\n" \
|
||||
"Please see the `UPGRADING.md` document shipped with the source:\n" \
|
||||
" https://github.com/keyboardio/Kaleidoscope-MagicCombo/blob/master/UPGRADING.md"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
class MagicCombo : public kaleidoscope::Plugin {
|
||||
public:
|
||||
typedef void (*ComboAction)(uint8_t combo_index);
|
||||
typedef struct {
|
||||
ComboAction action;
|
||||
int8_t keys[MAX_COMBO_LENGTH + 1];
|
||||
} Combo;
|
||||
typedef struct combo_t {
|
||||
uint32_t left_hand, right_hand;
|
||||
|
||||
template <typename T>
|
||||
combo_t(T l, T r) {
|
||||
static_assert(sizeof(T) < 0, _DEPRECATE(_MAGICCOMBO_API_CHANGE));
|
||||
}
|
||||
} combo_t;
|
||||
|
||||
MagicCombo(void) {}
|
||||
|
||||
static const combo_t *magic_combos;
|
||||
static uint16_t min_interval;
|
||||
|
||||
EventHandlerResult beforeReportingState();
|
||||
|
||||
private:
|
||||
static uint32_t end_time_;
|
||||
};
|
||||
|
||||
namespace magiccombo {
|
||||
extern const MagicCombo::Combo combos[];
|
||||
extern const uint8_t combos_length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Backward compatibility
|
||||
typedef plugin::MagicCombo MagicCombo;
|
||||
}
|
||||
|
||||
extern kaleidoscope::plugin::MagicCombo MagicCombo;
|
Loading…
Reference in new issue