mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-12-05 01:15:19 +00:00
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;
|
||
|
}
|
||
|
}
|
||
|
}
|