Convert Macros and DynamicMacros plugins to use MacroSupport plugin

Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
pull/1185/head
Michael Richters 3 years ago
parent 3976e3dd5b
commit 054dc9beb3
No known key found for this signature in database
GPG Key ID: 1288FD13E4EEF0C0

@ -19,9 +19,9 @@ except the amount of storage available on the keyboard.
## Using the plugin
To use the plugin, we need to include the header, tell the firmware to `use` the
plugin, and reserve storage space for the macros. This is best illustrated with
an example:
To use the plugin, we need to include the header, initialize the plugin with
`KALEIDOSCOPE_INIT_PLUGINS()`, and reserve storage space for the macros. This is
best illustrated with an example:
```c++
#include <Kaleidoscope.h>
@ -96,5 +96,6 @@ The plugin provides two Focus commands: `macros.map` and `macros.trigger`.
## Dependencies
* [Kaleidoscope-MacroSupport](Kaleidoscope-MacroSupport.md)
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)
* [Kaleidoscope-FocusSerial](Kaleidoscope-FocusSerial.md)

@ -20,7 +20,6 @@
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST, DYNAMIC_MACRO_LAST
#include "kaleidoscope/KeyAddr.h" // for KeyAddr
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for VirtualProps::Storage, Base<>::Storage
@ -35,31 +34,6 @@ namespace kaleidoscope {
namespace plugin {
// =============================================================================
// It might be possible to use Macros instead of reproducing it
void DynamicMacros::press(Key key) {
Runtime.handleKeyEvent(KeyEvent(KeyAddr::none(), IS_PRESSED | INJECTED, key));
for (Key &mkey : active_macro_keys_) {
if (mkey == Key_NoKey) {
mkey = key;
break;
}
}
}
void DynamicMacros::release(Key key) {
for (Key &mkey : active_macro_keys_) {
if (mkey == key) {
mkey = Key_NoKey;
}
}
Runtime.handleKeyEvent(KeyEvent(KeyAddr::none(), WAS_PRESSED | INJECTED, key));
}
void DynamicMacros::tap(Key key) {
Runtime.handleKeyEvent(KeyEvent(KeyAddr::none(), IS_PRESSED | INJECTED, key));
Runtime.handleKeyEvent(KeyEvent(KeyAddr::none(), WAS_PRESSED | INJECTED, key));
}
uint8_t DynamicMacros::updateDynamicMacroCache() {
uint16_t pos = storage_base_;
uint8_t current_id = 0;
@ -222,6 +196,7 @@ bool isDynamicMacrosKey(Key key) {
// -----------------------------------------------------------------------------
EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) {
// Ignore everything except DynamicMacros keys
if (!isDynamicMacrosKey(event.key))
return EventHandlerResult::OK;
@ -229,25 +204,12 @@ EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) {
uint8_t macro_id = event.key.getRaw() - ranges::DYNAMIC_MACRO_FIRST;
play(macro_id);
} else {
for (Key key : active_macro_keys_) {
release(key);
}
clear();
}
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult DynamicMacros::beforeReportingState(const KeyEvent &event) {
// Here we add keycodes to the HID report for keys held in a macro sequence.
// This is necessary because Kaleidoscope doesn't know about the supplemental
// `active_macro_keys_[]` array.
for (Key key : active_macro_keys_) {
if (key != Key_NoKey)
Runtime.addToReport(key);
}
return EventHandlerResult::OK;
}
EventHandlerResult DynamicMacros::onNameQuery() {
return ::Focus.sendName(F("DynamicMacros"));
}

@ -16,6 +16,7 @@
#pragma once
#include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST
#include <stdint.h> // for uint16_t, uint8_t
@ -24,9 +25,7 @@
#include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/plugin.h" // for Plugin
#define DM(n) ::kaleidoscope::plugin::DynamicMacrosKey(n)
#define MAX_CONCURRENT_DYNAMIC_MACRO_KEYS 8
#define DM(n) ::kaleidoscope::plugin::DynamicMacrosKey(n)
namespace kaleidoscope {
namespace plugin {
@ -39,8 +38,10 @@ class DynamicMacros : public kaleidoscope::Plugin {
public:
EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult beforeReportingState(const KeyEvent &event);
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult beforeReportingState(const KeyEvent &event) {
return ::MacroSupport.beforeReportingState(event);
}
void reserve_storage(uint16_t size);
@ -52,10 +53,11 @@ class DynamicMacros : public kaleidoscope::Plugin {
uint16_t map_[32];
uint8_t macro_count_;
uint8_t updateDynamicMacroCache();
Key active_macro_keys_[MAX_CONCURRENT_DYNAMIC_MACRO_KEYS];
void press(Key key);
void release(Key key);
void tap(Key key);
inline void press(Key key) { ::MacroSupport.press(key); }
inline void release(Key key) { ::MacroSupport.release(key); }
inline void tap(Key key) const { ::MacroSupport.tap(key); }
inline void clear() { ::MacroSupport.clear(); }
};
} // namespace plugin

@ -14,10 +14,10 @@ In Kaleidoscope, macros are implemented via this plugin. You can define upto 256
## 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:
To use the plugin, we need to include the header, initialize the plugins with
`KALEIDOSCOPE_INIT_PLUGINS()`, place macros on the keymap, and create a special
handler function (`macroAction()`) that will determine what happens when macro
keys are pressed. It is best illustrated with an example:
```c++
#include <Kaleidoscope.h>
@ -210,3 +210,7 @@ Due to technical and practical reasons, `Macros.type()` assumes a QWERTY layout
on the host side, and so do all other parts that work with keycodes. If your
operating system is set to a different layout, the strings and keycodes will
need to be adjusted accordingly.
## Dependencies
* [Kaleidoscope-MacroSupport](Kaleidoscope-MacroSupport.md)

