Convert Macros and DynamicMacros plugins to use MacroSupport plugin

Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
pull/1185/head
Michael Richters 2 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 ## Using the plugin
To use the plugin, we need to include the header, tell the firmware to `use` the To use the plugin, we need to include the header, initialize the plugin with
plugin, and reserve storage space for the macros. This is best illustrated with `KALEIDOSCOPE_INIT_PLUGINS()`, and reserve storage space for the macros. This is
an example: best illustrated with an example:
```c++ ```c++
#include <Kaleidoscope.h> #include <Kaleidoscope.h>
@ -96,5 +96,6 @@ The plugin provides two Focus commands: `macros.map` and `macros.trigger`.
## Dependencies ## Dependencies
* [Kaleidoscope-MacroSupport](Kaleidoscope-MacroSupport.md)
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md) * [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)
* [Kaleidoscope-FocusSerial](Kaleidoscope-FocusSerial.md) * [Kaleidoscope-FocusSerial](Kaleidoscope-FocusSerial.md)

@ -20,7 +20,6 @@
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST, DYNAMIC_MACRO_LAST #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/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_ #include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for VirtualProps::Storage, Base<>::Storage #include "kaleidoscope/device/device.h" // for VirtualProps::Storage, Base<>::Storage
@ -35,31 +34,6 @@ namespace kaleidoscope {
namespace plugin { 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() { uint8_t DynamicMacros::updateDynamicMacroCache() {
uint16_t pos = storage_base_; uint16_t pos = storage_base_;
uint8_t current_id = 0; uint8_t current_id = 0;
@ -222,6 +196,7 @@ bool isDynamicMacrosKey(Key key) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) { EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) {
// Ignore everything except DynamicMacros keys
if (!isDynamicMacrosKey(event.key)) if (!isDynamicMacrosKey(event.key))
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -229,25 +204,12 @@ EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) {
uint8_t macro_id = event.key.getRaw() - ranges::DYNAMIC_MACRO_FIRST; uint8_t macro_id = event.key.getRaw() - ranges::DYNAMIC_MACRO_FIRST;
play(macro_id); play(macro_id);
} else { } else {
for (Key key : active_macro_keys_) { clear();
release(key);
}
} }
return EventHandlerResult::EVENT_CONSUMED; 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() { EventHandlerResult DynamicMacros::onNameQuery() {
return ::Focus.sendName(F("DynamicMacros")); return ::Focus.sendName(F("DynamicMacros"));
} }

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

@ -14,10 +14,10 @@ In Kaleidoscope, macros are implemented via this plugin. You can define upto 256
## Using the plugin ## Using the plugin
To use the plugin, we need to include the header, tell the firmware to `use` the To use the plugin, we need to include the header, initialize the plugins with
plugin, place macros on the keymap, and create a special handler function `KALEIDOSCOPE_INIT_PLUGINS()`, place macros on the keymap, and create a special
(`macroAction`) that will tell the plugin what shall happen when macro keys are handler function (`macroAction()`) that will determine what happens when macro
pressed. It is best illustrated with an example: keys are pressed. It is best illustrated with an example:
```c++ ```c++
#include <Kaleidoscope.h> #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 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 operating system is set to a different layout, the strings and keycodes will
need to be adjusted accordingly. need to be adjusted accordingly.
## Dependencies
* [Kaleidoscope-MacroSupport](Kaleidoscope-MacroSupport.md)

@ -1,5 +1,5 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope. /* 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 * 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 * 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 <Kaleidoscope-Ranges.h> // for MACRO_FIRST
#include <stdint.h> // for uint8_t #include <stdint.h> // for uint8_t
#include "kaleidoscope/KeyAddr.h" // for KeyAddr
#include "kaleidoscope/KeyEvent.h" // for KeyEvent #include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResul... #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/key_defs.h" // for Key, LSHIFT, Key_NoKey, Key_0, Key_1
#include "kaleidoscope/keyswitch_state.h" // for INJECTED, IS_PRESSED, WAS_PRESSED #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 kaleidoscope {
namespace plugin { 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 // 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) { void Macros::play(const macro_t *macro_p) {
macro_t macro = MACRO_ACTION_END; macro_t macro = MACRO_ACTION_END;
uint8_t interval = 0; uint8_t interval = 0;
@ -307,39 +258,24 @@ EventHandlerResult Macros::onKeyEvent(KeyEvent &event) {
play(macro_ptr); play(macro_ptr);
if (keyToggledOff(event.state) || !isMacrosKey(event.key)) { if (keyToggledOff(event.state) || !isMacrosKey(event.key)) {
// If we toggled off or if the value of `event.key` has changed, send // If a Macros key toggled off or if the value of `event.key` has been
// release events for any active macro keys. Simply clearing // changed by the user-defined `macroAction()` function, we clear the array
// `active_macro_keys_` might be sufficient, but it's probably better to // of active macro keys so that they won't get "stuck on". There won't be a
// send the toggle off events so that other plugins get a chance to act on // subsequent event that Macros will recognize as actionable, so we need to
// them. // do it here.
clear(); 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 // Return `OK` to let Kaleidoscope finish processing this event as normal.
// for Kaleidoscope to do with a key press of a Macros key, so we return // This is so that, if the user-defined `macroAction(id, &event)` function
// `EVENT_CONSUMED`, causing Kaleidoscope to update `live_keys[]` correctly, // changes the value of `event.key`, it will take effect properly. Note that
// ensuring that the above block will clear `active_macro_keys_` on release, // we're counting on other plugins to not subsequently change the value of
// but not allowing any other plugins to change the `event.key` value, which // `event.key` if a Macros key has toggled on, because that would leave any
// would interfere. // keys in the supplemental array "stuck on". We could return
//return EventHandlerResult::EVENT_CONSUMED; // `EVENT_CONSUMED` if `event.key` is still a Macros key, but that would lead
return EventHandlerResult::OK; // 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()`).
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 EventHandlerResult::OK; return EventHandlerResult::OK;
} }

@ -1,5 +1,5 @@
/* Kaleidoscope-Macros - Macro keys for Kaleidoscope. /* 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 * 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 * 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 <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/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key #include "kaleidoscope/key_defs.h" // for Key
@ -29,14 +30,6 @@
// Define this function in a Kaleidoscope sketch in order to trigger Macros. // Define this function in a Kaleidoscope sketch in order to trigger Macros.
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event); 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 kaleidoscope {
namespace plugin { namespace plugin {
@ -48,26 +41,34 @@ class Macros : public kaleidoscope::Plugin {
/// specified `key`, then stores that `key` in an array of active macro key /// 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 /// 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. /// 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 /// Send a key release event from a Macro
/// ///
/// Generates a new `KeyEvent` and calls `Runtime.handleKeyEvent()` with the /// Generates a new `KeyEvent` and calls `Runtime.handleKeyEvent()` with the
/// specified `key`, then removes that key from the array of active macro /// specified `key`, then removes that key from the array of active macro
/// keys (see `Macros.press()`). /// keys (see `Macros.press()`).
void release(Key key); inline void release(Key key) {
::MacroSupport.release(key);
}
/// Clear all virtual keys held by Macros /// Clear all virtual keys held by Macros
/// ///
/// This function clears the active macro keys array, sending a release event /// This function clears the active macro keys array, sending a release event
/// for each key stored there. /// for each key stored there.
void clear(); inline void clear() {
::MacroSupport.clear();
}
/// Send a key "tap event" from a Macro /// Send a key "tap event" from a Macro
/// ///
/// Generates two new `KeyEvent` objects, one each to press and release the /// Generates two new `KeyEvent` objects, one each to press and release the
/// specified `key`, passing both in sequence to `Runtime.handleKeyEvent()`. /// 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 /// Play a macro sequence of key events
void play(const macro_t *macro_ptr); void play(const macro_t *macro_ptr);
@ -89,12 +90,11 @@ class Macros : public kaleidoscope::Plugin {
// Event handlers // Event handlers
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult beforeReportingState(const KeyEvent &event); EventHandlerResult beforeReportingState(const KeyEvent &event) {
return ::MacroSupport.beforeReportingState(event);
}
private: 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` // Translate and ASCII character value to a corresponding `Key`
Key lookupAsciiCode(uint8_t ascii_code) const; Key lookupAsciiCode(uint8_t ascii_code) const;

Loading…
Cancel
Save