Merge pull request #1185 from gedankenexperimenter/macros-support-plugin

Code sharing for Macros & DynamicMacros, et al
pull/1188/head
Jesse Vincent 3 years ago committed by GitHub
commit a61c211dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,7 +17,7 @@
#include <Kaleidoscope.h> #include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h> #include <Kaleidoscope-Leader.h>
#include <Kaleidoscope-Macros.h> #include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Ranges.h> #include <Kaleidoscope-Ranges.h>
#include "kaleidoscope/KeyEventTracker.h" #include "kaleidoscope/KeyEventTracker.h"
@ -176,7 +176,7 @@ void leaderTestPrefix(uint8_t seq_index) {
uint8_t prefix_arg = LeaderPrefix.arg(); uint8_t prefix_arg = LeaderPrefix.arg();
// Use a Macros helper function to tap the `X` key repeatedly. // Use a Macros helper function to tap the `X` key repeatedly.
while (prefix_arg-- > 0) while (prefix_arg-- > 0)
Macros.tap(Key_X); MacroSupport.tap(Key_X);
} }
static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM = static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM =

@ -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,11 +20,10 @@
#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
#include "kaleidoscope/keyswitch_state.h" // for INJECTED, IS_PRESSED, WAS_PRESSED #include "kaleidoscope/keyswitch_state.h" // for keyToggledOn
#include "kaleidoscope/plugin/EEPROM-Settings.h" // for EEPROMSettings #include "kaleidoscope/plugin/EEPROM-Settings.h" // for EEPROMSettings
// This is a special exception to the rule of only including a plugin's // This is a special exception to the rule of only including a plugin's
// top-level header file, because DynamicMacros doesn't depend on the Macros // top-level header file, because DynamicMacros doesn't depend on the Macros
@ -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,17 +16,16 @@
#pragma once #pragma once
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST #include <Kaleidoscope-MacroSupport.h> // for MacroSupport
#include <stdint.h> // for uint16_t, uint8_t #include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST
#include <stdint.h> // for uint16_t, uint8_t
#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
#include "kaleidoscope/plugin.h" // for Plugin #include "kaleidoscope/plugin.h" // for Plugin
#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

