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