diff options
author | Wojciech Siewierski <wojciech.siewierski@onet.pl> | 2016-08-18 01:37:13 +0200 |
---|---|---|
committer | Wojciech Siewierski <wojciech.siewierski@onet.pl> | 2016-08-18 01:37:13 +0200 |
commit | 39e8e61258b51a2c33a94dd877e983f0b1dae0c1 (patch) | |
tree | a7b2c3409f95cca2fbe58cee7ad12d4f947bb67f /quantum | |
parent | cc7acfb416d446a123d10d2c33c3344f1f684f1b (diff) | |
download | qmk_firmware-39e8e61258b51a2c33a94dd877e983f0b1dae0c1.tar.gz qmk_firmware-39e8e61258b51a2c33a94dd877e983f0b1dae0c1.zip |
Implement the dynamic macros that are recorded in runtime
Diffstat (limited to 'quantum')
-rw-r--r-- | quantum/dynamic_macro.h | 226 |
1 files changed, 226 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 | */ | ||
19 | enum 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. */ | ||
27 | void 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 | */ | ||
40 | void 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 | */ | ||
57 | void 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 | */ | ||
83 | void 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 | */ | ||
103 | void 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 | */ | ||
120 | bool 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 | * ¯o_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(¯o_pointer, macro_buffer); | ||
174 | macro_id = 1; | ||
175 | return false; | ||
176 | case DYN_REC_START2: | ||
177 | dynamic_macro_record_start(¯o_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, ¯o_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(¯o_pointer, r_macro_end, +1, record); | ||
213 | break; | ||
214 | case 2: | ||
215 | dynamic_macro_record_key(¯o_pointer, macro_end, -1, record); | ||
216 | break; | ||
217 | } | ||
218 | return true; | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | return true; | ||
224 | } | ||
225 | |||
226 | #endif | ||