logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

dynamic_macro.c (7867B)


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