mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-31 21:32:31 +01:00 
			
		
		
		
	 1c75385d76
			
		
	
	
		1c75385d76
		
	
	
	
	
		
			
			* Add omnikeyish keyboard support. * remove out of date comment * PCB Rev 1.1 moved Row5's pin to E6, because the teensy++ hangs an onboard LED off D6. * Move string.h include to .c file * Add pcb kicad link. * Add info.json * Move macro programming to numlock's keyposition, the most useless key on the post model M layout. Force numlock enabled on host at init time, so you're not stuck without a numpad (hopefully) * Make the macro blink function toggle LEDs from their previous state. * Use incorrect but code style compliant opening curly bracing style. * Make PCB rev 1.1 the default Omnikeyish config, as the author has the only rev 1.0 boards that'll ever be. * Fix silly spelling error in 3 defines * First set of review changes. * Layout macro and keymap defined using it. * Layout macros for the northgate factory plates. * minor rearrangements * ALL the layouts. * Forgot ultra-t in info.json
		
			
				
	
	
		
			253 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include QMK_KEYBOARD_H
 | |
| #include <string.h>
 | |
| 
 | |
| void dynamic_macro_init(void) {
 | |
|   /* zero out macro blocks  */
 | |
|   memset(&dynamic_macros, 0, DYNAMIC_MACRO_COUNT * sizeof(dynamic_macro_t));
 | |
| }
 | |
| 
 | |
| /* Blink the LEDs to notify the user about some event. */
 | |
