Parent directory

process_dynamic_macro.c

11241 bytes
  1/* Copyright 2016 Jack Humbert
  2 * Copyright 2019 Drashna Jael're (@drashna, aka Christopher Courtney)
  3 *
  4 * This program is free software: you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation, either version 2 of the License, or
  7 * (at your option) any later version.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU General Public License
 15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16 */
 17
 18/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */
 19#include "process_dynamic_macro.h"
 20#include <stddef.h>
 21#include "action_layer.h"
 22#include "keycodes.h"
 23#include "debug.h"
 24#include "wait.h"
 25
 26#ifdef BACKLIGHT_ENABLE
 27#    include "backlight.h"
 28#endif
 29
 30// default feedback method
 31void dynamic_macro_led_blink(void) {
 32#ifdef BACKLIGHT_ENABLE
 33    backlight_toggle();
 34    wait_ms(100);
 35    backlight_toggle();
 36#endif
 37}
 38
 39/* User hooks for Dynamic Macros */
 40
 41__attribute__((weak)) bool dynamic_macro_record_start_kb(int8_t direction) {
 42    return dynamic_macro_record_start_user(direction);
 43}
 44
 45__attribute__((weak)) bool dynamic_macro_record_start_user(int8_t direction) {
 46    dynamic_macro_led_blink();
 47    return true;
 48}
 49
 50__attribute__((weak)) bool dynamic_macro_play_kb(int8_t direction) {
 51    return dynamic_macro_play_user(direction);
 52}
 53
 54__attribute__((weak)) bool dynamic_macro_play_user(int8_t direction) {
 55    dynamic_macro_led_blink();
 56    return true;
 57}
 58
 59__attribute__((weak)) bool dynamic_macro_record_key_kb(int8_t direction, keyrecord_t *record) {
 60    return dynamic_macro_record_key_user(direction, record);
 61}
 62
 63__attribute__((weak)) bool dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record) {
 64    dynamic_macro_led_blink();
 65    return true;
 66}
 67
 68__attribute__((weak)) bool dynamic_macro_record_end_kb(int8_t direction) {
 69    return dynamic_macro_record_end_user(direction);
 70}
 71
 72__attribute__((weak)) bool dynamic_macro_record_end_user(int8_t direction) {
 73    dynamic_macro_led_blink();
 74    return true;
 75}
 76
 77__attribute__((weak)) bool dynamic_macro_valid_key_kb(uint16_t keycode, keyrecord_t *record) {
 78    return dynamic_macro_valid_key_user(keycode, record);
 79}
 80
 81__attribute__((weak)) bool dynamic_macro_valid_key_user(uint16_t keycode, keyrecord_t *record) {
 82    return true;
 83}
 84
 85/* Convenience macros used for retrieving the debug info. All of them
 86 * need a `direction` variable accessible at the call site.
 87 */
 88#define DYNAMIC_MACRO_CURRENT_SLOT() (direction > 0 ? 1 : 2)
 89#define DYNAMIC_MACRO_CURRENT_LENGTH(BEGIN, POINTER) ((int)(direction * ((POINTER) - (BEGIN))))
 90#define DYNAMIC_MACRO_CURRENT_CAPACITY(BEGIN, END2) ((int)(direction * ((END2) - (BEGIN)) + 1))
 91
 92/**
 93 * Start recording of the dynamic macro.
 94 *
 95 * @param[out] macro_pointer The new macro buffer iterator.
 96 * @param[in]  macro_buffer  The macro buffer used to initialize macro_pointer.
 97 */
 98void dynamic_macro_record_start(keyrecord_t **macro_pointer, keyrecord_t *macro_buffer, int8_t direction) {
 99    dprintln("dynamic macro recording: started");
100
101    dynamic_macro_record_start_kb(direction);
102
103    clear_keyboard();
104    layer_clear();
105    *macro_pointer = macro_buffer;
106}
107
108/**
109 * Play the dynamic macro.
110 *
111 * @param macro_buffer[in] The beginning of the macro buffer being played.
112 * @param macro_end[in]    The element after the last macro buffer element.
113 * @param direction[in]    Either +1 or -1, which way to iterate the buffer.
114 */
115void dynamic_macro_play(keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_t direction) {
116    dprintf("dynamic macro: slot %d playback\n", DYNAMIC_MACRO_CURRENT_SLOT());
117
118    layer_state_t saved_layer_state = layer_state;
119
120    clear_keyboard();
121    layer_clear();
122
123    while (macro_buffer != macro_end) {
124        process_record(macro_buffer);
125        macro_buffer += direction;
126#ifdef DYNAMIC_MACRO_DELAY
127        wait_ms(DYNAMIC_MACRO_DELAY);
128#endif
129    }
130
131    clear_keyboard();
132
133    layer_state_set(saved_layer_state);
134
135    dynamic_macro_play_kb(direction);
136}
137
138/**
139 * Record a single key in a dynamic macro.
140 *
141 * @param macro_buffer[in] The start of the used macro buffer.
142 * @param macro_pointer[in,out] The current buffer position.
143 * @param macro2_end[in] The end of the other macro.
144 * @param direction[in]  Either +1 or -1, which way to iterate the buffer.
145 * @param record[in]     The current keypress.
146 */
147void dynamic_macro_record_key(keyrecord_t *macro_buffer, keyrecord_t **macro_pointer, keyrecord_t *macro2_end, int8_t direction, keyrecord_t *record) {
148    /* If we've just started recording, ignore all the key releases. */
149    if (!record->event.pressed && *macro_pointer == macro_buffer) {
150        dprintln("dynamic macro: ignoring a leading key-up event");
151        return;
152    }
153
154    /* The other end of the other macro is the last buffer element it
155     * is safe to use before overwriting the other macro.
156     */
157    if (*macro_pointer - direction != macro2_end) {
158        **macro_pointer = *record;
159        *macro_pointer += direction;
160    }
161    dynamic_macro_record_key_kb(direction, record);
162
163    dprintf("dynamic macro: slot %d length: %d/%d\n", DYNAMIC_MACRO_CURRENT_SLOT(), DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, *macro_pointer), DYNAMIC_MACRO_CURRENT_CAPACITY(macro_buffer, macro2_end));
164}
165
166/**
167 * End recording of the dynamic macro. Essentially just update the
168 * pointer to the end of the macro.
169 */
170void dynamic_macro_record_end(keyrecord_t *macro_buffer, keyrecord_t *macro_pointer, int8_t direction, keyrecord_t **macro_end) {
171    dynamic_macro_record_end_kb(direction);
172
173    /* Do not save the keys being held when stopping the recording,
174     * i.e. the keys used to access the layer DM_RSTP is on.
175     */
176    while (macro_pointer != macro_buffer && (macro_pointer - direction)->event.pressed) {
177        dprintln("dynamic macro: trimming a trailing key-down event");
178        macro_pointer -= direction;
179    }
180
181    dprintf("dynamic macro: slot %d saved, length: %d\n", DYNAMIC_MACRO_CURRENT_SLOT(), DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, macro_pointer));
182
183    *macro_end = macro_pointer;
184}
185
186/* Both macros use the same buffer but read/write on different
187 * ends of it.
188 *
189 * Macro1 is written left-to-right starting from the beginning of
190 * the buffer.
191 *
192 * Macro2 is written right-to-left starting from the end of the
193 * buffer.
194 *
195 * &macro_buffer   macro_end
196 *  v                   v
197 * +------------------------------------------------------------+
198 * |>>>>>> MACRO1 >>>>>>      <<<<<<<<<<<<< MACRO2 <<<<<<<<<<<<<|
199 * +------------------------------------------------------------+
200 *                           ^                                 ^
201 *                         r_macro_end                  r_macro_buffer
202 *
203 * During the recording when one macro encounters the end of the
204 * other macro, the recording is stopped. Apart from this, there
205 * are no arbitrary limits for the macros' length in relation to
206 * each other: for example one can either have two medium sized
207 * macros or one long macro and one short macro. Or even one empty
208 * and one using the whole buffer.
209 */
210static keyrecord_t macro_buffer[DYNAMIC_MACRO_SIZE];
211
212/* Pointer to the first buffer element after the first macro.
213 * Initially points to the very beginning of the buffer since the
214 * macro is empty. */
215static keyrecord_t *macro_end = macro_buffer;
216
217/* The other end of the macro buffer. Serves as the beginning of
218 * the second macro. */
219static keyrecord_t *const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1;
220
221/* Like macro_end but for the second macro. */
222static keyrecord_t *r_macro_end = macro_buffer + DYNAMIC_MACRO_SIZE - 1;
223
224/* A persistent pointer to the current macro position (iterator)
225 * used during the recording. */
226static keyrecord_t *macro_pointer = NULL;
227
228/* 0   - no macro is being recorded right now
229 * 1,2 - either macro 1 or 2 is being recorded */
230static uint8_t macro_id = 0;
231
232/**
233 * If a dynamic macro is currently being recorded, stop recording.
234 */
235void dynamic_macro_stop_recording(void) {
236    switch (macro_id) {
237        case 1:
238            dynamic_macro_record_end(macro_buffer, macro_pointer, +1, &macro_end);
239            break;
240        case 2:
241            dynamic_macro_record_end(r_macro_buffer, macro_pointer, -1, &r_macro_end);
242            break;
243    }
244    macro_id = 0;
245}
246
247/* Handle the key events related to the dynamic macros.
248 */
249bool process_dynamic_macro(uint16_t keycode, keyrecord_t *record) {
250    if (macro_id == 0) {
251        /* No macro recording in progress. */
252        if (!record->event.pressed) {
253            switch (keycode) {
254                case QK_DYNAMIC_MACRO_RECORD_START_1:
255                    dynamic_macro_record_start(&macro_pointer, macro_buffer, +1);
256                    macro_id = 1;
257                    return false;
258                case QK_DYNAMIC_MACRO_RECORD_START_2:
259                    dynamic_macro_record_start(&macro_pointer, r_macro_buffer, -1);
260                    macro_id = 2;
261                    return false;
262                case QK_DYNAMIC_MACRO_PLAY_1:
263                    dynamic_macro_play(macro_buffer, macro_end, +1);
264                    return false;
265                case QK_DYNAMIC_MACRO_PLAY_2:
266                    dynamic_macro_play(r_macro_buffer, r_macro_end, -1);
267                    return false;
268            }
269        }
270    } else {
271        /* A macro is being recorded right now. */
272        switch (keycode) {
273            case QK_DYNAMIC_MACRO_RECORD_START_1:
274            case QK_DYNAMIC_MACRO_RECORD_START_2:
275            case QK_DYNAMIC_MACRO_RECORD_STOP:
276                /* Stop the macro recording. */
277                if (record->event.pressed ^ (keycode != QK_DYNAMIC_MACRO_RECORD_STOP)) { /* Ignore the initial release
278                                                                                          * just after the recording
279                                                                                          * starts for DM_RSTP. */
280                    dynamic_macro_stop_recording();
281                }
282                return false;
283#ifdef DYNAMIC_MACRO_NO_NESTING
284            case QK_DYNAMIC_MACRO_PLAY_1:
285            case QK_DYNAMIC_MACRO_PLAY_2:
286                dprintln("dynamic macro: ignoring macro play key while recording");
287                return false;
288#endif
289            default:
290                if (dynamic_macro_valid_key_kb(keycode, record)) {
291                    /* Store the key in the macro buffer and process it normally. */
292                    switch (macro_id) {
293                        case 1:
294                            dynamic_macro_record_key(macro_buffer, &macro_pointer, r_macro_end, +1, record);
295                            break;
296                        case 2:
297                            dynamic_macro_record_key(r_macro_buffer, &macro_pointer, macro_end, -1, record);
298                            break;
299                    }
300                }
301                return true;
302                break;
303        }
304    }
305
306    return true;
307}