@ -0,0 +1,51 @@
# MacroSupport
This plugin provides the supplemental key array used by the Macros and DynamicMacros plugins, and is necessary for the proper functioning of those plugins.
## Using the plugin
Any firmware sketch that uses either Macros or DynamicMacros automatically includes this plugin, so there's no need to add it explicitly. If your sketch doesn't require either type of Macros key, however, you can still make use of the MacroSupport plugin's helper methods (`tap()`, `press()`, _et al_). In that case, you should include the MacroSupport header file, and include it in `KALEIDOSCOPE_INIT_PLUGINS()`:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-MacroSupport.h>
// Other plugin code that calls `MacroSupport.press()` (for example)
KALEIDOSCOPE_INIT_PLUGINS(
MacroSupport,
// Other plugin(s) that make use of MacroSupport
);
void setup() {
Kaleidoscope.setup ();
}
```
## Plugin methods
The plugin provides a `MacroSupport` object, which contains a supplemental array of virtual keys that it adds to USB Keyboard reports. Other plugins and user code can interact with it via the following methods:
### `.press(key)`
> Sends a key press event for `key`, and will keep that virtual key active in
> the supplemental virtual keys array.
### `.release(key)`
> Sends a key release event for `key`, and removes it from the supplemental
> virtual keys array.
### `.clear()`
> Releases all active virtual keys held by MacroSupport. This both empties the
> supplemental keys array (see above) and sends a release event for each key
> stored there.
### `.tap(key)`
> Sends an immediate press and release event for `key` with no delay, using an
> invalid key address. This method doesn't actually use the supplemental keys
> array, but is provided here for convenience and simplicity.
It is not necessary to use either the Macros (or DynamicMacros) to make use of MacroSupport. When using it with custom code, however, please remember that the supplemental active keys array it provides will be shared by all clients (e.g. Macros, user-defined Leader or TapDance functions), so if you want more than one of those clients to be active simultaneously, be aware that calles to `MacroSupport.clear()` will affect all of them, not just the caller.

@ -0,0 +1,7 @@
name=Kaleidoscope-MacroSupport
version=0.0.0
sentence=Macro keys for Kaleidoscope
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
url=https://github.com/keyboardio/Kaleidoscope
author=Gergely Nagy, Jesse Vincent, Michael Richters
paragraph=

@ -0,0 +1,19 @@
/* Kaleidoscope-MacroSupport -- Macro keys for Kaleidoscope
* Copyright (C) 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
* 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/MacroSupport.h" // IWYU pragma: export

@ -0,0 +1,105 @@
/* Kaleidoscope-MacroSupport - Macros support functions for Kaleidoscope
* Copyright (C) 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
* 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/plugin/MacroSupport.h"
#include <Arduino.h> // for F, __FlashStringHelper
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#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, EventHandlerResult::OK
#include "kaleidoscope/key_defs.h" // for Key, Key_NoKey
#include "kaleidoscope/keyswitch_state.h" // for INJECTED, IS_PRESSED, WAS_PRESSED
// =============================================================================
// `Macros` plugin code
namespace kaleidoscope {
namespace plugin {
constexpr uint8_t press_state = IS_PRESSED | INJECTED;
constexpr uint8_t release_state = WAS_PRESSED | INJECTED;
// -----------------------------------------------------------------------------
// Public helper functions
void MacroSupport::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 MacroSupport::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 MacroSupport::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 MacroSupport::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});
}
// -----------------------------------------------------------------------------
// Event handlers
EventHandlerResult MacroSupport::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;
}
EventHandlerResult MacroSupport::onNameQuery() {
return ::Focus.sendName(F("MacroSupport"));
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::MacroSupport MacroSupport;

@ -0,0 +1,78 @@
/* Kaleidoscope-MacroSupport - Macros support functions for Kaleidoscope
* Copyright (C) 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
* 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/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/plugin.h" // for Plugin
// =============================================================================
// 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 {
class MacroSupport : public Plugin {
public:
/// Send a key press event from a Macro
///
/// Generates a new `KeyEvent` and calls `Runtime.handleKeyEvent()` with the
/// 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);
/// 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);
/// 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();
/// 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;
// ---------------------------------------------------------------------------
// Event handlers
EventHandlerResult onNameQuery();
EventHandlerResult beforeReportingState(const KeyEvent &event);
private:
// An array of key values that are active while a macro sequence is playing
Key active_macro_keys_[MAX_CONCURRENT_MACRO_KEYS];
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::MacroSupport MacroSupport;

@ -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,12 +21,10 @@
#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 keyToggledOff
#include "kaleidoscope/plugin/Macros/MacroSteps.h" // for macro_t, MACRO_NONE, MACRO_ACTION_END #include "kaleidoscope/plugin/Macros/MacroSteps.h" // for macro_t, MACRO_NONE, MACRO_ACTION_END
// ============================================================================= // =============================================================================
@ -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
@ -16,9 +16,10 @@
#pragma once #pragma once
#include <stdint.h> // for uint8_t #include <Kaleidoscope-MacroSupport.h> // for MacroSupport
#include <Kaleidoscope-Ranges.h> // for MACRO_FIRST, MACRO_LAST
#include <stdint.h> // for uint8_t
#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;

@ -17,7 +17,7 @@
#include <Kaleidoscope.h> #include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h> #include <Kaleidoscope-Leader.h>
#include <Kaleidoscope-Macros.h> #include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Ranges.h> #include <Kaleidoscope-Ranges.h>
#include "kaleidoscope/KeyEventTracker.h" #include "kaleidoscope/KeyEventTracker.h"
@ -114,19 +114,19 @@ kaleidoscope::plugin::LeaderPrefix LeaderPrefix;
auto &serial_port = Kaleidoscope.serialPort(); auto &serial_port = Kaleidoscope.serialPort();
static void leaderTestX(uint8_t seq_index) { static void leaderTestX(uint8_t seq_index) {
Macros.tap(Key_A); MacroSupport.tap(Key_A);
} }
static void leaderTestXX(uint8_t seq_index) { static void leaderTestXX(uint8_t seq_index) {
Macros.tap(Key_B); MacroSupport.tap(Key_B);
} }
void leaderTestPrefix(uint8_t seq_index) { void leaderTestPrefix(uint8_t seq_index) {
uint8_t prefix_arg = LeaderPrefix.arg(); uint8_t prefix_arg = LeaderPrefix.arg();
// DUMP(prefix_arg); // DUMP(prefix_arg);
Macros.tap(Key_Y); MacroSupport.tap(Key_Y);
while (prefix_arg-- > 0) while (prefix_arg-- > 0)
Macros.tap(Key_X); MacroSupport.tap(Key_X);
} }
static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM = static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM =

Loading…
Cancel
Save