You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
7.7 KiB

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