mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-26 19:19:59 +01:00 
			
		
		
		
	 e409fb47f2
			
		
	
	
		e409fb47f2
		
			
		
	
	
	
	
		
			
			* Selectively adding pieces * Adding georgi keymap * Adding more files, fixing make * Smaller makefiles * Fixing make rules * README more inline with QMK's guidelines * Turning off buggy assert * Improving documentation based on a user feedback. * Slightly better schema * Resurrected state machine diagram
		
			
				
	
	
		
			404 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Groff
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Groff
		
	
	
	
	
	
| bool are_hashed_keycodes_in_sound(HASH_TYPE keycodes_hash, HASH_TYPE sound) {
 | |
|     return (keycodes_hash & sound) == keycodes_hash;
 | |
| }
 | |
| 
 | |
| uint8_t keycode_to_index(uint16_t keycode) {
 | |
|     return keycode - FIRST_INTERNAL_KEYCODE;
 | |
| }
 | |
| 
 | |
| void sound_keycode_array(uint16_t keycode) {
 | |
|     uint8_t index = keycode_to_index(keycode);
 | |
|     keycode_index++;
 | |
|     keycodes_buffer_array[index] = keycode_index;
 | |
| }
 | |
| 
 | |
| void silence_keycode_hash_array(HASH_TYPE keycode_hash) {
 | |
|     for (int i = 0; i < NUMBER_OF_KEYS; i++) {
 | |
|         bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash;
 | |
|         if (index_in_hash) {
 | |
|             uint8_t current_val = keycodes_buffer_array[i];
 | |
|             keycodes_buffer_array[i] = 0;
 | |
|             for (int j = 0; j < NUMBER_OF_KEYS; j++) {
 | |
|                 if (keycodes_buffer_array[j] > current_val) {
 | |
|                     keycodes_buffer_array[j]--;
 | |
|                 }
 | |
|             }
 | |
|             keycode_index--;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool are_hashed_keycodes_in_array(HASH_TYPE keycode_hash) {
 | |
|     for (int i = 0; i < NUMBER_OF_KEYS; i++) {
 | |
|         bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash;
 | |
|         bool index_in_array = (bool) keycodes_buffer_array[i];
 | |
|         if (index_in_hash && !index_in_array) {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void kill_one_shots(void) {
 | |
|     struct Chord chord_storage;
 | |
|     struct Chord* chord_ptr;
 | |
|     struct Chord* chord;
 | |
|     
 | |
|     for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|         chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|         memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|         chord = &chord_storage;
 | |
|         
 | |
|         if (*chord->state == IN_ONE_SHOT) {
 | |
|             *chord->state = RESTART;
 | |
|             chord->function(chord);
 | |
|             if (*chord->state == RESTART) {
 | |
|                 *chord->state = IDLE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void process_finished_dances(void) {
 | |
|     struct Chord chord_storage;
 | |
|     struct Chord* chord_ptr;
 | |
|     struct Chord* chord;
 | |
|     
 | |
|     for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|         chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|         memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|         chord = &chord_storage;
 | |
|         
 | |
|         if (*chord->state == ACTIVATED) {
 | |
|             *chord->state = PRESS_FROM_ACTIVE;
 | |
|             chord->function(chord);
 | |
|             if (a_key_went_through) {
 | |
|                 kill_one_shots();
 | |
|             }
 | |
|             dance_timer = timer_read();
 | |
|         } else if (*chord->state == IDLE_IN_DANCE) {
 | |
|             *chord->state = FINISHED;
 | |
|             chord->function(chord);
 | |
|             if (*chord->state == FINISHED) {
 | |
|                 *chord->state = RESTART;
 | |
|                 if (*chord->state == RESTART) {
 | |
|                     *chord->state = IDLE;
 | |
|                 }
 | |
|             }
 | |
|         } else if (*chord->state == PRESS_FROM_ACTIVE) {
 | |
|             *chord->state = FINISHED_FROM_ACTIVE;
 | |
|             chord->function(chord);
 | |
|             if (a_key_went_through) {
 | |
|                 kill_one_shots();
 | |
|             }
 | |
|             dance_timer = timer_read();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| uint8_t keycodes_buffer_array_min(uint8_t* first_keycode_index) {
 | |
|     for (int i = 0; i < NUMBER_OF_KEYS; i++) {
 | |
|         if (keycodes_buffer_array[i] == 1) {
 | |
|             if (first_keycode_index != NULL) {
 | |
|                 *first_keycode_index = (uint8_t) i;
 | |
|             }
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void remove_subchords(void) {
 | |
|     struct Chord chord_storage;
 | |
|     struct Chord* chord_ptr;
 | |
|     struct Chord* chord;
 | |
|     
 | |
|     for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|         chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|         memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|         chord = &chord_storage;
 | |
|         
 | |
|         if (!(*chord->state == READY || *chord->state == READY_IN_DANCE || *chord->state == READY_LOCKED)) {
 | |
|             continue;
 | |
|         }
 | |
|         
 | |
|         struct Chord chord_storage_2;
 | |
|         struct Chord* chord_ptr_2;
 | |
|         struct Chord* chord_2;
 | |
|         for (int j = 0; j < NUMBER_OF_CHORDS; j++) {
 | |
|             if (i == j) {continue;}
 | |
|             
 | |
|             chord_ptr_2 = (struct Chord*) pgm_read_word (&list_of_chords[j]);
 | |
|             memcpy_P(&chord_storage_2, chord_ptr_2, sizeof(struct Chord));
 | |
|             chord_2 = &chord_storage_2;
 | |
|             
 | |
|             if (are_hashed_keycodes_in_sound(chord_2->keycodes_hash, chord->keycodes_hash)) {
 | |
|                 if (*chord_2->state == READY) {
 | |
|                     *chord_2->state = IDLE;
 | |
|                 }
 | |
|                 if (*chord_2->state == READY_IN_DANCE) {
 | |
|                     *chord_2->state = IDLE_IN_DANCE;
 | |
|                 }
 | |
|                 if (*chord_2->state == READY_LOCKED) {
 | |
|                     *chord_2->state = LOCKED;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void process_ready_chords(void) {
 | |
|     uint8_t first_keycode_index = 0;
 | |
|     while (keycodes_buffer_array_min(&first_keycode_index)) {
 | |
|         // find ready chords
 | |
|         struct Chord chord_storage;
 | |
|         struct Chord* chord_ptr;
 | |
|         struct Chord* chord;
 | |
|         
 | |
|         for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|             chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|             memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|             chord = &chord_storage;
 | |
|             
 | |
|             // if the chord does not contain the first keycode
 | |
|             bool contains_first_keycode = ((uint32_t) 1 << first_keycode_index) & chord->keycodes_hash;
 | |
|             if (!contains_first_keycode) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             if (!are_hashed_keycodes_in_array(chord->keycodes_hash)){
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             if (*chord->state == LOCKED) {
 | |
|                 *chord->state = READY_LOCKED;
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             if (!(chord->pseudolayer == current_pseudolayer || chord->pseudolayer == ALWAYS_ON)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             if (*chord->state == IDLE) {
 | |
|                 *chord->state = READY;
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             if (*chord->state == IDLE_IN_DANCE) {
 | |
|                 *chord->state = READY_IN_DANCE;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // remove subchords
 | |
|         remove_subchords();
 | |
|         
 | |
|         // execute logic
 | |
|         // this should be only one chord
 | |
|         for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|             chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|             memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|             chord = &chord_storage;
 | |
|             
 | |
|             if (*chord->state == READY_LOCKED) {
 | |
|                 *chord->state = RESTART;
 | |
|                 chord->function(chord);
 | |
|                 if (*chord->state == RESTART) {
 | |
|                     *chord->state = IDLE;
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             
 | |
|             if (*chord->state == READY || *chord->state == READY_IN_DANCE) {
 | |
|                 if (last_chord && last_chord != chord) {
 | |
|                     process_finished_dances();
 | |
|                 }
 | |
|                 
 | |
|                 bool lock_next_prev_state = lock_next;
 | |
|                 
 | |
|                 *chord->state = ACTIVATED;
 | |
|                 chord->function(chord);
 | |
|                 dance_timer = timer_read();
 | |
|                 
 | |
|                 if (lock_next && lock_next == lock_next_prev_state) {
 | |
|                     lock_next = false;
 | |
|                     *chord->state = PRESS_FROM_ACTIVE;
 | |
|                     chord->function(chord);
 | |
|                     if (*chord->state == PRESS_FROM_ACTIVE) {
 | |
|                         *chord->state = LOCKED;
 | |
|                     }
 | |
|                     if (a_key_went_through) {
 | |
|                         kill_one_shots();
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // silence notes
 | |
|         silence_keycode_hash_array(chord->keycodes_hash);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void deactivate_active_chords(uint16_t keycode) {
 | |
|     HASH_TYPE hash = (HASH_TYPE)1 << (keycode - SAFE_RANGE);
 | |
|     bool broken;
 | |
|     struct Chord chord_storage;
 | |
|     struct Chord* chord_ptr;
 | |
|     struct Chord* chord;
 | |
|     
 | |
|     for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|         chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|         memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|         chord = &chord_storage;
 | |
|         
 | |
|         broken = are_hashed_keycodes_in_sound(hash, chord->keycodes_hash);
 | |
|         if (!broken) {
 | |
|             continue;
 | |
|         }
 | |
|         
 | |
|         switch (*chord->state) {
 | |
|             case ACTIVATED:
 | |
|                 *chord->state = DEACTIVATED;
 | |
|                 chord->function(chord);
 | |
|                 
 | |
|                 if (*chord->state == DEACTIVATED) {
 | |
|                     dance_timer = timer_read();
 | |
|                     *chord->state = IDLE_IN_DANCE;
 | |
|                 }
 | |
|                 if (*chord->state != IN_ONE_SHOT) {
 | |
|                     kill_one_shots();
 | |
|                 }
 | |
|                 break;
 | |
|             case PRESS_FROM_ACTIVE:
 | |
|             case FINISHED_FROM_ACTIVE:
 | |
|                 *chord->state = RESTART;
 | |
|                 chord->function(chord);
 | |
|                 if (*chord->state == RESTART) {
 | |
|                     *chord->state = IDLE;
 | |
|                 }
 | |
|                 kill_one_shots();
 | |
|                 break;
 | |
|             default:
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
| }
 | |
| 
 | |
| void process_command(void) {
 | |
|     command_mode = 0;
 | |
|     for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
 | |
|         if (command_buffer[i]) {
 | |
|             register_code(command_buffer[i]);
 | |
|         }
 | |
|         send_keyboard_report();
 | |
|     }
 | |
|     wait_ms(TAP_TIMEOUT);
 | |
|     for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
 | |
|         if (command_buffer[i]) {
 | |
|             unregister_code(command_buffer[i]);
 | |
|         }
 | |
|         send_keyboard_report();
 | |
|     }
 | |
|     for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
 | |
|         command_buffer[i] = 0;
 | |
|     }
 | |
|     command_ind = 0;
 | |
| }
 | |
| 
 | |
| void process_leader(void) {
 | |
|     in_leader_mode = false;
 | |
|     for (int i = 0; i < NUMBER_OF_LEADER_COMBOS; i++) {
 | |
|         uint16_t trigger[LEADER_MAX_LENGTH];
 | |
|         memcpy_P(trigger, leader_triggers[i], LEADER_MAX_LENGTH * sizeof(uint16_t));
 | |
|         
 | |
|         if (identical(leader_buffer, trigger)) {
 | |
|             (*leader_functions[i])();
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     for (int i = 0; i < LEADER_MAX_LENGTH; i++) {
 | |
|         leader_buffer[i] = 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool process_record_user(uint16_t keycode, keyrecord_t *record) {
 | |
|     if (keycode < FIRST_INTERNAL_KEYCODE || keycode > LAST_INTERNAL_KEYCODE) {
 | |
|         return true;
 | |
|     }
 | |
|     
 | |
|     if (record->event.pressed) {
 | |
|         sound_keycode_array(keycode);
 | |
|     } else {
 | |
|         process_ready_chords();
 | |
|         deactivate_active_chords(keycode);
 | |
|     }
 | |
|     chord_timer = timer_read();
 | |
|     leader_timer = timer_read();
 | |
|     
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| void matrix_scan_user(void) {
 | |
|     bool chord_timer_expired = timer_elapsed(chord_timer) > CHORD_TIMEOUT;
 | |
|     if (chord_timer_expired && keycodes_buffer_array_min(NULL)) {
 | |
|         process_ready_chords();
 | |
|     }
 | |
|     
 | |
|     bool dance_timer_expired = timer_elapsed(dance_timer) > DANCE_TIMEOUT;
 | |
|     if (dance_timer_expired) { // would love to have && in_dance but not sure how
 | |
|         process_finished_dances();
 | |
|     }
 | |
|     
 | |
|     bool in_command_mode = command_mode == 2;
 | |
|     if (in_command_mode) {
 | |
|         process_command();
 | |
|     }
 | |
|     
 | |
|     bool leader_timer_expired = timer_elapsed(leader_timer) > LEADER_TIMEOUT;
 | |
|     if (leader_timer_expired && in_leader_mode) {
 | |
|         process_leader();
 | |
|     }
 | |
|     
 | |
| }
 | |
| 
 | |
| void clear(const struct Chord* self) {
 | |
|     if (*self->state == ACTIVATED) {
 | |
|         // kill all chords
 | |
|         struct Chord chord_storage;
 | |
|         struct Chord* chord_ptr;
 | |
|         struct Chord* chord;
 | |
|         
 | |
|         for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
 | |
|             chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
 | |
|             memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
 | |
|             chord = &chord_storage;
 | |
|             
 | |
|             *chord->state = IDLE;
 | |
|             
 | |
|             if (chord->counter) {
 | |
|                 *chord->counter = 0;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // clear keyboard
 | |
|         clear_keyboard();
 | |
|         send_keyboard_report();
 | |
|         
 | |
|         // switch to default pseudolayer
 | |
|         current_pseudolayer = DEFAULT_PSEUDOLAYER;
 | |
|         
 | |
|         // clear all keyboard states
 | |
|         lock_next = false;
 | |
|         autoshift_mode = true;
 | |
|         command_mode = 0;
 | |
|         in_leader_mode = false;
 | |
|         leader_ind = 0;
 | |
|         dynamic_macro_mode = false;
 | |
|         a_key_went_through = false;
 | |
|         
 | |
|         for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
 | |
|             dynamic_macro_buffer[i] = 0;
 | |
|         }
 | |
|     }
 | |
| } |