2
0

dynamic_macro.c 7.6 KB

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