qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }