-
Notifications
You must be signed in to change notification settings - Fork 262
/
DynamicMacros.cpp
262 lines (220 loc) · 8.04 KB
/
DynamicMacros.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/* DynamicMacros - Dynamic macro support for Kaleidoscope.
* Copyright (C) 2019-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/DynamicMacros.h"
#include <Arduino.h> // for delay, PSTR, F, __FlashStri...
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST, DYNAMIC_MACRO_LAST
#include "kaleidoscope/KeyEvent.h" // for KeyEvent
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for VirtualProps::Storage, Base<>::Storage
#include "kaleidoscope/keyswitch_state.h" // for keyToggledOn
#include "kaleidoscope/plugin/EEPROM-Settings.h" // for EEPROMSettings
// 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
// plugin itself; it's just using the same macro step definitions.
#include "kaleidoscope/plugin/Macros/MacroSteps.h" // for MACRO_ACTION_END, MACRO_ACTION_STEP_E...
namespace kaleidoscope {
namespace plugin {
// =============================================================================
uint8_t DynamicMacros::updateDynamicMacroCache() {
uint16_t pos = storage_base_;
uint8_t current_id = 0;
macro_t macro = MACRO_ACTION_END;
map_[0] = 0;
while (pos < storage_base_ + storage_size_ && current_id < MAX_MACRO_COUNT_) {
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:
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:
pos++;
break;
case MACRO_ACTION_STEP_KEYDOWN:
case MACRO_ACTION_STEP_KEYUP:
case MACRO_ACTION_STEP_TAP:
pos += 2;
break;
case MACRO_ACTION_STEP_TAP_SEQUENCE: {
uint8_t keyCode, flags;
do {
flags = Runtime.storage().read(pos++);
keyCode = Runtime.storage().read(pos++);
} while (!(flags == 0 && keyCode == 0) && (pos < storage_base_ + storage_size_));
break;
}
case MACRO_ACTION_STEP_TAP_CODE_SEQUENCE: {
uint8_t keyCode, flags;
do {
keyCode = Runtime.storage().read(pos++);
} while ((pos < (storage_base_ + storage_size_)) && keyCode != 0);
break;
}
case MACRO_ACTION_END:
map_[++current_id] = pos - storage_base_;
break;
default:
// When we encounter an unknown step type, stop processing. Whatever we
// encounter after is unknown, and there's no guarantee we can parse it
// properly.
return current_id;
}
}
return current_id;
}
// public
void DynamicMacros::play(uint8_t macro_id) {
macro_t macro = MACRO_ACTION_END;
uint8_t interval = 0;
uint16_t pos;
Key key;
// If the requested ID is higher than the number of macros we found during the
// cache update, bail out. Our map beyond `macro_count_` is unreliable.
if (macro_id >= macro_count_)
return;
auto &storage = Runtime.storage();
// Define a lambda function for common key operations to reduce redundancy
auto setKeyAndAction = [this, &key, ¯o, &pos]() {
// Keycode variants of actions don't have flags to set, but we want to make sure
// we're still initializing them properly.
key.setFlags((macro == MACRO_ACTION_STEP_KEYCODEDOWN || macro == MACRO_ACTION_STEP_KEYCODEUP || macro == MACRO_ACTION_STEP_TAPCODE) ? 0
: Runtime.storage().read(pos++));
key.setKeyCode(Runtime.storage().read(pos++));
switch (macro) {
case MACRO_ACTION_STEP_KEYCODEDOWN:
case MACRO_ACTION_STEP_KEYDOWN:
this->press(key);
break;
case MACRO_ACTION_STEP_KEYCODEUP:
case MACRO_ACTION_STEP_KEYUP:
this->release(key);
break;
case MACRO_ACTION_STEP_TAP:
case MACRO_ACTION_STEP_TAPCODE:
this->tap(key);
break;
default:
break;
}
};
pos = storage_base_ + map_[macro_id];
while (pos < storage_base_ + storage_size_) {
switch (macro = Runtime.storage().read(pos++)) {
case MACRO_ACTION_STEP_EXPLICIT_REPORT:
case MACRO_ACTION_STEP_IMPLICIT_REPORT:
case MACRO_ACTION_STEP_SEND_REPORT:
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:
case MACRO_ACTION_STEP_KEYUP:
case MACRO_ACTION_STEP_TAP:
case MACRO_ACTION_STEP_KEYCODEUP:
case MACRO_ACTION_STEP_TAPCODE:
case MACRO_ACTION_STEP_KEYCODEDOWN:
setKeyAndAction();
break;
case MACRO_ACTION_STEP_TAP_SEQUENCE:
case MACRO_ACTION_STEP_TAP_CODE_SEQUENCE: {
bool isKeycodeSequence = macro == MACRO_ACTION_STEP_TAP_CODE_SEQUENCE;
while (true) {
key.setFlags(isKeycodeSequence ? 0 : storage.read(pos++));
key.setKeyCode(storage.read(pos++));
if (key == Key_NoKey || pos >= storage_base_ + storage_size_)
break;
tap(key);
delay(interval);
}
break;
}
case MACRO_ACTION_END:
default:
return;
}
delay(interval);
}
}
bool isDynamicMacrosKey(Key key) {
return (key.getRaw() >= ranges::DYNAMIC_MACRO_FIRST &&
key.getRaw() <= ranges::DYNAMIC_MACRO_LAST);
}
// -----------------------------------------------------------------------------
EventHandlerResult DynamicMacros::onKeyEvent(KeyEvent &event) {
// Ignore everything except DynamicMacros keys
if (!isDynamicMacrosKey(event.key))
return EventHandlerResult::OK;
if (keyToggledOn(event.state)) {
uint8_t macro_id = event.key.getRaw() - ranges::DYNAMIC_MACRO_FIRST;
play(macro_id);
} else {
clear();
}
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult DynamicMacros::onNameQuery() {
return ::Focus.sendName(F("DynamicMacros"));
}
EventHandlerResult DynamicMacros::onFocusEvent(const char *input) {
const char *cmd_map = PSTR("macros.map");
const char *cmd_trigger = PSTR("macros.trigger");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_map, cmd_trigger);
if (::Focus.inputMatchesCommand(input, cmd_map)) {
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() && pos < storage_size_) {
uint8_t b;
::Focus.read(b);
Runtime.storage().update(storage_base_ + pos++, b);
}
Runtime.storage().commit();
macro_count_ = updateDynamicMacroCache();
}
return EventHandlerResult::EVENT_CONSUMED;
} else if (::Focus.inputMatchesCommand(input, cmd_trigger)) {
uint8_t id = 0;
::Focus.read(id);
play(id);
return EventHandlerResult::EVENT_CONSUMED;
}
return EventHandlerResult::OK;
}
// public
void DynamicMacros::reserve_storage(uint16_t size) {
storage_base_ = ::EEPROMSettings.requestSlice(size);
storage_size_ = size;
macro_count_ = updateDynamicMacroCache();
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::DynamicMacros DynamicMacros;