diff options
| author | DennyTom <denemark.tomas@gmail.com> | 2020-04-07 04:13:17 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-04-07 21:13:17 +1000 |
| commit | e409fb47f27f9cf56479928ed86eb2eb346eec54 (patch) | |
| tree | f7b27bec198b7bb6250fbcf73111c189bc22d107 /users/dennytom/chording_engine/engine.part.3 | |
| parent | ae74922d1485e3c8e120dbc141d003ed7696b1f9 (diff) | |
| download | qmk_firmware-e409fb47f27f9cf56479928ed86eb2eb346eec54.tar.gz qmk_firmware-e409fb47f27f9cf56479928ed86eb2eb346eec54.zip | |
DennyTom's buttery_engine (#8138)
* 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
Diffstat (limited to 'users/dennytom/chording_engine/engine.part.3')
| -rw-r--r-- | users/dennytom/chording_engine/engine.part.3 | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/users/dennytom/chording_engine/engine.part.3 b/users/dennytom/chording_engine/engine.part.3 new file mode 100644 index 000000000..cf19008ab --- /dev/null +++ b/users/dennytom/chording_engine/engine.part.3 | |||
| @@ -0,0 +1,404 @@ | |||
| 1 | bool are_hashed_keycodes_in_sound(HASH_TYPE keycodes_hash, HASH_TYPE sound) { | ||
| 2 | return (keycodes_hash & sound) == keycodes_hash; | ||
| 3 | } | ||
| 4 | |||
| 5 | uint8_t keycode_to_index(uint16_t keycode) { | ||
| 6 | return keycode - FIRST_INTERNAL_KEYCODE; | ||
| 7 | } | ||
| 8 | |||
| 9 | void sound_keycode_array(uint16_t keycode) { | ||
| 10 | uint8_t index = keycode_to_index(keycode); | ||
| 11 | keycode_index++; | ||
| 12 | keycodes_buffer_array[index] = keycode_index; | ||
| 13 | } | ||
| 14 | |||
| 15 | void silence_keycode_hash_array(HASH_TYPE keycode_hash) { | ||
| 16 | for (int i = 0; i < NUMBER_OF_KEYS; i++) { | ||
| 17 | bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash; | ||
| 18 | if (index_in_hash) { | ||
| 19 | uint8_t current_val = keycodes_buffer_array[i]; | ||
| 20 | keycodes_buffer_array[i] = 0; | ||
| 21 | for (int j = 0; j < NUMBER_OF_KEYS; j++) { | ||
| 22 | if (keycodes_buffer_array[j] > current_val) { | ||
| 23 | keycodes_buffer_array[j]--; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | keycode_index--; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | bool are_hashed_keycodes_in_array(HASH_TYPE keycode_hash) { | ||
| 32 | for (int i = 0; i < NUMBER_OF_KEYS; i++) { | ||
| 33 | bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash; | ||
| 34 | bool index_in_array = (bool) keycodes_buffer_array[i]; | ||
| 35 | if (index_in_hash && !index_in_array) { | ||
| 36 | return false; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | return true; | ||
| 40 | } | ||
| 41 | |||
| 42 | void kill_one_shots(void) { | ||
| 43 | struct Chord chord_storage; | ||
| 44 | struct Chord* chord_ptr; | ||
| 45 | struct Chord* chord; | ||
| 46 | |||
| 47 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 48 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 49 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 50 | chord = &chord_storage; | ||
| 51 | |||
| 52 | if (*chord->state == IN_ONE_SHOT) { | ||
| 53 | *chord->state = RESTART; | ||
| 54 | chord->function(chord); | ||
| 55 | if (*chord->state == RESTART) { | ||
| 56 | *chord->state = IDLE; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | void process_finished_dances(void) { | ||
| 63 | struct Chord chord_storage; | ||
| 64 | struct Chord* chord_ptr; | ||
| 65 | struct Chord* chord; | ||
| 66 | |||
| 67 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 68 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 69 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 70 | chord = &chord_storage; | ||
| 71 | |||
| 72 | if (*chord->state == ACTIVATED) { | ||
| 73 | *chord->state = PRESS_FROM_ACTIVE; | ||
| 74 | chord->function(chord); | ||
| 75 | if (a_key_went_through) { | ||
| 76 | kill_one_shots(); | ||
| 77 | } | ||
| 78 | dance_timer = timer_read(); | ||
| 79 | } else if (*chord->state == IDLE_IN_DANCE) { | ||
| 80 | *chord->state = FINISHED; | ||
| 81 | chord->function(chord); | ||
| 82 | if (*chord->state == FINISHED) { | ||
| 83 | *chord->state = RESTART; | ||
| 84 | if (*chord->state == RESTART) { | ||
| 85 | *chord->state = IDLE; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } else if (*chord->state == PRESS_FROM_ACTIVE) { | ||
| 89 | *chord->state = FINISHED_FROM_ACTIVE; | ||
| 90 | chord->function(chord); | ||
| 91 | if (a_key_went_through) { | ||
| 92 | kill_one_shots(); | ||
| 93 | } | ||
| 94 | dance_timer = timer_read(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | uint8_t keycodes_buffer_array_min(uint8_t* first_keycode_index) { | ||
| 100 | for (int i = 0; i < NUMBER_OF_KEYS; i++) { | ||
| 101 | if (keycodes_buffer_array[i] == 1) { | ||
| 102 | if (first_keycode_index != NULL) { | ||
| 103 | *first_keycode_index = (uint8_t) i; | ||
| 104 | } | ||
| 105 | return 1; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | void remove_subchords(void) { | ||
| 112 | struct Chord chord_storage; | ||
| 113 | struct Chord* chord_ptr; | ||
| 114 | struct Chord* chord; | ||
| 115 | |||
| 116 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 117 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 118 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 119 | chord = &chord_storage; | ||
| 120 | |||
| 121 | if (!(*chord->state == READY || *chord->state == READY_IN_DANCE || *chord->state == READY_LOCKED)) { | ||
| 122 | continue; | ||
| 123 | } | ||
| 124 | |||
| 125 | struct Chord chord_storage_2; | ||
| 126 | struct Chord* chord_ptr_2; | ||
| 127 | struct Chord* chord_2; | ||
| 128 | for (int j = 0; j < NUMBER_OF_CHORDS; j++) { | ||
| 129 | if (i == j) {continue;} | ||
| 130 | |||
| 131 | chord_ptr_2 = (struct Chord*) pgm_read_word (&list_of_chords[j]); | ||
| 132 | memcpy_P(&chord_storage_2, chord_ptr_2, sizeof(struct Chord)); | ||
| 133 | chord_2 = &chord_storage_2; | ||
| 134 | |||
| 135 | if (are_hashed_keycodes_in_sound(chord_2->keycodes_hash, chord->keycodes_hash)) { | ||
| 136 | if (*chord_2->state == READY) { | ||
| 137 | *chord_2->state = IDLE; | ||
| 138 | } | ||
| 139 | if (*chord_2->state == READY_IN_DANCE) { | ||
| 140 | *chord_2->state = IDLE_IN_DANCE; | ||
| 141 | } | ||
| 142 | if (*chord_2->state == READY_LOCKED) { | ||
| 143 | *chord_2->state = LOCKED; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | void process_ready_chords(void) { | ||
| 151 | uint8_t first_keycode_index = 0; | ||
| 152 | while (keycodes_buffer_array_min(&first_keycode_index)) { | ||
| 153 | // find ready chords | ||
| 154 | struct Chord chord_storage; | ||
| 155 | struct Chord* chord_ptr; | ||
| 156 | struct Chord* chord; | ||
| 157 | |||
| 158 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 159 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 160 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 161 | chord = &chord_storage; | ||
| 162 | |||
| 163 | // if the chord does not contain the first keycode | ||
| 164 | bool contains_first_keycode = ((uint32_t) 1 << first_keycode_index) & chord->keycodes_hash; | ||
| 165 | if (!contains_first_keycode) { | ||
| 166 | continue; | ||
| 167 | } | ||
| 168 | |||
| 169 | if (!are_hashed_keycodes_in_array(chord->keycodes_hash)){ | ||
| 170 | continue; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (*chord->state == LOCKED) { | ||
| 174 | *chord->state = READY_LOCKED; | ||
| 175 | continue; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (!(chord->pseudolayer == current_pseudolayer || chord->pseudolayer == ALWAYS_ON)) { | ||
| 179 | continue; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (*chord->state == IDLE) { | ||
| 183 | *chord->state = READY; | ||
| 184 | continue; | ||
| 185 | } | ||
| 186 | |||
| 187 | if (*chord->state == IDLE_IN_DANCE) { | ||
| 188 | *chord->state = READY_IN_DANCE; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | // remove subchords | ||
| 193 | remove_subchords(); | ||
| 194 | |||
| 195 | // execute logic | ||
| 196 | // this should be only one chord | ||
| 197 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 198 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 199 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 200 | chord = &chord_storage; | ||
| 201 | |||
| 202 | if (*chord->state == READY_LOCKED) { | ||
| 203 | *chord->state = RESTART; | ||
| 204 | chord->function(chord); | ||
| 205 | if (*chord->state == RESTART) { | ||
| 206 | *chord->state = IDLE; | ||
| 207 | } | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | |||
| 211 | if (*chord->state == READY || *chord->state == READY_IN_DANCE) { | ||
| 212 | if (last_chord && last_chord != chord) { | ||
| 213 | process_finished_dances(); | ||
| 214 | } | ||
| 215 | |||
| 216 | bool lock_next_prev_state = lock_next; | ||
| 217 | |||
| 218 | *chord->state = ACTIVATED; | ||
| 219 | chord->function(chord); | ||
| 220 | dance_timer = timer_read(); | ||
| 221 | |||
| 222 | if (lock_next && lock_next == lock_next_prev_state) { | ||
| 223 | lock_next = false; | ||
| 224 | *chord->state = PRESS_FROM_ACTIVE; | ||
| 225 | chord->function(chord); | ||
| 226 | if (*chord->state == PRESS_FROM_ACTIVE) { | ||
| 227 | *chord->state = LOCKED; | ||
| 228 | } | ||
| 229 | if (a_key_went_through) { | ||
| 230 | kill_one_shots(); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | // silence notes | ||
| 238 | silence_keycode_hash_array(chord->keycodes_hash); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | void deactivate_active_chords(uint16_t keycode) { | ||
| 243 | HASH_TYPE hash = (HASH_TYPE)1 << (keycode - SAFE_RANGE); | ||
| 244 | bool broken; | ||
| 245 | struct Chord chord_storage; | ||
| 246 | struct Chord* chord_ptr; | ||
| 247 | struct Chord* chord; | ||
| 248 | |||
| 249 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 250 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 251 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 252 | chord = &chord_storage; | ||
| 253 | |||
| 254 | broken = are_hashed_keycodes_in_sound(hash, chord->keycodes_hash); | ||
| 255 | if (!broken) { | ||
| 256 | continue; | ||
| 257 | } | ||
| 258 | |||
| 259 | switch (*chord->state) { | ||
| 260 | case ACTIVATED: | ||
| 261 | *chord->state = DEACTIVATED; | ||
| 262 | chord->function(chord); | ||
| 263 | |||
| 264 | if (*chord->state == DEACTIVATED) { | ||
| 265 | dance_timer = timer_read(); | ||
| 266 | *chord->state = IDLE_IN_DANCE; | ||
| 267 | } | ||
| 268 | if (*chord->state != IN_ONE_SHOT) { | ||
| 269 | kill_one_shots(); | ||
| 270 | } | ||
| 271 | break; | ||
| 272 | case PRESS_FROM_ACTIVE: | ||
| 273 | case FINISHED_FROM_ACTIVE: | ||
| 274 | *chord->state = RESTART; | ||
| 275 | chord->function(chord); | ||
| 276 | if (*chord->state == RESTART) { | ||
| 277 | *chord->state = IDLE; | ||
| 278 | } | ||
| 279 | kill_one_shots(); | ||
| 280 | break; | ||
| 281 | default: | ||
| 282 | break; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | } | ||
| 287 | |||
| 288 | void process_command(void) { | ||
| 289 | command_mode = 0; | ||
| 290 | for (int i = 0; i < COMMAND_MAX_LENGTH; i++) { | ||
| 291 | if (command_buffer[i]) { | ||
| 292 | register_code(command_buffer[i]); | ||
| 293 | } | ||
| 294 | send_keyboard_report(); | ||
| 295 | } | ||
| 296 | wait_ms(TAP_TIMEOUT); | ||
| 297 | for (int i = 0; i < COMMAND_MAX_LENGTH; i++) { | ||
| 298 | if (command_buffer[i]) { | ||
| 299 | unregister_code(command_buffer[i]); | ||
| 300 | } | ||
| 301 | send_keyboard_report(); | ||
| 302 | } | ||
| 303 | for (int i = 0; i < COMMAND_MAX_LENGTH; i++) { | ||
| 304 | command_buffer[i] = 0; | ||
| 305 | } | ||
| 306 | command_ind = 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | void process_leader(void) { | ||
| 310 | in_leader_mode = false; | ||
| 311 | for (int i = 0; i < NUMBER_OF_LEADER_COMBOS; i++) { | ||
| 312 | uint16_t trigger[LEADER_MAX_LENGTH]; | ||
| 313 | memcpy_P(trigger, leader_triggers[i], LEADER_MAX_LENGTH * sizeof(uint16_t)); | ||
| 314 | |||
| 315 | if (identical(leader_buffer, trigger)) { | ||
| 316 | (*leader_functions[i])(); | ||
| 317 | break; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | for (int i = 0; i < LEADER_MAX_LENGTH; i++) { | ||
| 321 | leader_buffer[i] = 0; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | bool process_record_user(uint16_t keycode, keyrecord_t *record) { | ||
| 326 | if (keycode < FIRST_INTERNAL_KEYCODE || keycode > LAST_INTERNAL_KEYCODE) { | ||
| 327 | return true; | ||
| 328 | } | ||
| 329 | |||
| 330 | if (record->event.pressed) { | ||
| 331 | sound_keycode_array(keycode); | ||
| 332 | } else { | ||
| 333 | process_ready_chords(); | ||
| 334 | deactivate_active_chords(keycode); | ||
| 335 | } | ||
| 336 | chord_timer = timer_read(); | ||
| 337 | leader_timer = timer_read(); | ||
| 338 | |||
| 339 | return false; | ||
| 340 | } | ||
| 341 | |||
| 342 | void matrix_scan_user(void) { | ||
| 343 | bool chord_timer_expired = timer_elapsed(chord_timer) > CHORD_TIMEOUT; | ||
| 344 | if (chord_timer_expired && keycodes_buffer_array_min(NULL)) { | ||
| 345 | process_ready_chords(); | ||
| 346 | } | ||
| 347 | |||
| 348 | bool dance_timer_expired = timer_elapsed(dance_timer) > DANCE_TIMEOUT; | ||
| 349 | if (dance_timer_expired) { // would love to have && in_dance but not sure how | ||
| 350 | process_finished_dances(); | ||
| 351 | } | ||
| 352 | |||
| 353 | bool in_command_mode = command_mode == 2; | ||
| 354 | if (in_command_mode) { | ||
| 355 | process_command(); | ||
| 356 | } | ||
| 357 | |||
| 358 | bool leader_timer_expired = timer_elapsed(leader_timer) > LEADER_TIMEOUT; | ||
| 359 | if (leader_timer_expired && in_leader_mode) { | ||
| 360 | process_leader(); | ||
| 361 | } | ||
| 362 | |||
| 363 | } | ||
| 364 | |||
| 365 | void clear(const struct Chord* self) { | ||
| 366 | if (*self->state == ACTIVATED) { | ||
| 367 | // kill all chords | ||
| 368 | struct Chord chord_storage; | ||
| 369 | struct Chord* chord_ptr; | ||
| 370 | struct Chord* chord; | ||
| 371 | |||
| 372 | for (int i = 0; i < NUMBER_OF_CHORDS; i++) { | ||
| 373 | chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]); | ||
| 374 | memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord)); | ||
| 375 | chord = &chord_storage; | ||
| 376 | |||
| 377 | *chord->state = IDLE; | ||
| 378 | |||
| 379 | if (chord->counter) { | ||
| 380 | *chord->counter = 0; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | // clear keyboard | ||
| 385 | clear_keyboard(); | ||
| 386 | send_keyboard_report(); | ||
| 387 | |||
| 388 | // switch to default pseudolayer | ||
| 389 | current_pseudolayer = DEFAULT_PSEUDOLAYER; | ||
| 390 | |||
| 391 | // clear all keyboard states | ||
| 392 | lock_next = false; | ||
| 393 | autoshift_mode = true; | ||
| 394 | command_mode = 0; | ||
| 395 | in_leader_mode = false; | ||
| 396 | leader_ind = 0; | ||
| 397 | dynamic_macro_mode = false; | ||
| 398 | a_key_went_through = false; | ||
| 399 | |||
| 400 | for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) { | ||
| 401 | dynamic_macro_buffer[i] = 0; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | } \ No newline at end of file | ||
