1 #include QMK_KEYBOARD_H
4 void dynamic_macro_init(void) {
5 /* zero out macro blocks */
6 memset(&dynamic_macros, 0, DYNAMIC_MACRO_COUNT * sizeof(dynamic_macro_t));
9 /* Blink the LEDs to notify the user about some event. */
10 void dynamic_macro_led_blink(void) {
11 #ifdef BACKLIGHT_ENABLE
16 led_set(host_keyboard_leds() ^ 0xFF);
18 led_set(host_keyboard_leds());
23 * Start recording of the dynamic macro.
25 * @param macro_id[in] The id of macro to be recorded
27 void dynamic_macro_record_start(uint8_t macro_id) {
28 dprintf("dynamic macro recording: started for slot %d\n", macro_id);
30 dynamic_macro_led_blink();
35 dynamic_macros[macro_id].length = 0;
39 * Play the dynamic macro.
41 * @param macro_id[in] The id of macro to be played
43 void dynamic_macro_play(uint8_t macro_id) {
44 dprintf("dynamic macro: slot %d playback, length %d\n", macro_id, dynamic_macros[macro_id].length);
46 uint32_t saved_layer_state = layer_state;
51 for (uint8_t i = 0; i < dynamic_macros[macro_id].length; ++i) {
52 process_record(&dynamic_macros[macro_id].events[i]);
57 layer_state = saved_layer_state;
61 * Record a single key in a dynamic macro.
63 * @param macro_id[in] The start of the used macro buffer.
64 * @param record[in] The current keypress.
66 void dynamic_macro_record_key(uint8_t macro_id, keyrecord_t* record) {
67 dynamic_macro_t* macro = &dynamic_macros[macro_id];
68 uint8_t length = macro->length;
70 /* If we've just started recording, ignore all the key releases. */
71 if (!record->event.pressed && length == 0) {
72 dprintln("dynamic macro: ignoring a leading key-up event");
76 if (length < DYNAMIC_MACRO_SIZE) {
77 macro->events[length] = *record;
78 macro->length = ++length;
80 dynamic_macro_led_blink();
83 dprintf("dynamic macro: slot %d length: %d/%d\n", macro_id, length, DYNAMIC_MACRO_SIZE);
87 * End recording of the dynamic macro. Essentially just update the
88 * pointer to the end of the macro.
90 void dynamic_macro_record_end(uint8_t macro_id) {
91 dynamic_macro_led_blink();
93 dynamic_macro_t* macro = &dynamic_macros[macro_id];
94 uint8_t length = macro->length;
96 keyrecord_t* events_begin = &(macro->events[0]);
97 keyrecord_t* events_pointer = &(macro->events[length - 1]);
99 dprintf("dynamic_macro: macro length before trimming: %d\n", macro->length);
100 while (events_pointer != events_begin && (events_pointer)->event.pressed) {
101 dprintln("dynamic macro: trimming a trailing key-down event");
106 #ifdef DYNAMIC_MACRO_EEPROM_STORAGE
107 macro->checksum = dynamic_macro_calc_crc(macro);
108 dynamic_macro_save_eeprom(macro_id);
111 dprintf("dynamic macro: slot %d saved, length: %d\n", macro_id, length);
114 /* Handle the key events related to the dynamic macros. Should be
115 * called from process_record_user() like this:
117 * bool process_record_user(uint16_t keycode, keyrecord_t *record) {
118 * if (!process_record_dynamic_macro(keycode, record)) {
121 * <...THE REST OF THE FUNCTION...>
124 bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t* record) {
125 /* 0 to DYNAMIC_MACRO_COUNT -1 - macro macro_id is being recorded */
126 static uint8_t macro_id = 255;
127 static uint8_t recording_state = STATE_NOT_RECORDING;
129 if (STATE_NOT_RECORDING == recording_state) {
130 /* Program key pressed to request programming mode */
131 if (keycode == DYN_MACRO_PROG && record->event.pressed) {
132 dynamic_macro_led_blink();
134 recording_state = STATE_RECORD_KEY_PRESSED;
135 dprintf("dynamic macro: programming key pressed, waiting for macro slot selection. %d\n", recording_state);
139 /* Macro key pressed to request macro playback */
140 if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
141 dynamic_macro_play(keycode - DYN_MACRO_KEY1);
146 /* Non-dynamic macro key, process it elsewhere. */
148 } else if (STATE_RECORD_KEY_PRESSED == recording_state) {
149 /* Program key pressed again before a macro selector key, cancel macro recording.
150 Blink leds to indicate cancelation. */
151 if (keycode == DYN_MACRO_PROG && record->event.pressed) {
152 dynamic_macro_led_blink();
154 recording_state = STATE_NOT_RECORDING;
155 dprintf("dynamic macro: programming key pressed, programming mode canceled. %d\n", recording_state);
158 } else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
159 macro_id = keycode - DYN_MACRO_KEY1;
161 /* Macro slot selected, enter recording state. */
162 recording_state = STATE_CURRENTLY_RECORDING;
163 dynamic_macro_record_start(macro_id);
167 /* Ignore any non-macro key press while in RECORD_KEY_PRESSED state. */
169 } else if (STATE_CURRENTLY_RECORDING == recording_state) {
170 /* Program key pressed to request end of macro recording. */
171 if (keycode == DYN_MACRO_PROG && record->event.pressed) {
172 dynamic_macro_record_end(macro_id);
173 recording_state = STATE_NOT_RECORDING;
177 /* Don't record other macro key presses. */
178 else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
179 dprintln("dynamic macro: playback key ignored in programming mode.");
182 /* Non-macro keypress that should be recorded */
184 dynamic_macro_record_key(macro_id, record);
186 /* Don't output recorded keypress. */
195 # include <util/crc16.h>
196 uint16_t dynamic_macro_calc_crc(dynamic_macro_t* macro) {
198 uint8_t* data = (uint8_t*)macro;
200 for (uint16_t i = 0; i < DYNAMIC_MACRO_CRC_LENGTH; ++i) {
201 crc = _crc16_update(crc, *(data++));
207 inline void* dynamic_macro_eeprom_macro_addr(uint8_t macro_id) {
208 return DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR + sizeof(dynamic_macro_t) * macro_id;
211 bool dynamic_macro_header_correct(void) {
212 return eeprom_read_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR) == DYNAMIC_MACRO_EEPROM_MAGIC;
215 void dynamic_macro_load_eeprom_all(void) {
216 if (!dynamic_macro_header_correct()) {
217 dprintf("dynamic_macro: eeprom header not valid, not restoring macros.\n");
221 for (uint8_t i = 0; i < DYNAMIC_MACRO_COUNT; ++i) {
222 dynamic_macro_load_eeprom(i);
226 void dynamic_macro_load_eeprom(uint8_t macro_id) {
227 dynamic_macro_t* dst = &dynamic_macros[macro_id];
229 eeprom_read_block(dst, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t));
231 /* Validate checksum, ifchecksum is NOT valid for macro, set its length to 0 to prevent its use. */
232 if (dynamic_macro_calc_crc(dst) != dst->checksum) {
233 dprintf("dynamic macro: slot %d not loaded, checksum mismatch\n", macro_id);
239 dprintf("dynamic macro: slot %d loaded from eeprom, checksum okay\n", macro_id);
242 void dynamic_macro_save_eeprom(uint8_t macro_id) {
243 if (!dynamic_macro_header_correct()) {
244 eeprom_write_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR, DYNAMIC_MACRO_EEPROM_MAGIC);
245 dprintf("dynamic macro: writing magic eeprom header\n");
248 dynamic_macro_t* src = &dynamic_macros[macro_id];
250 eeprom_update_block(src, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t));
251 dprintf("dynamic macro: slot %d saved to eeprom\n", macro_id);