@ -1,5 +1,5 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 2017-2021 Keyboard.io, Inc.
* Copyright (C) 2017-2022 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
@ -21,9 +21,7 @@
#include <Kaleidoscope-Ranges.h> // for MACRO_FIRST
#include <stdint.h> // for uint8_t
#include "kaleidoscope/KeyAddr.h" // for KeyAddr
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResul...
#include "kaleidoscope/key_defs.h" // for Key, LSHIFT, Key_NoKey, Key_0, Key_1
#include "kaleidoscope/keyswitch_state.h" // for INJECTED, IS_PRESSED, WAS_PRESSED
@ -42,56 +40,9 @@ macroAction(uint8_t macro_id, KeyEvent &event) {
namespace kaleidoscope {
namespace plugin {
constexpr uint8_t press_state = IS_PRESSED | INJECTED;
constexpr uint8_t release_state = WAS_PRESSED | INJECTED;
// Initialized to zeroes (i.e. `Key_NoKey`)
Key Macros::active_macro_keys_[];
// -----------------------------------------------------------------------------
// Public helper functions
void Macros::press(Key key) {
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), press_state, key});
// This key may remain active for several subsequent events, so we need to
// store it in the active macro keys array.
for (Key &macro_key : active_macro_keys_) {
if (macro_key == Key_NoKey) {
macro_key = key;
break;
}
}
}
void Macros::release(Key key) {
// Before sending the release event, we need to remove the key from the active
// macro keys array, or it will get inserted into the report anyway.
for (Key &macro_key : active_macro_keys_) {
if (macro_key == key) {
macro_key = Key_NoKey;
}
}
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), release_state, key});
}
void Macros::clear() {
// Clear the active macro keys array.
for (Key &macro_key : active_macro_keys_) {
if (macro_key == Key_NoKey)
continue;
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), release_state, macro_key});
macro_key = Key_NoKey;
}
}
void Macros::tap(Key key) const {
// No need to call `press()` & `release()`, because we're immediately
// releasing the key after pressing it. It is possible for some other plugin
// to insert an event in between, but very unlikely.
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), press_state, key});
Runtime.handleKeyEvent(KeyEvent{KeyAddr::none(), release_state, key});
}
void Macros::play(const macro_t *macro_p) {
macro_t macro = MACRO_ACTION_END;
uint8_t interval = 0;
@ -307,39 +258,24 @@ EventHandlerResult Macros::onKeyEvent(KeyEvent &event) {
play(macro_ptr);
if (keyToggledOff(event.state) || !isMacrosKey(event.key)) {
// If we toggled off or if the value of `event.key` has changed, send
// release events for any active macro keys. Simply clearing
// `active_macro_keys_` might be sufficient, but it's probably better to
// send the toggle off events so that other plugins get a chance to act on
// them.
// If a Macros key toggled off or if the value of `event.key` has been
// changed by the user-defined `macroAction()` function, we clear the array
// of active macro keys so that they won't get "stuck on". There won't be a
// subsequent event that Macros will recognize as actionable, so we need to
// do it here.
clear();
// Return `OK` to let Kaleidoscope finish processing this event as
// normal. This is so that, if the user-defined `macroAction(id, &event)`
// function changes the value of `event.key`, it will take effect properly.
return EventHandlerResult::OK;
}
// No other plugin should be handling Macros keys, and there's nothing more
// for Kaleidoscope to do with a key press of a Macros key, so we return
// `EVENT_CONSUMED`, causing Kaleidoscope to update `live_keys[]` correctly,
// ensuring that the above block will clear `active_macro_keys_` on release,
// but not allowing any other plugins to change the `event.key` value, which
// would interfere.
//return EventHandlerResult::EVENT_CONSUMED;
return EventHandlerResult::OK;
}
EventHandlerResult Macros::beforeReportingState(const KeyEvent &event) {
// Do this in beforeReportingState(), instead of `onAddToReport()` because
// `live_keys` won't get updated until after the macro sequence is played from
// the keypress. This could be changed by either updating `live_keys` manually
// ahead of time, or by executing the macro sequence on key release instead of
// key press. This is probably the simplest solution.
for (Key key : active_macro_keys_) {
if (key != Key_NoKey)
Runtime.addToReport(key);
}
// Return `OK` to let Kaleidoscope finish processing this event as normal.
// This is so that, if the user-defined `macroAction(id, &event)` function
// changes the value of `event.key`, it will take effect properly. Note that
// we're counting on other plugins to not subsequently change the value of
// `event.key` if a Macros key has toggled on, because that would leave any
// keys in the supplemental array "stuck on". We could return
// `EVENT_CONSUMED` if `event.key` is still a Macros key, but that would lead
// to other undesirable plugin interactions (e.g. OneShot keys wouldn't be
// triggered to turn off when a Macros key toggles on, assuming that Macros
// comes first in `KALEIDOSCOPE_INIT_PLUGINS()`).
return EventHandlerResult::OK;
}

@ -1,5 +1,5 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope.
* Copyright (C) 2017-2021 Keyboard.io, Inc.
* Copyright (C) 2017-2022 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
@ -18,7 +18,8 @@
#include <stdint.h> // for uint8_t
#include "Kaleidoscope-Ranges.h" // for MACRO_FIRST, MACRO_LAST
#include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Ranges.h> // for MACRO_FIRST, MACRO_LAST
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key
@ -29,14 +30,6 @@
// Define this function in a Kaleidoscope sketch in order to trigger Macros.
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event);
// The number of simultaneously-active `Key` values that a macro can have
// running during a call to `Macros.play()`. I don't know if it's actually
// possible to override this by defining it in a sketch before including
// "Kaleidoscope-Macros.h", but probably not.
#if !defined(MAX_CONCURRENT_MACRO_KEYS)
#define MAX_CONCURRENT_MACRO_KEYS 8
#endif
namespace kaleidoscope {
namespace plugin {
@ -48,26 +41,34 @@ class Macros : public kaleidoscope::Plugin {
/// specified `key`, then stores that `key` in an array of active macro key
/// values. This allows the macro to press one key and keep it active when a
/// subsequent key event is sent as part of the same macro sequence.
void press(Key key);
inline void press(Key key) {
::MacroSupport.press(key);
}
/// Send a key release event from a Macro
///
/// Generates a new `KeyEvent` and calls `Runtime.handleKeyEvent()` with the
/// specified `key`, then removes that key from the array of active macro
/// keys (see `Macros.press()`).
void release(Key key);
inline void release(Key key) {
::MacroSupport.release(key);
}
/// Clear all virtual keys held by Macros
///
/// This function clears the active macro keys array, sending a release event
/// for each key stored there.
void clear();
inline void clear() {
::MacroSupport.clear();
}
/// Send a key "tap event" from a Macro
///
/// Generates two new `KeyEvent` objects, one each to press and release the
/// specified `key`, passing both in sequence to `Runtime.handleKeyEvent()`.
void tap(Key key) const;
inline void tap(Key key) const {
::MacroSupport.tap(key);
}
/// Play a macro sequence of key events
void play(const macro_t *macro_ptr);
@ -89,12 +90,11 @@ class Macros : public kaleidoscope::Plugin {
// Event handlers
EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult beforeReportingState(const KeyEvent &event);
EventHandlerResult beforeReportingState(const KeyEvent &event) {
return ::MacroSupport.beforeReportingState(event);
}
private:
// An array of key values that are active while a macro sequence is playing
static Key active_macro_keys_[MAX_CONCURRENT_MACRO_KEYS];
// Translate and ASCII character value to a corresponding `Key`
Key lookupAsciiCode(uint8_t ascii_code) const;

Loading…
Cancel
Save