You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kaleidoscope/plugins/Kaleidoscope-DynamicMacros/src/kaleidoscope/plugin/DynamicMacros.cpp

263 lines
7.3 KiB

/* DynamicMacros - Dynamic macro support for Kaleidoscope.
* Copyright (C) 2019, 2021 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-DynamicMacros.h"
#include "Kaleidoscope-FocusSerial.h"
#include "kaleidoscope/keyswitch_state.h"
#include "kaleidoscope/key_events.h"
namespace kaleidoscope {
namespace plugin {
uint16_t DynamicMacros::storage_base_;
uint16_t DynamicMacros::storage_size_;
uint16_t DynamicMacros::map_[];
static void playMacroKeyswitchEvent(Key key, uint8_t keyswitch_state, bool explicit_report) {
handleKeyswitchEvent(key, UnknownKeyswitchLocation, keyswitch_state | INJECTED);
if (explicit_report)
return;
kaleidoscope::Runtime.hid().keyboard().sendReport();
kaleidoscope::Runtime.hid().mouse().sendReport();
}
static void playKeyCode(Key key, uint8_t keyStates, bool explicit_report) {
if (keyIsPressed(keyStates)) {
playMacroKeyswitchEvent(key, IS_PRESSED, explicit_report);
}
if (keyWasPressed(keyStates)) {
playMacroKeyswitchEvent(key, WAS_PRESSED, explicit_report);
}
}
static void readKeyCodeAndPlay(uint16_t pos, uint8_t flags, uint8_t keyStates, bool explicit_report) {
Key key(Runtime.storage().read(pos++), // key_code
flags);
playKeyCode(key, keyStates, explicit_report);
}
void DynamicMacros::updateDynamicMacroCache(void) {
uint16_t pos = storage_base_;
uint8_t current_id = 0;
macro_t macro = MACRO_ACTION_END;
bool previous_macro_ended = false;
map_[0] = 0;
while (pos < storage_base_ + storage_size_) {
macro = Runtime.storage().read(pos++);
switch (macro) {
case MACRO_ACTION_STEP_EXPLICIT_REPORT:
case MACRO_ACTION_STEP_IMPLICIT_REPORT:
case MACRO_ACTION_STEP_SEND_REPORT:
previous_macro_ended = false;
break;
case MACRO_ACTION_STEP_INTERVAL:
case MACRO_ACTION_STEP_WAIT:
case MACRO_ACTION_STEP_KEYCODEDOWN:
case MACRO_ACTION_STEP_KEYCODEUP:
case MACRO_ACTION_STEP_TAPCODE:
previous_macro_ended = false;
pos++;
break;
case MACRO_ACTION_STEP_KEYDOWN:
case MACRO_ACTION_STEP_KEYUP:
case MACRO_ACTION_STEP_TAP:
previous_macro_ended = false;
pos += 2;
break;
case MACRO_ACTION_STEP_TAP_SEQUENCE: {
previous_macro_ended = false;
uint8_t keyCode, flags;
do {
flags = Runtime.storage().read(pos++);
keyCode = Runtime.storage().read(pos++);
} while (!(flags == 0 && keyCode == 0));
break;
}
case MACRO_ACTION_STEP_TAP_CODE_SEQUENCE: {
previous_macro_ended = false;
uint8_t keyCode, flags;
do {
keyCode = Runtime.storage().read(pos++);
} while (keyCode != 0);
break;
}
case MACRO_ACTION_END:
map_[++current_id] = pos - storage_base_;
if (previous_macro_ended)
return;
previous_macro_ended = true;
break;
}
}
}
void DynamicMacros::play(uint8_t macro_id) {
macro_t macro = MACRO_ACTION_END;
uint8_t interval = 0;
uint8_t flags;
bool explicit_report = false;
uint16_t pos;
pos = storage_base_ + map_[macro_id];
while (true) {
switch (macro = Runtime.storage().read(pos++)) {
case MACRO_ACTION_STEP_EXPLICIT_REPORT:
explicit_report = true;
break;
case MACRO_ACTION_STEP_IMPLICIT_REPORT:
explicit_report = false;
break;
case MACRO_ACTION_STEP_SEND_REPORT:
kaleidoscope::Runtime.hid().keyboard().sendReport();
kaleidoscope::Runtime.hid().mouse().sendReport();
break;
case MACRO_ACTION_STEP_INTERVAL:
interval = Runtime.storage().read(pos++);
break;
case MACRO_ACTION_STEP_WAIT: {
uint8_t wait = Runtime.storage().read(pos++);
delay(wait);
break;
}
case MACRO_ACTION_STEP_KEYDOWN:
flags = Runtime.storage().read(pos++);
readKeyCodeAndPlay(pos++, flags, IS_PRESSED, explicit_report);
break;
case MACRO_ACTION_STEP_KEYUP:
flags = Runtime.storage().read(pos++);
readKeyCodeAndPlay(pos++, flags, WAS_PRESSED, explicit_report);
break;
case MACRO_ACTION_STEP_TAP:
flags = Runtime.storage().read(pos++);
readKeyCodeAndPlay(pos++, flags, IS_PRESSED | WAS_PRESSED, false);
break;
case MACRO_ACTION_STEP_KEYCODEDOWN:
readKeyCodeAndPlay(pos++, 0, IS_PRESSED, explicit_report);
break;
case MACRO_ACTION_STEP_KEYCODEUP:
readKeyCodeAndPlay(pos++, 0, WAS_PRESSED, explicit_report);
break;
case MACRO_ACTION_STEP_TAPCODE:
readKeyCodeAndPlay(pos++, 0, IS_PRESSED | WAS_PRESSED, false);
break;
case MACRO_ACTION_STEP_TAP_SEQUENCE: {
uint8_t keyCode;
do {
flags = Runtime.storage().read(pos++);
keyCode = Runtime.storage().read(pos++);
playKeyCode(Key(keyCode, flags), IS_PRESSED | WAS_PRESSED, false);
delay(interval);
} while (!(flags == 0 && keyCode == 0));
break;
}
case MACRO_ACTION_STEP_TAP_CODE_SEQUENCE: {
uint8_t keyCode;
do {
keyCode = Runtime.storage().read(pos++);
playKeyCode(Key(keyCode, 0), IS_PRESSED | WAS_PRESSED, false);
delay(interval);
} while (keyCode != 0);
break;
}
case MACRO_ACTION_END:
default:
return;
}
delay(interval);
}
}
EventHandlerResult DynamicMacros::onKeyswitchEvent(Key &mappedKey, KeyAddr key_addr, uint8_t keyState) {
if (mappedKey.getRaw() < ranges::DYNAMIC_MACRO_FIRST || mappedKey.getRaw() > ranges::DYNAMIC_MACRO_LAST)
return EventHandlerResult::OK;
if (keyToggledOn(keyState)) {
play(mappedKey.getRaw() - ranges::DYNAMIC_MACRO_FIRST);
}
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult DynamicMacros::onNameQuery() {
return ::Focus.sendName(F("DynamicMacros"));
}
EventHandlerResult DynamicMacros::onFocusEvent(const char *command) {
if (::Focus.handleHelp(command, PSTR("macros.map\nmacros.trigger")))
return EventHandlerResult::OK;
if (strncmp_P(command, PSTR("macros."), 7) != 0)
return EventHandlerResult::OK;
if (strcmp_P(command + 7, PSTR("map")) == 0) {
if (::Focus.isEOL()) {
for (uint16_t i = 0; i < storage_size_; i++) {
uint8_t b;
b = Runtime.storage().read(storage_base_ + i);
::Focus.send(b);
}
} else {
uint16_t pos = 0;
while (!::Focus.isEOL()) {
uint8_t b;
::Focus.read(b);
Runtime.storage().update(storage_base_ + pos++, b);
}
Runtime.storage().commit();
updateDynamicMacroCache();
}
}
if (strcmp_P(command + 7, PSTR("trigger")) == 0) {
uint8_t id = 0;
::Focus.read(id);
play(id);
}
return EventHandlerResult::EVENT_CONSUMED;
}
void DynamicMacros::reserve_storage(uint16_t size) {
storage_base_ = ::EEPROMSettings.requestSlice(size);
storage_size_ = size;
updateDynamicMacroCache();
}
}
}
kaleidoscope::plugin::DynamicMacros DynamicMacros;