dynamic_macro.c (7867B)
1 #include "omnikeyish.h" 2 #include <string.h> 3 #include "eeprom.h" 4 5 dynamic_macro_t dynamic_macros[DYNAMIC_MACRO_COUNT]; 6 7 void dynamic_macro_init(void) { 8 /* zero out macro blocks */ 9 memset(&dynamic_macros, 0, DYNAMIC_MACRO_COUNT * sizeof(dynamic_macro_t)); 10 } 11 12 /* Blink the LEDs to notify the user about some event. */ 13 void dynamic_macro_led_blink(void) { 14 #ifdef BACKLIGHT_ENABLE 15 backlight_toggle(); 16 wait_ms(100); 17 backlight_toggle(); 18 #else 19 led_set(host_keyboard_leds() ^ 0xFF); 20 wait_ms(100); 21 led_set(host_keyboard_leds()); 22 #endif 23 } 24 25 /** 26 * Start recording of the dynamic macro. 27 * 28 * @param macro_id[in] The id of macro to be recorded 29 */ 30 void dynamic_macro_record_start(uint8_t macro_id) { 31 dprintf("dynamic macro recording: started for slot %d\n", macro_id); 32 33 dynamic_macro_led_blink(); 34 35 clear_keyboard(); 36 layer_clear(); 37 38 dynamic_macros[macro_id].length = 0; 39 } 40 41 /** 42 * Play the dynamic macro. 43 * 44 * @param macro_id[in] The id of macro to be played 45 */ 46 void dynamic_macro_play(uint8_t macro_id) { 47 dprintf("dynamic macro: slot %d playback, length %d\n", macro_id, dynamic_macros[macro_id].length); 48 49 uint32_t saved_layer_state = layer_state; 50 51 clear_keyboard(); 52 layer_clear(); 53 54 for (uint8_t i = 0; i < dynamic_macros[macro_id].length; ++i) { 55 process_record(&dynamic_macros[macro_id].events[i]); 56 } 57 58 clear_keyboard(); 59 60 layer_state = saved_layer_state; 61 } 62 63 /** 64 * Record a single key in a dynamic macro. 65 * 66 * @param macro_id[in] The start of the used macro buffer. 67 * @param record[in] The current keypress. 68 */ 69 void dynamic_macro_record_key(uint8_t macro_id, keyrecord_t* record) { 70 dynamic_macro_t* macro = &dynamic_macros[macro_id]; 71 uint8_t length = macro->length; 72 73 /* If we've just started recording, ignore all the key releases. */ 74 if (!record->event.pressed && length == 0) { 75 dprintln("dynamic macro: ignoring a leading key-up event"); 76 return; 77 } 78 79 if (length < DYNAMIC_MACRO_SIZE) { 80 macro->events[length] = *record; 81 macro->length = ++length; 82 } else { 83 dynamic_macro_led_blink(); 84 } 85 86 dprintf("dynamic macro: slot %d length: %d/%d\n", macro_id, length, DYNAMIC_MACRO_SIZE); 87 } 88 89 /** 90 * End recording of the dynamic macro. Essentially just update the 91 * pointer to the end of the macro. 92 */ 93 void dynamic_macro_record_end(uint8_t macro_id) { 94 dynamic_macro_led_blink(); 95 96 dynamic_macro_t* macro = &dynamic_macros[macro_id]; 97 uint8_t length = macro->length; 98 99 keyrecord_t* events_begin = &(macro->events[0]); 100 keyrecord_t* events_pointer = &(macro->events[length - 1]); 101 102 dprintf("dynamic_macro: macro length before trimming: %d\n", macro->length); 103 while (events_pointer != events_begin && (events_pointer)->event.pressed) { 104 dprintln("dynamic macro: trimming a trailing key-down event"); 105 --(macro->length); 106 --events_pointer; 107 } 108 109 #ifdef DYNAMIC_MACRO_EEPROM_STORAGE 110 macro->checksum = dynamic_macro_calc_crc(macro); 111 dynamic_macro_save_eeprom(macro_id); 112 #endif 113 114 dprintf("dynamic macro: slot %d saved, length: %d\n", macro_id, length); 115 } 116 117 /* Handle the key events related to the dynamic macros. Should be 118 * called from process_record_user() like this: 119 * 120 * bool process_record_user(uint16_t keycode, keyrecord_t *record) { 121 * if (!process_record_dynamic_macro(keycode, record)) { 122 * return false; 123 * } 124 * <...THE REST OF THE FUNCTION...> 125 * } 126 */ 127 bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t* record) { 128 /* 0 to DYNAMIC_MACRO_COUNT -1 - macro macro_id is being recorded */ 129 static uint8_t macro_id = 255; 130 static uint8_t recording_state = STATE_NOT_RECORDING; 131 132 if (STATE_NOT_RECORDING == recording_state) { 133 /* Program key pressed to request programming mode */ 134 if (keycode == DYN_MACRO_PROG && record->event.pressed) { 135 dynamic_macro_led_blink(); 136 137 recording_state = STATE_RECORD_KEY_PRESSED; 138 dprintf("dynamic macro: programming key pressed, waiting for macro slot selection. %d\n", recording_state); 139 140 return false; 141 } 142 /* Macro key pressed to request macro playback */ 143 if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) { 144 dynamic_macro_play(keycode - DYN_MACRO_KEY1); 145 146 return false; 147 } 148 149 /* Non-dynamic macro key, process it elsewhere. */ 150 return true; 151 } else if (STATE_RECORD_KEY_PRESSED == recording_state) { 152 /* Program key pressed again before a macro selector key, cancel macro recording. 153 Blink leds to indicate cancelation. */ 154 if (keycode == DYN_MACRO_PROG && record->event.pressed) { 155 dynamic_macro_led_blink(); 156 157 recording_state = STATE_NOT_RECORDING; 158 dprintf("dynamic macro: programming key pressed, programming mode canceled. %d\n", recording_state); 159 160 return false; 161 } else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) { 162 macro_id = keycode - DYN_MACRO_KEY1; 163 164 /* Macro slot selected, enter recording state. */ 165 recording_state = STATE_CURRENTLY_RECORDING; 166 dynamic_macro_record_start(macro_id); 167 168 return false; 169 } 170 /* Ignore any non-macro key press while in RECORD_KEY_PRESSED state. */ 171 return false; 172 } else if (STATE_CURRENTLY_RECORDING == recording_state) { 173 /* Program key pressed to request end of macro recording. */ 174 if (keycode == DYN_MACRO_PROG && record->event.pressed) { 175 dynamic_macro_record_end(macro_id); 176 recording_state = STATE_NOT_RECORDING; 177 178 return false; 179 } 180 /* Don't record other macro key presses. */ 181 else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) { 182 dprintln("dynamic macro: playback key ignored in programming mode."); 183 return false; 184 } 185 /* Non-macro keypress that should be recorded */ 186 else { 187 dynamic_macro_record_key(macro_id, record); 188 189 /* Don't output recorded keypress. */ 190 return false; 191 } 192 } 193 194 return true; 195 } 196 197 #ifdef __AVR__ 198 # include <util/crc16.h> 199 uint16_t dynamic_macro_calc_crc(dynamic_macro_t* macro) { 200 uint16_t crc = 0; 201 uint8_t* data = (uint8_t*)macro; 202 203 for (uint16_t i = 0; i < DYNAMIC_MACRO_CRC_LENGTH; ++i) { 204 crc = _crc16_update(crc, *(data++)); 205 } 206 return crc; 207 } 208 #endif /* __AVR__ */ 209 210 inline void* dynamic_macro_eeprom_macro_addr(uint8_t macro_id) { 211 return DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR + sizeof(dynamic_macro_t) * macro_id; 212 } 213 214 bool dynamic_macro_header_correct(void) { 215 return eeprom_read_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR) == DYNAMIC_MACRO_EEPROM_MAGIC; 216 } 217 218 void dynamic_macro_load_eeprom_all(void) { 219 if (!dynamic_macro_header_correct()) { 220 dprintf("dynamic_macro: eeprom header not valid, not restoring macros.\n"); 221 return; 222 } 223 224 for (uint8_t i = 0; i < DYNAMIC_MACRO_COUNT; ++i) { 225 dynamic_macro_load_eeprom(i); 226 } 227 } 228 229 void dynamic_macro_load_eeprom(uint8_t macro_id) { 230 dynamic_macro_t* dst = &dynamic_macros[macro_id]; 231 232 eeprom_read_block(dst, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t)); 233 234 /* Validate checksum, ifchecksum is NOT valid for macro, set its length to 0 to prevent its use. */ 235 if (dynamic_macro_calc_crc(dst) != dst->checksum) { 236 dprintf("dynamic macro: slot %d not loaded, checksum mismatch\n", macro_id); 237 dst->length = 0; 238 239 return; 240 } 241 242 dprintf("dynamic macro: slot %d loaded from eeprom, checksum okay\n", macro_id); 243 } 244 245 void dynamic_macro_save_eeprom(uint8_t macro_id) { 246 if (!dynamic_macro_header_correct()) { 247 eeprom_write_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR, DYNAMIC_MACRO_EEPROM_MAGIC); 248 dprintf("dynamic macro: writing magic eeprom header\n"); 249 } 250 251 dynamic_macro_t* src = &dynamic_macros[macro_id]; 252 253 eeprom_update_block(src, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t)); 254 dprintf("dynamic macro: slot %d saved to eeprom\n", macro_id); 255 }