qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

process_dynamic_macro.c (11786B)


      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
     31 void 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 #ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
     93 static layer_state_t dm1_layer_state;
     94 static layer_state_t dm2_layer_state;
     95 #endif
     96 
     97 /**
     98  * Start recording of the dynamic macro.
     99  *
    100  * @param[out] macro_pointer The new macro buffer iterator.
    101  * @param[in]  macro_buffer  The macro buffer used to initialize macro_pointer.
    102  */
    103 void dynamic_macro_record_start(keyrecord_t **macro_pointer, keyrecord_t *macro_buffer, int8_t direction) {
    104     dprintln("dynamic macro recording: started");
    105 
    106     dynamic_macro_record_start_kb(direction);
    107 
    108 #ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
    109     if (direction == 1) {
    110         dm1_layer_state = layer_state;
    111     } else if (direction == -1) {
    112         dm2_layer_state = layer_state;
    113     }
    114 #else
    115     layer_clear();
    116 #endif
    117     clear_keyboard();
    118     *macro_pointer = macro_buffer;
    119 }
    120 
    121 /**
    122  * Play the dynamic macro.
    123  *
    124  * @param macro_buffer[in] The beginning of the macro buffer being played.
    125  * @param macro_end[in]    The element after the last macro buffer element.
    126  * @param direction[in]    Either +1 or -1, which way to iterate the buffer.
    127  */
    128 void dynamic_macro_play(keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_t direction) {
    129     dprintf("dynamic macro: slot %d playback\n", DYNAMIC_MACRO_CURRENT_SLOT());
    130 
    131     layer_state_t saved_layer_state = layer_state;
    132 
    133     clear_keyboard();
    134 #ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
    135     if (direction == 1) {
    136         layer_state_set(dm1_layer_state);
    137     } else if (direction == -1) {
    138         layer_state_set(dm2_layer_state);
    139     }
    140 #else
    141     layer_clear();
    142 #endif
    143 
    144     while (macro_buffer != macro_end) {
    145         process_record(macro_buffer);
    146         macro_buffer += direction;
    147 #ifdef DYNAMIC_MACRO_DELAY
    148         wait_ms(DYNAMIC_MACRO_DELAY);
    149 #endif
    150     }
    151 
    152     clear_keyboard();
    153 
    154     layer_state_set(saved_layer_state);
    155 
    156     dynamic_macro_play_kb(direction);
    157 }
    158 
    159 /**
    160  * Record a single key in a dynamic macro.
    161  *
    162  * @param macro_buffer[in] The start of the used macro buffer.
    163  * @param macro_pointer[in,out] The current buffer position.
    164  * @param macro2_end[in] The end of the other macro.
    165  * @param direction[in]  Either +1 or -1, which way to iterate the buffer.
    166  * @param record[in]     The current keypress.
    167  */
    168 void dynamic_macro_record_key(keyrecord_t *macro_buffer, keyrecord_t **macro_pointer, keyrecord_t *macro2_end, int8_t direction, keyrecord_t *record) {
    169     /* If we've just started recording, ignore all the key releases. */
    170     if (!record->event.pressed && *macro_pointer == macro_buffer) {
    171         dprintln("dynamic macro: ignoring a leading key-up event");
    172         return;
    173     }
    174 
    175     /* The other end of the other macro is the last buffer element it
    176      * is safe to use before overwriting the other macro.
    177      */
    178     if (*macro_pointer - direction != macro2_end) {
    179         **macro_pointer = *record;
    180         *macro_pointer += direction;
    181     }
    182     dynamic_macro_record_key_kb(direction, record);
    183 
    184     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));
    185 }
    186 
    187 /**
    188  * End recording of the dynamic macro. Essentially just update the
    189  * pointer to the end of the macro.
    190  */
    191 void dynamic_macro_record_end(keyrecord_t *macro_buffer, keyrecord_t *macro_pointer, int8_t direction, keyrecord_t **macro_end) {
    192     dynamic_macro_record_end_kb(direction);
    193 
    194     /* Do not save the keys being held when stopping the recording,
    195      * i.e. the keys used to access the layer DM_RSTP is on.
    196      */
    197     while (macro_pointer != macro_buffer && (macro_pointer - direction)->event.pressed) {
    198         dprintln("dynamic macro: trimming a trailing key-down event");
    199         macro_pointer -= direction;
    200     }
    201 
    202     dprintf("dynamic macro: slot %d saved, length: %d\n", DYNAMIC_MACRO_CURRENT_SLOT(), DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, macro_pointer));
    203 
    204     *macro_end = macro_pointer;
    205 }
    206 
    207 /* Both macros use the same buffer but read/write on different
    208  * ends of it.
    209  *
    210  * Macro1 is written left-to-right starting from the beginning of
    211  * the buffer.
    212  *
    213  * Macro2 is written right-to-left starting from the end of the
    214  * buffer.
    215  *
    216  * &macro_buffer   macro_end
    217  *  v                   v
    218  * +------------------------------------------------------------+
    219  * |>>>>>> MACRO1 >>>>>>      <<<<<<<<<<<<< MACRO2 <<<<<<<<<<<<<|
    220  * +------------------------------------------------------------+
    221  *                           ^                                 ^
    222  *                         r_macro_end                  r_macro_buffer
    223  *
    224  * During the recording when one macro encounters the end of the
    225  * other macro, the recording is stopped. Apart from this, there
    226  * are no arbitrary limits for the macros' length in relation to
    227  * each other: for example one can either have two medium sized
    228  * macros or one long macro and one short macro. Or even one empty
    229  * and one using the whole buffer.
    230  */
    231 static keyrecord_t macro_buffer[DYNAMIC_MACRO_SIZE];
    232 
    233 /* Pointer to the first buffer element after the first macro.
    234  * Initially points to the very beginning of the buffer since the
    235  * macro is empty. */
    236 static keyrecord_t *macro_end = macro_buffer;
    237 
    238 /* The other end of the macro buffer. Serves as the beginning of
    239  * the second macro. */
    240 static keyrecord_t *const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1;
    241 
    242 /* Like macro_end but for the second macro. */
    243 static keyrecord_t *r_macro_end = macro_buffer + DYNAMIC_MACRO_SIZE - 1;
    244 
    245 /* A persistent pointer to the current macro position (iterator)
    246  * used during the recording. */
    247 static keyrecord_t *macro_pointer = NULL;
    248 
    249 /* 0   - no macro is being recorded right now
    250  * 1,2 - either macro 1 or 2 is being recorded */
    251 static uint8_t macro_id = 0;
    252 
    253 /**
    254  * If a dynamic macro is currently being recorded, stop recording.
    255  */
    256 void dynamic_macro_stop_recording(void) {
    257     switch (macro_id) {
    258         case 1:
    259             dynamic_macro_record_end(macro_buffer, macro_pointer, +1, &macro_end);
    260             break;
    261         case 2:
    262             dynamic_macro_record_end(r_macro_buffer, macro_pointer, -1, &r_macro_end);
    263             break;
    264     }
    265     macro_id = 0;
    266 }
    267 
    268 /* Handle the key events related to the dynamic macros.
    269  */
    270 bool process_dynamic_macro(uint16_t keycode, keyrecord_t *record) {
    271     if (macro_id == 0) {
    272         /* No macro recording in progress. */
    273         if (!record->event.pressed) {
    274             switch (keycode) {
    275                 case QK_DYNAMIC_MACRO_RECORD_START_1:
    276                     dynamic_macro_record_start(&macro_pointer, macro_buffer, +1);
    277                     macro_id = 1;
    278                     return false;
    279                 case QK_DYNAMIC_MACRO_RECORD_START_2:
    280                     dynamic_macro_record_start(&macro_pointer, r_macro_buffer, -1);
    281                     macro_id = 2;
    282                     return false;
    283                 case QK_DYNAMIC_MACRO_PLAY_1:
    284                     dynamic_macro_play(macro_buffer, macro_end, +1);
    285                     return false;
    286                 case QK_DYNAMIC_MACRO_PLAY_2:
    287                     dynamic_macro_play(r_macro_buffer, r_macro_end, -1);
    288                     return false;
    289             }
    290         }
    291     } else {
    292         /* A macro is being recorded right now. */
    293         switch (keycode) {
    294             case QK_DYNAMIC_MACRO_RECORD_START_1:
    295             case QK_DYNAMIC_MACRO_RECORD_START_2:
    296             case QK_DYNAMIC_MACRO_RECORD_STOP:
    297                 /* Stop the macro recording. */
    298                 if (record->event.pressed ^ (keycode != QK_DYNAMIC_MACRO_RECORD_STOP)) { /* Ignore the initial release
    299                                                                                           * just after the recording
    300                                                                                           * starts for DM_RSTP. */
    301                     dynamic_macro_stop_recording();
    302                 }
    303                 return false;
    304 #ifdef DYNAMIC_MACRO_NO_NESTING
    305             case QK_DYNAMIC_MACRO_PLAY_1:
    306             case QK_DYNAMIC_MACRO_PLAY_2:
    307                 dprintln("dynamic macro: ignoring macro play key while recording");
    308                 return false;
    309 #endif
    310             default:
    311                 if (dynamic_macro_valid_key_kb(keycode, record)) {
    312                     /* Store the key in the macro buffer and process it normally. */
    313                     switch (macro_id) {
    314                         case 1:
    315                             dynamic_macro_record_key(macro_buffer, &macro_pointer, r_macro_end, +1, record);
    316                             break;
    317                         case 2:
    318                             dynamic_macro_record_key(r_macro_buffer, &macro_pointer, macro_end, -1, record);
    319                             break;
    320                     }
    321                 }
    322                 return true;
    323                 break;
    324         }
    325     }
    326 
    327     return true;
    328 }