| void dynamic_macro_led_blink(void) {
 | |
| #ifdef BACKLIGHT_ENABLE
 | |
|   backlight_toggle();
 | |
|   wait_ms(100);
 | |
|   backlight_toggle();
 | |
| #else
 | |
|   led_set(host_keyboard_leds() ^ 0xFF);
 | |
|   wait_ms(100);
 | |
|   led_set(host_keyboard_leds());
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Start recording of the dynamic macro.
 | |
|  *
 | |
|  * @param macro_id[in]     The id of macro to be recorded
 | |
|  */
 | |
| void dynamic_macro_record_start(uint8_t macro_id) {
 | |
|   dprintf("dynamic macro recording: started for slot %d\n", macro_id);
 | |
| 
 | |
|   dynamic_macro_led_blink();
 | |
| 
 | |
|   clear_keyboard();
 | |
|   layer_clear();
 | |
| 
 | |
|   dynamic_macros[macro_id].length = 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Play the dynamic macro.
 | |
|  *
 | |
|  * @param macro_id[in]     The id of macro to be played
 | |
|  */
 | |
| void dynamic_macro_play(uint8_t macro_id) {
 | |
|   dprintf("dynamic macro: slot %d playback, length %d\n", macro_id, dynamic_macros[macro_id].length);
 | |
| 
 | |
|   uint32_t saved_layer_state = layer_state;
 | |
| 
 | |
|   clear_keyboard();
 | |
|   layer_clear();
 | |
| 
 | |
|   for (uint8_t i = 0; i < dynamic_macros[macro_id].length; ++i) {
 | |
|     process_record(&dynamic_macros[macro_id].events[i]);
 | |
|   }
 | |
| 
 | |
|   clear_keyboard();
 | |
| 
 | |
|   layer_state = saved_layer_state;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Record a single key in a dynamic macro.
 | |
|  *
 | |
|  * @param macro_id[in] The start of the used macro buffer.
 | |
|  * @param record[in]     The current keypress.
 | |
|  */
 | |
| void dynamic_macro_record_key(uint8_t macro_id, keyrecord_t* record) {
 | |
|   dynamic_macro_t* macro  = &dynamic_macros[macro_id];
 | |
|   uint8_t          length = macro->length;
 | |
| 
 | |
|   /* If we've just started recording, ignore all the key releases. */
 | |
|   if (!record->event.pressed && length == 0) {
 | |
|     dprintln("dynamic macro: ignoring a leading key-up event");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (length < DYNAMIC_MACRO_SIZE) {
 | |
|     macro->events[length] = *record;
 | |
|     macro->length         = ++length;
 | |
|   } else {
 | |
|     dynamic_macro_led_blink();
 | |
|   }
 | |
| 
 | |
|   dprintf("dynamic macro: slot %d length: %d/%d\n", macro_id, length, DYNAMIC_MACRO_SIZE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * End recording of the dynamic macro. Essentially just update the
 | |
|  * pointer to the end of the macro.
 | |
|  */
 | |
| void dynamic_macro_record_end(uint8_t macro_id) {
 | |
|   dynamic_macro_led_blink();
 | |
| 
 | |
|   dynamic_macro_t* macro  = &dynamic_macros[macro_id];
 | |
|   uint8_t          length = macro->length;
 | |
| 
 | |
|   keyrecord_t* events_begin   = &(macro->events[0]);
 | |
|   keyrecord_t* events_pointer = &(macro->events[length - 1]);
 | |
| 
 | |
|   dprintf("dynamic_macro: macro length before trimming: %d\n", macro->length);
 | |
|   while (events_pointer != events_begin && (events_pointer)->event.pressed) {
 | |
|     dprintln("dynamic macro: trimming a trailing key-down event");
 | |
|     --(macro->length);
 | |
|     --events_pointer;
 | |
|   }
 | |
| 
 | |
| #ifdef DYNAMIC_MACRO_EEPROM_STORAGE
 | |
|   macro->checksum = dynamic_macro_calc_crc(macro);
 | |
|   dynamic_macro_save_eeprom(macro_id);
 | |
| #endif
 | |
| 
 | |
|   dprintf("dynamic macro: slot %d saved, length: %d\n", macro_id, length);
 | |
| }
 | |
| 
 | |
| /* Handle the key events related to the dynamic macros. Should be
 | |
|  * called from process_record_user() like this:
 | |
|  *
 | |
|  *   bool process_record_user(uint16_t keycode, keyrecord_t *record) {
 | |
|  *       if (!process_record_dynamic_macro(keycode, record)) {
 | |
|  *           return false;
 | |
|  *       }
 | |
|  *       <...THE REST OF THE FUNCTION...>
 | |
|  *   }
 | |
|  */
 | |
| bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t* record) {
 | |
|   /* 0 to DYNAMIC_MACRO_COUNT -1 - macro macro_id is being recorded */
 | |
|   static uint8_t macro_id        = 255;
 | |
|   static uint8_t recording_state = STATE_NOT_RECORDING;
 | |
| 
 | |
|   if (STATE_NOT_RECORDING == recording_state) {
 | |
|     /* Program key pressed to request programming mode */
 | |
|     if (keycode == DYN_MACRO_PROG && record->event.pressed) {
 | |
|       dynamic_macro_led_blink();
 | |
| 
 | |
|       recording_state = STATE_RECORD_KEY_PRESSED;
 | |
|       dprintf("dynamic macro: programming key pressed, waiting for macro slot selection. %d\n", recording_state);
 | |
| 
 | |
|       return false;
 | |
|     }
 | |
|     /* Macro key pressed to request macro playback */
 | |
|     if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
 | |
|       dynamic_macro_play(keycode - DYN_MACRO_KEY1);
 | |
| 
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     /* Non-dynamic macro key, process it elsewhere. */
 | |
|     return true;
 | |
|   } else if (STATE_RECORD_KEY_PRESSED == recording_state) {
 | |
|     /* Program key pressed again before a macro selector key, cancel macro recording.
 | |
|        Blink leds to indicate cancelation. */
 | |
|     if (keycode == DYN_MACRO_PROG && record->event.pressed) {
 | |
|       dynamic_macro_led_blink();
 | |
| 
 | |
|       recording_state = STATE_NOT_RECORDING;
 | |
|       dprintf("dynamic macro: programming key pressed, programming mode canceled. %d\n", recording_state);
 | |
| 
 | |
|       return false;
 | |
|     } else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
 | |
|       macro_id = keycode - DYN_MACRO_KEY1;
 | |
| 
 | |
|       /* Macro slot selected, enter recording state. */
 | |
|       recording_state = STATE_CURRENTLY_RECORDING;
 | |
|       dynamic_macro_record_start(macro_id);
 | |
| 
 | |
|       return false;
 | |
|     }
 | |
|     /* Ignore any non-macro key press while in RECORD_KEY_PRESSED state. */
 | |
|     return false;
 | |
|   } else if (STATE_CURRENTLY_RECORDING == recording_state) {
 | |
|     /* Program key pressed to request end of macro recording. */
 | |
|     if (keycode == DYN_MACRO_PROG && record->event.pressed) {
 | |
|       dynamic_macro_record_end(macro_id);
 | |
|       recording_state = STATE_NOT_RECORDING;
 | |
| 
 | |
|       return false;
 | |
|     }
 | |
|     /* Don't record other macro key presses. */
 | |
|     else if (keycode >= DYN_MACRO_KEY1 && keycode <= DYN_MACRO_KEY12 && record->event.pressed) {
 | |
|       dprintln("dynamic macro: playback key ignored in programming mode.");
 | |
|       return false;
 | |
|     }
 | |
|     /* Non-macro keypress that should be recorded  */
 | |
|     else {
 | |
|       dynamic_macro_record_key(macro_id, record);
 | |
| 
 | |
|       /* Don't output recorded keypress. */
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| #ifdef __AVR__
 | |
| #  include <util/crc16.h>
 | |
| uint16_t dynamic_macro_calc_crc(dynamic_macro_t* macro) {
 | |
|   uint16_t crc  = 0;
 | |
|   uint8_t* data = (uint8_t*)macro;
 | |
| 
 | |
|   for (uint16_t i = 0; i < DYNAMIC_MACRO_CRC_LENGTH; ++i) {
 | |
|     crc = _crc16_update(crc, *(data++));
 | |
|   }
 | |
|   return crc;
 | |
| }
 | |
| #endif /* __AVR__ */
 | |
| 
 | |
| inline void* dynamic_macro_eeprom_macro_addr(uint8_t macro_id) { 
 | |
|     return DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR + sizeof(dynamic_macro_t) * macro_id;
 | |
| }
 | |
| 
 | |
| bool dynamic_macro_header_correct(void) { 
 | |
|     return eeprom_read_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR) == DYNAMIC_MACRO_EEPROM_MAGIC;
 | |
| }
 | |
| 
 | |
| void dynamic_macro_load_eeprom_all(void) {
 | |
|   if (!dynamic_macro_header_correct()) {
 | |
|     dprintf("dynamic_macro: eeprom header not valid, not restoring macros.\n");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (uint8_t i = 0; i < DYNAMIC_MACRO_COUNT; ++i) {
 | |
|     dynamic_macro_load_eeprom(i);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void dynamic_macro_load_eeprom(uint8_t macro_id) {
 | |
|   dynamic_macro_t* dst = &dynamic_macros[macro_id];
 | |
| 
 | |
|   eeprom_read_block(dst, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t));
 | |
| 
 | |
|   /* Validate checksum, ifchecksum is NOT valid for macro, set its length to 0 to prevent its use. */
 | |
|   if (dynamic_macro_calc_crc(dst) != dst->checksum) {
 | |
|     dprintf("dynamic macro: slot %d not loaded, checksum mismatch\n", macro_id);
 | |
|     dst->length = 0;
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   dprintf("dynamic macro: slot %d loaded from eeprom, checksum okay\n", macro_id);
 | |
| }
 | |
| 
 | |
| void dynamic_macro_save_eeprom(uint8_t macro_id) {
 | |
|   if (!dynamic_macro_header_correct()) {
 | |
|     eeprom_write_word(DYNAMIC_MACRO_EEPROM_MAGIC_ADDR, DYNAMIC_MACRO_EEPROM_MAGIC);
 | |
|     dprintf("dynamic macro: writing magic eeprom header\n");
 | |
|   }
 | |
| 
 | |
|   dynamic_macro_t* src = &dynamic_macros[macro_id];
 | |
| 
 | |
|   eeprom_update_block(src, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t));
 | |
|   dprintf("dynamic macro: slot %d saved to eeprom\n", macro_id);
 | |
| }
 |