aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--quantum/dynamic_macro.h226
-rw-r--r--readme.md43
2 files changed, 269 insertions, 0 deletions
diff --git a/quantum/dynamic_macro.h b/quantum/dynamic_macro.h
new file mode 100644
index 000000000..a3ad61bc7
--- /dev/null
+++ b/quantum/dynamic_macro.h
@@ -0,0 +1,226 @@
1/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */
2#ifndef DYNAMIC_MACROS_H
3#define DYNAMIC_MACROS_H
4
5#include "action_layer.h"
6
7#ifndef DYNAMIC_MACRO_SIZE
8/* May be overridden with a custom value. Be aware that the effective
9 * macro length is half of this value: each keypress is recorded twice
10 * because of the down-event and up-event. This is not a bug, it's the
11 * intended behavior. */
12#define DYNAMIC_MACRO_SIZE 256
13#endif
14
15/* DYNAMIC_MACRO_RANGE must be set as the last element of user's
16 * "planck_keycodes" enum prior to including this header. This allows
17 * us to 'extend' it.
18 */
19enum dynamic_macro_keycodes {
20 DYN_REC_START1 = DYNAMIC_MACRO_RANGE,
21 DYN_REC_START2,
22 DYN_MACRO_PLAY1,
23 DYN_MACRO_PLAY2,
24};
25
26/* Blink the LEDs to notify the user about some event. */
27void dynamic_macro_led_blink(void)
28{
29 backlight_toggle();
30 _delay_ms(100);
31 backlight_toggle();
32}
33
34/**
35 * Start recording of the dynamic macro.
36 *
37 * @param[out] macro_pointer The new macro buffer iterator.
38 * @param[in] macro_buffer The macro buffer used to initialize macro_pointer.
39 */
40void dynamic_macro_record_start(
41 keyrecord_t **macro_pointer, keyrecord_t *macro_buffer)
42{
43 dynamic_macro_led_blink();
44
45 clear_keyboard();
46 layer_clear();
47 *macro_pointer = macro_buffer;
48}
49
50/**
51 * Play the dynamic macro.
52 *
53 * @param macro_buffer[in] The beginning of the macro buffer being played.
54 * @param macro_end[in] The element after the last macro buffer element.
55 * @param direction[in] Either +1 or -1, which way to iterate the buffer.
56 */
57void dynamic_macro_play(
58 keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_t direction)
59{
60 uint32_t saved_layer_state = layer_state;
61
62 clear_keyboard();
63 layer_clear();
64
65 while (macro_buffer != macro_end) {
66 process_record(macro_buffer);
67 macro_buffer += direction;
68 }
69
70 clear_keyboard();
71
72 layer_state = saved_layer_state;
73}
74
75/**
76 * Record a single key in a dynamic macro.
77 *
78 * @param macro_pointer[in,out] The current buffer position.
79 * @param macro_end2[in] The end of the other macro which shouldn't be overwritten.
80 * @param direction[in] Either +1 or -1, which way to iterate the buffer.
81 * @param record[in] The current keypress.
82 */
83void dynamic_macro_record_key(
84 keyrecord_t **macro_pointer,
85 keyrecord_t *macro_end2,
86 int8_t direction,
87 keyrecord_t *record)
88{
89 if (*macro_pointer + direction != macro_end2) {
90 **macro_pointer = *record;
91 *macro_pointer += direction;
92 } else {
93 /* Notify about the end of buffer. The blinks are paired
94 * because they should happen on both down and up events. */
95 backlight_toggle();
96 }
97}
98
99/**
100 * End recording of the dynamic macro. Essentially just update the
101 * pointer to the end of the macro.
102 */
103void dynamic_macro_record_end(keyrecord_t *macro_pointer, keyrecord_t **macro_end)
104{
105 dynamic_macro_led_blink();
106
107 *macro_end = macro_pointer;
108}
109
110/* Handle the key events related to the dynamic macros. Should be
111 * called from process_record_user() like this:
112 *
113 * bool process_record_user(uint16_t keycode, keyrecord_t *record) {
114 * if (!process_record_dynamic_macro(keycode, record)) {
115 * return false;
116 * }
117 * <...THE REST OF THE FUNCTION...>
118 * }
119 */
120bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t *record)
121{
122 /* Both macros use the same buffer but read/write on different
123 * ends of it.
124 *
125 * Macro1 is written left-to-right starting from the beginning of
126 * the buffer.
127 *
128 * Macro2 is written right-to-left starting from the end of the
129 * buffer.
130 *
131 * &macro_buffer macro_end
132 * v v
133 * +------------------------------------------------------------+
134 * |>>>>>> MACRO1 >>>>>>| |<<<<<<<<<<<<< MACRO2 <<<<<<<<<<<<<|
135 * +------------------------------------------------------------+
136 * ^ ^
137 * r_macro_end r_macro_buffer
138 *
139 * During the recording when one macro encounters the end of the
140 * other macro, the recording is stopped. Apart from this, there
141 * are no arbitrary limits for the macros' length in relation to
142 * each other: for example one can either have two medium sized
143 * macros or one long macro and one short macro. Or even one empty
144 * and one using the whole buffer.
145 */
146 static keyrecord_t macro_buffer[DYNAMIC_MACRO_SIZE];
147
148 /* Pointer to the first buffer element after the first macro.
149 * Initially points to the very beginning of the buffer since the
150 * macro is empty. */
151 static keyrecord_t *macro_end = macro_buffer;
152
153 /* The other end of the macro buffer. Serves as the beginning of
154 * the second macro. */
155 static keyrecord_t *const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1;
156
157 /* Like macro_end but for the second macro. */
158 static keyrecord_t *r_macro_end = r_macro_buffer;
159
160 /* A persistent pointer to the current macro position (iterator)
161 * used during the recording. */
162 static keyrecord_t *macro_pointer = NULL;
163
164 /* 0 - no macro is being recorded right now
165 * 1,2 - either macro 1 or 2 is being recorded */
166 static uint8_t macro_id = 0;
167
168 if (macro_id == 0) {
169 /* No macro recording in progress. */
170 if (!record->event.pressed) {
171 switch (keycode) {
172 case DYN_REC_START1:
173 dynamic_macro_record_start(&macro_pointer, macro_buffer);
174 macro_id = 1;
175 return false;
176 case DYN_REC_START2:
177 dynamic_macro_record_start(&macro_pointer, r_macro_buffer);
178 macro_id = 2;
179 return false;
180 case DYN_MACRO_PLAY1:
181 dynamic_macro_play(macro_buffer, macro_end, +1);
182 return false;
183 case DYN_MACRO_PLAY2:
184 dynamic_macro_play(r_macro_buffer, r_macro_end, -1);
185 return false;
186 }
187 }
188 } else {
189 /* A macro is being recorded right now. */
190 switch (keycode) {
191 case MO(_DYN):
192 /* Use the layer key used to access the macro recording as
193 * a stop button. */
194 if (record->event.pressed) { /* Ignore the initial release
195 * just after the recoding
196 * starts. */
197 switch (macro_id) {
198 case 1:
199 dynamic_macro_record_end(macro_pointer, &macro_end);
200 break;
201 case 2:
202 dynamic_macro_record_end(macro_pointer, &r_macro_end);
203 break;
204 }
205 macro_id = 0;
206 }
207 return false;
208 default:
209 /* Store the key in the macro buffer and process it normally. */
210 switch (macro_id) {
211 case 1:
212 dynamic_macro_record_key(&macro_pointer, r_macro_end, +1, record);
213 break;
214 case 2:
215 dynamic_macro_record_key(&macro_pointer, macro_end, -1, record);
216 break;
217 }
218 return true;
219 break;
220 }
221 }
222
223 return true;
224}
225
226#endif
diff --git a/readme.md b/readme.md
index 8c07a5d1f..119995a5c 100644
--- a/readme.md
+++ b/readme.md
@@ -695,6 +695,49 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
695 695
696And then, to assign this macro to a key on your keyboard layout, you just use `M(0)` on the key you want to press for copy/paste. 696And then, to assign this macro to a key on your keyboard layout, you just use `M(0)` on the key you want to press for copy/paste.
697 697
698## Dynamic macros: record and replay macros in runtime
699
700In addition to the static macros described above, you may enable the dynamic macros which you may record while writing. They are forgotten as soon as the keyboard is unplugged. Only two such macros may be stored at the same time, with the total length of 128 keypresses.
701
702To enable them, first add a new element to the `planck_keycodes` enum -- `DYNAMIC_MACRO_RANGE`:
703
704 enum planck_keycodes {
705 QWERTY = SAFE_RANGE,
706 COLEMAK,
707 DVORAK,
708 PLOVER,
709 LOWER,
710 RAISE,
711 BACKLIT,
712 EXT_PLV,
713 DYNAMIC_MACRO_RANGE,
714 };
715
716Afterwards create a new layer called `_DYN`:
717
718 #define _DYN 6 /* almost any other free number should be ok */
719
720Below these two modifications include the `dynamic_macro.h` header:
721
722 #include "dynamic_macro.h"`
723
724Then define the `_DYN` layer with the following keys: `DYN_REC_START1`, `DYN_REC_PLAY1`,`DYN_REC_START2` and `DYN_REC_PLAY2`. It may also contain other keys, it doesn't matter apart from the fact that you won't be able to record these keys in the dynamic macros.
725
726 [_DYN]= {
727 {_______, DYN_REC_START1, DYN_MACRO_PLAY1, _______, _______, _______, _______, _______, _______, _______, _______, _______},
728 {_______, DYN_REC_START2, DYN_MACRO_PLAY2, _______, _______, _______, _______, _______, _______, _______, _______, _______},
729 {_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______},
730 {_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______}
731 },
732
733Add the following code to the very beginning of your `process_record_user()` function:
734
735 if (!process_record_dynamic_macro(keycode, record)) {
736 return false;
737 }
738
739The usage should be pretty self-explanatory. For the details, please read the comments in the `dynamic_macro.h` header.
740
698## Additional keycode aliases for software-implemented layouts (Colemak, Dvorak, etc) 741## Additional keycode aliases for software-implemented layouts (Colemak, Dvorak, etc)
699 742
700Everything is assuming you're in Qwerty (in software) by default, but there is built-in support for using a Colemak or Dvorak layout by including this at the top of your keymap: 743Everything is assuming you're in Qwerty (in software) by default, but there is built-in support for using a Colemak or Dvorak layout by including this at the top of your keymap: