]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/omnikeyish/dynamic_macro.c
[Keyboard] Add new keyboard: Omnikeyish - A replacement PCB for the Northgate Omnikey...
[qmk_firmware.git] / keyboards / omnikeyish / dynamic_macro.c
1 #include QMK_KEYBOARD_H
2 #include <string.h>
3
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
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 /**
23  * Start recording of the dynamic macro.
24  *
25  * @param macro_id[in]     The id of macro to be recorded
26  */
27 void dynamic_macro_record_start(uint8_t macro_id) {
28   dprintf("dynamic macro recording: started for slot %d\n", macro_id);
29
30   dynamic_macro_led_blink();
31
32   clear_keyboard();
33   layer_clear();
34
35   dynamic_macros[macro_id].length = 0;
36 }
37
38 /**
39  * Play the dynamic macro.
40  *
41  * @param macro_id[in]     The id of macro to be played
42  */
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);
45
46   uint32_t saved_layer_state = layer_state;
47
48   clear_keyboard();
49   layer_clear();
50
51   for (uint8_t i = 0; i < dynamic_macros[macro_id].length; ++i) {
52     process_record(&dynamic_macros[macro_id].events[i]);
53   }
54
55   clear_keyboard();
56
57   layer_state = saved_layer_state;
58 }
59
60 /**
61  * Record a single key in a dynamic macro.
62  *
63  * @param macro_id[in] The start of the used macro buffer.
64  * @param record[in]     The current keypress.
65  */
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;
69
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");
73     return;
74   }
75
76   if (length < DYNAMIC_MACRO_SIZE) {
77     macro->events[length] = *record;
78     macro->length         = ++length;
79   } else {
80     dynamic_macro_led_blink();
81   }
82
83   dprintf("dynamic macro: slot %d length: %d/%d\n", macro_id, length, DYNAMIC_MACRO_SIZE);
84 }
85
86 /**
87  * End recording of the dynamic macro. Essentially just update the
88  * pointer to the end of the macro.
89  */
90 void dynamic_macro_record_end(uint8_t macro_id) {
91   dynamic_macro_led_blink();
92
93   dynamic_macro_t* macro  = &dynamic_macros[macro_id];
94   uint8_t          length = macro->length;
95
96   keyrecord_t* events_begin   = &(macro->events[0]);
97   keyrecord_t* events_pointer = &(macro->events[length - 1]);
98
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");
102     --(macro->length);
103     --events_pointer;
104   }
105
106 #ifdef DYNAMIC_MACRO_EEPROM_STORAGE
107   macro->checksum = dynamic_macro_calc_crc(macro);
108   dynamic_macro_save_eeprom(macro_id);
109 #endif
110
111   dprintf("dynamic macro: slot %d saved, length: %d\n", macro_id, length);
112 }
113
114 /* Handle the key events related to the dynamic macros. Should be
115  * called from process_record_user() like this:
116  *
117  *   bool process_record_user(uint16_t keycode, keyrecord_t *record) {
118  *       if (!process_record_dynamic_macro(keycode, record)) {
119  *           return false;
120  *       }
121  *       <...THE REST OF THE FUNCTION...>
122  *   }
123  */
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;
128
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();
133
134       recording_state = STATE_RECORD_KEY_PRESSED;
135       dprintf("dynamic macro: programming key pressed, waiting for macro slot selection. %d\n", recording_state);
136
137       return false;
138     }
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);
142
143       return false;
144     }
145
146     /* Non-dynamic macro key, process it elsewhere. */
147     return true;
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();
153
154       recording_state = STATE_NOT_RECORDING;
155       dprintf("dynamic macro: programming key pressed, programming mode canceled. %d\n", recording_state);
156
157       return false;
158     } else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
159       macro_id = keycode - DYN_MACRO_KEY1;
160
161       /* Macro slot selected, enter recording state. */
162       recording_state = STATE_CURRENTLY_RECORDING;
163       dynamic_macro_record_start(macro_id);
164
165       return false;
166     }
167     /* Ignore any non-macro key press while in RECORD_KEY_PRESSED state. */
168     return false;
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;
174
175       return false;
176     }
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.");
180       return false;
181     }
182     /* Non-macro keypress that should be recorded  */
183     else {
184       dynamic_macro_record_key(macro_id, record);
185
186       /* Don't output recorded keypress. */
187       return false;
188     }
189   }
190
191   return true;
192 }
193
194 #ifdef __AVR__
195 #  include <util/crc16.h>
196 uint16_t dynamic_macro_calc_crc(dynamic_macro_t* macro) {
197   uint16_t crc  = 0;
198   uint8_t* data = (uint8_t*)macro;
199
200   for (uint16_t i = 0; i < DYNAMIC_MACRO_CRC_LENGTH; ++i) {
201     crc = _crc16_update(crc, *(data++));
202   }
203   return crc;
204 }
205 #endif /* __AVR__ */
206
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;
209 }
210
211 bool dynamic_macro_header_correct(void) { 
212     return eeprom_read_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR) == DYNAMIC_MACRO_EEPROM_MAGIC;
213 }
214
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");
218     return;
219   }
220
221   for (uint8_t i = 0; i < DYNAMIC_MACRO_COUNT; ++i) {
222     dynamic_macro_load_eeprom(i);
223   }
224 }
225
226 void dynamic_macro_load_eeprom(uint8_t macro_id) {
227   dynamic_macro_t* dst = &dynamic_macros[macro_id];
228
229   eeprom_read_block(dst, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t));
230
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);
234     dst->length = 0;
235
236     return;
237   }
238
239   dprintf("dynamic macro: slot %d loaded from eeprom, checksum okay\n", macro_id);
240 }
241
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");
246   }
247
248   dynamic_macro_t* src = &dynamic_macros[macro_id];
249
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);
252 }