Merge remote-tracking branch 'plugin/Macros/f/monorepo' into f/monorepo

pull/365/head
Gergely Nagy 6 years ago
commit 3d20fa0aff
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -0,0 +1,183 @@
# Kaleidoscope-Macros
Macros are a standard feature on many keyboards and Kaleidoscope-powered ones
are no exceptions. Macros are a way to have a single key-press do a whole lot of
things under the hood: conventionally, macros play back a key sequence, but with
Kaleidoscope, there is much more we can do. Nevertheless, playing back a
sequence of events is still the primary use of macros.
Playing back a sequence means that when we press a macro key, we can have it
play pretty much any sequence. It can type some text for us, or invoke a
complicated shortcut - the possibilities are endless!
In Kaleidoscope, macros are implemented via this plugin. You can define upto 256 macros.
## Using the plugin
To use the plugin, we need to include the header, tell the firmware to `use` the
plugin, place macros on the keymap, and create a special handler function
(`macroAction`) that will tell the plugin what shall happen when macro keys are
pressed. It is best illustrated with an example:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-Macros.h>
// Give a name to the macros!
enum {
MACRO_MODEL01,
MACRO_HELLO,
MACRO_SPECIAL,
};
// Somewhere in the keymap:
M(MACRO_MODEL01), M(MACRO_HELLO), M(MACRO_SPECIAL)
// later in the Sketch:
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
case MACRO_MODEL01:
return MACRODOWN(I(25),
D(LeftShift), T(M), U(LeftShift), T(O), T(D), T(E), T(L),
T(Spacebar),
W(100),
T(0), T(1) );
case MACRO_HELLO:
if (keyToggledOn(keyState)) {
return Macros.type(PSTR("Hello "), PSTR("world!"));
}
break;
case MACRO_SPECIAL:
if (keyToggledOn(keyState)) {
// Do something special
}
break;
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(Macros);
void setup() {
Kaleidoscope.setup ();
}
```
## Keymap markup
### `M(id)`
> Places a macro key on the keymap, with the `id` number (0 to 255) as identifier. Whenever this key
> has to be handled, the `macroAction` overrideable function will be called,
> with the identifier and key state as arguments.
>
> It is recommended to give a *name* to macro ids, by using an `enum`.
## Plugin methods
The plugin provides a `Macros` object, with the following methods and properties available:
### `.play(macro)`
> Plays back a macro, where a macro is a sequence created with the `MACRO()`
> helper discussed below. This method will be used by the plugin to play back
> the result of the `macroAction()` method, but is used rarely otherwise.
>
> The `macro` argument must be a sequence created with the `MACRO()` helper!
### `.type(strings...)`
> In cases where we only want to type some strings, it is far more convenient to
> use this method: we do not have to use the `MACRO()` helper, but just give
> this one a set of strings, and it will type them for us on the keyboard. We
> can use as many strings as we want, and all of them will be typed in order.
>
> Each string is limited to a sequence of printable ASCII characters. No
> international symbols, or unicode, or anything like it: just plain ASCII.
>
> Each of `strings` arguments must also reside in program memory, and the
> easiest way to do that is to wrap the string in a `PSTR()` helper. See the
> program code at the beginning of this documentation for an example!
### `.row`, `.col`
> The `row` and `col` properties describe the physical position a macro was
> triggered from if it was triggered by a key. The playback functions
> do not use these properties, but they are available, would one want to create
> a macro that needs to know which key triggered it.
>
> When the macro was not triggered by a key the value of these properties are
> unspecified.
## Macro helpers
Macros need to be able to simulate key down and key up events for any key - even
keys that may not be on the keymap otherwise. For this reason and others, we
need to define them in a special way, using the `MACRO` helper (or its
`MACRODOWN()` variant, see below):
### `MACRO(steps...)`
> Defines a macro, that is built up from `steps` (explained below). The plugin
> will iterate through the sequence, and re-play the steps in order.
>
> Note: In older versions of the Macros plugin, the sequence of steps had to end
> with a special step called END. This is no longer required. Existing macros
> that end with END will still work correctly, but new code should not use END;
> usage of END is deprecated.
### `MACRODOWN(steps...)`
> The same as the `MACRO()` helper above, but it will create a special sequence,
> where the steps are only played back when the triggering key was just pressed.
> That is, the macro will not be performed when the key is released, or held, or
> not pressed at all.
>
> Use this over `MACRO()` when you only want to perform an action when the key
> actuates, and no action should be taken when it is held, released, or when it
> is not pressed at all. For a lot of macros that emit a sequence without any
> other side effects, `MACRODOWN()` is usually the better choice.
>
> Can only be used from the `macroAction()` overrideable method.
## `MACRO` steps
Macro steps can be divided into two groups:
### Delays
* `I(millis)`: Sets the interval between steps to `millis`. By default, there is
no delay between steps, and they are played back as fast as possible. Useful
when we want to see the macro being typed, or need to slow it down, to allow
the host to process it.
* `W(millis)`: Waits for `millis` milliseconds. For dramatic effects.
### Key events
Key event steps have three variants: one that prefixes its argument with `Key_`,
one that does not, and a third that allows for a more compact - but also more
limited - representation. The first are the `D`, `U`, and `T` variants, the
second are `Dr`, `Ur`, and `Tr`, and the last variant are `Dc`, `Uc`, and `Tc`.
In most cases, one is likely use normal keys for the steps, so the `D`, `U`, and
`T` steps apply the `Key_` prefix. This allows us to write `MACRO(T(X))` instead
of `MACRO(Tr(Key_X))` - making the macro definition shorter, and more readable.
The compact variant (`Dc`, `Uc`, and `Tc`) prefix the argument with `Key_` too,
but unlike `D`, `U`, and `T`, they ignore the `flags` component of the key, and
as such, are limited to ordinary keys. Mouse keys, consumer- or system keys are
not supported by this compact representation.
* `D(key)`, `Dr(key)`, `Dc(key)`: Simulates a key being pressed (pushed down).
* `U(key)`, `Ur(key)`, `Uc(key)`: Simulates a key being released (going up).
* `T(key)`, `Tr(key)`, `Tc(key)`: Simulates a key being tapped (pressed first, then released).
## Overrideable methods
### `macroAction(macroIndex, keyState)`
> The `macroAction` method is the brain of the macro support in Kaleidoscope:
> this function tells the plugin what sequence to play when given a macro index
> and a key state.
>
> It should return a macro sequence, or `MACRO_NONE` if nothing is to be played
> back.

@ -0,0 +1,19 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 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/plugin/Macros.h"

@ -0,0 +1,244 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 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-Macros.h"
#include "kaleidoscope/hid.h"
__attribute__((weak))
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
return MACRO_NONE;
}
namespace kaleidoscope {
namespace plugin {
MacroKeyEvent Macros_::active_macros[];
byte Macros_::active_macro_count;
byte Macros_::row, Macros_::col;
void playMacroKeyswitchEvent(Key key, uint8_t keyswitch_state) {
handleKeyswitchEvent(key, UNKNOWN_KEYSWITCH_LOCATION, keyswitch_state | INJECTED);
kaleidoscope::hid::sendKeyboardReport();
kaleidoscope::hid::sendMouseReport();
}
static void readKeyCodeAndPlay(const macro_t *macro_p, uint8_t flags, uint8_t keyStates) {
Key key;
key.flags = flags;
key.keyCode = pgm_read_byte(macro_p++);
if (keyIsPressed(keyStates)) {
playMacroKeyswitchEvent(key, IS_PRESSED);
}
if (keyWasPressed(keyStates)) {
playMacroKeyswitchEvent(key, WAS_PRESSED);
}
}
void Macros_::play(const macro_t *macro_p) {
macro_t macro = MACRO_ACTION_END;
uint8_t interval = 0;
uint8_t flags;
if (!macro_p)
return;
while (true) {
switch (macro = pgm_read_byte(macro_p++)) {
case MACRO_ACTION_STEP_INTERVAL:
interval = pgm_read_byte(macro_p++);
break;
case MACRO_ACTION_STEP_WAIT: {
uint8_t wait = pgm_read_byte(macro_p++);
delay(wait);
break;
}
case MACRO_ACTION_STEP_KEYDOWN:
flags = pgm_read_byte(macro_p++);
readKeyCodeAndPlay(macro_p++, flags, IS_PRESSED);
break;
case MACRO_ACTION_STEP_KEYUP:
flags = pgm_read_byte(macro_p++);
readKeyCodeAndPlay(macro_p++, flags, WAS_PRESSED);
break;
case MACRO_ACTION_STEP_TAP:
flags = pgm_read_byte(macro_p++);
readKeyCodeAndPlay(macro_p++, flags, IS_PRESSED | WAS_PRESSED);
break;
case MACRO_ACTION_STEP_KEYCODEDOWN:
readKeyCodeAndPlay(macro_p++, 0, IS_PRESSED);
break;
case MACRO_ACTION_STEP_KEYCODEUP:
readKeyCodeAndPlay(macro_p++, 0, WAS_PRESSED);
break;
case MACRO_ACTION_STEP_TAPCODE:
readKeyCodeAndPlay(macro_p++, 0, IS_PRESSED | WAS_PRESSED);
break;
case MACRO_ACTION_END:
default:
return;
}
delay(interval);
}
}
static const Key ascii_to_key_map[] PROGMEM = {
// 0x21 - 0x30
LSHIFT(Key_1),
LSHIFT(Key_Quote),
LSHIFT(Key_3),
LSHIFT(Key_4),
LSHIFT(Key_5),
LSHIFT(Key_7),
Key_Quote,
LSHIFT(Key_9),
LSHIFT(Key_0),
LSHIFT(Key_8),
LSHIFT(Key_Equals),
Key_Comma,
Key_Minus,
Key_Period,
Key_Slash,
Key_0,
// 0x3a ... 0x40
LSHIFT(Key_Semicolon),
Key_Semicolon,
LSHIFT(Key_Comma),
Key_Equals,
LSHIFT(Key_Period),
LSHIFT(Key_Slash),
LSHIFT(Key_2),
// 0x5b ... 0x60
Key_LeftBracket,
Key_Backslash,
Key_RightBracket,
LSHIFT(Key_6),
LSHIFT(Key_Minus),
Key_Backtick,
// 0x7b ... 0x7e
LSHIFT(Key_LeftBracket),
LSHIFT(Key_Backslash),
LSHIFT(Key_RightBracket),
LSHIFT(Key_Backtick),
};
Key Macros_::lookupAsciiCode(uint8_t ascii_code) {
Key key = Key_NoKey;
switch (ascii_code) {
case 0x08 ... 0x09:
key.keyCode = Key_Backspace.keyCode + ascii_code - 0x08;
break;
case 0x0A:
key.keyCode = Key_Enter.keyCode;
break;
case 0x1B:
key.keyCode = Key_Escape.keyCode;
break;
case 0x20:
key.keyCode = Key_Spacebar.keyCode;
break;
case 0x21 ... 0x30:
key.raw = pgm_read_word(&ascii_to_key_map[ascii_code - 0x21]);
break;
case 0x31 ... 0x39:
key.keyCode = Key_1.keyCode + ascii_code - 0x31;
break;
case 0x3A ... 0x40:
key.raw = pgm_read_word(&ascii_to_key_map[ascii_code - 0x3A + 16]);
break;
case 0x41 ... 0x5A:
key.flags = SHIFT_HELD;
key.keyCode = Key_A.keyCode + ascii_code - 0x41;
break;
case 0x5B ... 0x60:
key.raw = pgm_read_word(&ascii_to_key_map[ascii_code - 0x5B + 23]);
break;
case 0x61 ... 0x7A:
key.keyCode = Key_A.keyCode + ascii_code - 0x61;
break;
case 0x7B ... 0x7E:
key.raw = pgm_read_word(&ascii_to_key_map[ascii_code - 0x7B + 29]);
break;
}
return key;
}
const macro_t *Macros_::type(const char *string) {
while (true) {
uint8_t ascii_code = pgm_read_byte(string++);
if (!ascii_code)
break;
Key key = lookupAsciiCode(ascii_code);
if (key.raw == Key_NoKey.raw)
continue;
playMacroKeyswitchEvent(key, IS_PRESSED);
playMacroKeyswitchEvent(key, WAS_PRESSED);
}
return MACRO_NONE;
}
EventHandlerResult Macros_::onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState) {
if (mappedKey.flags != (SYNTHETIC | IS_MACRO))
return EventHandlerResult::OK;
byte key_id = (row * COLS) + col;
addActiveMacroKey(mappedKey.keyCode, key_id, keyState);
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult Macros_::afterEachCycle() {
active_macro_count = 0;
return EventHandlerResult::OK;
}
EventHandlerResult Macros_::beforeReportingState() {
for (byte i = 0; i < active_macro_count; ++i) {
if (active_macros[i].key_id == 0xFF) {
// i.e. UNKNOWN_KEYSWITCH_LOCATION
row = 0xFF;
col = 0xFF;
} else {
row = active_macros[i].key_id / COLS;
col = active_macros[i].key_id % COLS;
}
const macro_t *m = macroAction(active_macros[i].key_code,
active_macros[i].key_state);
Macros.play(m);
}
return EventHandlerResult::OK;
}
}
}
kaleidoscope::plugin::Macros_ Macros;

@ -0,0 +1,86 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 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>
#include "kaleidoscope/plugin/Macros/MacroKeyDefs.h"
#include "kaleidoscope/plugin/Macros/MacroSteps.h"
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState);
#if !defined(MAX_CONCURRENT_MACROS)
#define MAX_CONCURRENT_MACROS 8
#endif
struct MacroKeyEvent {
byte key_code;
byte key_id;
byte key_state;
};
namespace kaleidoscope {
namespace plugin {
class Macros_ : public kaleidoscope::Plugin {
public:
Macros_(void) {}
static MacroKeyEvent active_macros[MAX_CONCURRENT_MACROS];
static byte active_macro_count;
static void addActiveMacroKey(byte key_code, byte key_id, byte key_state) {
// If we've got too many active macros, give up:
if (active_macro_count >= MAX_CONCURRENT_MACROS) {
return;
}
active_macros[active_macro_count].key_code = key_code;
active_macros[active_macro_count].key_id = key_id;
active_macros[active_macro_count].key_state = key_state;
++active_macro_count;
}
EventHandlerResult onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState);
EventHandlerResult beforeReportingState();
EventHandlerResult afterEachCycle();
void play(const macro_t *macro_p);
/* What follows below, is a bit of template magic that allows us to use
Macros.type() with any number of arguments, without having to use a
sentinel. See the comments on Kaleidoscope.use() for more details - this is
the same trick.
*/
inline const macro_t *type() {
return MACRO_NONE;
}
const macro_t *type(const char *string);
template <typename... Strings>
const macro_t *type(const char *first, Strings&&... strings) {
type(first);
return type(strings...);
}
static byte row, col;
private:
Key lookupAsciiCode(uint8_t ascii_code);
};
}
}
extern kaleidoscope::plugin::Macros_ Macros;

@ -0,0 +1,31 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 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
#define IS_MACRO B00100000
#define M(n) (Key){ n, KEY_FLAGS|SYNTHETIC|IS_MACRO }
#define Key_macroKey1 M(1)
#define Key_macroKey2 M(2)
#define Key_macroKey3 M(3)
#define Key_macroKey4 M(4)
#define Key_macroKey5 M(5)
#define Key_macroKey6 M(6)
#define Key_macroKey7 M(7)
#define Key_macroKey8 M(8)
#define Key_macroKey9 M(9)
#define Key_macroKey10 M(10)

@ -0,0 +1,54 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 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
typedef enum {
MACRO_ACTION_END,
MACRO_ACTION_STEP_INTERVAL,
MACRO_ACTION_STEP_WAIT,
MACRO_ACTION_STEP_KEYDOWN,
MACRO_ACTION_STEP_KEYUP,
MACRO_ACTION_STEP_TAP,
MACRO_ACTION_STEP_KEYCODEDOWN,
MACRO_ACTION_STEP_KEYCODEUP,
MACRO_ACTION_STEP_TAPCODE,
} MacroActionStepType;
typedef uint8_t macro_t;
#define MACRO_NONE 0
#define MACRO(...) ({static const macro_t __m[] PROGMEM = { __VA_ARGS__, MACRO_ACTION_END }; &__m[0]; })
#define MACRODOWN(...) (keyToggledOn(keyState) ? MACRO(__VA_ARGS__) : MACRO_NONE)
#define I(n) MACRO_ACTION_STEP_INTERVAL, n
#define W(n) MACRO_ACTION_STEP_WAIT, n
#define Dr(k) MACRO_ACTION_STEP_KEYDOWN, (k).flags, (k).keyCode
#define D(k) Dr(Key_ ## k)
#define Ur(k) MACRO_ACTION_STEP_KEYUP, (k).flags, (k).keyCode
#define U(k) Ur(Key_ ## k)
#define Tr(k) MACRO_ACTION_STEP_TAP, (k).flags, (k).keyCode
#define T(k) Tr(Key_ ## k)
#define Dc(k) MACRO_ACTION_STEP_KEYCODEDOWN, (Key_ ## k).keyCode
#define Uc(k) MACRO_ACTION_STEP_KEYCODEUP, (Key_ ## k).keyCode
#define Tc(k) MACRO_ACTION_STEP_TAPCODE, (Key_ ## k).keyCode
__attribute__((deprecated("END is no longer required to end macros"))) const MacroActionStepType END = MACRO_ACTION_END;
Loading…
Cancel
Save