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 | ||
