Merge remote-tracking branch 'upstream/develop' into xap
This commit is contained in:
		
						commit
						bf66b91433
					
				
					 5591 changed files with 131128 additions and 54530 deletions
				
			
		
							
								
								
									
										322
									
								
								quantum/action.c
									
										
									
									
									
								
							
							
						
						
									
										322
									
								
								quantum/action.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -27,6 +27,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#include "action_util.h"
 | 
			
		||||
#include "action.h"
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
#include "keycode_config.h"
 | 
			
		||||
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
#    include "backlight.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -44,10 +45,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 | 
			
		||||
int tp_buttons;
 | 
			
		||||
 | 
			
		||||
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
 | 
			
		||||
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
 | 
			
		||||
int retro_tapping_counter = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
#    include "process_auto_shift.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
 | 
			
		||||
__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; }
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +73,7 @@ void action_exec(keyevent_t event) {
 | 
			
		|||
        dprint("EVENT: ");
 | 
			
		||||
        debug_event(event);
 | 
			
		||||
        dprintln();
 | 
			
		||||
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
 | 
			
		||||
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
 | 
			
		||||
        retro_tapping_counter++;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -87,22 +92,29 @@ void action_exec(keyevent_t event) {
 | 
			
		|||
    keyrecord_t record = {.event = event};
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    if (!keymap_config.oneshot_disable) {
 | 
			
		||||
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
 | 
			
		||||
    if (has_oneshot_layer_timed_out()) {
 | 
			
		||||
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
    }
 | 
			
		||||
    if (has_oneshot_mods_timed_out()) {
 | 
			
		||||
        clear_oneshot_mods();
 | 
			
		||||
    }
 | 
			
		||||
        if (has_oneshot_layer_timed_out()) {
 | 
			
		||||
            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
        }
 | 
			
		||||
        if (has_oneshot_mods_timed_out()) {
 | 
			
		||||
            clear_oneshot_mods();
 | 
			
		||||
        }
 | 
			
		||||
#        ifdef SWAP_HANDS_ENABLE
 | 
			
		||||
    if (has_oneshot_swaphands_timed_out()) {
 | 
			
		||||
        clear_oneshot_swaphands();
 | 
			
		||||
    }
 | 
			
		||||
        if (has_oneshot_swaphands_timed_out()) {
 | 
			
		||||
            clear_oneshot_swaphands();
 | 
			
		||||
        }
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_TAPPING
 | 
			
		||||
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
    if (event.pressed) {
 | 
			
		||||
        retroshift_poll_time(&event);
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
    if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
 | 
			
		||||
        action_tapping_process(record);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +207,7 @@ void process_record(keyrecord_t *record) {
 | 
			
		|||
 | 
			
		||||
    if (!process_record_quantum(record)) {
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
        if (is_oneshot_layer_active() && record->event.pressed) {
 | 
			
		||||
        if (is_oneshot_layer_active() && record->event.pressed && !keymap_config.oneshot_disable) {
 | 
			
		||||
            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +272,7 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
#    ifdef SWAP_HANDS_ENABLE
 | 
			
		||||
        && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
 | 
			
		||||
#    endif
 | 
			
		||||
    ) {
 | 
			
		||||
        && !keymap_config.oneshot_disable) {
 | 
			
		||||
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
        do_release_oneshot = !is_oneshot_layer_active();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -274,8 +286,8 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
            if (event.pressed) {
 | 
			
		||||
                if (mods) {
 | 
			
		||||
                    if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
 | 
			
		||||
                        // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
 | 
			
		||||
                        // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
 | 
			
		||||
                        // e.g. LSFT(KC_LEFT_GUI): we don't want the LSFT to be weak as it would make it useless.
 | 
			
		||||
                        // This also makes LSFT(KC_LEFT_GUI) behave exactly the same as LGUI(KC_LEFT_SHIFT).
 | 
			
		||||
                        // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
 | 
			
		||||
                        add_mods(mods);
 | 
			
		||||
                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -304,41 +316,68 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
#    ifndef NO_ACTION_ONESHOT
 | 
			
		||||
                case MODS_ONESHOT:
 | 
			
		||||
                    // Oneshot modifier
 | 
			
		||||
                    if (event.pressed) {
 | 
			
		||||
                        if (tap_count == 0) {
 | 
			
		||||
                            dprint("MODS_TAP: Oneshot: 0\n");
 | 
			
		||||
                            register_mods(mods | get_oneshot_mods());
 | 
			
		||||
                        } else if (tap_count == 1) {
 | 
			
		||||
                            dprint("MODS_TAP: Oneshot: start\n");
 | 
			
		||||
                            set_oneshot_mods(mods | get_oneshot_mods());
 | 
			
		||||
#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
 | 
			
		||||
                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                            dprint("MODS_TAP: Toggling oneshot");
 | 
			
		||||
                            clear_oneshot_mods();
 | 
			
		||||
                            set_oneshot_locked_mods(mods);
 | 
			
		||||
                            register_mods(mods);
 | 
			
		||||
#        endif
 | 
			
		||||
                    if (keymap_config.oneshot_disable) {
 | 
			
		||||
                        if (event.pressed) {
 | 
			
		||||
                            if (mods) {
 | 
			
		||||
                                if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
 | 
			
		||||
                                    // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
 | 
			
		||||
                                    // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
 | 
			
		||||
                                    // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
 | 
			
		||||
                                    add_mods(mods);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    add_weak_mods(mods);
 | 
			
		||||
                                }
 | 
			
		||||
                                send_keyboard_report();
 | 
			
		||||
                            }
 | 
			
		||||
                            register_code(action.key.code);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            register_mods(mods | get_oneshot_mods());
 | 
			
		||||
                            unregister_code(action.key.code);
 | 
			
		||||
                            if (mods) {
 | 
			
		||||
                                if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
 | 
			
		||||
                                    del_mods(mods);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    del_weak_mods(mods);
 | 
			
		||||
                                }
 | 
			
		||||
                                send_keyboard_report();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (tap_count == 0) {
 | 
			
		||||
                            clear_oneshot_mods();
 | 
			
		||||
                            unregister_mods(mods);
 | 
			
		||||
                        } else if (tap_count == 1) {
 | 
			
		||||
                            // Retain Oneshot mods
 | 
			
		||||
                        if (event.pressed) {
 | 
			
		||||
                            if (tap_count == 0) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: 0\n");
 | 
			
		||||
                                register_mods(mods | get_oneshot_mods());
 | 
			
		||||
                            } else if (tap_count == 1) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: start\n");
 | 
			
		||||
                                set_oneshot_mods(mods | get_oneshot_mods());
 | 
			
		||||
#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
 | 
			
		||||
                            if (mods & get_mods()) {
 | 
			
		||||
                                clear_oneshot_locked_mods();
 | 
			
		||||
                            } else if (tap_count == ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                                dprint("MODS_TAP: Toggling oneshot");
 | 
			
		||||
                                clear_oneshot_mods();
 | 
			
		||||
                                set_oneshot_locked_mods(mods);
 | 
			
		||||
                                register_mods(mods);
 | 
			
		||||
#        endif
 | 
			
		||||
                            } else {
 | 
			
		||||
                                register_mods(mods | get_oneshot_mods());
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (tap_count == 0) {
 | 
			
		||||
                                clear_oneshot_mods();
 | 
			
		||||
                                unregister_mods(mods);
 | 
			
		||||
                            } else if (tap_count == 1) {
 | 
			
		||||
                                // Retain Oneshot mods
 | 
			
		||||
#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
 | 
			
		||||
                                if (mods & get_mods()) {
 | 
			
		||||
                                    clear_oneshot_locked_mods();
 | 
			
		||||
                                    clear_oneshot_mods();
 | 
			
		||||
                                    unregister_mods(mods);
 | 
			
		||||
                                }
 | 
			
		||||
                            } else if (tap_count == ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                                // Toggle Oneshot Layer
 | 
			
		||||
#        endif
 | 
			
		||||
                            } else {
 | 
			
		||||
                                clear_oneshot_mods();
 | 
			
		||||
                                unregister_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                            // Toggle Oneshot Layer
 | 
			
		||||
#        endif
 | 
			
		||||
                        } else {
 | 
			
		||||
                            clear_oneshot_mods();
 | 
			
		||||
                            unregister_mods(mods);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			@ -380,7 +419,7 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
                    } else {
 | 
			
		||||
                        if (tap_count > 0) {
 | 
			
		||||
                            dprint("MODS_TAP: Tap: unregister_code\n");
 | 
			
		||||
                            if (action.layer_tap.code == KC_CAPS) {
 | 
			
		||||
                            if (action.layer_tap.code == KC_CAPS_LOCK) {
 | 
			
		||||
                                wait_ms(TAP_HOLD_CAPS_DELAY);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                wait_ms(TAP_CODE_DELAY);
 | 
			
		||||
| 
						 | 
				
			
			@ -523,39 +562,47 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
                case OP_ONESHOT:
 | 
			
		||||
                    // Oneshot modifier
 | 
			
		||||
#            if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
 | 
			
		||||
                    do_release_oneshot = false;
 | 
			
		||||
                    if (event.pressed) {
 | 
			
		||||
                        del_mods(get_oneshot_locked_mods());
 | 
			
		||||
                        if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
 | 
			
		||||
                            reset_oneshot_layer();
 | 
			
		||||
                    if (keymap_config.oneshot_disable) {
 | 
			
		||||
                        if (event.pressed) {
 | 
			
		||||
                            layer_on(action.layer_tap.val);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            layer_off(action.layer_tap.val);
 | 
			
		||||
                            break;
 | 
			
		||||
                        } else if (tap_count < ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
#            if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
 | 
			
		||||
                        do_release_oneshot = false;
 | 
			
		||||
                        if (event.pressed) {
 | 
			
		||||
                            del_mods(get_oneshot_locked_mods());
 | 
			
		||||
                            if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
 | 
			
		||||
                                reset_oneshot_layer();
 | 
			
		||||
                                layer_off(action.layer_tap.val);
 | 
			
		||||
                                break;
 | 
			
		||||
                            } else if (tap_count < ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                                layer_on(action.layer_tap.val);
 | 
			
		||||
                                set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            add_mods(get_oneshot_locked_mods());
 | 
			
		||||
                            if (tap_count >= ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                                reset_oneshot_layer();
 | 
			
		||||
                                clear_oneshot_locked_mods();
 | 
			
		||||
                                set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                clear_oneshot_layer_state(ONESHOT_PRESSED);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
#            else
 | 
			
		||||
                        if (event.pressed) {
 | 
			
		||||
                            layer_on(action.layer_tap.val);
 | 
			
		||||
                            set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        add_mods(get_oneshot_locked_mods());
 | 
			
		||||
                        if (tap_count >= ONESHOT_TAP_TOGGLE) {
 | 
			
		||||
                            reset_oneshot_layer();
 | 
			
		||||
                            clear_oneshot_locked_mods();
 | 
			
		||||
                            set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            clear_oneshot_layer_state(ONESHOT_PRESSED);
 | 
			
		||||
                            if (tap_count > 1) {
 | 
			
		||||
                                clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
#            else
 | 
			
		||||
                    if (event.pressed) {
 | 
			
		||||
                        layer_on(action.layer_tap.val);
 | 
			
		||||
                        set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        clear_oneshot_layer_state(ONESHOT_PRESSED);
 | 
			
		||||
                        if (tap_count > 1) {
 | 
			
		||||
                            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
#            endif
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
#        endif
 | 
			
		||||
                default:
 | 
			
		||||
| 
						 | 
				
			
			@ -571,7 +618,7 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
                    } else {
 | 
			
		||||
                        if (tap_count > 0) {
 | 
			
		||||
                            dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
 | 
			
		||||
                            if (action.layer_tap.code == KC_CAPS) {
 | 
			
		||||
                            if (action.layer_tap.code == KC_CAPS_LOCK) {
 | 
			
		||||
                                wait_ms(TAP_HOLD_CAPS_DELAY);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                wait_ms(TAP_CODE_DELAY);
 | 
			
		||||
| 
						 | 
				
			
			@ -692,7 +739,7 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_TAPPING
 | 
			
		||||
#    if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
 | 
			
		||||
#    if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
 | 
			
		||||
    if (!is_tap_action(action)) {
 | 
			
		||||
        retro_tapping_counter = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -709,7 +756,11 @@ void process_action(keyrecord_t *record, action_t action) {
 | 
			
		|||
                    get_retro_tapping(get_event_keycode(record->event, false), record) &&
 | 
			
		||||
#        endif
 | 
			
		||||
                    retro_tapping_counter == 2) {
 | 
			
		||||
#        if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
                    process_auto_shift(action.layer_tap.code, record);
 | 
			
		||||
#        else
 | 
			
		||||
                    tap_code(action.layer_tap.code);
 | 
			
		||||
#        endif
 | 
			
		||||
                }
 | 
			
		||||
                retro_tapping_counter = 0;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -748,44 +799,45 @@ void register_code(uint8_t code) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef LOCKING_SUPPORT_ENABLE
 | 
			
		||||
    else if (KC_LOCKING_CAPS == code) {
 | 
			
		||||
    else if (KC_LOCKING_CAPS_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        // Resync: ignore if caps lock already is on
 | 
			
		||||
        if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_CAPSLOCK);
 | 
			
		||||
        add_key(KC_CAPS_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        wait_ms(100);
 | 
			
		||||
        del_key(KC_CAPSLOCK);
 | 
			
		||||
        del_key(KC_CAPS_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_NUM == code) {
 | 
			
		||||
    else if (KC_LOCKING_NUM_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_NUMLOCK);
 | 
			
		||||
        add_key(KC_NUM_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        wait_ms(100);
 | 
			
		||||
        del_key(KC_NUMLOCK);
 | 
			
		||||
        del_key(KC_NUM_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_SCROLL == code) {
 | 
			
		||||
    else if (KC_LOCKING_SCROLL_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_SCROLLLOCK);
 | 
			
		||||
        add_key(KC_SCROLL_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        wait_ms(100);
 | 
			
		||||
        del_key(KC_SCROLLLOCK);
 | 
			
		||||
        del_key(KC_SCROLL_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    else if IS_KEY (code) {
 | 
			
		||||
        // TODO: should push command_proc out of this block?
 | 
			
		||||
        if (command_proc(code)) return;
 | 
			
		||||
    else if
 | 
			
		||||
        IS_KEY(code) {
 | 
			
		||||
            // TODO: should push command_proc out of this block?
 | 
			
		||||
            if (command_proc(code)) return;
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
/* TODO: remove
 | 
			
		||||
| 
						 | 
				
			
			@ -802,33 +854,35 @@ void register_code(uint8_t code) {
 | 
			
		|||
        } else
 | 
			
		||||
*/
 | 
			
		||||
#endif
 | 
			
		||||
        {
 | 
			
		||||
            // Force a new key press if the key is already pressed
 | 
			
		||||
            // without this, keys with the same keycode, but different
 | 
			
		||||
            // modifiers will be reported incorrectly, see issue #1708
 | 
			
		||||
            if (is_key_pressed(keyboard_report, code)) {
 | 
			
		||||
                del_key(code);
 | 
			
		||||
            {
 | 
			
		||||
                // Force a new key press if the key is already pressed
 | 
			
		||||
                // without this, keys with the same keycode, but different
 | 
			
		||||
                // modifiers will be reported incorrectly, see issue #1708
 | 
			
		||||
                if (is_key_pressed(keyboard_report, code)) {
 | 
			
		||||
                    del_key(code);
 | 
			
		||||
                    send_keyboard_report();
 | 
			
		||||
                }
 | 
			
		||||
                add_key(code);
 | 
			
		||||
                send_keyboard_report();
 | 
			
		||||
            }
 | 
			
		||||
            add_key(code);
 | 
			
		||||
        }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_MOD(code) {
 | 
			
		||||
            add_mods(MOD_BIT(code));
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
        }
 | 
			
		||||
    } else if IS_MOD (code) {
 | 
			
		||||
        add_mods(MOD_BIT(code));
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
#ifdef EXTRAKEY_ENABLE
 | 
			
		||||
    else if IS_SYSTEM (code) {
 | 
			
		||||
        host_system_send(KEYCODE2SYSTEM(code));
 | 
			
		||||
    } else if IS_CONSUMER (code) {
 | 
			
		||||
        host_consumer_send(KEYCODE2CONSUMER(code));
 | 
			
		||||
    }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_SYSTEM(code) { host_system_send(KEYCODE2SYSTEM(code)); }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_CONSUMER(code) { host_consumer_send(KEYCODE2CONSUMER(code)); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef MOUSEKEY_ENABLE
 | 
			
		||||
    else if IS_MOUSEKEY (code) {
 | 
			
		||||
        mousekey_on(code);
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
    }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_MOUSEKEY(code) {
 | 
			
		||||
            mousekey_on(code);
 | 
			
		||||
            mousekey_send();
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -841,54 +895,58 @@ void unregister_code(uint8_t code) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef LOCKING_SUPPORT_ENABLE
 | 
			
		||||
    else if (KC_LOCKING_CAPS == code) {
 | 
			
		||||
    else if (KC_LOCKING_CAPS_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        // Resync: ignore if caps lock already is off
 | 
			
		||||
        if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_CAPSLOCK);
 | 
			
		||||
        add_key(KC_CAPS_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_CAPSLOCK);
 | 
			
		||||
        del_key(KC_CAPS_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_NUM == code) {
 | 
			
		||||
    else if (KC_LOCKING_NUM_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_NUMLOCK);
 | 
			
		||||
        add_key(KC_NUM_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_NUMLOCK);
 | 
			
		||||
        del_key(KC_NUM_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_SCROLL == code) {
 | 
			
		||||
    else if (KC_LOCKING_SCROLL_LOCK == code) {
 | 
			
		||||
#    ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return;
 | 
			
		||||
#    endif
 | 
			
		||||
        add_key(KC_SCROLLLOCK);
 | 
			
		||||
        add_key(KC_SCROLL_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_SCROLLLOCK);
 | 
			
		||||
        del_key(KC_SCROLL_LOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    else if IS_KEY (code) {
 | 
			
		||||
        del_key(code);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    } else if IS_MOD (code) {
 | 
			
		||||
        del_mods(MOD_BIT(code));
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    } else if IS_SYSTEM (code) {
 | 
			
		||||
        host_system_send(0);
 | 
			
		||||
    } else if IS_CONSUMER (code) {
 | 
			
		||||
        host_consumer_send(0);
 | 
			
		||||
    }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_KEY(code) {
 | 
			
		||||
            del_key(code);
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
        }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_MOD(code) {
 | 
			
		||||
            del_mods(MOD_BIT(code));
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
        }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_SYSTEM(code) { host_system_send(0); }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_CONSUMER(code) { host_consumer_send(0); }
 | 
			
		||||
#ifdef MOUSEKEY_ENABLE
 | 
			
		||||
    else if IS_MOUSEKEY (code) {
 | 
			
		||||
        mousekey_off(code);
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
    }
 | 
			
		||||
    else if
 | 
			
		||||
        IS_MOUSEKEY(code) {
 | 
			
		||||
            mousekey_off(code);
 | 
			
		||||
            mousekey_send();
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -907,9 +965,9 @@ void tap_code_delay(uint8_t code, uint16_t delay) {
 | 
			
		|||
 | 
			
		||||
/** \brief Tap a keycode with the default delay.
 | 
			
		||||
 *
 | 
			
		||||
 * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
 | 
			
		||||
 * \param code The basic keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
 | 
			
		||||
 */
 | 
			
		||||
void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
 | 
			
		||||
void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
 | 
			
		||||
 | 
			
		||||
/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -1033,7 +1091,7 @@ bool is_tap_action(action_t action) {
 | 
			
		|||
        case ACT_LAYER_TAP:
 | 
			
		||||
        case ACT_LAYER_TAP_EXT:
 | 
			
		||||
            switch (action.layer_tap.code) {
 | 
			
		||||
                case KC_NO ... KC_RGUI:
 | 
			
		||||
                case KC_NO ... KC_RIGHT_GUI:
 | 
			
		||||
                case OP_TAP_TOGGLE:
 | 
			
		||||
                case OP_ONESHOT:
 | 
			
		||||
                    return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1041,7 +1099,7 @@ bool is_tap_action(action_t action) {
 | 
			
		|||
            return false;
 | 
			
		||||
        case ACT_SWAP_HANDS:
 | 
			
		||||
            switch (action.swap.code) {
 | 
			
		||||
                case KC_NO ... KC_RGUI:
 | 
			
		||||
                case KC_NO ... KC_RIGHT_GUI:
 | 
			
		||||
                case OP_SH_TAP_TOGGLE:
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ extern bool disable_action_cache;
 | 
			
		|||
 | 
			
		||||
/* Code for handling one-handed key modifiers. */
 | 
			
		||||
#ifdef SWAP_HANDS_ENABLE
 | 
			
		||||
extern bool                   swap_hands;
 | 
			
		||||
extern bool           swap_hands;
 | 
			
		||||
extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
 | 
			
		||||
#    if (MATRIX_COLS <= 8)
 | 
			
		||||
typedef uint8_t swap_state_row_t;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,18 +18,20 @@
 | 
			
		|||
#    define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
 | 
			
		||||
#    define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
 | 
			
		||||
#    define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
 | 
			
		||||
#ifndef COMBO_ENABLE
 | 
			
		||||
#    define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
 | 
			
		||||
#else
 | 
			
		||||
#    define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
 | 
			
		||||
#endif
 | 
			
		||||
#    ifndef COMBO_ENABLE
 | 
			
		||||
#        define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
 | 
			
		||||
#    else
 | 
			
		||||
#        define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
 | 
			
		||||
uint16_t g_tapping_term = TAPPING_TERM;
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return g_tapping_term; }
 | 
			
		||||
 | 
			
		||||
#    ifdef TAPPING_TERM_PER_KEY
 | 
			
		||||
#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key))
 | 
			
		||||
#    else
 | 
			
		||||
#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
 | 
			
		||||
#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < g_tapping_term)
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef TAPPING_FORCE_HOLD_PER_KEY
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +46,10 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
 | 
			
		|||
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
#        include "process_auto_shift.h"
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
static keyrecord_t tapping_key                         = {};
 | 
			
		||||
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
 | 
			
		||||
static uint8_t     waiting_buffer_head                 = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,12 +113,29 @@ void action_tapping_process(keyrecord_t record) {
 | 
			
		|||
/* return true when key event is processed or consumed. */
 | 
			
		||||
bool process_tapping(keyrecord_t *keyp) {
 | 
			
		||||
    keyevent_t event = keyp->event;
 | 
			
		||||
#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(TAPPING_TERM_PER_KEY) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
 | 
			
		||||
    uint16_t tapping_keycode = get_record_keycode(&tapping_key, false);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    // if tapping
 | 
			
		||||
    if (IS_TAPPING_PRESSED()) {
 | 
			
		||||
        if (WITHIN_TAPPING_TERM(event)) {
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        if (WITHIN_TAPPING_TERM(event)
 | 
			
		||||
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
            || (
 | 
			
		||||
#        ifdef RETRO_TAPPING_PER_KEY
 | 
			
		||||
                get_retro_tapping(tapping_keycode, keyp) &&
 | 
			
		||||
#        endif
 | 
			
		||||
                (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
 | 
			
		||||
            )
 | 
			
		||||
#    endif
 | 
			
		||||
        ) {
 | 
			
		||||
            // clang-format on
 | 
			
		||||
            if (tapping_key.tap.count == 0) {
 | 
			
		||||
                if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
 | 
			
		||||
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
                    retroshift_swap_times();
 | 
			
		||||
#    endif
 | 
			
		||||
                    // first tap!
 | 
			
		||||
                    debug("Tapping: First tap(0->1).\n");
 | 
			
		||||
                    tapping_key.tap.count = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -128,22 +151,70 @@ bool process_tapping(keyrecord_t *keyp) {
 | 
			
		|||
                 * This can register the key before settlement of tapping,
 | 
			
		||||
                 * useful for long TAPPING_TERM but may prevent fast typing.
 | 
			
		||||
                 */
 | 
			
		||||
#    if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
 | 
			
		||||
                else if (((
 | 
			
		||||
                // clang-format off
 | 
			
		||||
#    if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
 | 
			
		||||
                else if (
 | 
			
		||||
                    (
 | 
			
		||||
                        (
 | 
			
		||||
                            (
 | 
			
		||||
#        ifdef TAPPING_TERM_PER_KEY
 | 
			
		||||
                              get_tapping_term(get_record_keycode(&tapping_key, false), keyp)
 | 
			
		||||
                                get_tapping_term(tapping_keycode, keyp)
 | 
			
		||||
#        else
 | 
			
		||||
                              TAPPING_TERM
 | 
			
		||||
                                g_tapping_term
 | 
			
		||||
#        endif
 | 
			
		||||
                              >= 500)
 | 
			
		||||
                                >= 500
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
#        ifdef PERMISSIVE_HOLD_PER_KEY
 | 
			
		||||
                          || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp)
 | 
			
		||||
                            || get_permissive_hold(tapping_keycode, keyp)
 | 
			
		||||
#        elif defined(PERMISSIVE_HOLD)
 | 
			
		||||
                          || true
 | 
			
		||||
                            || true
 | 
			
		||||
#        endif
 | 
			
		||||
                              ) &&
 | 
			
		||||
                         IS_RELEASED(event) && waiting_buffer_typed(event)) {
 | 
			
		||||
                        ) && IS_RELEASED(event) && waiting_buffer_typed(event)
 | 
			
		||||
                    )
 | 
			
		||||
                    // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
 | 
			
		||||
                    // unnecessarily and fixes them for Layer Taps.
 | 
			
		||||
#        if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
                    || (
 | 
			
		||||
#            ifdef RETRO_TAPPING_PER_KEY
 | 
			
		||||
                        get_retro_tapping(tapping_keycode, keyp) &&
 | 
			
		||||
#            endif
 | 
			
		||||
                        (
 | 
			
		||||
                            // Rolled over the two keys.
 | 
			
		||||
                            (
 | 
			
		||||
                                (
 | 
			
		||||
                                    false
 | 
			
		||||
#            if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
 | 
			
		||||
                                    || (
 | 
			
		||||
                                        IS_LT(tapping_keycode)
 | 
			
		||||
#                ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
 | 
			
		||||
                                        && get_hold_on_other_key_press(tapping_keycode, keyp)
 | 
			
		||||
#                endif
 | 
			
		||||
                                    )
 | 
			
		||||
#            endif
 | 
			
		||||
#            if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
 | 
			
		||||
                                    || (
 | 
			
		||||
                                        IS_MT(tapping_keycode)
 | 
			
		||||
#                ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
 | 
			
		||||
                                        && !get_ignore_mod_tap_interrupt(tapping_keycode, keyp)
 | 
			
		||||
#                endif
 | 
			
		||||
                                    )
 | 
			
		||||
#            endif
 | 
			
		||||
                                ) && tapping_key.tap.interrupted == true
 | 
			
		||||
                            )
 | 
			
		||||
                            // Makes Retro Shift ignore [IGNORE_MOD_TAP_INTERRUPT's
 | 
			
		||||
                            // effects on nested taps for MTs and the default
 | 
			
		||||
                            // behavior of LTs] below TAPPING_TERM or RETRO_SHIFT.
 | 
			
		||||
                            || (
 | 
			
		||||
                                IS_RETRO(tapping_keycode)
 | 
			
		||||
                                && (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row)
 | 
			
		||||
                                && IS_RELEASED(event) && waiting_buffer_typed(event)
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
#        endif
 | 
			
		||||
                ) {
 | 
			
		||||
                    // clang-format on
 | 
			
		||||
                    debug("Tapping: End. No tap. Interfered by typing key\n");
 | 
			
		||||
                    process_record(&tapping_key);
 | 
			
		||||
                    tapping_key = (keyrecord_t){};
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +252,7 @@ bool process_tapping(keyrecord_t *keyp) {
 | 
			
		|||
                        tapping_key.tap.interrupted = true;
 | 
			
		||||
#    if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
 | 
			
		||||
#        if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
 | 
			
		||||
                        if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp))
 | 
			
		||||
                        if (get_hold_on_other_key_press(tapping_keycode, keyp))
 | 
			
		||||
#        endif
 | 
			
		||||
                        {
 | 
			
		||||
                            debug("Tapping: End. No tap. Interfered by pressed key\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -212,11 +283,15 @@ bool process_tapping(keyrecord_t *keyp) {
 | 
			
		|||
                    if (tapping_key.tap.count > 1) {
 | 
			
		||||
                        debug("Tapping: Start new tap with releasing last tap(>1).\n");
 | 
			
		||||
                        // unregister key
 | 
			
		||||
                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
 | 
			
		||||
#ifdef COMBO_ENABLE
 | 
			
		||||
                                .keycode = tapping_key.keycode,
 | 
			
		||||
#endif
 | 
			
		||||
                                });
 | 
			
		||||
                        process_record(&(keyrecord_t){
 | 
			
		||||
                            .tap           = tapping_key.tap,
 | 
			
		||||
                            .event.key     = tapping_key.event.key,
 | 
			
		||||
                            .event.time    = event.time,
 | 
			
		||||
                            .event.pressed = false,
 | 
			
		||||
#    ifdef COMBO_ENABLE
 | 
			
		||||
                            .keycode = tapping_key.keycode,
 | 
			
		||||
#    endif
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        debug("Tapping: Start while last tap(1).\n");
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -254,11 +329,15 @@ bool process_tapping(keyrecord_t *keyp) {
 | 
			
		|||
                    if (tapping_key.tap.count > 1) {
 | 
			
		||||
                        debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
 | 
			
		||||
                        // unregister key
 | 
			
		||||
                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
 | 
			
		||||
#ifdef COMBO_ENABLE
 | 
			
		||||
                                .keycode = tapping_key.keycode,
 | 
			
		||||
#endif
 | 
			
		||||
                                });
 | 
			
		||||
                        process_record(&(keyrecord_t){
 | 
			
		||||
                            .tap           = tapping_key.tap,
 | 
			
		||||
                            .event.key     = tapping_key.event.key,
 | 
			
		||||
                            .event.time    = event.time,
 | 
			
		||||
                            .event.pressed = false,
 | 
			
		||||
#    ifdef COMBO_ENABLE
 | 
			
		||||
                            .keycode = tapping_key.keycode,
 | 
			
		||||
#    endif
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        debug("Tapping: Start while last timeout tap(1).\n");
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -276,14 +355,25 @@ bool process_tapping(keyrecord_t *keyp) {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else if (IS_TAPPING_RELEASED()) {
 | 
			
		||||
        if (WITHIN_TAPPING_TERM(event)) {
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        if (WITHIN_TAPPING_TERM(event)
 | 
			
		||||
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
 | 
			
		||||
            || (
 | 
			
		||||
#        ifdef RETRO_TAPPING_PER_KEY
 | 
			
		||||
                get_retro_tapping(tapping_keycode, keyp) &&
 | 
			
		||||
#        endif
 | 
			
		||||
                (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
 | 
			
		||||
            )
 | 
			
		||||
#    endif
 | 
			
		||||
        ) {
 | 
			
		||||
            // clang-format on
 | 
			
		||||
            if (event.pressed) {
 | 
			
		||||
                if (IS_TAPPING_RECORD(keyp)) {
 | 
			
		||||
//#    ifndef TAPPING_FORCE_HOLD
 | 
			
		||||
#    if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
 | 
			
		||||
                    if (
 | 
			
		||||
#        ifdef TAPPING_FORCE_HOLD_PER_KEY
 | 
			
		||||
                        !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) &&
 | 
			
		||||
                        !get_tapping_force_hold(tapping_keycode, keyp) &&
 | 
			
		||||
#        endif
 | 
			
		||||
                        !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
 | 
			
		||||
                        // sequential tap.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,10 +33,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
 | 
			
		||||
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
 | 
			
		||||
void     action_tapping_process(keyrecord_t record);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
bool     get_permissive_hold(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
bool     get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
bool     get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
bool     get_retro_tapping(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
 | 
			
		||||
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
 | 
			
		||||
extern uint16_t g_tapping_term;
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,16 +32,6 @@ static uint8_t weak_override_mods = 0;
 | 
			
		|||
static uint8_t suppressed_mods    = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USB_6KRO_ENABLE
 | 
			
		||||
#    define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
 | 
			
		||||
#    define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
 | 
			
		||||
#    define RO_INC(a) RO_ADD(a, 1)
 | 
			
		||||
#    define RO_DEC(a) RO_SUB(a, 1)
 | 
			
		||||
static int8_t cb_head  = 0;
 | 
			
		||||
static int8_t cb_tail  = 0;
 | 
			
		||||
static int8_t cb_count = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// TODO: pointer variable is not needed
 | 
			
		||||
// report_keyboard_t keyboard_report = {};
 | 
			
		||||
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +170,7 @@ void reset_oneshot_layer(void) {
 | 
			
		|||
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
 | 
			
		||||
    uint8_t start_state = oneshot_layer_data;
 | 
			
		||||
    oneshot_layer_data &= ~state;
 | 
			
		||||
    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
 | 
			
		||||
    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && !keymap_config.oneshot_disable) {
 | 
			
		||||
        layer_off(get_oneshot_layer());
 | 
			
		||||
        reset_oneshot_layer();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -199,6 +189,7 @@ void oneshot_set(bool active) {
 | 
			
		|||
    if (keymap_config.oneshot_disable != active) {
 | 
			
		||||
        keymap_config.oneshot_disable = active;
 | 
			
		||||
        eeconfig_update_keymap(keymap_config.raw);
 | 
			
		||||
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
        dprintf("Oneshot: active: %d\n", active);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										182
									
								
								quantum/api.c
									
										
									
									
									
								
							
							
						
						
									
										182
									
								
								quantum/api.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,182 +0,0 @@
 | 
			
		|||
/* Copyright 2016 Jack Humbert
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "api.h"
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
 | 
			
		||||
void dword_to_bytes(uint32_t dword, uint8_t* bytes) {
 | 
			
		||||
    bytes[0] = (dword >> 24) & 0xFF;
 | 
			
		||||
    bytes[1] = (dword >> 16) & 0xFF;
 | 
			
		||||
    bytes[2] = (dword >> 8) & 0xFF;
 | 
			
		||||
    bytes[3] = (dword >> 0) & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index) { return ((uint32_t)bytes[index + 0] << 24) | ((uint32_t)bytes[index + 1] << 16) | ((uint32_t)bytes[index + 2] << 8) | (uint32_t)bytes[index + 3]; }
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data) { return process_api_keyboard(length, data); }
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data) { return process_api_user(length, data); }
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data) { return true; }
 | 
			
		||||
 | 
			
		||||
void process_api(uint16_t length, uint8_t* data) {
 | 
			
		||||
    // SEND_STRING("\nRX: ");
 | 
			
		||||
    // for (uint8_t i = 0; i < length; i++) {
 | 
			
		||||
    //     send_byte(data[i]);
 | 
			
		||||
    //     SEND_STRING(" ");
 | 
			
		||||
    // }
 | 
			
		||||
    if (!process_api_quantum(length, data)) return;
 | 
			
		||||
 | 
			
		||||
    switch (data[0]) {
 | 
			
		||||
        case MT_SET_DATA:
 | 
			
		||||
            switch (data[1]) {
 | 
			
		||||
                case DT_DEFAULT_LAYER: {
 | 
			
		||||
                    eeconfig_update_default_layer(data[2]);
 | 
			
		||||
                    default_layer_set((uint32_t)(data[2]));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_KEYMAP_OPTIONS: {
 | 
			
		||||
                    eeconfig_update_keymap(data[2]);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_RGBLIGHT: {
 | 
			
		||||
#ifdef RGBLIGHT_ENABLE
 | 
			
		||||
                    uint32_t rgblight = bytes_to_dword(data, 2);
 | 
			
		||||
                    eeconfig_update_rgblight(rgblight);
 | 
			
		||||
#endif
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        case MT_GET_DATA:
 | 
			
		||||
            switch (data[1]) {
 | 
			
		||||
                case DT_HANDSHAKE: {
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_HANDSHAKE, NULL, 0);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_DEBUG: {
 | 
			
		||||
                    uint8_t debug_bytes[1] = {eeprom_read_byte(EECONFIG_DEBUG)};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_DEBUG, debug_bytes, 1);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_DEFAULT_LAYER: {
 | 
			
		||||
                    uint8_t default_bytes[1] = {eeprom_read_byte(EECONFIG_DEFAULT_LAYER)};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_DEFAULT_LAYER, default_bytes, 1);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_CURRENT_LAYER: {
 | 
			
		||||
                    uint8_t layer_state_bytes[4];
 | 
			
		||||
                    dword_to_bytes(layer_state, layer_state_bytes);
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_CURRENT_LAYER, layer_state_bytes, 4);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_AUDIO: {
 | 
			
		||||
#ifdef AUDIO_ENABLE
 | 
			
		||||
                    uint8_t audio_bytes[1] = {eeprom_read_byte(EECONFIG_AUDIO)};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_AUDIO, audio_bytes, 1);
 | 
			
		||||
#else
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_AUDIO, NULL, 0);
 | 
			
		||||
#endif
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_BACKLIGHT: {
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
                    uint8_t backlight_bytes[1] = {eeprom_read_byte(EECONFIG_BACKLIGHT)};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_BACKLIGHT, backlight_bytes, 1);
 | 
			
		||||
#else
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_BACKLIGHT, NULL, 0);
 | 
			
		||||
#endif
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_RGBLIGHT: {
 | 
			
		||||
#ifdef RGBLIGHT_ENABLE
 | 
			
		||||
                    uint8_t rgblight_bytes[4];
 | 
			
		||||
                    dword_to_bytes(eeconfig_read_rgblight(), rgblight_bytes);
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_RGBLIGHT, rgblight_bytes, 4);
 | 
			
		||||
#else
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_RGBLIGHT, NULL, 0);
 | 
			
		||||
#endif
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_KEYMAP_OPTIONS: {
 | 
			
		||||
                    uint8_t keymap_bytes[1] = {eeconfig_read_keymap()};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_KEYMAP_OPTIONS, keymap_bytes, 1);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DT_KEYMAP_SIZE: {
 | 
			
		||||
                    uint8_t keymap_size[2] = {MATRIX_ROWS, MATRIX_COLS};
 | 
			
		||||
                    MT_GET_DATA_ACK(DT_KEYMAP_SIZE, keymap_size, 2);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // This may be too much
 | 
			
		||||
                // case DT_KEYMAP: {
 | 
			
		||||
                //     uint8_t keymap_data[MATRIX_ROWS * MATRIX_COLS * 4 + 3];
 | 
			
		||||
                //     keymap_data[0] = data[2];
 | 
			
		||||
                //     keymap_data[1] = MATRIX_ROWS;
 | 
			
		||||
                //     keymap_data[2] = MATRIX_COLS;
 | 
			
		||||
                //     for (int i = 0; i < MATRIX_ROWS; i++) {
 | 
			
		||||
                //         for (int j = 0; j < MATRIX_COLS; j++) {
 | 
			
		||||
                //             keymap_data[3 + (i*MATRIX_COLS*2) + (j*2)] = pgm_read_word(&keymaps[data[2]][i][j]) >> 8;
 | 
			
		||||
                //             keymap_data[3 + (i*MATRIX_COLS*2) + (j*2) + 1] = pgm_read_word(&keymaps[data[2]][i][j]) & 0xFF;
 | 
			
		||||
                //         }
 | 
			
		||||
                //     }
 | 
			
		||||
                //     MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, MATRIX_ROWS * MATRIX_COLS * 4 + 3);
 | 
			
		||||
                //     // uint8_t keymap_data[5];
 | 
			
		||||
                //     // keymap_data[0] = data[2];
 | 
			
		||||
                //     // keymap_data[1] = data[3];
 | 
			
		||||
                //     // keymap_data[2] = data[4];
 | 
			
		||||
                //     // keymap_data[3] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) >> 8;
 | 
			
		||||
                //     // keymap_data[4] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) & 0xFF;
 | 
			
		||||
 | 
			
		||||
                //     // MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, 5);
 | 
			
		||||
                //     break;
 | 
			
		||||
                // }
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_SET_DATA_ACK:
 | 
			
		||||
        case MT_GET_DATA_ACK:
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_SEND_DATA:
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_SEND_DATA_ACK:
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_EXE_ACTION:
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_EXE_ACTION_ACK:
 | 
			
		||||
            break;
 | 
			
		||||
        case MT_TYPE_ERROR:
 | 
			
		||||
            break;
 | 
			
		||||
        default:;  // command not recognised
 | 
			
		||||
            SEND_BYTES(MT_TYPE_ERROR, DT_NONE, data, length);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
            // #ifdef RGBLIGHT_ENABLE
 | 
			
		||||
            // case 0x27: ; // RGB LED functions
 | 
			
		||||
            //     switch (*data++) {
 | 
			
		||||
            //         case 0x00: ; // Update HSV
 | 
			
		||||
            //             rgblight_sethsv((data[0] << 8 | data[1]) % 360, data[2], data[3]);
 | 
			
		||||
            //             break;
 | 
			
		||||
            //         case 0x01: ; // Update RGB
 | 
			
		||||
            //             break;
 | 
			
		||||
            //         case 0x02: ; // Update mode
 | 
			
		||||
            //             rgblight_mode(data[0]);
 | 
			
		||||
            //             break;
 | 
			
		||||
            //     }
 | 
			
		||||
            //     break;
 | 
			
		||||
            // #endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,55 +0,0 @@
 | 
			
		|||
/* Copyright 2016 Jack Humbert
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __AVR__
 | 
			
		||||
#    include "lufa.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum MESSAGE_TYPE {
 | 
			
		||||
    MT_GET_DATA       = 0x10,  // Get data from keyboard
 | 
			
		||||
    MT_GET_DATA_ACK   = 0x11,  // returned data to process (ACK)
 | 
			
		||||
    MT_SET_DATA       = 0x20,  // Set data on keyboard
 | 
			
		||||
    MT_SET_DATA_ACK   = 0x21,  // returned data to confirm (ACK)
 | 
			
		||||
    MT_SEND_DATA      = 0x30,  // Sending data/action from keyboard
 | 
			
		||||
    MT_SEND_DATA_ACK  = 0x31,  // returned data/action confirmation (ACK)
 | 
			
		||||
    MT_EXE_ACTION     = 0x40,  // executing actions on keyboard
 | 
			
		||||
    MT_EXE_ACTION_ACK = 0x41,  // return confirmation/value (ACK)
 | 
			
		||||
    MT_TYPE_ERROR     = 0x80   // type not recognised (ACK)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum DATA_TYPE { DT_NONE = 0x00, DT_HANDSHAKE, DT_DEFAULT_LAYER, DT_CURRENT_LAYER, DT_KEYMAP_OPTIONS, DT_BACKLIGHT, DT_RGBLIGHT, DT_UNICODE, DT_DEBUG, DT_AUDIO, DT_QUANTUM_ACTION, DT_KEYBOARD_ACTION, DT_USER_ACTION, DT_KEYMAP_SIZE, DT_KEYMAP };
 | 
			
		||||
 | 
			
		||||
void     dword_to_bytes(uint32_t dword, uint8_t* bytes);
 | 
			
		||||
uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index);
 | 
			
		||||
 | 
			
		||||
#define MT_GET_DATA(data_type, data, length) SEND_BYTES(MT_GET_DATA, data_type, data, length)
 | 
			
		||||
#define MT_GET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_GET_DATA_ACK, data_type, data, length)
 | 
			
		||||
#define MT_SET_DATA(data_type, data, length) SEND_BYTES(MT_SET_DATA, data_type, data, length)
 | 
			
		||||
#define MT_SET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SET_DATA_ACK, data_type, data, length)
 | 
			
		||||
#define MT_SEND_DATA(data_type, data, length) SEND_BYTES(MT_SEND_DATA, data_type, data, length)
 | 
			
		||||
#define MT_SEND_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SEND_DATA_ACK, data_type, data, length)
 | 
			
		||||
#define MT_EXE_ACTION(data_type, data, length) SEND_BYTES(MT_EXE_ACTION, data_type, data, length)
 | 
			
		||||
#define MT_EXE_ACTION_ACK(data_type, data, length) SEND_BYTES(MT_EXE_ACTION_ACK, data_type, data, length)
 | 
			
		||||
 | 
			
		||||
void process_api(uint16_t length, uint8_t* data);
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data);
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data);
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,72 +0,0 @@
 | 
			
		|||
/* Copyright 2016 Jack Humbert, Fred Sundvik
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
#include "api_sysex.h"
 | 
			
		||||
#include "sysex_tools.h"
 | 
			
		||||
#include "print.h"
 | 
			
		||||
#include "qmk_midi.h"
 | 
			
		||||
 | 
			
		||||
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length) {
 | 
			
		||||
    // SEND_STRING("\nTX: ");
 | 
			
		||||
    // for (uint8_t i = 0; i < length; i++) {
 | 
			
		||||
    //     send_byte(bytes[i]);
 | 
			
		||||
    //     SEND_STRING(" ");
 | 
			
		||||
    // }
 | 
			
		||||
    if (length > API_SYSEX_MAX_SIZE) {
 | 
			
		||||
        xprintf("Sysex msg too big %d %d %d", message_type, data_type, length);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The buffer size required is calculated as the following
 | 
			
		||||
    // API_SYSEX_MAX_SIZE is the maximum length
 | 
			
		||||
    // In addition to that we have a two byte message header consisting of the message_type and data_type
 | 
			
		||||
    // This has to be encoded with an additional overhead of one byte for every starting 7 bytes
 | 
			
		||||
    // We just add one extra byte in case it's not divisible by 7
 | 
			
		||||
    // Then we have an unencoded header consisting of 4 bytes
 | 
			
		||||
    // Plus a one byte terminator
 | 
			
		||||
    const unsigned message_header    = 2;
 | 
			
		||||
    const unsigned unencoded_message = API_SYSEX_MAX_SIZE + message_header;
 | 
			
		||||
    const unsigned encoding_overhead = unencoded_message / 7 + 1;
 | 
			
		||||
    const unsigned encoded_size      = unencoded_message + encoding_overhead;
 | 
			
		||||
    const unsigned unencoded_header  = 4;
 | 
			
		||||
    const unsigned terminator        = 1;
 | 
			
		||||
    const unsigned buffer_size       = encoded_size + unencoded_header + terminator;
 | 
			
		||||
    uint8_t        buffer[encoded_size + unencoded_header + terminator];
 | 
			
		||||
    // The unencoded header
 | 
			
		||||
    buffer[0] = 0xF0;
 | 
			
		||||
    buffer[1] = 0x00;
 | 
			
		||||
    buffer[2] = 0x00;
 | 
			
		||||
    buffer[3] = 0x00;
 | 
			
		||||
 | 
			
		||||
    // We copy the message to the end of the array, this way we can do an inplace encoding, using the same
 | 
			
		||||
    // buffer for both input and output
 | 
			
		||||
    const unsigned message_size    = length + message_header;
 | 
			
		||||
    uint8_t*       unencoded_start = buffer + buffer_size - message_size;
 | 
			
		||||
    uint8_t*       ptr             = unencoded_start;
 | 
			
		||||
    *(ptr++)                       = message_type;
 | 
			
		||||
    *(ptr++)                       = data_type;
 | 
			
		||||
    memcpy(ptr, bytes, length);
 | 
			
		||||
 | 
			
		||||
    unsigned encoded_length = sysex_encode(buffer + unencoded_header, unencoded_start, message_size);
 | 
			
		||||
    unsigned final_size     = unencoded_header + encoded_length + terminator;
 | 
			
		||||
    buffer[final_size - 1]  = 0xF7;
 | 
			
		||||
    midi_send_array(&midi_device, final_size, buffer);
 | 
			
		||||
 | 
			
		||||
    // SEND_STRING("\nTD: ");
 | 
			
		||||
    // for (uint8_t i = 0; i < encoded_length + 5; i++) {
 | 
			
		||||
    //     send_byte(buffer[i]);
 | 
			
		||||
    //     SEND_STRING(" ");
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -26,17 +26,12 @@
 | 
			
		|||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
#    include <avr/io.h>
 | 
			
		||||
#    if defined(AUDIO_DRIVER_PWM)
 | 
			
		||||
#        include "driver_avr_pwm.h"
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
#    if defined(AUDIO_DRIVER_PWM)
 | 
			
		||||
#        include "driver_chibios_pwm.h"
 | 
			
		||||
#    elif defined(AUDIO_DRIVER_DAC)
 | 
			
		||||
#        include "driver_chibios_dac.h"
 | 
			
		||||
#    endif
 | 
			
		||||
#if defined(AUDIO_DRIVER_PWM)
 | 
			
		||||
#    include "audio_pwm.h"
 | 
			
		||||
#elif defined(AUDIO_DRIVER_DAC)
 | 
			
		||||
#    include "audio_dac.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef union {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
/* Copyright 2020 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
| 
						 | 
				
			
			@ -1,332 +0,0 @@
 | 
			
		|||
/* Copyright 2016 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
#    include <avr/pgmspace.h>
 | 
			
		||||
#    include <avr/interrupt.h>
 | 
			
		||||
#    include <avr/io.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
extern bool    playing_note;
 | 
			
		||||
extern bool    playing_melody;
 | 
			
		||||
extern uint8_t note_timbre;
 | 
			
		||||
 | 
			
		||||
#define CPU_PRESCALER 8
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Audio Driver: PWM
 | 
			
		||||
 | 
			
		||||
  drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
 | 
			
		||||
 | 
			
		||||
  the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
 | 
			
		||||
  and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
 | 
			
		||||
 | 
			
		||||
  alternatively, the PWM pins on PORTB can be used as only/primary speaker
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
 | 
			
		||||
#    error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
 | 
			
		||||
#    define AUDIO1_PIN_SET
 | 
			
		||||
#    define AUDIO1_TIMSKx TIMSK3
 | 
			
		||||
#    define AUDIO1_TCCRxA TCCR3A
 | 
			
		||||
#    define AUDIO1_TCCRxB TCCR3B
 | 
			
		||||
#    define AUDIO1_ICRx ICR3
 | 
			
		||||
#    define AUDIO1_WGMx0 WGM30
 | 
			
		||||
#    define AUDIO1_WGMx1 WGM31
 | 
			
		||||
#    define AUDIO1_WGMx2 WGM32
 | 
			
		||||
#    define AUDIO1_WGMx3 WGM33
 | 
			
		||||
#    define AUDIO1_CSx0 CS30
 | 
			
		||||
#    define AUDIO1_CSx1 CS31
 | 
			
		||||
#    define AUDIO1_CSx2 CS32
 | 
			
		||||
 | 
			
		||||
#    if (AUDIO_PIN == C6)
 | 
			
		||||
#        define AUDIO1_COMxy0 COM3A0
 | 
			
		||||
#        define AUDIO1_COMxy1 COM3A1
 | 
			
		||||
#        define AUDIO1_OCIExy OCIE3A
 | 
			
		||||
#        define AUDIO1_OCRxy OCR3A
 | 
			
		||||
#        define AUDIO1_PIN C6
 | 
			
		||||
#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
 | 
			
		||||
#    elif (AUDIO_PIN == C5)
 | 
			
		||||
#        define AUDIO1_COMxy0 COM3B0
 | 
			
		||||
#        define AUDIO1_COMxy1 COM3B1
 | 
			
		||||
#        define AUDIO1_OCIExy OCIE3B
 | 
			
		||||
#        define AUDIO1_OCRxy OCR3B
 | 
			
		||||
#        define AUDIO1_PIN C5
 | 
			
		||||
#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
 | 
			
		||||
#    elif (AUDIO_PIN == C4)
 | 
			
		||||
#        define AUDIO1_COMxy0 COM3C0
 | 
			
		||||
#        define AUDIO1_COMxy1 COM3C1
 | 
			
		||||
#        define AUDIO1_OCIExy OCIE3C
 | 
			
		||||
#        define AUDIO1_OCRxy OCR3C
 | 
			
		||||
#        define AUDIO1_PIN C4
 | 
			
		||||
#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
 | 
			
		||||
#    error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
 | 
			
		||||
#    error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
 | 
			
		||||
#    error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
 | 
			
		||||
#    define AUDIO2_PIN_SET
 | 
			
		||||
#    define AUDIO2_TIMSKx TIMSK1
 | 
			
		||||
#    define AUDIO2_TCCRxA TCCR1A
 | 
			
		||||
#    define AUDIO2_TCCRxB TCCR1B
 | 
			
		||||
#    define AUDIO2_ICRx ICR1
 | 
			
		||||
#    define AUDIO2_WGMx0 WGM10
 | 
			
		||||
#    define AUDIO2_WGMx1 WGM11
 | 
			
		||||
#    define AUDIO2_WGMx2 WGM12
 | 
			
		||||
#    define AUDIO2_WGMx3 WGM13
 | 
			
		||||
#    define AUDIO2_CSx0 CS10
 | 
			
		||||
#    define AUDIO2_CSx1 CS11
 | 
			
		||||
#    define AUDIO2_CSx2 CS12
 | 
			
		||||
 | 
			
		||||
#    if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
 | 
			
		||||
#        define AUDIO2_COMxy0 COM1A0
 | 
			
		||||
#        define AUDIO2_COMxy1 COM1A1
 | 
			
		||||
#        define AUDIO2_OCIExy OCIE1A
 | 
			
		||||
#        define AUDIO2_OCRxy OCR1A
 | 
			
		||||
#        define AUDIO2_PIN B5
 | 
			
		||||
#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
 | 
			
		||||
#    elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
 | 
			
		||||
#        define AUDIO2_COMxy0 COM1B0
 | 
			
		||||
#        define AUDIO2_COMxy1 COM1B1
 | 
			
		||||
#        define AUDIO2_OCIExy OCIE1B
 | 
			
		||||
#        define AUDIO2_OCRxy OCR1B
 | 
			
		||||
#        define AUDIO2_PIN B6
 | 
			
		||||
#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
 | 
			
		||||
#    elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
 | 
			
		||||
#        define AUDIO2_COMxy0 COM1C0
 | 
			
		||||
#        define AUDIO2_COMxy1 COM1C1
 | 
			
		||||
#        define AUDIO2_OCIExy OCIE1C
 | 
			
		||||
#        define AUDIO2_OCRxy OCR1C
 | 
			
		||||
#        define AUDIO2_PIN B7
 | 
			
		||||
#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
 | 
			
		||||
#    elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
 | 
			
		||||
#        pragma message "Audio support for ATmega32A is experimental and can cause crashes."
 | 
			
		||||
#        undef AUDIO2_TIMSKx
 | 
			
		||||
#        define AUDIO2_TIMSKx TIMSK
 | 
			
		||||
#        define AUDIO2_COMxy0 COM1A0
 | 
			
		||||
#        define AUDIO2_COMxy1 COM1A1
 | 
			
		||||
#        define AUDIO2_OCIExy OCIE1A
 | 
			
		||||
#        define AUDIO2_OCRxy OCR1A
 | 
			
		||||
#        define AUDIO2_PIN D5
 | 
			
		||||
#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
 | 
			
		||||
#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
 | 
			
		||||
#    pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
 | 
			
		||||
// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
 | 
			
		||||
#endif
 | 
			
		||||
// -----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
static float channel_1_frequency = 0.0f;
 | 
			
		||||
void         channel_1_set_frequency(float freq) {
 | 
			
		||||
    if (freq == 0.0f)  // a pause/rest is a valid "note" with freq=0
 | 
			
		||||
    {
 | 
			
		||||
        // disable the output, but keep the pwm-ISR going (with the previous
 | 
			
		||||
        // frequency) so the audio-state keeps getting updated
 | 
			
		||||
        // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
 | 
			
		||||
        AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);  // enable output, PWM mode
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    channel_1_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    // set pwm period
 | 
			
		||||
    AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
 | 
			
		||||
    // and duty cycle
 | 
			
		||||
    AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_1_start(void) {
 | 
			
		||||
    // enable timer-counter ISR
 | 
			
		||||
    AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
 | 
			
		||||
    // enable timer-counter output
 | 
			
		||||
    AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_1_stop(void) {
 | 
			
		||||
    // disable timer-counter ISR
 | 
			
		||||
    AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
 | 
			
		||||
    // disable timer-counter output
 | 
			
		||||
    AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO2_PIN_SET
 | 
			
		||||
static float channel_2_frequency = 0.0f;
 | 
			
		||||
void         channel_2_set_frequency(float freq) {
 | 
			
		||||
    if (freq == 0.0f) {
 | 
			
		||||
        AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    channel_2_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    AUDIO2_ICRx  = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
 | 
			
		||||
    AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float channel_2_get_frequency(void) { return channel_2_frequency; }
 | 
			
		||||
 | 
			
		||||
void channel_2_start(void) {
 | 
			
		||||
    AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
 | 
			
		||||
    AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_2_stop(void) {
 | 
			
		||||
    AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
 | 
			
		||||
    AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void audio_driver_initialize() {
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    setPinOutput(AUDIO1_PIN);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO2_PIN_SET
 | 
			
		||||
    channel_2_stop();
 | 
			
		||||
    setPinOutput(AUDIO2_PIN);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
 | 
			
		||||
    // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
 | 
			
		||||
    //   OC3A -- PC6
 | 
			
		||||
    //   OC3B -- PC5
 | 
			
		||||
    //   OC3C -- PC4
 | 
			
		||||
    //   OC1A -- PB5
 | 
			
		||||
    //   OC1B -- PB6
 | 
			
		||||
    //   OC1C -- PB7
 | 
			
		||||
 | 
			
		||||
    // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
 | 
			
		||||
    //   OCR3A - PC6
 | 
			
		||||
    //   OCR3B - PC5
 | 
			
		||||
    //   OCR3C - PC4
 | 
			
		||||
    //   OCR1A - PB5
 | 
			
		||||
    //   OCR1B - PB6
 | 
			
		||||
    //   OCR1C - PB7
 | 
			
		||||
 | 
			
		||||
    // Clock Select (CS3n) = 0b010 = Clock / 8
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
    // initialize timer-counter
 | 
			
		||||
    AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
 | 
			
		||||
    AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO2_PIN_SET
 | 
			
		||||
    AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
 | 
			
		||||
    AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_stop() {
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO2_PIN_SET
 | 
			
		||||
    channel_2_stop();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_start(void) {
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
    channel_1_start();
 | 
			
		||||
    if (playing_note) {
 | 
			
		||||
        channel_1_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
 | 
			
		||||
    channel_2_start();
 | 
			
		||||
    if (playing_note) {
 | 
			
		||||
        channel_2_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static volatile uint32_t isr_counter = 0;
 | 
			
		||||
#ifdef AUDIO1_PIN_SET
 | 
			
		||||
ISR(AUDIO1_TIMERx_COMPy_vect) {
 | 
			
		||||
    isr_counter++;
 | 
			
		||||
    if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
 | 
			
		||||
 | 
			
		||||
    isr_counter        = 0;
 | 
			
		||||
    bool state_changed = audio_update_state();
 | 
			
		||||
 | 
			
		||||
    if (!playing_note && !playing_melody) {
 | 
			
		||||
        channel_1_stop();
 | 
			
		||||
#    ifdef AUDIO2_PIN_SET
 | 
			
		||||
        channel_2_stop();
 | 
			
		||||
#    endif
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (state_changed) {
 | 
			
		||||
        channel_1_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
#    ifdef AUDIO2_PIN_SET
 | 
			
		||||
        if (audio_get_number_of_active_tones() > 1) {
 | 
			
		||||
            channel_2_set_frequency(audio_get_processed_frequency(1));
 | 
			
		||||
        } else {
 | 
			
		||||
            channel_2_stop();
 | 
			
		||||
        }
 | 
			
		||||
#    endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
 | 
			
		||||
ISR(AUDIO2_TIMERx_COMPy_vect) {
 | 
			
		||||
    isr_counter++;
 | 
			
		||||
    if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
 | 
			
		||||
 | 
			
		||||
    isr_counter        = 0;
 | 
			
		||||
    bool state_changed = audio_update_state();
 | 
			
		||||
 | 
			
		||||
    if (!playing_note && !playing_melody) {
 | 
			
		||||
        channel_2_stop();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (state_changed) {
 | 
			
		||||
        channel_2_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,126 +0,0 @@
 | 
			
		|||
/* Copyright 2019 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef A4
 | 
			
		||||
#    define A4 PAL_LINE(GPIOA, 4)
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef A5
 | 
			
		||||
#    define A5 PAL_LINE(GPIOA, 5)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Size of the dac_buffer arrays. All must be the same size.
 | 
			
		||||
 */
 | 
			
		||||
#define AUDIO_DAC_BUFFER_SIZE 256U
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Highest value allowed sample value.
 | 
			
		||||
 | 
			
		||||
 * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;
 | 
			
		||||
 * lower values adjust the peak-voltage aka volume down.
 | 
			
		||||
 * adjusting this value has only an effect on a sample-buffer whose values are
 | 
			
		||||
 * are NOT pregenerated - see square-wave
 | 
			
		||||
 */
 | 
			
		||||
#ifndef AUDIO_DAC_SAMPLE_MAX
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_MAX 4095U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH)
 | 
			
		||||
#    define AUDIO_DAC_QUALITY_SANE_MINIMUM
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * These presets allow you to quickly switch between quality settings for
 | 
			
		||||
 * the DAC. The sample rate and maximum number of simultaneous tones roughly
 | 
			
		||||
 * has an inverse relationship - slightly higher sample rates may be possible.
 | 
			
		||||
 *
 | 
			
		||||
 * NOTE: a high sample-rate results in a higher cpu-load, which might lead to
 | 
			
		||||
 *       (audible) discontinuities and/or starve other processes of cpu-time
 | 
			
		||||
 *       (like RGB-led back-lighting, ...)
 | 
			
		||||
 */
 | 
			
		||||
#ifdef AUDIO_DAC_QUALITY_VERY_LOW
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 11025U
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 8
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO_DAC_QUALITY_LOW
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 22050U
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 4
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO_DAC_QUALITY_HIGH
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 44100U
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 2
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO_DAC_QUALITY_VERY_HIGH
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 88200U
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM
 | 
			
		||||
/* a sane-minimum config: with a trade-off between cpu-load and tone-range
 | 
			
		||||
 *
 | 
			
		||||
 * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now
 | 
			
		||||
 * aim for an even even multiple of the buffer-size, we end up with:
 | 
			
		||||
 * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)
 | 
			
		||||
 *                              7902/256 = 30.867        *       2      * 256 ~= 16384
 | 
			
		||||
 * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)
 | 
			
		||||
 */
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 16384U
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 8
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any
 | 
			
		||||
 * lower will sacrifice perceptible audio quality. Any higher will limit the
 | 
			
		||||
 * number of simultaneous tones. In most situations, a tenth (1/10) of the
 | 
			
		||||
 * sample rate is where notes become unbearable.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef AUDIO_DAC_SAMPLE_RATE
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_RATE 44100U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The number of tones that can be played simultaneously. If too high a value
 | 
			
		||||
 * is used here, the keyboard will freeze and glitch-out when that many tones
 | 
			
		||||
 * are being played.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef AUDIO_MAX_SIMULTANEOUS_TONES
 | 
			
		||||
#    define AUDIO_MAX_SIMULTANEOUS_TONES 2
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The default value of the DAC when not playing anything. Certain hardware
 | 
			
		||||
 * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.
 | 
			
		||||
 * Since multiple added sine waves tend to oscillate around the midpoint,
 | 
			
		||||
 * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a
 | 
			
		||||
 * reasonable default value.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef AUDIO_DAC_OFF_VALUE
 | 
			
		||||
#    define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX
 | 
			
		||||
#    error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *user overridable sample generation/processing
 | 
			
		||||
 */
 | 
			
		||||
uint16_t dac_value_generate(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,335 +0,0 @@
 | 
			
		|||
/* Copyright 2016-2019 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include <ch.h>
 | 
			
		||||
#include <hal.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Audio Driver: DAC
 | 
			
		||||
 | 
			
		||||
  which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA
 | 
			
		||||
 | 
			
		||||
  it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
 | 
			
		||||
 | 
			
		||||
  this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PIN)
 | 
			
		||||
#    error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
#    pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PIN_ALT)
 | 
			
		||||
// no ALT pin defined is valid, but the c-ifs below need some value set
 | 
			
		||||
#    define AUDIO_PIN_ALT PAL_NOLINE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
 | 
			
		||||
#    define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
 | 
			
		||||
/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
 | 
			
		||||
 */
 | 
			
		||||
static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    // 256 values, max 4095
 | 
			
		||||
    0x0,   0x1,   0x2,   0x6,   0xa,   0xf,   0x16,  0x1e,  0x27,  0x32,  0x3d,  0x4a,  0x58,  0x67,  0x78,  0x89,  0x9c,  0xb0,  0xc5,  0xdb,  0xf2,  0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe,
 | 
			
		||||
    0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2,  0xdb,  0xc5,  0xb0,  0x9c,  0x89,  0x78,  0x67,  0x58,  0x4a,  0x3d,  0x32,  0x27,  0x1e,  0x16,  0xf,   0xa,   0x6,   0x2,   0x1};
 | 
			
		||||
#endif  // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
 | 
			
		||||
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
 | 
			
		||||
static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    // 256 values, max 4095
 | 
			
		||||
    0x0,   0x20,  0x40,  0x60,  0x80,  0xa0,  0xc0,  0xe0,  0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf,
 | 
			
		||||
    0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0,  0xc0,  0xa0,  0x80,  0x60,  0x40,  0x20};
 | 
			
		||||
#endif  // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
 | 
			
		||||
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
 | 
			
		||||
static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1]                     = 0,                     // first and
 | 
			
		||||
    [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,  // second half
 | 
			
		||||
};
 | 
			
		||||
#endif  // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
 | 
			
		||||
/*
 | 
			
		||||
// four steps: 0, 1/3, 2/3 and 1
 | 
			
		||||
static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    [0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ]                               = 0,
 | 
			
		||||
    [AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ]     = AUDIO_DAC_SAMPLE_MAX / 3,
 | 
			
		||||
    [AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3,
 | 
			
		||||
    [3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ]     = AUDIO_DAC_SAMPLE_MAX,
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
 | 
			
		||||
static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0,   0x1f,  0x7f,  0xdf,  0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
 | 
			
		||||
                                                                        0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf,  0x7f,  0x1f,  0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0};
 | 
			
		||||
#endif  // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
 | 
			
		||||
 | 
			
		||||
static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE};
 | 
			
		||||
 | 
			
		||||
/* keep track of the sample position for for each frequency */
 | 
			
		||||
static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
 | 
			
		||||
 | 
			
		||||
static float   active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0};
 | 
			
		||||
static uint8_t active_tones_snapshot_length                        = 0;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    OUTPUT_SHOULD_START,
 | 
			
		||||
    OUTPUT_RUN_NORMALLY,
 | 
			
		||||
    // path 1: wait for zero, then change/update active tones
 | 
			
		||||
    OUTPUT_TONES_CHANGED,
 | 
			
		||||
    OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
 | 
			
		||||
    // path 2: hardware should stop, wait for zero then turn output off = stop the timer
 | 
			
		||||
    OUTPUT_SHOULD_STOP,
 | 
			
		||||
    OUTPUT_REACHED_ZERO_BEFORE_OFF,
 | 
			
		||||
    OUTPUT_OFF,
 | 
			
		||||
    OUTPUT_OFF_1,
 | 
			
		||||
    OUTPUT_OFF_2,  // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level
 | 
			
		||||
    number_of_output_states
 | 
			
		||||
} output_states_t;
 | 
			
		||||
output_states_t state = OUTPUT_OFF_2;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generation of the waveform being passed to the callback. Declared weak so users
 | 
			
		||||
 * can override it with their own wave-forms/noises.
 | 
			
		||||
 */
 | 
			
		||||
__attribute__((weak)) uint16_t dac_value_generate(void) {
 | 
			
		||||
    // DAC is running/asking for values but snapshot length is zero -> must be playing a pause
 | 
			
		||||
    if (active_tones_snapshot_length == 0) {
 | 
			
		||||
        return AUDIO_DAC_OFF_VALUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* doing additive wave synthesis over all currently playing tones = adding up
 | 
			
		||||
     * sine-wave-samples for each frequency, scaled by the number of active tones
 | 
			
		||||
     */
 | 
			
		||||
    uint16_t value     = 0;
 | 
			
		||||
    float    frequency = 0.0f;
 | 
			
		||||
 | 
			
		||||
    for (uint8_t i = 0; i < active_tones_snapshot_length; i++) {
 | 
			
		||||
        /* Note: a user implementation does not have to rely on the active_tones_snapshot, but
 | 
			
		||||
         * could directly query the active frequencies through audio_get_processed_frequency */
 | 
			
		||||
        frequency = active_tones_snapshot[i];
 | 
			
		||||
 | 
			
		||||
        dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3;
 | 
			
		||||
        /*Note: the 2/3 are necessary to get the correct frequencies on the
 | 
			
		||||
         *      DAC output (as measured with an oscilloscope), since the gpt
 | 
			
		||||
         *      timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
 | 
			
		||||
         *      is called twice per conversion.*/
 | 
			
		||||
 | 
			
		||||
        dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE);
 | 
			
		||||
 | 
			
		||||
        // Wavetable generation/lookup
 | 
			
		||||
        uint16_t dac_i = (uint16_t)dac_if[i];
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
 | 
			
		||||
        value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
 | 
			
		||||
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
 | 
			
		||||
        value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
 | 
			
		||||
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
 | 
			
		||||
        value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
 | 
			
		||||
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
 | 
			
		||||
        value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
 | 
			
		||||
#endif
 | 
			
		||||
        /*
 | 
			
		||||
        // SINE
 | 
			
		||||
        value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
 | 
			
		||||
        // TRIANGLE
 | 
			
		||||
        value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
 | 
			
		||||
        // SQUARE
 | 
			
		||||
        value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
 | 
			
		||||
        //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        // STAIRS (mostly usefully as test-pattern)
 | 
			
		||||
        // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DAC streaming callback. Does all of the main computing for playing songs.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
 | 
			
		||||
 */
 | 
			
		||||
static void dac_end(DACDriver *dacp) {
 | 
			
		||||
    dacsample_t *sample_p = (dacp)->samples;
 | 
			
		||||
 | 
			
		||||
    // work on the other half of the buffer
 | 
			
		||||
    if (dacIsBufferComplete(dacp)) {
 | 
			
		||||
        sample_p += AUDIO_DAC_BUFFER_SIZE / 2;  // 'half_index'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
 | 
			
		||||
        if (OUTPUT_OFF <= state) {
 | 
			
		||||
            sample_p[s] = AUDIO_DAC_OFF_VALUE;
 | 
			
		||||
            continue;
 | 
			
		||||
        } else {
 | 
			
		||||
            sample_p[s] = dac_value_generate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
 | 
			
		||||
         * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
 | 
			
		||||
         *                          *       *
 | 
			
		||||
         *                        *           *
 | 
			
		||||
         * ---------------------------------------------------------
 | 
			
		||||
         *                     *                 *                  } AUDIO_DAC_SAMPLE_MAX/100
 | 
			
		||||
         * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
 | 
			
		||||
         *                  *                       *               } AUDIO_DAC_SAMPLE_MAX/100
 | 
			
		||||
         * ---------------------------------------------------------
 | 
			
		||||
         *               *
 | 
			
		||||
         * *           *
 | 
			
		||||
         *   *       *
 | 
			
		||||
         * =====*=*================================================= 0x0
 | 
			
		||||
         */
 | 
			
		||||
        if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) &&  // value approaches from below
 | 
			
		||||
            (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100)))     // or above
 | 
			
		||||
        ) {
 | 
			
		||||
            if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
 | 
			
		||||
                state = OUTPUT_RUN_NORMALLY;
 | 
			
		||||
            } else if (OUTPUT_TONES_CHANGED == state) {
 | 
			
		||||
                state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
 | 
			
		||||
            } else if (OUTPUT_SHOULD_STOP == state) {
 | 
			
		||||
                state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
 | 
			
		||||
        if (OUTPUT_SHOULD_START == state) {
 | 
			
		||||
            sample_p[s] = AUDIO_DAC_OFF_VALUE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
 | 
			
		||||
            uint8_t active_tones         = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
 | 
			
		||||
            active_tones_snapshot_length = 0;
 | 
			
		||||
            // update the snapshot - once, and only on occasion that something changed;
 | 
			
		||||
            // -> saves cpu cycles (?)
 | 
			
		||||
            for (uint8_t i = 0; i < active_tones; i++) {
 | 
			
		||||
                float freq = audio_get_processed_frequency(i);
 | 
			
		||||
                if (freq > 0) {  // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
 | 
			
		||||
                    active_tones_snapshot[active_tones_snapshot_length++] = freq;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
 | 
			
		||||
                state = OUTPUT_OFF;
 | 
			
		||||
            }
 | 
			
		||||
            if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
 | 
			
		||||
                state = OUTPUT_RUN_NORMALLY;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // update audio internal state (note position, current_note, ...)
 | 
			
		||||
    if (audio_update_state()) {
 | 
			
		||||
        if (OUTPUT_SHOULD_STOP != state) {
 | 
			
		||||
            state = OUTPUT_TONES_CHANGED;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (OUTPUT_OFF <= state) {
 | 
			
		||||
        if (OUTPUT_OFF_2 == state) {
 | 
			
		||||
            // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
 | 
			
		||||
            gptStopTimer(&GPTD6);
 | 
			
		||||
        } else {
 | 
			
		||||
            state++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dac_error(DACDriver *dacp, dacerror_t err) {
 | 
			
		||||
    (void)dacp;
 | 
			
		||||
    (void)err;
 | 
			
		||||
 | 
			
		||||
    chSysHalt("DAC failure. halp");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
 | 
			
		||||
                                   .callback  = NULL,
 | 
			
		||||
                                   .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.  */
 | 
			
		||||
                                   .dier      = 0U};
 | 
			
		||||
 | 
			
		||||
static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
 | 
			
		||||
 * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
 | 
			
		||||
 * to be a third of what we expect.
 | 
			
		||||
 *
 | 
			
		||||
 * Here are all the values for DAC_TRG (TSEL in the ref manual)
 | 
			
		||||
 * TIM15_TRGO 0b011
 | 
			
		||||
 * TIM2_TRGO  0b100
 | 
			
		||||
 * TIM3_TRGO  0b001
 | 
			
		||||
 * TIM6_TRGO  0b000
 | 
			
		||||
 * TIM7_TRGO  0b010
 | 
			
		||||
 * EXTI9      0b110
 | 
			
		||||
 * SWTRIG     0b111
 | 
			
		||||
 */
 | 
			
		||||
static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
 | 
			
		||||
 | 
			
		||||
void audio_driver_initialize() {
 | 
			
		||||
    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
 | 
			
		||||
        palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
        dacStart(&DACD1, &dac_conf);
 | 
			
		||||
    }
 | 
			
		||||
    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
 | 
			
		||||
        palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
        dacStart(&DACD2, &dac_conf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* enable the output buffer, to directly drive external loads with no additional circuitry
 | 
			
		||||
     *
 | 
			
		||||
     * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
 | 
			
		||||
     * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
 | 
			
		||||
     * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
 | 
			
		||||
     *
 | 
			
		||||
     * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
 | 
			
		||||
     * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
 | 
			
		||||
     */
 | 
			
		||||
    DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
 | 
			
		||||
    DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
 | 
			
		||||
 | 
			
		||||
    if (AUDIO_PIN == A4) {
 | 
			
		||||
        dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
 | 
			
		||||
    } else if (AUDIO_PIN == A5) {
 | 
			
		||||
        dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
 | 
			
		||||
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
    if (AUDIO_PIN_ALT == A4) {
 | 
			
		||||
        dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
 | 
			
		||||
    } else if (AUDIO_PIN_ALT == A5) {
 | 
			
		||||
        dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    gptStart(&GPTD6, &gpt6cfg1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; }
 | 
			
		||||
 | 
			
		||||
void audio_driver_start(void) {
 | 
			
		||||
    gptStartContinuous(&GPTD6, 2U);
 | 
			
		||||
 | 
			
		||||
    for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
 | 
			
		||||
        dac_if[i]                = 0.0f;
 | 
			
		||||
        active_tones_snapshot[i] = 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    active_tones_snapshot_length = 0;
 | 
			
		||||
    state                        = OUTPUT_SHOULD_START;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,245 +0,0 @@
 | 
			
		|||
/* Copyright 2016-2020 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Audio Driver: DAC
 | 
			
		||||
 | 
			
		||||
  which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA
 | 
			
		||||
 | 
			
		||||
  this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously
 | 
			
		||||
  OR
 | 
			
		||||
  one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PIN)
 | 
			
		||||
#    pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options."
 | 
			
		||||
// TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here
 | 
			
		||||
#    define AUDIO_PIN A5
 | 
			
		||||
#endif
 | 
			
		||||
// check configuration for ONE speaker, connected to both DAC pins
 | 
			
		||||
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)
 | 
			
		||||
#    error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef AUDIO_PIN_ALT
 | 
			
		||||
// no ALT pin defined is valid, but the c-ifs below need some value set
 | 
			
		||||
#    define AUDIO_PIN_ALT -1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_STATE_TIMER)
 | 
			
		||||
#    define AUDIO_STATE_TIMER GPTD8
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// square-wave
 | 
			
		||||
static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    // First half is max, second half is 0
 | 
			
		||||
    [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1]                     = AUDIO_DAC_SAMPLE_MAX,
 | 
			
		||||
    [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// square-wave
 | 
			
		||||
static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {
 | 
			
		||||
    // opposite of dac_buffer above
 | 
			
		||||
    [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1]                     = 0,
 | 
			
		||||
    [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
 | 
			
		||||
                      .callback  = NULL,
 | 
			
		||||
                      .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */
 | 
			
		||||
                      .dier      = 0U};
 | 
			
		||||
GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
 | 
			
		||||
                      .callback  = NULL,
 | 
			
		||||
                      .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */
 | 
			
		||||
                      .dier      = 0U};
 | 
			
		||||
 | 
			
		||||
static void gpt_audio_state_cb(GPTDriver *gptp);
 | 
			
		||||
GPTConfig   gptStateUpdateCfg = {.frequency = 10,
 | 
			
		||||
                               .callback  = gpt_audio_state_cb,
 | 
			
		||||
                               .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */
 | 
			
		||||
                               .dier      = 0U};
 | 
			
		||||
 | 
			
		||||
static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
 | 
			
		||||
static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
 | 
			
		||||
 * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
 | 
			
		||||
 * to be a third of what we expect.
 | 
			
		||||
 *
 | 
			
		||||
 * Here are all the values for DAC_TRG (TSEL in the ref manual)
 | 
			
		||||
 * TIM15_TRGO 0b011
 | 
			
		||||
 * TIM2_TRGO  0b100
 | 
			
		||||
 * TIM3_TRGO  0b001
 | 
			
		||||
 * TIM6_TRGO  0b000
 | 
			
		||||
 * TIM7_TRGO  0b010
 | 
			
		||||
 * EXTI9      0b110
 | 
			
		||||
 * SWTRIG     0b111
 | 
			
		||||
 */
 | 
			
		||||
static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};
 | 
			
		||||
static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};
 | 
			
		||||
 | 
			
		||||
void channel_1_start(void) {
 | 
			
		||||
    gptStart(&GPTD6, &gpt6cfg1);
 | 
			
		||||
    gptStartContinuous(&GPTD6, 2U);
 | 
			
		||||
    palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_1_stop(void) {
 | 
			
		||||
    gptStopTimer(&GPTD6);
 | 
			
		||||
    palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);
 | 
			
		||||
    palSetPad(GPIOA, 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float channel_1_frequency = 0.0f;
 | 
			
		||||
void         channel_1_set_frequency(float freq) {
 | 
			
		||||
    channel_1_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    if (freq <= 0.0)  // a pause/rest has freq=0
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
 | 
			
		||||
    channel_1_start();
 | 
			
		||||
}
 | 
			
		||||
float channel_1_get_frequency(void) { return channel_1_frequency; }
 | 
			
		||||
 | 
			
		||||
void channel_2_start(void) {
 | 
			
		||||
    gptStart(&GPTD7, &gpt7cfg1);
 | 
			
		||||
    gptStartContinuous(&GPTD7, 2U);
 | 
			
		||||
    palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_2_stop(void) {
 | 
			
		||||
    gptStopTimer(&GPTD7);
 | 
			
		||||
    palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL);
 | 
			
		||||
    palSetPad(GPIOA, 5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float channel_2_frequency = 0.0f;
 | 
			
		||||
void         channel_2_set_frequency(float freq) {
 | 
			
		||||
    channel_2_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    channel_2_stop();
 | 
			
		||||
    if (freq <= 0.0)  // a pause/rest has freq=0
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
 | 
			
		||||
    channel_2_start();
 | 
			
		||||
}
 | 
			
		||||
float channel_2_get_frequency(void) { return channel_2_frequency; }
 | 
			
		||||
 | 
			
		||||
static void gpt_audio_state_cb(GPTDriver *gptp) {
 | 
			
		||||
    if (audio_update_state()) {
 | 
			
		||||
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
        // one piezo/speaker connected to both audio pins, the generated square-waves are inverted
 | 
			
		||||
        channel_1_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
        channel_2_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
 | 
			
		||||
#else  // two separate audio outputs/speakers
 | 
			
		||||
       // primary speaker on A4, optional secondary on A5
 | 
			
		||||
        if (AUDIO_PIN == A4) {
 | 
			
		||||
            channel_1_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
            if (AUDIO_PIN_ALT == A5) {
 | 
			
		||||
                if (audio_get_number_of_active_tones() > 1) {
 | 
			
		||||
                    channel_2_set_frequency(audio_get_processed_frequency(1));
 | 
			
		||||
                } else {
 | 
			
		||||
                    channel_2_stop();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // primary speaker on A5, optional secondary on A4
 | 
			
		||||
        if (AUDIO_PIN == A5) {
 | 
			
		||||
            channel_2_set_frequency(audio_get_processed_frequency(0));
 | 
			
		||||
            if (AUDIO_PIN_ALT == A4) {
 | 
			
		||||
                if (audio_get_number_of_active_tones() > 1) {
 | 
			
		||||
                    channel_1_set_frequency(audio_get_processed_frequency(1));
 | 
			
		||||
                } else {
 | 
			
		||||
                    channel_1_stop();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_initialize() {
 | 
			
		||||
    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
 | 
			
		||||
        palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
        dacStart(&DACD1, &dac_conf_ch1);
 | 
			
		||||
 | 
			
		||||
        // initial setup of the dac-triggering timer is still required, even
 | 
			
		||||
        // though it gets reconfigured and restarted later on
 | 
			
		||||
        gptStart(&GPTD6, &gpt6cfg1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
 | 
			
		||||
        palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
 | 
			
		||||
        dacStart(&DACD2, &dac_conf_ch2);
 | 
			
		||||
 | 
			
		||||
        gptStart(&GPTD7, &gpt7cfg1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* enable the output buffer, to directly drive external loads with no additional circuitry
 | 
			
		||||
     *
 | 
			
		||||
     * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
 | 
			
		||||
     * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
 | 
			
		||||
     * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
 | 
			
		||||
     *
 | 
			
		||||
     * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
 | 
			
		||||
     * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
 | 
			
		||||
     */
 | 
			
		||||
    DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
 | 
			
		||||
    DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
 | 
			
		||||
 | 
			
		||||
    // start state-updater
 | 
			
		||||
    gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_stop(void) {
 | 
			
		||||
    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
 | 
			
		||||
        gptStopTimer(&GPTD6);
 | 
			
		||||
 | 
			
		||||
        // stop the ongoing conversion and put the output in a known state
 | 
			
		||||
        dacStopConversion(&DACD1);
 | 
			
		||||
        dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
 | 
			
		||||
        gptStopTimer(&GPTD7);
 | 
			
		||||
 | 
			
		||||
        dacStopConversion(&DACD2);
 | 
			
		||||
        dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
 | 
			
		||||
    }
 | 
			
		||||
    gptStopTimer(&AUDIO_STATE_TIMER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_start(void) {
 | 
			
		||||
    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
 | 
			
		||||
        dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
 | 
			
		||||
        dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
    gptStartContinuous(&AUDIO_STATE_TIMER, 2U);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,40 +0,0 @@
 | 
			
		|||
/* Copyright 2020 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PWM_DRIVER)
 | 
			
		||||
// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))
 | 
			
		||||
#    define AUDIO_PWM_DRIVER PWMD1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PWM_CHANNEL)
 | 
			
		||||
// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4
 | 
			
		||||
// default: STM32F303CC PA8+TIM1_CH1 -> 1
 | 
			
		||||
#    define AUDIO_PWM_CHANNEL 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PWM_PAL_MODE)
 | 
			
		||||
// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy
 | 
			
		||||
// default: STM32F303CC PA8+TIM1_CH1 -> 6
 | 
			
		||||
#    define AUDIO_PWM_PAL_MODE 6
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_STATE_TIMER)
 | 
			
		||||
// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.
 | 
			
		||||
// Tim6 is the default for "larger" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4)
 | 
			
		||||
#    define AUDIO_STATE_TIMER GPTD6
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,144 +0,0 @@
 | 
			
		|||
/* Copyright 2020 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Audio Driver: PWM
 | 
			
		||||
 | 
			
		||||
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
 | 
			
		||||
 | 
			
		||||
this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
 | 
			
		||||
The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PIN)
 | 
			
		||||
#    error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern bool    playing_note;
 | 
			
		||||
extern bool    playing_melody;
 | 
			
		||||
extern uint8_t note_timbre;
 | 
			
		||||
 | 
			
		||||
static PWMConfig pwmCFG = {
 | 
			
		||||
    .frequency = 100000, /* PWM clock frequency  */
 | 
			
		||||
    // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
 | 
			
		||||
    .period   = 2,    /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
 | 
			
		||||
    .callback = NULL, /* no callback, the hardware directly toggles the pin */
 | 
			
		||||
    .channels =
 | 
			
		||||
        {
 | 
			
		||||
#if AUDIO_PWM_CHANNEL == 4
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},   /* channel 0 -> TIMx_CH1 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},   /* channel 1 -> TIMx_CH2 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},   /* channel 2 -> TIMx_CH3 */
 | 
			
		||||
            {PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
 | 
			
		||||
#elif AUDIO_PWM_CHANNEL == 3
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL}
 | 
			
		||||
#elif AUDIO_PWM_CHANNEL == 2
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL}
 | 
			
		||||
#else /*fallback to CH1 */
 | 
			
		||||
            {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL}
 | 
			
		||||
#endif
 | 
			
		||||
        },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static float channel_1_frequency = 0.0f;
 | 
			
		||||
void         channel_1_set_frequency(float freq) {
 | 
			
		||||
    channel_1_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    if (freq <= 0.0)  // a pause/rest has freq=0
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    pwmcnt_t period = (pwmCFG.frequency / freq);
 | 
			
		||||
    pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
 | 
			
		||||
    pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
 | 
			
		||||
                     // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
 | 
			
		||||
                     PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float channel_1_get_frequency(void) { return channel_1_frequency; }
 | 
			
		||||
 | 
			
		||||
void channel_1_start(void) {
 | 
			
		||||
    pwmStop(&AUDIO_PWM_DRIVER);
 | 
			
		||||
    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); }
 | 
			
		||||
 | 
			
		||||
static void gpt_callback(GPTDriver *gptp);
 | 
			
		||||
GPTConfig   gptCFG = {
 | 
			
		||||
    /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
 | 
			
		||||
       the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
 | 
			
		||||
       the tempo (which might vary!) is in bpm (beats per minute)
 | 
			
		||||
       therefore: if the timer ticks away at .frequency = (60*64)Hz,
 | 
			
		||||
       and the .interval counts from 64 downwards - audio_update_state is
 | 
			
		||||
       called just often enough to not miss any notes
 | 
			
		||||
    */
 | 
			
		||||
    .frequency = 60 * 64,
 | 
			
		||||
    .callback  = gpt_callback,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void audio_driver_initialize(void) {
 | 
			
		||||
    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
 | 
			
		||||
 | 
			
		||||
    // connect the AUDIO_PIN to the PWM hardware
 | 
			
		||||
#if defined(USE_GPIOV1)  // STM32F103C8
 | 
			
		||||
    palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
 | 
			
		||||
#else  // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
 | 
			
		||||
    palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE(AUDIO_PWM_PAL_MODE));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    gptStart(&AUDIO_STATE_TIMER, &gptCFG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_start(void) {
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    channel_1_start();
 | 
			
		||||
 | 
			
		||||
    if (playing_note || playing_melody) {
 | 
			
		||||
        gptStartContinuous(&AUDIO_STATE_TIMER, 64);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_stop(void) {
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    gptStopTimer(&AUDIO_STATE_TIMER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* a regular timer task, that checks the note to be currently played
 | 
			
		||||
 * and updates the pwm to output that frequency
 | 
			
		||||
 */
 | 
			
		||||
static void gpt_callback(GPTDriver *gptp) {
 | 
			
		||||
    float freq;  // TODO: freq_alt
 | 
			
		||||
 | 
			
		||||
    if (audio_update_state()) {
 | 
			
		||||
        freq = audio_get_processed_frequency(0);  // freq_alt would be index=1
 | 
			
		||||
        channel_1_set_frequency(freq);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,164 +0,0 @@
 | 
			
		|||
/* Copyright 2020 Jack Humbert
 | 
			
		||||
 * Copyright 2020 JohSchneider
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Audio Driver: PWM
 | 
			
		||||
 | 
			
		||||
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
 | 
			
		||||
 | 
			
		||||
this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software
 | 
			
		||||
- a pwm callback is used to set/clear the configured pin.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
#if !defined(AUDIO_PIN)
 | 
			
		||||
#    error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
 | 
			
		||||
#endif
 | 
			
		||||
extern bool    playing_note;
 | 
			
		||||
extern bool    playing_melody;
 | 
			
		||||
extern uint8_t note_timbre;
 | 
			
		||||
 | 
			
		||||
static void pwm_audio_period_callback(PWMDriver *pwmp);
 | 
			
		||||
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
 | 
			
		||||
 | 
			
		||||
static PWMConfig pwmCFG = {
 | 
			
		||||
    .frequency = 100000, /* PWM clock frequency  */
 | 
			
		||||
    // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
 | 
			
		||||
    .period   = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
 | 
			
		||||
    .callback = pwm_audio_period_callback,
 | 
			
		||||
    .channels =
 | 
			
		||||
        {
 | 
			
		||||
            // software-PWM just needs another callback on any channel
 | 
			
		||||
            {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},                                    /* channel 1 -> TIMx_CH2 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL},                                    /* channel 2 -> TIMx_CH3 */
 | 
			
		||||
            {PWM_OUTPUT_DISABLED, NULL}                                     /* channel 3 -> TIMx_CH4 */
 | 
			
		||||
        },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static float channel_1_frequency = 0.0f;
 | 
			
		||||
void         channel_1_set_frequency(float freq) {
 | 
			
		||||
    channel_1_frequency = freq;
 | 
			
		||||
 | 
			
		||||
    if (freq <= 0.0)  // a pause/rest has freq=0
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    pwmcnt_t period = (pwmCFG.frequency / freq);
 | 
			
		||||
    pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
 | 
			
		||||
 | 
			
		||||
    pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
 | 
			
		||||
                     // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
 | 
			
		||||
                     PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float channel_1_get_frequency(void) { return channel_1_frequency; }
 | 
			
		||||
 | 
			
		||||
void channel_1_start(void) {
 | 
			
		||||
    pwmStop(&AUDIO_PWM_DRIVER);
 | 
			
		||||
    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
 | 
			
		||||
 | 
			
		||||
    pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
 | 
			
		||||
    pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void channel_1_stop(void) {
 | 
			
		||||
    pwmStop(&AUDIO_PWM_DRIVER);
 | 
			
		||||
 | 
			
		||||
    palClearLine(AUDIO_PIN);  // leave the line low, after last note was played
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
    palClearLine(AUDIO_PIN_ALT);  // leave the line low, after last note was played
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// generate a PWM signal on any pin, not necessarily the one connected to the timer
 | 
			
		||||
static void pwm_audio_period_callback(PWMDriver *pwmp) {
 | 
			
		||||
    (void)pwmp;
 | 
			
		||||
    palClearLine(AUDIO_PIN);
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
    palSetLine(AUDIO_PIN_ALT);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
 | 
			
		||||
    (void)pwmp;
 | 
			
		||||
    if (channel_1_frequency > 0) {
 | 
			
		||||
        palSetLine(AUDIO_PIN);  // generate a PWM signal on any pin, not necessarily the one connected to the timer
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
        palClearLine(AUDIO_PIN_ALT);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gpt_callback(GPTDriver *gptp);
 | 
			
		||||
GPTConfig   gptCFG = {
 | 
			
		||||
    /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
 | 
			
		||||
       the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
 | 
			
		||||
       the tempo (which might vary!) is in bpm (beats per minute)
 | 
			
		||||
       therefore: if the timer ticks away at .frequency = (60*64)Hz,
 | 
			
		||||
       and the .interval counts from 64 downwards - audio_update_state is
 | 
			
		||||
       called just often enough to not miss anything
 | 
			
		||||
    */
 | 
			
		||||
    .frequency = 60 * 64,
 | 
			
		||||
    .callback  = gpt_callback,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void audio_driver_initialize(void) {
 | 
			
		||||
    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
 | 
			
		||||
 | 
			
		||||
    palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
 | 
			
		||||
    palClearLine(AUDIO_PIN);
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
 | 
			
		||||
    palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
 | 
			
		||||
    palClearLine(AUDIO_PIN_ALT);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);  // enable pwm callbacks
 | 
			
		||||
    pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
 | 
			
		||||
 | 
			
		||||
    gptStart(&AUDIO_STATE_TIMER, &gptCFG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_start(void) {
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    channel_1_start();
 | 
			
		||||
 | 
			
		||||
    if (playing_note || playing_melody) {
 | 
			
		||||
        gptStartContinuous(&AUDIO_STATE_TIMER, 64);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_driver_stop(void) {
 | 
			
		||||
    channel_1_stop();
 | 
			
		||||
    gptStopTimer(&AUDIO_STATE_TIMER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* a regular timer task, that checks the note to be currently played
 | 
			
		||||
 * and updates the pwm to output that frequency
 | 
			
		||||
 */
 | 
			
		||||
static void gpt_callback(GPTDriver *gptp) {
 | 
			
		||||
    float freq;  // TODO: freq_alt
 | 
			
		||||
 | 
			
		||||
    if (audio_update_state()) {
 | 
			
		||||
        freq = audio_get_processed_frequency(0);  // freq_alt would be index=1
 | 
			
		||||
        channel_1_set_frequency(freq);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -20,11 +20,9 @@
 | 
			
		|||
 | 
			
		||||
#include "musical_notes.h"
 | 
			
		||||
 | 
			
		||||
#if __GNUC__ > 5  // don't use for older gcc compilers since check isn't supported.
 | 
			
		||||
#    if __has_include("user_song_list.h")
 | 
			
		||||
#        include "user_song_list.h"
 | 
			
		||||
#    endif  // if file exists
 | 
			
		||||
#endif      // __GNUC__
 | 
			
		||||
#if __has_include("user_song_list.h")
 | 
			
		||||
#    include "user_song_list.h"
 | 
			
		||||
#endif  // if file exists
 | 
			
		||||
 | 
			
		||||
#define NO_SOUND
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,13 +199,14 @@ static inline void disable_pwm(void) {
 | 
			
		|||
// reaches the backlight level, where we turn off the LEDs,
 | 
			
		||||
// but also an overflow interrupt when the counter rolls back to 0,
 | 
			
		||||
// in which we're going to turn on the LEDs.
 | 
			
		||||
// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz.
 | 
			
		||||
// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz,
 | 
			
		||||
// or F_CPU/BACKLIGHT_CUSTOM_RESOLUTION if used.
 | 
			
		||||
 | 
			
		||||
// Triggered when the counter reaches the OCRx value
 | 
			
		||||
ISR(TIMERx_COMPA_vect) { backlight_pins_off(); }
 | 
			
		||||
 | 
			
		||||
// Triggered when the counter reaches the TOP value
 | 
			
		||||
// this one triggers at F_CPU/65536 =~ 244 Hz
 | 
			
		||||
// this one triggers at F_CPU/ICRx = 16MHz/65536 =~ 244 Hz
 | 
			
		||||
ISR(TIMERx_OVF_vect) {
 | 
			
		||||
#    ifdef BACKLIGHT_BREATHING
 | 
			
		||||
    if (is_breathing()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +221,7 @@ ISR(TIMERx_OVF_vect) {
 | 
			
		|||
    // artifacts (especially while breathing, because breathing_task
 | 
			
		||||
    // takes many computation cycles).
 | 
			
		||||
    // so better not turn them on while the counter TOP is very low.
 | 
			
		||||
    if (OCRxx > 256) {
 | 
			
		||||
    if (OCRxx > ICRx / 250 + 5) {
 | 
			
		||||
        backlight_pins_on();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -231,24 +232,26 @@ ISR(TIMERx_OVF_vect) {
 | 
			
		|||
 | 
			
		||||
// See http://jared.geek.nz/2013/feb/linear-led-pwm
 | 
			
		||||
static uint16_t cie_lightness(uint16_t v) {
 | 
			
		||||
    if (v <= 5243)     // if below 8% of max
 | 
			
		||||
        return v / 9;  // same as dividing by 900%
 | 
			
		||||
    else {
 | 
			
		||||
        uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL);  // add 16% of max and compare
 | 
			
		||||
        // to get a useful result with integer division, we shift left in the expression above
 | 
			
		||||
        // and revert what we've done again after squaring.
 | 
			
		||||
        y = y * y * y >> 8;
 | 
			
		||||
        if (y > 0xFFFFUL)  // prevent overflow
 | 
			
		||||
            return 0xFFFFU;
 | 
			
		||||
        else
 | 
			
		||||
            return (uint16_t)y;
 | 
			
		||||
    if (v <= ICRx / 12)  // If the value is less than or equal to ~8% of max
 | 
			
		||||
    {
 | 
			
		||||
        return v / 9;  // Same as dividing by 900%
 | 
			
		||||
    } else {
 | 
			
		||||
        // In the next two lines values are bit-shifted. This is to avoid loosing decimals in integer math.
 | 
			
		||||
        uint32_t y   = (((uint32_t)v + ICRx / 6) << 5) / (ICRx / 6 + ICRx);  // If above 8%, add ~16% of max, and normalize with (max + ~16% max)
 | 
			
		||||
        uint32_t out = (y * y * y * ICRx) >> 15;                             // Cube it and undo the bit-shifting. (which is now three times as much due to the cubing)
 | 
			
		||||
 | 
			
		||||
        if (out > ICRx)  // Avoid overflows
 | 
			
		||||
        {
 | 
			
		||||
            out = ICRx;
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rescale the supplied backlight value to be in terms of the value limit
 | 
			
		||||
// rescale the supplied backlight value to be in terms of the value limit	// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.
 | 
			
		||||
static uint32_t rescale_limit_val(uint32_t val) { return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; }
 | 
			
		||||
 | 
			
		||||
// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
 | 
			
		||||
// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.
 | 
			
		||||
static inline void set_pwm(uint16_t val) { OCRxx = val; }
 | 
			
		||||
 | 
			
		||||
void backlight_set(uint8_t level) {
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +280,7 @@ void backlight_set(uint8_t level) {
 | 
			
		|||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    // Set the brightness
 | 
			
		||||
    set_pwm(cie_lightness(rescale_limit_val(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)));
 | 
			
		||||
    set_pwm(cie_lightness(rescale_limit_val(ICRx * (uint32_t)level / BACKLIGHT_LEVELS)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void backlight_task(void) {}
 | 
			
		||||
| 
						 | 
				
			
			@ -292,6 +295,11 @@ void backlight_task(void) {}
 | 
			
		|||
static uint8_t  breathing_halt    = BREATHING_NO_HALT;
 | 
			
		||||
static uint16_t breathing_counter = 0;
 | 
			
		||||
 | 
			
		||||
static uint8_t breath_scale_counter = 1;
 | 
			
		||||
/* Run the breathing loop at ~120Hz*/
 | 
			
		||||
const uint8_t   breathing_ISR_frequency     = 120;
 | 
			
		||||
static uint16_t breathing_freq_scale_factor = 2;
 | 
			
		||||
 | 
			
		||||
#    ifdef BACKLIGHT_PWM_TIMER
 | 
			
		||||
static bool breathing = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -323,9 +331,9 @@ bool is_breathing(void) { return !!(TIMSKx & _BV(TOIEx)); }
 | 
			
		|||
        do {                       \
 | 
			
		||||
            breathing_counter = 0; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define breathing_max()                                       \
 | 
			
		||||
        do {                                                      \
 | 
			
		||||
            breathing_counter = get_breathing_period() * 244 / 2; \
 | 
			
		||||
#    define breathing_max()                                                           \
 | 
			
		||||
        do {                                                                          \
 | 
			
		||||
            breathing_counter = get_breathing_period() * breathing_ISR_frequency / 2; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
 | 
			
		||||
void breathing_enable(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -369,21 +377,30 @@ void breathing_task(void)
 | 
			
		|||
#    else
 | 
			
		||||
/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
 | 
			
		||||
 * about 244 times per second.
 | 
			
		||||
 *
 | 
			
		||||
 * The following ISR runs at F_CPU/ISRx. With a 16MHz clock and default pwm resolution, that means 244Hz
 | 
			
		||||
 */
 | 
			
		||||
ISR(TIMERx_OVF_vect)
 | 
			
		||||
#    endif
 | 
			
		||||
{
 | 
			
		||||
    uint8_t  breathing_period = get_breathing_period();
 | 
			
		||||
    uint16_t interval         = (uint16_t)breathing_period * 244 / BREATHING_STEPS;
 | 
			
		||||
 | 
			
		||||
    // Only run this ISR at ~120 Hz
 | 
			
		||||
    if (breath_scale_counter++ == breathing_freq_scale_factor) {
 | 
			
		||||
        breath_scale_counter = 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    uint16_t interval = (uint16_t)get_breathing_period() * breathing_ISR_frequency / BREATHING_STEPS;
 | 
			
		||||
    // resetting after one period to prevent ugly reset at overflow.
 | 
			
		||||
    breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
 | 
			
		||||
    breathing_counter = (breathing_counter + 1) % (get_breathing_period() * breathing_ISR_frequency);
 | 
			
		||||
    uint8_t index     = breathing_counter / interval % BREATHING_STEPS;
 | 
			
		||||
 | 
			
		||||
    if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) {
 | 
			
		||||
        breathing_interrupt_disable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))));
 | 
			
		||||
    // Set PWM to a brightnessvalue scaled to the configured resolution
 | 
			
		||||
    set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif  // BACKLIGHT_BREATHING
 | 
			
		||||
| 
						 | 
				
			
			@ -413,16 +430,23 @@ void backlight_init_ports(void) {
 | 
			
		|||
    "In fast PWM mode, the compare units allow generation of PWM waveforms on the OCnx pins. Setting the COMnx1:0 bits to two will produce a non-inverted PWM [..]."
 | 
			
		||||
    "In fast PWM mode the counter is incremented until the counter value matches either one of the fixed values 0x00FF, 0x01FF, or 0x03FF (WGMn3:0 = 5, 6, or 7), the value in ICRn (WGMn3:0 = 14), or the value in OCRnA (WGMn3:0 = 15)."
 | 
			
		||||
    */
 | 
			
		||||
#    if BACKLIGHT_ON_STATE == 1
 | 
			
		||||
    TCCRxA = _BV(COMxx1) | _BV(WGM11);
 | 
			
		||||
#    else
 | 
			
		||||
    TCCRxA = _BV(COMxx1) | _BV(COMxx0) | _BV(WGM11);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
 | 
			
		||||
    TCCRxA = _BV(COMxx1) | _BV(WGM11);             // = 0b00001010;
 | 
			
		||||
    TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10);  // = 0b00011001;
 | 
			
		||||
#endif
 | 
			
		||||
    // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
 | 
			
		||||
 | 
			
		||||
#ifdef BACKLIGHT_CUSTOM_RESOLUTION
 | 
			
		||||
#    if (BACKLIGHT_CUSTOM_RESOLUTION > 0xFFFF || BACKLIGHT_CUSTOM_RESOLUTION < 1)
 | 
			
		||||
#        error "This out of range of the timer capabilities"
 | 
			
		||||
#    elif (BACKLIGHT_CUSTOM_RESOLUTION < 0xFF)
 | 
			
		||||
#        warning "Resolution lower than 0xFF isn't recommended"
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef BACKLIGHT_BREATHING
 | 
			
		||||
    breathing_freq_scale_factor = F_CPU / BACKLIGHT_CUSTOM_RESOLUTION / 120;
 | 
			
		||||
#    endif
 | 
			
		||||
    ICRx = BACKLIGHT_CUSTOM_RESOLUTION;
 | 
			
		||||
#else
 | 
			
		||||
    ICRx = TIMER_TOP;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    backlight_init();
 | 
			
		||||
#ifdef BACKLIGHT_BREATHING
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#    include "backlight.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE)
 | 
			
		||||
#    include "mousekey.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ static void print_version(void);
 | 
			
		|||
static void print_status(void);
 | 
			
		||||
static bool command_console(uint8_t code);
 | 
			
		||||
static void command_console_help(void);
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE)
 | 
			
		||||
static bool mousekey_console(uint8_t code);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ bool command_proc(uint8_t code) {
 | 
			
		|||
            else
 | 
			
		||||
                return (command_console_extra(code) || command_console(code));
 | 
			
		||||
            break;
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
 | 
			
		||||
#if defined(MOUSEKEY_ENABLE)
 | 
			
		||||
        case MOUSEKEY:
 | 
			
		||||
            mousekey_console(code);
 | 
			
		||||
            break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,4 @@
 | 
			
		|||
#define COL2ROW 0
 | 
			
		||||
#define ROW2COL 1
 | 
			
		||||
 | 
			
		||||
// Deprecated alias - avoid using
 | 
			
		||||
#define KEYMAP LAYOUT
 | 
			
		||||
 | 
			
		||||
#include "song_list.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,17 +46,17 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
 | 
			
		|||
#define ROW_SHIFTER ((matrix_row_t)1)
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    bool pressed : 1;
 | 
			
		||||
    bool    pressed : 1;
 | 
			
		||||
    uint8_t time : 7;
 | 
			
		||||
} debounce_counter_t;
 | 
			
		||||
 | 
			
		||||
#if DEBOUNCE > 0
 | 
			
		||||
static debounce_counter_t *debounce_counters;
 | 
			
		||||
static fast_timer_t last_time;
 | 
			
		||||
static bool counters_need_update;
 | 
			
		||||
static bool matrix_need_update;
 | 
			
		||||
static fast_timer_t        last_time;
 | 
			
		||||
static bool                counters_need_update;
 | 
			
		||||
static bool                matrix_need_update;
 | 
			
		||||
 | 
			
		||||
#define DEBOUNCE_ELAPSED 0
 | 
			
		||||
#    define DEBOUNCE_ELAPSED 0
 | 
			
		||||
 | 
			
		||||
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
 | 
			
		||||
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
 | 
			
		|||
// we use num_rows rather than MATRIX_ROWS to support split keyboards
 | 
			
		||||
void debounce_init(uint8_t num_rows) {
 | 
			
		||||
    debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    int i             = 0;
 | 
			
		||||
    for (uint8_t r = 0; r < num_rows; r++) {
 | 
			
		||||
        for (uint8_t c = 0; c < MATRIX_COLS; c++) {
 | 
			
		||||
            debounce_counters[i++].time = DEBOUNCE_ELAPSED;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,10 +81,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
 | 
			
		|||
    bool updated_last = false;
 | 
			
		||||
 | 
			
		||||
    if (counters_need_update) {
 | 
			
		||||
        fast_timer_t now = timer_read_fast();
 | 
			
		||||
        fast_timer_t now          = timer_read_fast();
 | 
			
		||||
        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
 | 
			
		||||
 | 
			
		||||
        last_time = now;
 | 
			
		||||
        last_time    = now;
 | 
			
		||||
        updated_last = true;
 | 
			
		||||
        if (elapsed_time > UINT8_MAX) {
 | 
			
		||||
            elapsed_time = UINT8_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +108,7 @@ static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[],
 | 
			
		|||
    debounce_counter_t *debounce_pointer = debounce_counters;
 | 
			
		||||
 | 
			
		||||
    counters_need_update = false;
 | 
			
		||||
    matrix_need_update = false;
 | 
			
		||||
    matrix_need_update   = false;
 | 
			
		||||
 | 
			
		||||
    for (uint8_t row = 0; row < num_rows; row++) {
 | 
			
		||||
        for (uint8_t col = 0; col < MATRIX_COLS; col++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,8 +146,8 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
 | 
			
		|||
            if (delta & col_mask) {
 | 
			
		||||
                if (debounce_pointer->time == DEBOUNCE_ELAPSED) {
 | 
			
		||||
                    debounce_pointer->pressed = (raw[row] & col_mask);
 | 
			
		||||
                    debounce_pointer->time = DEBOUNCE;
 | 
			
		||||
                    counters_need_update = true;
 | 
			
		||||
                    debounce_pointer->time    = DEBOUNCE;
 | 
			
		||||
                    counters_need_update      = true;
 | 
			
		||||
 | 
			
		||||
                    if (debounce_pointer->pressed) {
 | 
			
		||||
                        // key-down: eager
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#if DEBOUNCE > 0
 | 
			
		||||
static bool debouncing = false;
 | 
			
		||||
static bool         debouncing = false;
 | 
			
		||||
static fast_timer_t debouncing_time;
 | 
			
		||||
 | 
			
		||||
void debounce_init(uint8_t num_rows) {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ static debounce_counter_t *debounce_counters;
 | 
			
		|||
static fast_timer_t        last_time;
 | 
			
		||||
static bool                counters_need_update;
 | 
			
		||||
 | 
			
		||||
#define DEBOUNCE_ELAPSED 0
 | 
			
		||||
#    define DEBOUNCE_ELAPSED 0
 | 
			
		||||
 | 
			
		||||
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
 | 
			
		||||
static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
 | 
			
		||||
| 
						 | 
				
			
			@ -74,10 +74,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
 | 
			
		|||
    bool updated_last = false;
 | 
			
		||||
 | 
			
		||||
    if (counters_need_update) {
 | 
			
		||||
        fast_timer_t now = timer_read_fast();
 | 
			
		||||
        fast_timer_t now          = timer_read_fast();
 | 
			
		||||
        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
 | 
			
		||||
 | 
			
		||||
        last_time = now;
 | 
			
		||||
        last_time    = now;
 | 
			
		||||
        updated_last = true;
 | 
			
		||||
        if (elapsed_time > UINT8_MAX) {
 | 
			
		||||
            elapsed_time = UINT8_MAX;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ static fast_timer_t        last_time;
 | 
			
		|||
static bool                counters_need_update;
 | 
			
		||||
static bool                matrix_need_update;
 | 
			
		||||
 | 
			
		||||
#define DEBOUNCE_ELAPSED 0
 | 
			
		||||
#    define DEBOUNCE_ELAPSED 0
 | 
			
		||||
 | 
			
		||||
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
 | 
			
		||||
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
 | 
			
		||||
| 
						 | 
				
			
			@ -75,10 +75,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
 | 
			
		|||
    bool updated_last = false;
 | 
			
		||||
 | 
			
		||||
    if (counters_need_update) {
 | 
			
		||||
        fast_timer_t now = timer_read_fast();
 | 
			
		||||
        fast_timer_t now          = timer_read_fast();
 | 
			
		||||
        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
 | 
			
		||||
 | 
			
		||||
        last_time = now;
 | 
			
		||||
        last_time    = now;
 | 
			
		||||
        updated_last = true;
 | 
			
		||||
        if (elapsed_time > UINT8_MAX) {
 | 
			
		||||
            elapsed_time = UINT8_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
 | 
			
		|||
        for (uint8_t col = 0; col < MATRIX_COLS; col++) {
 | 
			
		||||
            if (*debounce_pointer != DEBOUNCE_ELAPSED) {
 | 
			
		||||
                if (*debounce_pointer <= elapsed_time) {
 | 
			
		||||
                    *debounce_pointer = DEBOUNCE_ELAPSED;
 | 
			
		||||
                    *debounce_pointer  = DEBOUNCE_ELAPSED;
 | 
			
		||||
                    matrix_need_update = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    *debounce_pointer -= elapsed_time;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ static debounce_counter_t *debounce_counters;
 | 
			
		|||
static fast_timer_t        last_time;
 | 
			
		||||
static bool                counters_need_update;
 | 
			
		||||
 | 
			
		||||
#define DEBOUNCE_ELAPSED 0
 | 
			
		||||
#    define DEBOUNCE_ELAPSED 0
 | 
			
		||||
 | 
			
		||||
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
 | 
			
		||||
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
 | 
			
		||||
| 
						 | 
				
			
			@ -71,10 +71,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
 | 
			
		|||
    bool updated_last = false;
 | 
			
		||||
 | 
			
		||||
    if (counters_need_update) {
 | 
			
		||||
        fast_timer_t now = timer_read_fast();
 | 
			
		||||
        fast_timer_t now          = timer_read_fast();
 | 
			
		||||
        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
 | 
			
		||||
 | 
			
		||||
        last_time = now;
 | 
			
		||||
        last_time    = now;
 | 
			
		||||
        updated_last = true;
 | 
			
		||||
        if (elapsed_time > UINT8_MAX) {
 | 
			
		||||
            elapsed_time = UINT8_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
 | 
			
		|||
    for (uint8_t row = 0; row < num_rows; row++) {
 | 
			
		||||
        if (*debounce_pointer != DEBOUNCE_ELAPSED) {
 | 
			
		||||
            if (*debounce_pointer <= elapsed_time) {
 | 
			
		||||
                *debounce_pointer = DEBOUNCE_ELAPSED;
 | 
			
		||||
                *debounce_pointer  = DEBOUNCE_ELAPSED;
 | 
			
		||||
                matrix_need_update = true;
 | 
			
		||||
            } else {
 | 
			
		||||
                *debounce_pointer -= elapsed_time;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@
 | 
			
		|||
#include "debounce_test_common.h"
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 1ms delay */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 2ms delay */
 | 
			
		||||
        {2, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +60,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 3ms delay */
 | 
			
		||||
        {3, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +76,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 4ms delay */
 | 
			
		||||
        {4, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +92,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Release key after 5ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +107,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Release key after 6ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +122,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort7) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Release key after 7ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +137,8 @@ TEST_F(DebounceTest, OneKeyShort7) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort8) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 1ms delay */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +153,8 @@ TEST_F(DebounceTest, OneKeyShort8) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort9) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Release key after 1ms delay */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +168,8 @@ TEST_F(DebounceTest, OneKeyShort9) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 1, DOWN}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +195,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Change twice in the same time period */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -217,7 +228,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {25, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +248,8 @@ TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
 | 
			
		||||
        /* Release key after 2ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -249,14 +262,14 @@ TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		|||
        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
 | 
			
		||||
        /* Press key again after 1ms delay */
 | 
			
		||||
        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */
 | 
			
		||||
        {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */
 | 
			
		||||
        {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}},             /* 5ms+5ms after DOWN at time 0 */
 | 
			
		||||
    });
 | 
			
		||||
    runEvents();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late, immediately release key */
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +282,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late, immediately release key */
 | 
			
		||||
| 
						 | 
				
			
			@ -283,7 +297,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +313,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +330,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {5, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -329,7 +346,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {5, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -345,7 +363,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan6) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan7) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {5, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +377,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan7) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan8) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is a bit late */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,9 +31,7 @@ void set_time(uint32_t t);
 | 
			
		|||
void advance_time(uint32_t ms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {
 | 
			
		||||
    events_.insert(events_.end(), events.begin(), events.end());
 | 
			
		||||
}
 | 
			
		||||
void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) { events_.insert(events_.end(), events.begin(), events.end()); }
 | 
			
		||||
 | 
			
		||||
void DebounceTest::runEvents() {
 | 
			
		||||
    /* Run the test multiple times, from 1kHz to 10kHz scan rate */
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +52,7 @@ void DebounceTest::runEvents() {
 | 
			
		|||
 | 
			
		||||
void DebounceTest::runEventsInternal() {
 | 
			
		||||
    fast_timer_t previous = 0;
 | 
			
		||||
    bool first = true;
 | 
			
		||||
    bool         first    = true;
 | 
			
		||||
 | 
			
		||||
    /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
 | 
			
		||||
    debounce_init(MATRIX_ROWS);
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +78,7 @@ void DebounceTest::runEventsInternal() {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        first = false;
 | 
			
		||||
        first    = false;
 | 
			
		||||
        previous = event.time_;
 | 
			
		||||
 | 
			
		||||
        /* Prepare input matrix */
 | 
			
		||||
| 
						 | 
				
			
			@ -98,12 +96,7 @@ void DebounceTest::runEventsInternal() {
 | 
			
		|||
 | 
			
		||||
        /* Check output matrix has expected change events */
 | 
			
		||||
        for (auto &output : event.outputs_) {
 | 
			
		||||
            EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_))
 | 
			
		||||
                    << "Missing event at " << strTime()
 | 
			
		||||
                    << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_)
 | 
			
		||||
                    << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_)
 | 
			
		||||
                    << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
 | 
			
		||||
                    << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
 | 
			
		||||
            EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_)) << "Missing event at " << strTime() << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_) << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_) << "\nexpected_matrix:\n" << strMatrix(output_matrix_) << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Check output matrix has no other changes */
 | 
			
		||||
| 
						 | 
				
			
			@ -133,27 +126,20 @@ void DebounceTest::runDebounce(bool changed) {
 | 
			
		|||
    debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
 | 
			
		||||
 | 
			
		||||
    if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
 | 
			
		||||
        FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime()
 | 
			
		||||
            << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
 | 
			
		||||
            << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
 | 
			
		||||
        FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) {
 | 
			
		||||
    if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) {
 | 
			
		||||
        FAIL() << "Unexpected event: " << error_message << " at " << strTime()
 | 
			
		||||
            << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
 | 
			
		||||
            << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
 | 
			
		||||
            << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
 | 
			
		||||
        FAIL() << "Unexpected event: " << error_message << " at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nexpected_matrix:\n" << strMatrix(output_matrix_) << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string DebounceTest::strTime() {
 | 
			
		||||
    std::stringstream text;
 | 
			
		||||
 | 
			
		||||
    text << "time " << (timer_read_fast() - time_offset_)
 | 
			
		||||
        << " (extra_iterations=" << extra_iterations_
 | 
			
		||||
        << ", auto_advance_time=" << auto_advance_time_ << ")";
 | 
			
		||||
    text << "time " << (timer_read_fast() - time_offset_) << " (extra_iterations=" << extra_iterations_ << ", auto_advance_time=" << auto_advance_time_ << ")";
 | 
			
		||||
 | 
			
		||||
    return text.str();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -181,49 +167,39 @@ std::string DebounceTest::strMatrix(matrix_row_t matrix[]) {
 | 
			
		|||
 | 
			
		||||
bool DebounceTest::directionValue(Direction direction) {
 | 
			
		||||
    switch (direction) {
 | 
			
		||||
    case DOWN:
 | 
			
		||||
        return true;
 | 
			
		||||
        case DOWN:
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
    case UP:
 | 
			
		||||
        return false;
 | 
			
		||||
        case UP:
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string DebounceTest::directionLabel(Direction direction) {
 | 
			
		||||
    switch (direction) {
 | 
			
		||||
    case DOWN:
 | 
			
		||||
        return "DOWN";
 | 
			
		||||
        case DOWN:
 | 
			
		||||
            return "DOWN";
 | 
			
		||||
 | 
			
		||||
    case UP:
 | 
			
		||||
        return "UP";
 | 
			
		||||
        case UP:
 | 
			
		||||
            return "UP";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Modify a matrix and verify that events always specify a change */
 | 
			
		||||
void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) {
 | 
			
		||||
    ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_))
 | 
			
		||||
        << "Test " << name << " at " << strTime()
 | 
			
		||||
        << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_)
 | 
			
		||||
        << " but it is already " << directionLabel(event.direction_)
 | 
			
		||||
        << "\n" << name << "_matrix:\n" << strMatrix(matrix);
 | 
			
		||||
    ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_)) << "Test " << name << " at " << strTime() << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_) << " but it is already " << directionLabel(event.direction_) << "\n" << name << "_matrix:\n" << strMatrix(matrix);
 | 
			
		||||
 | 
			
		||||
    switch (event.direction_) {
 | 
			
		||||
    case DOWN:
 | 
			
		||||
        matrix[event.row_] |= (1U << event.col_);
 | 
			
		||||
        break;
 | 
			
		||||
        case DOWN:
 | 
			
		||||
            matrix[event.row_] |= (1U << event.col_);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
    case UP:
 | 
			
		||||
        matrix[event.row_] &= ~(1U << event.col_);
 | 
			
		||||
        break;
 | 
			
		||||
        case UP:
 | 
			
		||||
            matrix[event.row_] &= ~(1U << event.col_);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DebounceTestEvent::DebounceTestEvent(fast_timer_t time,
 | 
			
		||||
        std::initializer_list<MatrixTestEvent> inputs,
 | 
			
		||||
        std::initializer_list<MatrixTestEvent> outputs)
 | 
			
		||||
        : time_(time), inputs_(inputs), outputs_(outputs) {
 | 
			
		||||
}
 | 
			
		||||
DebounceTestEvent::DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs) : time_(time), inputs_(inputs), outputs_(outputs) {}
 | 
			
		||||
 | 
			
		||||
MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction)
 | 
			
		||||
        : row_(row), col_(col), direction_(direction) {
 | 
			
		||||
}
 | 
			
		||||
MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction) : row_(row), col_(col), direction_(direction) {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,36 +31,34 @@ enum Direction {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
class MatrixTestEvent {
 | 
			
		||||
public:
 | 
			
		||||
   public:
 | 
			
		||||
    MatrixTestEvent(int row, int col, Direction direction);
 | 
			
		||||
 | 
			
		||||
    const int row_;
 | 
			
		||||
    const int col_;
 | 
			
		||||
    const int       row_;
 | 
			
		||||
    const int       col_;
 | 
			
		||||
    const Direction direction_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DebounceTestEvent {
 | 
			
		||||
public:
 | 
			
		||||
   public:
 | 
			
		||||
    // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}})
 | 
			
		||||
    DebounceTestEvent(fast_timer_t time,
 | 
			
		||||
        std::initializer_list<MatrixTestEvent> inputs,
 | 
			
		||||
        std::initializer_list<MatrixTestEvent> outputs);
 | 
			
		||||
    DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs);
 | 
			
		||||
 | 
			
		||||
    const fast_timer_t time_;
 | 
			
		||||
    const fast_timer_t               time_;
 | 
			
		||||
    const std::list<MatrixTestEvent> inputs_;
 | 
			
		||||
    const std::list<MatrixTestEvent> outputs_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DebounceTest : public ::testing::Test {
 | 
			
		||||
protected:
 | 
			
		||||
   protected:
 | 
			
		||||
    void addEvents(std::initializer_list<DebounceTestEvent> events);
 | 
			
		||||
    void runEvents();
 | 
			
		||||
 | 
			
		||||
    fast_timer_t time_offset_ = 7777;
 | 
			
		||||
    bool time_jumps_ = false;
 | 
			
		||||
    bool         time_jumps_  = false;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static bool directionValue(Direction direction);
 | 
			
		||||
   private:
 | 
			
		||||
    static bool        directionValue(Direction direction);
 | 
			
		||||
    static std::string directionLabel(Direction direction);
 | 
			
		||||
 | 
			
		||||
    void runEventsInternal();
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +76,6 @@ private:
 | 
			
		|||
    matrix_row_t cooked_matrix_[MATRIX_ROWS];
 | 
			
		||||
    matrix_row_t output_matrix_[MATRIX_ROWS];
 | 
			
		||||
 | 
			
		||||
    int extra_iterations_;
 | 
			
		||||
    int  extra_iterations_;
 | 
			
		||||
    bool auto_advance_time_;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@
 | 
			
		|||
DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5
 | 
			
		||||
 | 
			
		||||
DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \
 | 
			
		||||
	$(TMK_PATH)/common/test/timer.c
 | 
			
		||||
	$(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c
 | 
			
		||||
 | 
			
		||||
debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS)
 | 
			
		||||
debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@
 | 
			
		|||
#include "debounce_test_common.h"
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyTooQuick1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        /* Release key exactly on the debounce time */
 | 
			
		||||
        {5, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +71,8 @@ TEST_F(DebounceTest, OneKeyTooQuick1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyTooQuick2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +85,8 @@ TEST_F(DebounceTest, OneKeyTooQuick2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 1, DOWN}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +100,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
        {6, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +115,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +133,8 @@ TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +149,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +162,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +178,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +194,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +210,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Release key before debounce expires */
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +222,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is a bit late */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@
 | 
			
		|||
#include "debounce_test_common.h"
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyTooQuick1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        /* Release key exactly on the debounce time */
 | 
			
		||||
        {5, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +71,8 @@ TEST_F(DebounceTest, OneKeyTooQuick1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyTooQuick2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +85,8 @@ TEST_F(DebounceTest, OneKeyTooQuick2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 1, DOWN}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +100,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
        {6, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +115,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +133,8 @@ TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +151,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +164,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
        {1, {{0, 2, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +180,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +196,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late */
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +212,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Release key before debounce expires */
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +224,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is a bit late */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@
 | 
			
		|||
#include "debounce_test_common.h"
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +75,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +88,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +101,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 1, DOWN}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +117,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Change twice in the same time period */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +143,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {25, {{0, 1, UP}}, {{0, 1, UP}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +155,8 @@ TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +177,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted */
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +189,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1 scan delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +202,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +215,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is a bit late but the change will now be accepted */
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +227,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1 scan delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +240,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1ms delay */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@
 | 
			
		|||
#include "debounce_test_common.h"
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +75,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +88,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +101,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{0, 1, DOWN}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +117,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        /* Change twice in the same time period */
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +143,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        {25, {{0, 1, UP}}, {{0, 1, UP}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +155,8 @@ TEST_F(DebounceTest, OneKeyLong) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoRowsShort) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}},
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +177,8 @@ TEST_F(DebounceTest, TwoRowsShort) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysOverlap) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
        {1, {{0, 1, UP}}, {}},
 | 
			
		||||
        /* Press a second key during the first debounce */
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +201,8 @@ TEST_F(DebounceTest, TwoKeysOverlap) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
 | 
			
		||||
        {20, {{0, 1, UP}}, {{0, 1, UP}}},
 | 
			
		||||
        {21, {{0, 2, UP}}, {}},
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +214,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
 | 
			
		||||
        {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +223,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted */
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +235,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1 scan delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +248,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1ms delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +261,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is a bit late but the change will now be accepted */
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +273,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1 scan delay */
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +286,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(DebounceTest, OneKeyDelayedScan6) {
 | 
			
		||||
    addEvents({ /* Time, Inputs, Outputs */
 | 
			
		||||
    addEvents({
 | 
			
		||||
        /* Time, Inputs, Outputs */
 | 
			
		||||
        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
 | 
			
		||||
 | 
			
		||||
        /* Processing is very late but the change will now be accepted even with a 1ms delay */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										152
									
								
								quantum/deferred_exec.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								quantum/deferred_exec.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,152 @@
 | 
			
		|||
// Copyright 2021 Nick Brassel (@tzarc)
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <timer.h>
 | 
			
		||||
#include <deferred_exec.h>
 | 
			
		||||
 | 
			
		||||
#ifndef MAX_DEFERRED_EXECUTORS
 | 
			
		||||
#    define MAX_DEFERRED_EXECUTORS 8
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct deferred_executor_t {
 | 
			
		||||
    deferred_token         token;
 | 
			
		||||
    uint32_t               trigger_time;
 | 
			
		||||
    deferred_exec_callback callback;
 | 
			
		||||
    void *                 cb_arg;
 | 
			
		||||
} deferred_executor_t;
 | 
			
		||||
 | 
			
		||||
static deferred_token      current_token                     = 0;
 | 
			
		||||
static uint32_t            last_deferred_exec_check          = 0;
 | 
			
		||||
static deferred_executor_t executors[MAX_DEFERRED_EXECUTORS] = {0};
 | 
			
		||||
 | 
			
		||||
static inline bool token_can_be_used(deferred_token token) {
 | 
			
		||||
    if (token == INVALID_DEFERRED_TOKEN) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
 | 
			
		||||
        if (executors[i].token == token) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline deferred_token allocate_token(void) {
 | 
			
		||||
    deferred_token first = ++current_token;
 | 
			
		||||
    while (!token_can_be_used(current_token)) {
 | 
			
		||||
        ++current_token;
 | 
			
		||||
        if (current_token == first) {
 | 
			
		||||
            // If we've looped back around to the first, everything is already allocated (yikes!). Need to exit with a failure.
 | 
			
		||||
            return INVALID_DEFERRED_TOKEN;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return current_token;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {
 | 
			
		||||
    // Ignore queueing if it's a zero-time delay, or invalid callback
 | 
			
		||||
    if (delay_ms == 0 || !callback) {
 | 
			
		||||
        return INVALID_DEFERRED_TOKEN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find an unused slot and claim it
 | 
			
		||||
    for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
 | 
			
		||||
        deferred_executor_t *entry = &executors[i];
 | 
			
		||||
        if (entry->token == INVALID_DEFERRED_TOKEN) {
 | 
			
		||||
            // Work out the new token value, dropping out if none were available
 | 
			
		||||
            deferred_token token = allocate_token();
 | 
			
		||||
            if (token == INVALID_DEFERRED_TOKEN) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set up the executor table entry
 | 
			
		||||
            entry->token        = current_token;
 | 
			
		||||
            entry->trigger_time = timer_read32() + delay_ms;
 | 
			
		||||
            entry->callback     = callback;
 | 
			
		||||
            entry->cb_arg       = cb_arg;
 | 
			
		||||
            return current_token;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // None available
 | 
			
		||||
    return INVALID_DEFERRED_TOKEN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) {
 | 
			
		||||
    // Ignore queueing if it's a zero-time delay, or the token is not valid
 | 
			
		||||
    if (delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find the entry corresponding to the token
 | 
			
		||||
    for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
 | 
			
		||||
        deferred_executor_t *entry = &executors[i];
 | 
			
		||||
        if (entry->token == token) {
 | 
			
		||||
            // Found it, extend the delay
 | 
			
		||||
            entry->trigger_time = timer_read32() + delay_ms;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Not found
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool cancel_deferred_exec(deferred_token token) {
 | 
			
		||||
    // Ignore request if the token is not valid
 | 
			
		||||
    if (token == INVALID_DEFERRED_TOKEN) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find the entry corresponding to the token
 | 
			
		||||
    for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
 | 
			
		||||
        deferred_executor_t *entry = &executors[i];
 | 
			
		||||
        if (entry->token == token) {
 | 
			
		||||
            // Found it, cancel and clear the table entry
 | 
			
		||||
            entry->token        = INVALID_DEFERRED_TOKEN;
 | 
			
		||||
            entry->trigger_time = 0;
 | 
			
		||||
            entry->callback     = NULL;
 | 
			
		||||
            entry->cb_arg       = NULL;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Not found
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void deferred_exec_task(void) {
 | 
			
		||||
    uint32_t now = timer_read32();
 | 
			
		||||
 | 
			
		||||
    // Throttle only once per millisecond
 | 
			
		||||
    if (((int32_t)TIMER_DIFF_32(now, last_deferred_exec_check)) > 0) {
 | 
			
		||||
        last_deferred_exec_check = now;
 | 
			
		||||
 | 
			
		||||
        // Run through each of the executors
 | 
			
		||||
        for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
 | 
			
		||||
            deferred_executor_t *entry = &executors[i];
 | 
			
		||||
 | 
			
		||||
            // Check if we're supposed to execute this entry
 | 
			
		||||
            if (entry->token != INVALID_DEFERRED_TOKEN && ((int32_t)TIMER_DIFF_32(entry->trigger_time, now)) <= 0) {
 | 
			
		||||
                // Invoke the callback and work work out if we should be requeued
 | 
			
		||||
                uint32_t delay_ms = entry->callback(entry->trigger_time, entry->cb_arg);
 | 
			
		||||
 | 
			
		||||
                // Update the trigger time if we have to repeat, otherwise clear it out
 | 
			
		||||
                if (delay_ms > 0) {
 | 
			
		||||
                    // Intentionally add just the delay to the existing trigger time -- this ensures the next
 | 
			
		||||
                    // invocation is with respect to the previous trigger, rather than when it got to execution. Under
 | 
			
		||||
                    // normal circumstances this won't cause issue, but if another executor is invoked that takes a
 | 
			
		||||
                    // considerable length of time, then this ensures best-effort timing between invocations.
 | 
			
		||||
                    entry->trigger_time += delay_ms;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // If it was zero, then the callback is cancelling repeated execution. Free up the slot.
 | 
			
		||||
                    entry->token        = INVALID_DEFERRED_TOKEN;
 | 
			
		||||
                    entry->trigger_time = 0;
 | 
			
		||||
                    entry->callback     = NULL;
 | 
			
		||||
                    entry->cb_arg       = NULL;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								quantum/deferred_exec.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								quantum/deferred_exec.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
// Copyright 2021 Nick Brassel (@tzarc)
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
// A token that can be used to cancel an existing deferred execution.
 | 
			
		||||
typedef uint8_t deferred_token;
 | 
			
		||||
#define INVALID_DEFERRED_TOKEN 0
 | 
			
		||||
 | 
			
		||||
// Callback to execute.
 | 
			
		||||
//  -- Parameter trigger_time: the intended trigger time to execute the callback -- equivalent time-space as timer_read32()
 | 
			
		||||
//               cb_arg: the callback argument specified when enqueueing the deferred executor
 | 
			
		||||
//  -- Return value: Non-zero re-queues the callback to execute after the returned number of milliseconds. Zero cancels repeated execution.
 | 
			
		||||
typedef uint32_t (*deferred_exec_callback)(uint32_t trigger_time, void *cb_arg);
 | 
			
		||||
 | 
			
		||||
// Configures the supplied deferred executor to be executed after the required number of milliseconds.
 | 
			
		||||
//  -- Parameter delay_ms: the number of milliseconds before executing the callback
 | 
			
		||||
//  --           callback: the executor to invoke
 | 
			
		||||
//  --           cb_arg: the argument to pass to the executor, may be NULL if unused by the executor
 | 
			
		||||
//  -- Return value: a token usable for cancellation, or INVALID_DEFERRED_TOKEN if an error occurred
 | 
			
		||||
deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);
 | 
			
		||||
 | 
			
		||||
// Allows for extending the timeframe before an existing deferred execution is invoked.
 | 
			
		||||
//  -- Parameter token: the returned value from defer_exec for the deferred execution you wish to extend.
 | 
			
		||||
//  --           delay_ms: the new delay (with respect to the current time)
 | 
			
		||||
//  -- Return value: if the token was found, and the delay was extended
 | 
			
		||||
bool extend_deferred_exec(deferred_token token, uint32_t delay_ms);
 | 
			
		||||
 | 
			
		||||
// Allows for cancellation of an existing deferred execution.
 | 
			
		||||
//  -- Parameter token: the returned value from defer_exec for the deferred execution you wish to cancel.
 | 
			
		||||
//  -- Return value: if the token was found, and the executor was cancelled
 | 
			
		||||
bool cancel_deferred_exec(deferred_token token);
 | 
			
		||||
 | 
			
		||||
// Forward declaration for the main loop in order to execute any deferred executors. Should not be invoked by keyboard/user code.
 | 
			
		||||
void deferred_exec_task(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@
 | 
			
		|||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "keymap.h"  // to get keymaps[][][]
 | 
			
		||||
#include "tmk_core/common/eeprom.h"
 | 
			
		||||
#include "eeprom.h"
 | 
			
		||||
#include "progmem.h"  // to read default from flash
 | 
			
		||||
#include "quantum.h"  // for send_string()
 | 
			
		||||
#include "dynamic_keymap.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,3 +111,29 @@ void     eeconfig_update_haptic(uint32_t val);
 | 
			
		|||
 | 
			
		||||
bool eeconfig_read_handedness(void);
 | 
			
		||||
void eeconfig_update_handedness(bool val);
 | 
			
		||||
 | 
			
		||||
#define EECONFIG_DEBOUNCE_HELPER(name, offset, config)                     \
 | 
			
		||||
    static uint8_t dirty_##name = false;                                   \
 | 
			
		||||
                                                                           \
 | 
			
		||||
    static inline void eeconfig_init_##name(void) {                        \
 | 
			
		||||
        eeprom_read_block(&config, offset, sizeof(config));                \
 | 
			
		||||
        dirty_##name = false;                                              \
 | 
			
		||||
    }                                                                      \
 | 
			
		||||
    static inline void eeconfig_flush_##name(bool force) {                 \
 | 
			
		||||
        if (force || dirty_##name) {                                       \
 | 
			
		||||
            eeprom_update_block(&config, offset, sizeof(config));          \
 | 
			
		||||
            dirty_##name = false;                                          \
 | 
			
		||||
        }                                                                  \
 | 
			
		||||
    }                                                                      \
 | 
			
		||||
    static inline void eeconfig_flush_##name##_task(uint16_t timeout) {    \
 | 
			
		||||
        static uint16_t flush_timer = 0;                                   \
 | 
			
		||||
        if (timer_elapsed(flush_timer) > timeout) {                        \
 | 
			
		||||
            eeconfig_flush_##name(false);                                  \
 | 
			
		||||
            flush_timer = timer_read();                                    \
 | 
			
		||||
        }                                                                  \
 | 
			
		||||
    }                                                                      \
 | 
			
		||||
    static inline void eeconfig_flag_##name(bool v) { dirty_##name |= v; } \
 | 
			
		||||
    static inline void eeconfig_write_##name(typeof(config) conf) {        \
 | 
			
		||||
        memcpy(&config, &conf, sizeof(config));                            \
 | 
			
		||||
        eeconfig_flag_##name(true);                                        \
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										144
									
								
								quantum/encoder/tests/encoder_tests.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								quantum/encoder/tests/encoder_tests.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gtest/gtest.h"
 | 
			
		||||
#include "gmock/gmock.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "encoder.h"
 | 
			
		||||
#include "encoder/tests/mock.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct update {
 | 
			
		||||
    int8_t index;
 | 
			
		||||
    bool   clockwise;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint8_t uidx = 0;
 | 
			
		||||
update  updates[32];
 | 
			
		||||
 | 
			
		||||
bool encoder_update_kb(uint8_t index, bool clockwise) {
 | 
			
		||||
    updates[uidx % 32] = {index, clockwise};
 | 
			
		||||
    uidx++;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool setAndRead(pin_t pin, bool val) {
 | 
			
		||||
    setPin(pin, val);
 | 
			
		||||
    return encoder_read();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EncoderTest : public ::testing::Test {};
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestInit) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[0], true);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[1], true);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestOneClockwise) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestOneCounterClockwise) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(uidx, 3);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
    EXPECT_EQ(updates[1].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[1].clockwise, true);
 | 
			
		||||
    EXPECT_EQ(updates[2].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[2].clockwise, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestNoEarly) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    // send 3 pulses. with resolution 4, that's not enough for a step.
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
    // now send last pulse
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestHalfway) {
 | 
			
		||||
    uidx = 0;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    // go halfway
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
    // back off
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
    // go all the way
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
    // should result in 1 update
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								quantum/encoder/tests/encoder_tests_split.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								quantum/encoder/tests/encoder_tests_split.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gtest/gtest.h"
 | 
			
		||||
#include "gmock/gmock.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "encoder.h"
 | 
			
		||||
#include "encoder/tests/mock_split.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct update {
 | 
			
		||||
    int8_t index;
 | 
			
		||||
    bool   clockwise;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint8_t uidx = 0;
 | 
			
		||||
update  updates[32];
 | 
			
		||||
 | 
			
		||||
bool isLeftHand;
 | 
			
		||||
 | 
			
		||||
bool encoder_update_kb(uint8_t index, bool clockwise) {
 | 
			
		||||
    if (!isLeftHand) {
 | 
			
		||||
        // this method has no effect on slave half
 | 
			
		||||
        printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    updates[uidx % 32] = {index, clockwise};
 | 
			
		||||
    uidx++;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool setAndRead(pin_t pin, bool val) {
 | 
			
		||||
    setPin(pin, val);
 | 
			
		||||
    return encoder_read();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EncoderTest : public ::testing::Test {
 | 
			
		||||
   protected:
 | 
			
		||||
    void SetUp() override {
 | 
			
		||||
        uidx = 0;
 | 
			
		||||
        for (int i = 0; i < 32; i++) {
 | 
			
		||||
            pinIsInputHigh[i] = 0;
 | 
			
		||||
            pins[i]           = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestInitLeft) {
 | 
			
		||||
    isLeftHand = true;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[0], true);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[1], true);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[2], false);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[3], false);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestInitRight) {
 | 
			
		||||
    isLeftHand = false;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[0], false);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[1], false);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[2], true);
 | 
			
		||||
    EXPECT_EQ(pinIsInputHigh[3], true);
 | 
			
		||||
    EXPECT_EQ(uidx, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestOneClockwiseLeft) {
 | 
			
		||||
    isLeftHand = true;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
 | 
			
		||||
    setAndRead(0, false);
 | 
			
		||||
    setAndRead(1, false);
 | 
			
		||||
    setAndRead(0, true);
 | 
			
		||||
    setAndRead(1, true);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 0);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestOneClockwiseRightSent) {
 | 
			
		||||
    isLeftHand = false;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
 | 
			
		||||
    setAndRead(2, false);
 | 
			
		||||
    setAndRead(3, false);
 | 
			
		||||
    setAndRead(2, true);
 | 
			
		||||
    setAndRead(3, true);
 | 
			
		||||
 | 
			
		||||
    uint8_t slave_state[2] = {0};
 | 
			
		||||
    encoder_state_raw(slave_state);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ((int8_t)slave_state[0], -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* this test will not work after the previous test.
 | 
			
		||||
 * this is due to encoder_value[1] already being set to -1 when simulating the right half.
 | 
			
		||||
 * When we now receive this update acting as the left half, there is no change.
 | 
			
		||||
 * This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half,
 | 
			
		||||
 * but here, they only exist once.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// TEST_F(EncoderTest, TestOneClockwiseRightReceived) {
 | 
			
		||||
//     isLeftHand = true;
 | 
			
		||||
//     encoder_init();
 | 
			
		||||
 | 
			
		||||
//     uint8_t slave_state[2] = {255, 0};
 | 
			
		||||
//     encoder_update_raw(slave_state);
 | 
			
		||||
 | 
			
		||||
//     EXPECT_EQ(uidx, 1);
 | 
			
		||||
//     EXPECT_EQ(updates[0].index, 1);
 | 
			
		||||
//     EXPECT_EQ(updates[0].clockwise, true);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) {
 | 
			
		||||
    isLeftHand = true;
 | 
			
		||||
    encoder_init();
 | 
			
		||||
 | 
			
		||||
    uint8_t slave_state[2] = {0, 0};
 | 
			
		||||
    encoder_update_raw(slave_state);
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(uidx, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].index, 1);
 | 
			
		||||
    EXPECT_EQ(updates[0].clockwise, false);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
/* Copyright 2016 Jack Humbert
 | 
			
		||||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +14,21 @@
 | 
			
		|||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "mock.h"
 | 
			
		||||
 | 
			
		||||
#include "api.h"
 | 
			
		||||
bool pins[32]           = {0};
 | 
			
		||||
bool pinIsInputHigh[32] = {0};
 | 
			
		||||
 | 
			
		||||
#define API_SYSEX_MAX_SIZE 32
 | 
			
		||||
uint8_t mockSetPinInputHigh(pin_t pin) {
 | 
			
		||||
    // dprintf("Setting pin %d input high.", pin);
 | 
			
		||||
    pins[pin]           = true;
 | 
			
		||||
    pinIsInputHigh[pin] = true;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length);
 | 
			
		||||
bool mockReadPin(pin_t pin) { return pins[pin]; }
 | 
			
		||||
 | 
			
		||||
#define SEND_BYTES(mt, dt, b, l) send_bytes_sysex(mt, dt, b, l)
 | 
			
		||||
bool setPin(pin_t pin, bool val) {
 | 
			
		||||
    pins[pin] = val;
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
/* Copyright 2017 Fred Sundvik
 | 
			
		||||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
| 
						 | 
				
			
			@ -16,12 +16,25 @@
 | 
			
		|||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "visualizer.h"
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
// You can use these default animations, but of course you can also write your own custom ones instead
 | 
			
		||||
extern keyframe_animation_t default_startup_animation;
 | 
			
		||||
extern keyframe_animation_t default_suspend_animation;
 | 
			
		||||
/* Here, "pins" from 0 to 31 are allowed. */
 | 
			
		||||
#define ENCODERS_PAD_A \
 | 
			
		||||
    { 0 }
 | 
			
		||||
#define ENCODERS_PAD_B \
 | 
			
		||||
    { 1 }
 | 
			
		||||
 | 
			
		||||
// An animation for testing and demonstrating the led support, should probably not be used for real world
 | 
			
		||||
// cases
 | 
			
		||||
extern keyframe_animation_t led_test_animation;
 | 
			
		||||
typedef uint8_t pin_t;
 | 
			
		||||
 | 
			
		||||
extern bool pins[];
 | 
			
		||||
extern bool pinIsInputHigh[];
 | 
			
		||||
 | 
			
		||||
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
 | 
			
		||||
#define readPin(pin) (mockReadPin(pin))
 | 
			
		||||
 | 
			
		||||
uint8_t mockSetPinInputHigh(pin_t pin);
 | 
			
		||||
 | 
			
		||||
bool mockReadPin(pin_t pin);
 | 
			
		||||
 | 
			
		||||
bool setPin(pin_t pin, bool val);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
/* Copyright 2017 Fred Sundvik
 | 
			
		||||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
| 
						 | 
				
			
			@ -14,10 +14,23 @@
 | 
			
		|||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "visualizer_keyframes.h"
 | 
			
		||||
#include "mock_split.h"
 | 
			
		||||
 | 
			
		||||
bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
 | 
			
		||||
    (void)animation;
 | 
			
		||||
    (void)state;
 | 
			
		||||
    return false;
 | 
			
		||||
bool pins[32]           = {0};
 | 
			
		||||
bool pinIsInputHigh[32] = {0};
 | 
			
		||||
 | 
			
		||||
uint8_t mockSetPinInputHigh(pin_t pin) {
 | 
			
		||||
    // dprintf("Setting pin %d input high.", pin);
 | 
			
		||||
    pins[pin]           = true;
 | 
			
		||||
    pinIsInputHigh[pin] = true;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mockReadPin(pin_t pin) { return pins[pin]; }
 | 
			
		||||
 | 
			
		||||
bool setPin(pin_t pin, bool val) {
 | 
			
		||||
    pins[pin] = val;
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void last_encoder_activity_trigger(void) {}
 | 
			
		||||
							
								
								
									
										48
									
								
								quantum/encoder/tests/mock_split.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								quantum/encoder/tests/mock_split.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/* Copyright 2021 Balz Guenat
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#define SPLIT_KEYBOARD
 | 
			
		||||
/* Here, "pins" from 0 to 31 are allowed. */
 | 
			
		||||
#define ENCODERS_PAD_A \
 | 
			
		||||
    { 0 }
 | 
			
		||||
#define ENCODERS_PAD_B \
 | 
			
		||||
    { 1 }
 | 
			
		||||
#define ENCODERS_PAD_A_RIGHT \
 | 
			
		||||
    { 2 }
 | 
			
		||||
#define ENCODERS_PAD_B_RIGHT \
 | 
			
		||||
    { 3 }
 | 
			
		||||
 | 
			
		||||
typedef uint8_t pin_t;
 | 
			
		||||
extern bool     isLeftHand;
 | 
			
		||||
void            encoder_state_raw(uint8_t* slave_state);
 | 
			
		||||
void            encoder_update_raw(uint8_t* slave_state);
 | 
			
		||||
 | 
			
		||||
extern bool pins[];
 | 
			
		||||
extern bool pinIsInputHigh[];
 | 
			
		||||
 | 
			
		||||
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
 | 
			
		||||
#define readPin(pin) (mockReadPin(pin))
 | 
			
		||||
 | 
			
		||||
uint8_t mockSetPinInputHigh(pin_t pin);
 | 
			
		||||
 | 
			
		||||
bool mockReadPin(pin_t pin);
 | 
			
		||||
 | 
			
		||||
bool setPin(pin_t pin, bool val);
 | 
			
		||||
							
								
								
									
										13
									
								
								quantum/encoder/tests/rules.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								quantum/encoder/tests/rules.mk
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
encoder_DEFS := -DENCODER_MOCK_SINGLE
 | 
			
		||||
 | 
			
		||||
encoder_SRC := \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder/tests/mock.c \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder.c
 | 
			
		||||
 | 
			
		||||
encoder_split_DEFS := -DENCODER_MOCK_SPLIT
 | 
			
		||||
 | 
			
		||||
encoder_split_SRC := \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder/tests/mock_split.c \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \
 | 
			
		||||
	$(QUANTUM_PATH)/encoder.c
 | 
			
		||||
							
								
								
									
										3
									
								
								quantum/encoder/tests/testlist.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								quantum/encoder/tests/testlist.mk
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
TEST_LIST += \
 | 
			
		||||
	encoder \
 | 
			
		||||
	encoder_split
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,8 @@
 | 
			
		|||
#include "haptic.h"
 | 
			
		||||
#include "eeconfig.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "usb_device_state.h"
 | 
			
		||||
#include "gpio.h"
 | 
			
		||||
#ifdef DRV2605L
 | 
			
		||||
#    include "DRV2605L.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +28,29 @@
 | 
			
		|||
 | 
			
		||||
haptic_config_t haptic_config;
 | 
			
		||||
 | 
			
		||||
static void update_haptic_enable_gpios(void) {
 | 
			
		||||
    if (haptic_config.enable && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED))) {
 | 
			
		||||
#if defined(HAPTIC_ENABLE_PIN)
 | 
			
		||||
        HAPTIC_ENABLE_PIN_WRITE_ACTIVE();
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
        HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE();
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
#if defined(HAPTIC_ENABLE_PIN)
 | 
			
		||||
        HAPTIC_ENABLE_PIN_WRITE_INACTIVE();
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
        HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_haptic_config_enable(bool enabled) {
 | 
			
		||||
    haptic_config.enable = enabled;
 | 
			
		||||
    update_haptic_enable_gpios();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void haptic_init(void) {
 | 
			
		||||
    if (!eeconfig_is_enabled()) {
 | 
			
		||||
        eeconfig_init();
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +69,10 @@ void haptic_init(void) {
 | 
			
		|||
        // or the previous firmware didn't have solenoid enabled,
 | 
			
		||||
        // and the current one has solenoid enabled.
 | 
			
		||||
        haptic_reset();
 | 
			
		||||
    } else {
 | 
			
		||||
        // Haptic configuration has been loaded through the "raw" union item.
 | 
			
		||||
        // This is to execute any side effects of the configuration.
 | 
			
		||||
        set_haptic_config_enable(haptic_config.enable);
 | 
			
		||||
    }
 | 
			
		||||
#ifdef SOLENOID_ENABLE
 | 
			
		||||
    solenoid_setup();
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +83,12 @@ void haptic_init(void) {
 | 
			
		|||
    dprintf("DRV2605 driver initialized\n");
 | 
			
		||||
#endif
 | 
			
		||||
    eeconfig_debug_haptic();
 | 
			
		||||
#ifdef HAPTIC_ENABLE_PIN
 | 
			
		||||
    setPinOutput(HAPTIC_ENABLE_PIN);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAPTIC_ENABLE_STATUS_LED
 | 
			
		||||
    setPinOutput(HAPTIC_ENABLE_STATUS_LED);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void haptic_task(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,13 +104,13 @@ void eeconfig_debug_haptic(void) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void haptic_enable(void) {
 | 
			
		||||
    haptic_config.enable = 1;
 | 
			
		||||
    set_haptic_config_enable(true);
 | 
			
		||||
    xprintf("haptic_config.enable = %u\n", haptic_config.enable);
 | 
			
		||||
    eeconfig_update_haptic(haptic_config.raw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void haptic_disable(void) {
 | 
			
		||||
    haptic_config.enable = 0;
 | 
			
		||||
    set_haptic_config_enable(false);
 | 
			
		||||
    xprintf("haptic_config.enable = %u\n", haptic_config.enable);
 | 
			
		||||
    eeconfig_update_haptic(haptic_config.raw);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +192,7 @@ void haptic_dwell_decrease(void) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void haptic_reset(void) {
 | 
			
		||||
    haptic_config.enable   = true;
 | 
			
		||||
    set_haptic_config_enable(true);
 | 
			
		||||
    uint8_t feedback       = HAPTIC_FEEDBACK_DEFAULT;
 | 
			
		||||
    haptic_config.feedback = feedback;
 | 
			
		||||
#ifdef DRV2605L
 | 
			
		||||
| 
						 | 
				
			
			@ -293,3 +328,13 @@ void haptic_shutdown(void) {
 | 
			
		|||
    solenoid_shutdown();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void haptic_notify_usb_device_state_change(void) {
 | 
			
		||||
    update_haptic_enable_gpios();
 | 
			
		||||
#if defined(HAPTIC_ENABLE_PIN)
 | 
			
		||||
    setPinOutput(HAPTIC_ENABLE_PIN);
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
    setPinOutput(HAPTIC_ENABLE_STATUS_LED);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,3 +75,30 @@ void    haptic_cont_decrease(void);
 | 
			
		|||
 | 
			
		||||
void haptic_play(void);
 | 
			
		||||
void haptic_shutdown(void);
 | 
			
		||||
void haptic_notify_usb_device_state_change(void);
 | 
			
		||||
 | 
			
		||||
#ifdef HAPTIC_ENABLE_PIN_ACTIVE_LOW
 | 
			
		||||
#    ifndef HAPTIC_ENABLE_PIN
 | 
			
		||||
#        error HAPTIC_ENABLE_PIN not defined
 | 
			
		||||
#    endif
 | 
			
		||||
#    define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
 | 
			
		||||
#    define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
 | 
			
		||||
#else
 | 
			
		||||
#    define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
 | 
			
		||||
#    define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW
 | 
			
		||||
#    ifndef HAPTIC_ENABLE_STATUS_LED
 | 
			
		||||
#        error HAPTIC_ENABLE_STATUS_LED not defined
 | 
			
		||||
#    endif
 | 
			
		||||
#    define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
#    define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
#else
 | 
			
		||||
#    define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
#    define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef HAPTIC_OFF_IN_LOW_POWER
 | 
			
		||||
#    define HAPTIC_OFF_IN_LOW_POWER 0
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,12 +40,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#ifdef PS2_MOUSE_ENABLE
 | 
			
		||||
#    include "ps2_mouse.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SERIAL_MOUSE_ENABLE
 | 
			
		||||
#    include "serial_mouse.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ADB_MOUSE_ENABLE
 | 
			
		||||
#    include "adb.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RGBLIGHT_ENABLE
 | 
			
		||||
#    include "rgblight.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -61,12 +55,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#ifdef STENO_ENABLE
 | 
			
		||||
#    include "process_steno.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SERIAL_LINK_ENABLE
 | 
			
		||||
#    include "serial_link/system/serial_link.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef VISUALIZER_ENABLE
 | 
			
		||||
#    include "visualizer/visualizer.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef POINTING_DEVICE_ENABLE
 | 
			
		||||
#    include "pointing_device.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +70,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#ifdef HD44780_ENABLE
 | 
			
		||||
#    include "hd44780.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef QWIIC_ENABLE
 | 
			
		||||
#    include "qwiic.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef OLED_ENABLE
 | 
			
		||||
#    include "oled_driver.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +94,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#ifdef DIGITIZER_ENABLE
 | 
			
		||||
#    include "digitizer.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef VIRTSER_ENABLE
 | 
			
		||||
#    include "virtser.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SLEEP_LED_ENABLE
 | 
			
		||||
#    include "sleep_led.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static uint32_t last_input_modification_time = 0;
 | 
			
		||||
uint32_t        last_input_activity_time(void) { return last_input_modification_time; }
 | 
			
		||||
| 
						 | 
				
			
			@ -313,9 +304,6 @@ void keyboard_init(void) {
 | 
			
		|||
#if defined(CRC_ENABLE)
 | 
			
		||||
    crc_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef QWIIC_ENABLE
 | 
			
		||||
    qwiic_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef OLED_ENABLE
 | 
			
		||||
    oled_init(OLED_ROTATION_0);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -325,12 +313,6 @@ void keyboard_init(void) {
 | 
			
		|||
#ifdef PS2_MOUSE_ENABLE
 | 
			
		||||
    ps2_mouse_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SERIAL_MOUSE_ENABLE
 | 
			
		||||
    serial_mouse_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ADB_MOUSE_ENABLE
 | 
			
		||||
    adb_mouse_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    backlight_init();
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -353,6 +335,12 @@ void keyboard_init(void) {
 | 
			
		|||
#ifdef DIP_SWITCH_ENABLE
 | 
			
		||||
    dip_switch_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SLEEP_LED_ENABLE
 | 
			
		||||
    sleep_led_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef VIRTSER_ENABLE
 | 
			
		||||
    virtser_init();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
 | 
			
		||||
    debug_enable = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -381,7 +369,6 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {
 | 
			
		|||
 *
 | 
			
		||||
 * * scan matrix
 | 
			
		||||
 * * handle mouse movements
 | 
			
		||||
 * * run visualizer code
 | 
			
		||||
 * * handle midi commands
 | 
			
		||||
 * * light LEDs
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -470,10 +457,6 @@ MATRIX_LOOP_END:
 | 
			
		|||
    if (encoders_changed) last_encoder_activity_trigger();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef QWIIC_ENABLE
 | 
			
		||||
    qwiic_task();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef OLED_ENABLE
 | 
			
		||||
    oled_task();
 | 
			
		||||
#    if OLED_TIMEOUT > 0
 | 
			
		||||
| 
						 | 
				
			
			@ -507,22 +490,6 @@ MATRIX_LOOP_END:
 | 
			
		|||
    ps2_mouse_task();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SERIAL_MOUSE_ENABLE
 | 
			
		||||
    serial_mouse_task();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ADB_MOUSE_ENABLE
 | 
			
		||||
    adb_mouse_task();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SERIAL_LINK_ENABLE
 | 
			
		||||
    serial_link_update();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef VISUALIZER_ENABLE
 | 
			
		||||
    visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef POINTING_DEVICE_ENABLE
 | 
			
		||||
    pointing_device_task();
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
 | 
			
		||||
#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
 | 
			
		||||
#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
 | 
			
		||||
#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
 | 
			
		||||
#define IS_MOD(code) (KC_LEFT_CTRL <= (code) && (code) <= KC_RIGHT_GUI)
 | 
			
		||||
 | 
			
		||||
#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
 | 
			
		||||
#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
 | 
			
		||||
| 
						 | 
				
			
			@ -46,10 +46,10 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define MOD_BIT(code) (1 << MOD_INDEX(code))
 | 
			
		||||
#define MOD_INDEX(code) ((code)&0x07)
 | 
			
		||||
 | 
			
		||||
#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
 | 
			
		||||
#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
 | 
			
		||||
#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
 | 
			
		||||
#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
 | 
			
		||||
#define MOD_MASK_CTRL (MOD_BIT(KC_LEFT_CTRL) | MOD_BIT(KC_RIGHT_CTRL))
 | 
			
		||||
#define MOD_MASK_SHIFT (MOD_BIT(KC_LEFT_SHIFT) | MOD_BIT(KC_RIGHT_SHIFT))
 | 
			
		||||
#define MOD_MASK_ALT (MOD_BIT(KC_LEFT_ALT) | MOD_BIT(KC_RIGHT_ALT))
 | 
			
		||||
#define MOD_MASK_GUI (MOD_BIT(KC_LEFT_GUI) | MOD_BIT(KC_RIGHT_GUI))
 | 
			
		||||
#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
 | 
			
		||||
#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
 | 
			
		||||
#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +67,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define FN_MIN KC_FN0
 | 
			
		||||
#define FN_MAX KC_FN31
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Short names for ease of definition of keymap
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -75,47 +77,55 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define KC_TRNS KC_TRANSPARENT
 | 
			
		||||
 | 
			
		||||
/* Punctuation */
 | 
			
		||||
#define KC_ENT KC_ENTER
 | 
			
		||||
#define KC_ESC KC_ESCAPE
 | 
			
		||||
#define KC_BSPC KC_BSPACE
 | 
			
		||||
#define KC_SPC KC_SPACE
 | 
			
		||||
#define KC_ENT  KC_ENTER
 | 
			
		||||
#define KC_ESC  KC_ESCAPE
 | 
			
		||||
#define KC_BSPC KC_BACKSPACE
 | 
			
		||||
#define KC_SPC  KC_SPACE
 | 
			
		||||
#define KC_MINS KC_MINUS
 | 
			
		||||
#define KC_EQL KC_EQUAL
 | 
			
		||||
#define KC_LBRC KC_LBRACKET
 | 
			
		||||
#define KC_RBRC KC_RBRACKET
 | 
			
		||||
#define KC_BSLS KC_BSLASH
 | 
			
		||||
#define KC_EQL  KC_EQUAL
 | 
			
		||||
#define KC_LBRC KC_LEFT_BRACKET
 | 
			
		||||
#define KC_RBRC KC_RIGHT_BRACKET
 | 
			
		||||
#define KC_BSLS KC_BACKSLASH
 | 
			
		||||
#define KC_NUHS KC_NONUS_HASH
 | 
			
		||||
#define KC_SCLN KC_SCOLON
 | 
			
		||||
#define KC_SCLN KC_SEMICOLON
 | 
			
		||||
#define KC_QUOT KC_QUOTE
 | 
			
		||||
#define KC_GRV KC_GRAVE
 | 
			
		||||
#define KC_GRV  KC_GRAVE
 | 
			
		||||
#define KC_COMM KC_COMMA
 | 
			
		||||
#define KC_SLSH KC_SLASH
 | 
			
		||||
#define KC_NUBS KC_NONUS_BSLASH
 | 
			
		||||
#define KC_NUBS KC_NONUS_BACKSLASH
 | 
			
		||||
 | 
			
		||||
/* Lock Keys */
 | 
			
		||||
#define KC_CLCK KC_CAPSLOCK
 | 
			
		||||
#define KC_CAPS KC_CAPSLOCK
 | 
			
		||||
#define KC_SLCK KC_SCROLLLOCK
 | 
			
		||||
#define KC_NLCK KC_NUMLOCK
 | 
			
		||||
#define KC_LCAP KC_LOCKING_CAPS
 | 
			
		||||
#define KC_LNUM KC_LOCKING_NUM
 | 
			
		||||
#define KC_LSCR KC_LOCKING_SCROLL
 | 
			
		||||
#define KC_CAPS KC_CAPS_LOCK
 | 
			
		||||
#define KC_SCRL KC_SCROLL_LOCK
 | 
			
		||||
#define KC_NUM  KC_NUM_LOCK
 | 
			
		||||
#define KC_LCAP KC_LOCKING_CAPS_LOCK
 | 
			
		||||
#define KC_LNUM KC_LOCKING_NUM_LOCK
 | 
			
		||||
#define KC_LSCR KC_LOCKING_SCROLL_LOCK
 | 
			
		||||
 | 
			
		||||
/* Commands */
 | 
			
		||||
#define KC_PSCR KC_PSCREEN
 | 
			
		||||
#define KC_PSCR KC_PRINT_SCREEN
 | 
			
		||||
#define KC_PAUS KC_PAUSE
 | 
			
		||||
#define KC_BRK KC_PAUSE
 | 
			
		||||
#define KC_INS KC_INSERT
 | 
			
		||||
#define KC_DEL KC_DELETE
 | 
			
		||||
#define KC_PGDN KC_PGDOWN
 | 
			
		||||
#define KC_BRK  KC_PAUSE
 | 
			
		||||
#define KC_INS  KC_INSERT
 | 
			
		||||
#define KC_PGUP KC_PAGE_UP
 | 
			
		||||
#define KC_DEL  KC_DELETE
 | 
			
		||||
#define KC_PGDN KC_PAGE_DOWN
 | 
			
		||||
#define KC_RGHT KC_RIGHT
 | 
			
		||||
#define KC_APP KC_APPLICATION
 | 
			
		||||
#define KC_APP  KC_APPLICATION
 | 
			
		||||
#define KC_EXEC KC_EXECUTE
 | 
			
		||||
#define KC_SLCT KC_SELECT
 | 
			
		||||
#define KC_AGIN KC_AGAIN
 | 
			
		||||
#define KC_PSTE KC_PASTE
 | 
			
		||||
#define KC_ERAS KC_ALT_ERASE
 | 
			
		||||
#define KC_CLR KC_CLEAR
 | 
			
		||||
#define KC_ERAS KC_ALTERNATE_ERASE
 | 
			
		||||
#define KC_SYRQ KC_SYSTEM_REQUEST
 | 
			
		||||
#define KC_CNCL KC_CANCEL
 | 
			
		||||
#define KC_CLR  KC_CLEAR
 | 
			
		||||
#define KC_PRIR KC_PRIOR
 | 
			
		||||
#define KC_RETN KC_RETURN
 | 
			
		||||
#define KC_SEPR KC_SEPARATOR
 | 
			
		||||
#define KC_CLAG KC_CLEAR_AGAIN
 | 
			
		||||
#define KC_CRSL KC_CRSEL
 | 
			
		||||
#define KC_EXSL KC_EXSEL
 | 
			
		||||
 | 
			
		||||
/* Keypad */
 | 
			
		||||
#define KC_PSLS KC_KP_SLASH
 | 
			
		||||
| 
						 | 
				
			
			@ -123,47 +133,59 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define KC_PMNS KC_KP_MINUS
 | 
			
		||||
#define KC_PPLS KC_KP_PLUS
 | 
			
		||||
#define KC_PENT KC_KP_ENTER
 | 
			
		||||
#define KC_P1 KC_KP_1
 | 
			
		||||
#define KC_P2 KC_KP_2
 | 
			
		||||
#define KC_P3 KC_KP_3
 | 
			
		||||
#define KC_P4 KC_KP_4
 | 
			
		||||
#define KC_P5 KC_KP_5
 | 
			
		||||
#define KC_P6 KC_KP_6
 | 
			
		||||
#define KC_P7 KC_KP_7
 | 
			
		||||
#define KC_P8 KC_KP_8
 | 
			
		||||
#define KC_P9 KC_KP_9
 | 
			
		||||
#define KC_P0 KC_KP_0
 | 
			
		||||
#define KC_P1   KC_KP_1
 | 
			
		||||
#define KC_P2   KC_KP_2
 | 
			
		||||
#define KC_P3   KC_KP_3
 | 
			
		||||
#define KC_P4   KC_KP_4
 | 
			
		||||
#define KC_P5   KC_KP_5
 | 
			
		||||
#define KC_P6   KC_KP_6
 | 
			
		||||
#define KC_P7   KC_KP_7
 | 
			
		||||
#define KC_P8   KC_KP_8
 | 
			
		||||
#define KC_P9   KC_KP_9
 | 
			
		||||
#define KC_P0   KC_KP_0
 | 
			
		||||
#define KC_PDOT KC_KP_DOT
 | 
			
		||||
#define KC_PEQL KC_KP_EQUAL
 | 
			
		||||
#define KC_PCMM KC_KP_COMMA
 | 
			
		||||
 | 
			
		||||
/* Japanese specific */
 | 
			
		||||
#define KC_ZKHK KC_GRAVE
 | 
			
		||||
#define KC_RO KC_INT1
 | 
			
		||||
#define KC_KANA KC_INT2
 | 
			
		||||
#define KC_JYEN KC_INT3
 | 
			
		||||
#define KC_HENK KC_INT4
 | 
			
		||||
#define KC_MHEN KC_INT5
 | 
			
		||||
 | 
			
		||||
/* Korean specific */
 | 
			
		||||
#define KC_HAEN KC_LANG1
 | 
			
		||||
#define KC_HANJ KC_LANG2
 | 
			
		||||
/* Language Specific */
 | 
			
		||||
#define KC_INT1 KC_INTERNATIONAL_1
 | 
			
		||||
#define KC_INT2 KC_INTERNATIONAL_2
 | 
			
		||||
#define KC_INT3 KC_INTERNATIONAL_3
 | 
			
		||||
#define KC_INT4 KC_INTERNATIONAL_4
 | 
			
		||||
#define KC_INT5 KC_INTERNATIONAL_5
 | 
			
		||||
#define KC_INT6 KC_INTERNATIONAL_6
 | 
			
		||||
#define KC_INT7 KC_INTERNATIONAL_7
 | 
			
		||||
#define KC_INT8 KC_INTERNATIONAL_8
 | 
			
		||||
#define KC_INT9 KC_INTERNATIONAL_9
 | 
			
		||||
#define KC_LNG1 KC_LANGUAGE_1
 | 
			
		||||
#define KC_LNG2 KC_LANGUAGE_2
 | 
			
		||||
#define KC_LNG3 KC_LANGUAGE_3
 | 
			
		||||
#define KC_LNG4 KC_LANGUAGE_4
 | 
			
		||||
#define KC_LNG5 KC_LANGUAGE_5
 | 
			
		||||
#define KC_LNG6 KC_LANGUAGE_6
 | 
			
		||||
#define KC_LNG7 KC_LANGUAGE_7
 | 
			
		||||
#define KC_LNG8 KC_LANGUAGE_8
 | 
			
		||||
#define KC_LNG9 KC_LANGUAGE_9
 | 
			
		||||
 | 
			
		||||
/* Modifiers */
 | 
			
		||||
#define KC_LCTL KC_LCTRL
 | 
			
		||||
#define KC_LSFT KC_LSHIFT
 | 
			
		||||
#define KC_LOPT KC_LALT
 | 
			
		||||
#define KC_LCMD KC_LGUI
 | 
			
		||||
#define KC_LWIN KC_LGUI
 | 
			
		||||
#define KC_RCTL KC_RCTRL
 | 
			
		||||
#define KC_RSFT KC_RSHIFT
 | 
			
		||||
#define KC_ALGR KC_RALT
 | 
			
		||||
#define KC_ROPT KC_RALT
 | 
			
		||||
#define KC_RCMD KC_RGUI
 | 
			
		||||
#define KC_RWIN KC_RGUI
 | 
			
		||||
#define KC_LCTL KC_LEFT_CTRL
 | 
			
		||||
#define KC_LSFT KC_LEFT_SHIFT
 | 
			
		||||
#define KC_LALT KC_LEFT_ALT
 | 
			
		||||
#define KC_LOPT KC_LEFT_ALT
 | 
			
		||||
#define KC_LGUI KC_LEFT_GUI
 | 
			
		||||
#define KC_LCMD KC_LEFT_GUI
 | 
			
		||||
#define KC_LWIN KC_LEFT_GUI
 | 
			
		||||
#define KC_RCTL KC_RIGHT_CTRL
 | 
			
		||||
#define KC_RSFT KC_RIGHT_SHIFT
 | 
			
		||||
#define KC_RALT KC_RIGHT_ALT
 | 
			
		||||
#define KC_ALGR KC_RIGHT_ALT
 | 
			
		||||
#define KC_ROPT KC_RIGHT_ALT
 | 
			
		||||
#define KC_RGUI KC_RIGHT_GUI
 | 
			
		||||
#define KC_RCMD KC_RIGHT_GUI
 | 
			
		||||
#define KC_RWIN KC_RIGHT_GUI
 | 
			
		||||
 | 
			
		||||
/* Generic Desktop Page (0x01) */
 | 
			
		||||
#define KC_PWR KC_SYSTEM_POWER
 | 
			
		||||
#define KC_PWR  KC_SYSTEM_POWER
 | 
			
		||||
#define KC_SLEP KC_SYSTEM_SLEEP
 | 
			
		||||
#define KC_WAKE KC_SYSTEM_WAKE
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +215,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 | 
			
		||||
/* System Specific */
 | 
			
		||||
#define KC_BRMU KC_PAUSE
 | 
			
		||||
#define KC_BRMD KC_SCROLLLOCK
 | 
			
		||||
#define KC_BRMD KC_SCROLL_LOCK
 | 
			
		||||
 | 
			
		||||
/* Mouse Keys */
 | 
			
		||||
#define KC_MS_U KC_MS_UP
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +238,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#define KC_ACL1 KC_MS_ACCEL1
 | 
			
		||||
#define KC_ACL2 KC_MS_ACCEL2
 | 
			
		||||
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
/* Keyboard/Keypad Page (0x07) */
 | 
			
		||||
enum hid_keyboard_keypad_usage {
 | 
			
		||||
    KC_NO = 0x00,
 | 
			
		||||
| 
						 | 
				
			
			@ -260,22 +284,22 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
    KC_0,
 | 
			
		||||
    KC_ENTER,
 | 
			
		||||
    KC_ESCAPE,
 | 
			
		||||
    KC_BSPACE,
 | 
			
		||||
    KC_BACKSPACE,
 | 
			
		||||
    KC_TAB,
 | 
			
		||||
    KC_SPACE,
 | 
			
		||||
    KC_MINUS,
 | 
			
		||||
    KC_EQUAL,
 | 
			
		||||
    KC_LBRACKET,
 | 
			
		||||
    KC_RBRACKET,  // 0x30
 | 
			
		||||
    KC_BSLASH,
 | 
			
		||||
    KC_LEFT_BRACKET,
 | 
			
		||||
    KC_RIGHT_BRACKET,  // 0x30
 | 
			
		||||
    KC_BACKSLASH,
 | 
			
		||||
    KC_NONUS_HASH,
 | 
			
		||||
    KC_SCOLON,
 | 
			
		||||
    KC_SEMICOLON,
 | 
			
		||||
    KC_QUOTE,
 | 
			
		||||
    KC_GRAVE,
 | 
			
		||||
    KC_COMMA,
 | 
			
		||||
    KC_DOT,
 | 
			
		||||
    KC_SLASH,
 | 
			
		||||
    KC_CAPSLOCK,
 | 
			
		||||
    KC_CAPS_LOCK,
 | 
			
		||||
    KC_F1,
 | 
			
		||||
    KC_F2,
 | 
			
		||||
    KC_F3,
 | 
			
		||||
| 
						 | 
				
			
			@ -288,20 +312,20 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
    KC_F10,
 | 
			
		||||
    KC_F11,
 | 
			
		||||
    KC_F12,
 | 
			
		||||
    KC_PSCREEN,
 | 
			
		||||
    KC_SCROLLLOCK,
 | 
			
		||||
    KC_PRINT_SCREEN,
 | 
			
		||||
    KC_SCROLL_LOCK,
 | 
			
		||||
    KC_PAUSE,
 | 
			
		||||
    KC_INSERT,
 | 
			
		||||
    KC_HOME,
 | 
			
		||||
    KC_PGUP,
 | 
			
		||||
    KC_PAGE_UP,
 | 
			
		||||
    KC_DELETE,
 | 
			
		||||
    KC_END,
 | 
			
		||||
    KC_PGDOWN,
 | 
			
		||||
    KC_PAGE_DOWN,
 | 
			
		||||
    KC_RIGHT,
 | 
			
		||||
    KC_LEFT,  // 0x50
 | 
			
		||||
    KC_DOWN,
 | 
			
		||||
    KC_UP,
 | 
			
		||||
    KC_NUMLOCK,
 | 
			
		||||
    KC_NUM_LOCK,
 | 
			
		||||
    KC_KP_SLASH,
 | 
			
		||||
    KC_KP_ASTERISK,
 | 
			
		||||
    KC_KP_MINUS,
 | 
			
		||||
| 
						 | 
				
			
			@ -318,9 +342,9 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
    KC_KP_9,
 | 
			
		||||
    KC_KP_0,
 | 
			
		||||
    KC_KP_DOT,
 | 
			
		||||
    KC_NONUS_BSLASH,
 | 
			
		||||
    KC_NONUS_BACKSLASH,
 | 
			
		||||
    KC_APPLICATION,
 | 
			
		||||
    KC_POWER,
 | 
			
		||||
    KC_KB_POWER,
 | 
			
		||||
    KC_KP_EQUAL,
 | 
			
		||||
    KC_F13,
 | 
			
		||||
    KC_F14,
 | 
			
		||||
| 
						 | 
				
			
			@ -345,34 +369,34 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
    KC_COPY,
 | 
			
		||||
    KC_PASTE,
 | 
			
		||||
    KC_FIND,
 | 
			
		||||
    KC__MUTE,
 | 
			
		||||
    KC__VOLUP,  // 0x80
 | 
			
		||||
    KC__VOLDOWN,
 | 
			
		||||
    KC_LOCKING_CAPS,
 | 
			
		||||
    KC_LOCKING_NUM,
 | 
			
		||||
    KC_LOCKING_SCROLL,
 | 
			
		||||
    KC_KB_MUTE,
 | 
			
		||||
    KC_KB_VOLUME_UP,  // 0x80
 | 
			
		||||
    KC_KB_VOLUME_DOWN,
 | 
			
		||||
    KC_LOCKING_CAPS_LOCK,
 | 
			
		||||
    KC_LOCKING_NUM_LOCK,
 | 
			
		||||
    KC_LOCKING_SCROLL_LOCK,
 | 
			
		||||
    KC_KP_COMMA,
 | 
			
		||||
    KC_KP_EQUAL_AS400,
 | 
			
		||||
    KC_INT1,
 | 
			
		||||
    KC_INT2,
 | 
			
		||||
    KC_INT3,
 | 
			
		||||
    KC_INT4,
 | 
			
		||||
    KC_INT5,
 | 
			
		||||
    KC_INT6,
 | 
			
		||||
    KC_INT7,
 | 
			
		||||
    KC_INT8,
 | 
			
		||||
    KC_INT9,
 | 
			
		||||
    KC_LANG1,  // 0x90
 | 
			
		||||
    KC_LANG2,
 | 
			
		||||
    KC_LANG3,
 | 
			
		||||
    KC_LANG4,
 | 
			
		||||
    KC_LANG5,
 | 
			
		||||
    KC_LANG6,
 | 
			
		||||
    KC_LANG7,
 | 
			
		||||
    KC_LANG8,
 | 
			
		||||
    KC_LANG9,
 | 
			
		||||
    KC_ALT_ERASE,
 | 
			
		||||
    KC_SYSREQ,
 | 
			
		||||
    KC_INTERNATIONAL_1,
 | 
			
		||||
    KC_INTERNATIONAL_2,
 | 
			
		||||
    KC_INTERNATIONAL_3,
 | 
			
		||||
    KC_INTERNATIONAL_4,
 | 
			
		||||
    KC_INTERNATIONAL_5,
 | 
			
		||||
    KC_INTERNATIONAL_6,
 | 
			
		||||
    KC_INTERNATIONAL_7,
 | 
			
		||||
    KC_INTERNATIONAL_8,
 | 
			
		||||
    KC_INTERNATIONAL_9,
 | 
			
		||||
    KC_LANGUAGE_1,  // 0x90
 | 
			
		||||
    KC_LANGUAGE_2,
 | 
			
		||||
    KC_LANGUAGE_3,
 | 
			
		||||
    KC_LANGUAGE_4,
 | 
			
		||||
    KC_LANGUAGE_5,
 | 
			
		||||
    KC_LANGUAGE_6,
 | 
			
		||||
    KC_LANGUAGE_7,
 | 
			
		||||
    KC_LANGUAGE_8,
 | 
			
		||||
    KC_LANGUAGE_9,
 | 
			
		||||
    KC_ALTERNATE_ERASE,
 | 
			
		||||
    KC_SYSTEM_REQUEST,
 | 
			
		||||
    KC_CANCEL,
 | 
			
		||||
    KC_CLEAR,
 | 
			
		||||
    KC_PRIOR,
 | 
			
		||||
| 
						 | 
				
			
			@ -397,12 +421,12 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
  KC_DECIMAL_SEPARATOR,
 | 
			
		||||
  KC_CURRENCY_UNIT,
 | 
			
		||||
  KC_CURRENCY_SUB_UNIT,
 | 
			
		||||
  KC_KP_LPAREN,
 | 
			
		||||
  KC_KP_RPAREN,
 | 
			
		||||
  KC_KP_LCBRACKET,
 | 
			
		||||
  KC_KP_RCBRACKET,
 | 
			
		||||
  KC_KP_LEFT_PARENTHESIS,
 | 
			
		||||
  KC_KP_RIGHT_PARENTHESIS,
 | 
			
		||||
  KC_KP_LEFT_BRACE,
 | 
			
		||||
  KC_KP_RIGHT_BRACE,
 | 
			
		||||
  KC_KP_TAB,
 | 
			
		||||
  KC_KP_BSPACE,
 | 
			
		||||
  KC_KP_BACKSPACE,
 | 
			
		||||
  KC_KP_A,
 | 
			
		||||
  KC_KP_B,
 | 
			
		||||
  KC_KP_C,
 | 
			
		||||
| 
						 | 
				
			
			@ -411,17 +435,17 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
  KC_KP_F,
 | 
			
		||||
  KC_KP_XOR,
 | 
			
		||||
  KC_KP_HAT,
 | 
			
		||||
  KC_KP_PERC,
 | 
			
		||||
  KC_KP_LT,
 | 
			
		||||
  KC_KP_GT,
 | 
			
		||||
  KC_KP_PERCENT,
 | 
			
		||||
  KC_KP_LESS_THAN,
 | 
			
		||||
  KC_KP_GREATER_THAN,
 | 
			
		||||
  KC_KP_AND,
 | 
			
		||||
  KC_KP_LAZYAND,
 | 
			
		||||
  KC_KP_LAZY_AND,
 | 
			
		||||
  KC_KP_OR,
 | 
			
		||||
  KC_KP_LAZYOR,
 | 
			
		||||
  KC_KP_LAZY_OR,
 | 
			
		||||
  KC_KP_COLON,
 | 
			
		||||
  KC_KP_HASH,
 | 
			
		||||
  KC_KP_SPACE,
 | 
			
		||||
  KC_KP_ATMARK,
 | 
			
		||||
  KC_KP_AT,
 | 
			
		||||
  KC_KP_EXCLAMATION,
 | 
			
		||||
  KC_KP_MEM_STORE,        //0xD0
 | 
			
		||||
  KC_KP_MEM_RECALL,
 | 
			
		||||
| 
						 | 
				
			
			@ -440,14 +464,14 @@ enum hid_keyboard_keypad_usage {
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Modifiers */
 | 
			
		||||
    KC_LCTRL = 0xE0,
 | 
			
		||||
    KC_LSHIFT,
 | 
			
		||||
    KC_LALT,
 | 
			
		||||
    KC_LGUI,
 | 
			
		||||
    KC_RCTRL,
 | 
			
		||||
    KC_RSHIFT,
 | 
			
		||||
    KC_RALT,
 | 
			
		||||
    KC_RGUI
 | 
			
		||||
    KC_LEFT_CTRL = 0xE0,
 | 
			
		||||
    KC_LEFT_SHIFT,
 | 
			
		||||
    KC_LEFT_ALT,
 | 
			
		||||
    KC_LEFT_GUI,
 | 
			
		||||
    KC_RIGHT_CTRL,
 | 
			
		||||
    KC_RIGHT_SHIFT,
 | 
			
		||||
    KC_RIGHT_ALT,
 | 
			
		||||
    KC_RIGHT_GUI
 | 
			
		||||
 | 
			
		||||
    // **********************************************
 | 
			
		||||
    // * 0xF0-0xFF are unallocated in the HID spec. *
 | 
			
		||||
| 
						 | 
				
			
			@ -558,3 +582,5 @@ enum mouse_keys {
 | 
			
		|||
    KC_MS_ACCEL1,
 | 
			
		||||
    KC_MS_ACCEL2  // 0xFF
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "keycode_legacy.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,89 +25,89 @@ extern keymap_config_t keymap_config;
 | 
			
		|||
 */
 | 
			
		||||
uint16_t keycode_config(uint16_t keycode) {
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
        case KC_CAPSLOCK:
 | 
			
		||||
        case KC_LOCKING_CAPS:
 | 
			
		||||
        case KC_CAPS_LOCK:
 | 
			
		||||
        case KC_LOCKING_CAPS_LOCK:
 | 
			
		||||
            if (keymap_config.swap_control_capslock || keymap_config.capslock_to_control) {
 | 
			
		||||
                return KC_LCTL;
 | 
			
		||||
                return KC_LEFT_CTRL;
 | 
			
		||||
            }
 | 
			
		||||
            return keycode;
 | 
			
		||||
        case KC_LCTL:
 | 
			
		||||
        case KC_LEFT_CTRL:
 | 
			
		||||
            if (keymap_config.swap_control_capslock) {
 | 
			
		||||
                return KC_CAPSLOCK;
 | 
			
		||||
                return KC_CAPS_LOCK;
 | 
			
		||||
            }
 | 
			
		||||
            if (keymap_config.swap_lctl_lgui) {
 | 
			
		||||
                if (keymap_config.no_gui) {
 | 
			
		||||
                    return KC_NO;
 | 
			
		||||
                }
 | 
			
		||||
                return KC_LGUI;
 | 
			
		||||
                return KC_LEFT_GUI;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_LCTL;
 | 
			
		||||
        case KC_LALT:
 | 
			
		||||
            return KC_LEFT_CTRL;
 | 
			
		||||
        case KC_LEFT_ALT:
 | 
			
		||||
            if (keymap_config.swap_lalt_lgui) {
 | 
			
		||||
                if (keymap_config.no_gui) {
 | 
			
		||||
                    return KC_NO;
 | 
			
		||||
                }
 | 
			
		||||
                return KC_LGUI;
 | 
			
		||||
                return KC_LEFT_GUI;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_LALT;
 | 
			
		||||
        case KC_LGUI:
 | 
			
		||||
            return KC_LEFT_ALT;
 | 
			
		||||
        case KC_LEFT_GUI:
 | 
			
		||||
            if (keymap_config.swap_lalt_lgui) {
 | 
			
		||||
                return KC_LALT;
 | 
			
		||||
                return KC_LEFT_ALT;
 | 
			
		||||
            }
 | 
			
		||||
            if (keymap_config.swap_lctl_lgui) {
 | 
			
		||||
                return KC_LCTRL;
 | 
			
		||||
                return KC_LEFT_CTRL;
 | 
			
		||||
            }
 | 
			
		||||
            if (keymap_config.no_gui) {
 | 
			
		||||
                return KC_NO;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_LGUI;
 | 
			
		||||
        case KC_RCTL:
 | 
			
		||||
            return KC_LEFT_GUI;
 | 
			
		||||
        case KC_RIGHT_CTRL:
 | 
			
		||||
            if (keymap_config.swap_rctl_rgui) {
 | 
			
		||||
                if (keymap_config.no_gui) {
 | 
			
		||||
                    return KC_NO;
 | 
			
		||||
                }
 | 
			
		||||
                return KC_RGUI;
 | 
			
		||||
                return KC_RIGHT_GUI;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_RCTL;
 | 
			
		||||
        case KC_RALT:
 | 
			
		||||
            return KC_RIGHT_CTRL;
 | 
			
		||||
        case KC_RIGHT_ALT:
 | 
			
		||||
            if (keymap_config.swap_ralt_rgui) {
 | 
			
		||||
                if (keymap_config.no_gui) {
 | 
			
		||||
                    return KC_NO;
 | 
			
		||||
                }
 | 
			
		||||
                return KC_RGUI;
 | 
			
		||||
                return KC_RIGHT_GUI;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_RALT;
 | 
			
		||||
        case KC_RGUI:
 | 
			
		||||
            return KC_RIGHT_ALT;
 | 
			
		||||
        case KC_RIGHT_GUI:
 | 
			
		||||
            if (keymap_config.swap_ralt_rgui) {
 | 
			
		||||
                return KC_RALT;
 | 
			
		||||
                return KC_RIGHT_ALT;
 | 
			
		||||
            }
 | 
			
		||||
            if (keymap_config.swap_rctl_rgui) {
 | 
			
		||||
                return KC_RCTL;
 | 
			
		||||
                return KC_RIGHT_CTRL;
 | 
			
		||||
            }
 | 
			
		||||
            if (keymap_config.no_gui) {
 | 
			
		||||
                return KC_NO;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_RGUI;
 | 
			
		||||
            return KC_RIGHT_GUI;
 | 
			
		||||
        case KC_GRAVE:
 | 
			
		||||
            if (keymap_config.swap_grave_esc) {
 | 
			
		||||
                return KC_ESC;
 | 
			
		||||
                return KC_ESCAPE;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_GRAVE;
 | 
			
		||||
        case KC_ESC:
 | 
			
		||||
        case KC_ESCAPE:
 | 
			
		||||
            if (keymap_config.swap_grave_esc) {
 | 
			
		||||
                return KC_GRAVE;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_ESC;
 | 
			
		||||
        case KC_BSLASH:
 | 
			
		||||
            return KC_ESCAPE;
 | 
			
		||||
        case KC_BACKSLASH:
 | 
			
		||||
            if (keymap_config.swap_backslash_backspace) {
 | 
			
		||||
                return KC_BSPACE;
 | 
			
		||||
                return KC_BACKSPACE;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_BSLASH;
 | 
			
		||||
        case KC_BSPACE:
 | 
			
		||||
            return KC_BACKSLASH;
 | 
			
		||||
        case KC_BACKSPACE:
 | 
			
		||||
            if (keymap_config.swap_backslash_backspace) {
 | 
			
		||||
                return KC_BSLASH;
 | 
			
		||||
                return KC_BACKSLASH;
 | 
			
		||||
            }
 | 
			
		||||
            return KC_BSPACE;
 | 
			
		||||
            return KC_BACKSPACE;
 | 
			
		||||
        default:
 | 
			
		||||
            return keycode;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										53
									
								
								quantum/keycode_legacy.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								quantum/keycode_legacy.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
// These keycode names have been deprecated
 | 
			
		||||
 | 
			
		||||
#define KC_BSPACE         KC_BACKSPACE
 | 
			
		||||
#define KC_LBRACKET       KC_LEFT_BRACKET
 | 
			
		||||
#define KC_RBRACKET       KC_RIGHT_BRACKET
 | 
			
		||||
#define KC_BSLASH         KC_BACKSLASH
 | 
			
		||||
#define KC_SCOLON         KC_SEMICOLON
 | 
			
		||||
#define KC_CAPSLOCK       KC_CAPS_LOCK
 | 
			
		||||
#define KC_PSCREEN        KC_PRINT_SCREEN
 | 
			
		||||
#define KC_SCROLLLOCK     KC_SCROLL_LOCK
 | 
			
		||||
#define KC_PGDOWN         KC_PAGE_DOWN
 | 
			
		||||
#define KC_NUMLOCK        KC_NUM_LOCK
 | 
			
		||||
#define KC_NONUS_BSLASH   KC_NONUS_BACKSLASH
 | 
			
		||||
#define KC_POWER          KC_KB_POWER
 | 
			
		||||
#define KC__MUTE          KC_KB_MUTE
 | 
			
		||||
#define KC__VOLUP         KC_KB_VOLUME_UP
 | 
			
		||||
#define KC__VOLDOWN       KC_KB_VOLUME_DOWN
 | 
			
		||||
#define KC_LOCKING_CAPS   KC_LOCKING_CAPS_LOCK
 | 
			
		||||
#define KC_LOCKING_NUM    KC_LOCKING_NUM_LOCK
 | 
			
		||||
#define KC_LOCKING_SCROLL KC_LOCKING_SCROLL_LOCK
 | 
			
		||||
#define KC_LANG1          KC_LANGUAGE_1
 | 
			
		||||
#define KC_LANG2          KC_LANGUAGE_2
 | 
			
		||||
#define KC_LANG3          KC_LANGUAGE_3
 | 
			
		||||
#define KC_LANG4          KC_LANGUAGE_4
 | 
			
		||||
#define KC_LANG5          KC_LANGUAGE_5
 | 
			
		||||
#define KC_LANG6          KC_LANGUAGE_6
 | 
			
		||||
#define KC_LANG7          KC_LANGUAGE_7
 | 
			
		||||
#define KC_LANG8          KC_LANGUAGE_8
 | 
			
		||||
#define KC_LANG9          KC_LANGUAGE_9
 | 
			
		||||
#define KC_ALT_ERASE      KC_ALTERNATE_ERASE
 | 
			
		||||
#define KC_SYSREQ         KC_SYSTEM_REQUEST
 | 
			
		||||
 | 
			
		||||
#define KC_LCTRL          KC_LEFT_CTRL
 | 
			
		||||
#define KC_LSHIFT         KC_LEFT_SHIFT
 | 
			
		||||
#define KC_RCTRL          KC_RIGHT_CTRL
 | 
			
		||||
#define KC_RSHIFT         KC_RIGHT_SHIFT
 | 
			
		||||
 | 
			
		||||
#define KC_ZKHK KC_GRAVE
 | 
			
		||||
#define KC_RO   KC_INTERNATIONAL_1
 | 
			
		||||
#define KC_KANA KC_INTERNATIONAL_2
 | 
			
		||||
#define KC_JYEN KC_INTERNATIONAL_3
 | 
			
		||||
#define KC_HENK KC_INTERNATIONAL_4
 | 
			
		||||
#define KC_MHEN KC_INTERNATIONAL_5
 | 
			
		||||
#define KC_HAEN KC_LANGUAGE_1
 | 
			
		||||
#define KC_HANJ KC_LANGUAGE_2
 | 
			
		||||
 | 
			
		||||
#define KC_CLCK KC_CAPS_LOCK
 | 
			
		||||
#define KC_SLCK KC_SCROLL_LOCK
 | 
			
		||||
#define KC_NLCK KC_NUM_LOCK
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ action_t action_for_keycode(uint16_t keycode) {
 | 
			
		|||
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
        case KC_A ... KC_EXSEL:
 | 
			
		||||
        case KC_LCTRL ... KC_RGUI:
 | 
			
		||||
        case KC_LEFT_CTRL ... KC_RIGHT_GUI:
 | 
			
		||||
            action.code = ACTION_KEY(keycode);
 | 
			
		||||
            break;
 | 
			
		||||
#ifdef EXTRAKEY_ENABLE
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ action_t action_for_keycode(uint16_t keycode) {
 | 
			
		|||
            action.code = ACTION_MOUSEKEY(keycode);
 | 
			
		||||
            break;
 | 
			
		||||
#endif
 | 
			
		||||
        case KC_TRNS:
 | 
			
		||||
        case KC_TRANSPARENT:
 | 
			
		||||
            action.code = ACTION_TRANSPARENT;
 | 
			
		||||
            break;
 | 
			
		||||
        case QK_MODS ... QK_MODS_MAX:;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,8 +85,8 @@
 | 
			
		|||
#define KR_DOT  KC_DOT  // .
 | 
			
		||||
#define KR_SLSH KC_SLSH // /
 | 
			
		||||
// Row 5
 | 
			
		||||
#define KR_HANJ KC_LANG2 // Hanja (한자)
 | 
			
		||||
#define KR_HAEN KC_LANG1 // Han ↔ Yeong (한 ↔ 영)
 | 
			
		||||
#define KR_HANJ KC_LNG2 // Hanja (한자)
 | 
			
		||||
#define KR_HAEN KC_LNG1 // Han ↔ Yeong (한 ↔ 영)
 | 
			
		||||
 | 
			
		||||
/* Shifted symbols
 | 
			
		||||
 * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,8 +74,7 @@ enum steno_keycodes {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
#ifdef STENO_COMBINEDMAP
 | 
			
		||||
enum steno_combined_keycodes
 | 
			
		||||
{
 | 
			
		||||
enum steno_combined_keycodes {
 | 
			
		||||
    STN_S3 = QK_STENO_COMB,
 | 
			
		||||
    STN_TKL,
 | 
			
		||||
    STN_PWL,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,7 +111,7 @@
 | 
			
		|||
#define TR_LPRN S(TR_8)    // (
 | 
			
		||||
#define TR_RPRN S(TR_9)    // )
 | 
			
		||||
#define TR_EQL  S(TR_0)    // =
 | 
			
		||||
#define TR_QUES S(TR_ASTR) // ?
 | 
			
		||||
#define TR_QUES S(TR_SLSH) // ?
 | 
			
		||||
#define TR_UNDS S(TR_MINS) // _
 | 
			
		||||
// Row 4
 | 
			
		||||
#define TR_RABK S(TR_LABK) // >
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,8 @@ bool ALPHAS_MODS(effect_params_t* params) {
 | 
			
		|||
            led_matrix_set_value(i, val1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_ALPHAS_MODS
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_ALPHAS_MODS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,4 +10,4 @@ static uint8_t BAND_math(uint8_t val, uint8_t i, uint8_t time) {
 | 
			
		|||
bool BAND(effect_params_t* params) { return effect_runner_i(params, &BAND_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_BAND
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_BAND
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t t
 | 
			
		|||
bool BAND_PINWHEEL(effect_params_t* params) { return effect_runner_dx_dy(params, &BAND_PINWHEEL_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_BAND_PINWHEEL
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_BAND_PINWHEEL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dis
 | 
			
		|||
bool BAND_SPIRAL(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_BAND_SPIRAL
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_BAND_SPIRAL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@ bool BREATHING(effect_params_t* params) {
 | 
			
		|||
        LED_MATRIX_TEST_LED_FLAGS();
 | 
			
		||||
        led_matrix_set_value(i, val);
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_BREATHING
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_BREATHING
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { ret
 | 
			
		|||
bool CYCLE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t di
 | 
			
		|||
bool CYCLE_OUT_IN(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_CYCLE_OUT_IN
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_CYCLE_OUT_IN
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
 | 
			
		|||
bool CYCLE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &CYCLE_UP_DOWN_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_CYCLE_UP_DOWN
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_CYCLE_UP_DOWN
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i,
 | 
			
		|||
bool DUAL_BEACON(effect_params_t* params) { return effect_runner_sin_cos_i(params, &DUAL_BEACON_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_DUAL_BEACON
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_DUAL_BEACON
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,5 +12,5 @@ bool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) {
 | 
			
		|||
        int16_t dy = g_led_config.point[i].y - k_led_matrix_center.y;
 | 
			
		||||
        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, time));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,5 +13,5 @@ bool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func)
 | 
			
		|||
        uint8_t dist = sqrt16(dx * dx + dy * dy);
 | 
			
		||||
        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, dist, time));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,5 +10,5 @@ bool effect_runner_i(effect_params_t* params, i_f effect_func) {
 | 
			
		|||
        LED_MATRIX_TEST_LED_FLAGS();
 | 
			
		||||
        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, i, time));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ bool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) {
 | 
			
		|||
        uint16_t offset = scale16by8(tick, led_matrix_eeconfig.speed);
 | 
			
		||||
        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, offset));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif  // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ bool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, react
 | 
			
		|||
        }
 | 
			
		||||
        led_matrix_set_value(i, scale8(val, led_matrix_eeconfig.val));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif  // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,5 +12,5 @@ bool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) {
 | 
			
		|||
        LED_MATRIX_TEST_LED_FLAGS();
 | 
			
		||||
        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, cos_value, sin_value, i, time));
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ bool SOLID(effect_params_t* params) {
 | 
			
		|||
        LED_MATRIX_TEST_LED_FLAGS();
 | 
			
		||||
        led_matrix_set_value(i, val);
 | 
			
		||||
    }
 | 
			
		||||
    return led_max < DRIVER_LED_TOTAL;
 | 
			
		||||
    return led_matrix_check_finished_leds(led_max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
#    if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
 | 
			
		||||
#    if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
 | 
			
		||||
 | 
			
		||||
#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
 | 
			
		||||
LED_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)
 | 
			
		||||
| 
						 | 
				
			
			@ -31,5 +31,5 @@ bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { return effect_runner_r
 | 
			
		|||
#            endif
 | 
			
		||||
 | 
			
		||||
#        endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#    endif      // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
 | 
			
		||||
#    endif      // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
 | 
			
		||||
#endif          // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,5 +28,5 @@ bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { return effect_runner_r
 | 
			
		|||
#            endif
 | 
			
		||||
 | 
			
		||||
#        endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#    endif      // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
 | 
			
		||||
#    endif      // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
 | 
			
		||||
#endif          // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,5 +8,5 @@ static uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) { return
 | 
			
		|||
bool SOLID_REACTIVE_SIMPLE(effect_params_t* params) { return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math); }
 | 
			
		||||
 | 
			
		||||
#        endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#    endif      // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
 | 
			
		||||
#    endif      // ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
 | 
			
		||||
#endif          // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
#    if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
 | 
			
		||||
#    if !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
 | 
			
		||||
 | 
			
		||||
#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
 | 
			
		||||
LED_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,5 +26,5 @@ bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { return effect_runner_re
 | 
			
		|||
#            endif
 | 
			
		||||
 | 
			
		||||
#        endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#    endif      // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
 | 
			
		||||
#    endif      // !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
 | 
			
		||||
#endif          // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
#    if !defined(DISABLE_LED_MATRIX_SOLID_SPLASH) || !defined(DISABLE_LED_MATRIX_SOLID_MULTISPLASH)
 | 
			
		||||
#    if defined(ENABLE_LED_MATRIX_SOLID_SPLASH) || defined(ENABLE_LED_MATRIX_SOLID_MULTISPLASH)
 | 
			
		||||
 | 
			
		||||
#        ifdef ENABLE_LED_MATRIX_SOLID_SPLASH
 | 
			
		||||
LED_MATRIX_EFFECT(SOLID_SPLASH)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,5 +26,5 @@ bool SOLID_MULTISPLASH(effect_params_t* params) { return effect_runner_reactive_
 | 
			
		|||
#            endif
 | 
			
		||||
 | 
			
		||||
#        endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#    endif      // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH)
 | 
			
		||||
#    endif      // defined(ENABLE_LED_MATRIX_SPLASH) || defined(ENABLE_LED_MATRIX_MULTISPLASH)
 | 
			
		||||
#endif          // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { retu
 | 
			
		|||
bool WAVE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &WAVE_LEFT_RIGHT_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ static uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
 | 
			
		|||
bool WAVE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &WAVE_UP_DOWN_math); }
 | 
			
		||||
 | 
			
		||||
#    endif  // LED_MATRIX_CUSTOM_EFFECT_IMPLS
 | 
			
		||||
#endif      // DISABLE_LED_MATRIX_WAVE_UP_DOWN
 | 
			
		||||
#endif      // ENABLE_LED_MATRIX_WAVE_UP_DOWN
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,14 +33,6 @@ const led_point_t k_led_matrix_center = {112, 32};
 | 
			
		|||
const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
#ifndef LED_MATRIX_IMMEDIATE_EEPROM
 | 
			
		||||
#    define led_eeconfig_update(v) led_update_eeprom |= v
 | 
			
		||||
#else
 | 
			
		||||
#    define led_eeconfig_update(v) if (v) eeconfig_update_led_matrix()
 | 
			
		||||
#endif
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
// Generic effect runners
 | 
			
		||||
#include "led_matrix_runners.inc"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +99,6 @@ last_hit_t g_last_hit_tracker;
 | 
			
		|||
 | 
			
		||||
// internals
 | 
			
		||||
static bool            suspend_state     = false;
 | 
			
		||||
static bool            led_update_eeprom = false;
 | 
			
		||||
static uint8_t         led_last_enable   = UINT8_MAX;
 | 
			
		||||
static uint8_t         led_last_effect   = UINT8_MAX;
 | 
			
		||||
static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
 | 
			
		||||
| 
						 | 
				
			
			@ -127,9 +118,9 @@ static last_hit_t last_hit_buffer;
 | 
			
		|||
const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void eeconfig_read_led_matrix(void) { eeprom_read_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); }
 | 
			
		||||
EECONFIG_DEBOUNCE_HELPER(led_matrix, EECONFIG_LED_MATRIX, led_matrix_eeconfig);
 | 
			
		||||
 | 
			
		||||
void eeconfig_update_led_matrix(void) { eeprom_update_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); }
 | 
			
		||||
void eeconfig_update_led_matrix(void) { eeconfig_flush_led_matrix(true); }
 | 
			
		||||
 | 
			
		||||
void eeconfig_update_led_matrix_default(void) {
 | 
			
		||||
    dprintf("eeconfig_update_led_matrix_default\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +129,7 @@ void eeconfig_update_led_matrix_default(void) {
 | 
			
		|||
    led_matrix_eeconfig.val    = LED_MATRIX_STARTUP_VAL;
 | 
			
		||||
    led_matrix_eeconfig.speed  = LED_MATRIX_STARTUP_SPD;
 | 
			
		||||
    led_matrix_eeconfig.flags  = LED_FLAG_ALL;
 | 
			
		||||
    eeconfig_update_led_matrix();
 | 
			
		||||
    eeconfig_flush_led_matrix(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eeconfig_debug_led_matrix(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,20 +156,10 @@ uint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l
 | 
			
		|||
void led_matrix_update_pwm_buffers(void) { led_matrix_driver.flush(); }
 | 
			
		||||
 | 
			
		||||
void led_matrix_set_value(int index, uint8_t value) {
 | 
			
		||||
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    if (!is_keyboard_left() && index >= k_led_matrix_split[0])
 | 
			
		||||
#    ifdef USE_CIE1931_CURVE
 | 
			
		||||
        led_matrix_driver.set_value(index - k_led_matrix_split[0], pgm_read_byte(&CIE1931_CURVE[value]));
 | 
			
		||||
#    else
 | 
			
		||||
        led_matrix_driver.set_value(index - k_led_matrix_split[0], value);
 | 
			
		||||
#    endif
 | 
			
		||||
    else if (is_keyboard_left() && index < k_led_matrix_split[0])
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CIE1931_CURVE
 | 
			
		||||
        led_matrix_driver.set_value(index, pgm_read_byte(&CIE1931_CURVE[value]));
 | 
			
		||||
#else
 | 
			
		||||
    led_matrix_driver.set_value(index, value);
 | 
			
		||||
    value = pgm_read_byte(&CIE1931_CURVE[value]);
 | 
			
		||||
#endif
 | 
			
		||||
    led_matrix_driver.set_value(index, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void led_matrix_set_value_all(uint8_t value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -232,11 +213,11 @@ void process_led_matrix(uint8_t row, uint8_t col, bool pressed) {
 | 
			
		|||
    }
 | 
			
		||||
#endif  // LED_MATRIX_KEYREACTIVE_ENABLED
 | 
			
		||||
 | 
			
		||||
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_LED_MATRIX_TYPING_HEATMAP)
 | 
			
		||||
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
 | 
			
		||||
    if (led_matrix_eeconfig.mode == LED_MATRIX_TYPING_HEATMAP) {
 | 
			
		||||
        process_led_matrix_typing_heatmap(row, col);
 | 
			
		||||
    }
 | 
			
		||||
#endif  // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_LED_MATRIX_TYPING_HEATMAP)
 | 
			
		||||
#endif  // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool led_matrix_none(effect_params_t *params) {
 | 
			
		||||
| 
						 | 
				
			
			@ -279,9 +260,8 @@ static void led_task_timers(void) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void led_task_sync(void) {
 | 
			
		||||
    eeconfig_flush_led_matrix(false);
 | 
			
		||||
    // next task
 | 
			
		||||
    if (led_update_eeprom) eeconfig_update_led_matrix();
 | 
			
		||||
    led_update_eeprom = false;
 | 
			
		||||
    if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -449,7 +429,7 @@ void led_matrix_init(void) {
 | 
			
		|||
        eeconfig_update_led_matrix_default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    eeconfig_read_led_matrix();
 | 
			
		||||
    eeconfig_init_led_matrix();
 | 
			
		||||
    if (!led_matrix_eeconfig.mode) {
 | 
			
		||||
        dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n");
 | 
			
		||||
        eeconfig_update_led_matrix_default();
 | 
			
		||||
| 
						 | 
				
			
			@ -472,7 +452,7 @@ bool led_matrix_get_suspend_state(void) { return suspend_state; }
 | 
			
		|||
void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
 | 
			
		||||
    led_matrix_eeconfig.enable ^= 1;
 | 
			
		||||
    led_task_state = STARTING;
 | 
			
		||||
    led_eeconfig_update(write_to_eeprom);
 | 
			
		||||
    eeconfig_flag_led_matrix(write_to_eeprom);
 | 
			
		||||
    dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable);
 | 
			
		||||
}
 | 
			
		||||
void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); }
 | 
			
		||||
| 
						 | 
				
			
			@ -480,7 +460,7 @@ void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); }
 | 
			
		|||
 | 
			
		||||
void led_matrix_enable(void) {
 | 
			
		||||
    led_matrix_enable_noeeprom();
 | 
			
		||||
    led_eeconfig_update(true);
 | 
			
		||||
    eeconfig_flag_led_matrix(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void led_matrix_enable_noeeprom(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -490,7 +470,7 @@ void led_matrix_enable_noeeprom(void) {
 | 
			
		|||
 | 
			
		||||
void led_matrix_disable(void) {
 | 
			
		||||
    led_matrix_disable_noeeprom();
 | 
			
		||||
    led_eeconfig_update(true);
 | 
			
		||||
    eeconfig_flag_led_matrix(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void led_matrix_disable_noeeprom(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -512,7 +492,7 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
 | 
			
		|||
        led_matrix_eeconfig.mode = mode;
 | 
			
		||||
    }
 | 
			
		||||
    led_task_state = STARTING;
 | 
			
		||||
    led_eeconfig_update(write_to_eeprom);
 | 
			
		||||
    eeconfig_flag_led_matrix(write_to_eeprom);
 | 
			
		||||
    dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode);
 | 
			
		||||
}
 | 
			
		||||
void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); }
 | 
			
		||||
| 
						 | 
				
			
			@ -539,7 +519,7 @@ void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;
 | 
			
		||||
    led_eeconfig_update(write_to_eeprom);
 | 
			
		||||
    eeconfig_flag_led_matrix(write_to_eeprom);
 | 
			
		||||
    dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val);
 | 
			
		||||
}
 | 
			
		||||
void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); }
 | 
			
		||||
| 
						 | 
				
			
			@ -557,7 +537,7 @@ void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); }
 | 
			
		|||
 | 
			
		||||
void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
 | 
			
		||||
    led_matrix_eeconfig.speed = speed;
 | 
			
		||||
    led_eeconfig_update(write_to_eeprom);
 | 
			
		||||
    eeconfig_flag_led_matrix(write_to_eeprom);
 | 
			
		||||
    dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed);
 | 
			
		||||
}
 | 
			
		||||
void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@
 | 
			
		|||
#include <stdbool.h>
 | 
			
		||||
#include "led_matrix_types.h"
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
#include "led_matrix_legacy_enables.h"
 | 
			
		||||
 | 
			
		||||
#ifdef IS31FL3731
 | 
			
		||||
#    include "is31fl3731-simple.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -38,14 +37,33 @@
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(LED_MATRIX_LED_PROCESS_LIMIT) && LED_MATRIX_LED_PROCESS_LIMIT > 0 && LED_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
 | 
			
		||||
#    define LED_MATRIX_USE_LIMITS(min, max)                        \
 | 
			
		||||
        uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter; \
 | 
			
		||||
        uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT;          \
 | 
			
		||||
        if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
 | 
			
		||||
#    if defined(LED_MATRIX_SPLIT)
 | 
			
		||||
#        define LED_MATRIX_USE_LIMITS(min, max)                                                   \
 | 
			
		||||
            uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter;                            \
 | 
			
		||||
            uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT;                                     \
 | 
			
		||||
            if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;                                   \
 | 
			
		||||
            uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;                                     \
 | 
			
		||||
            if (is_keyboard_left() && (max > k_led_matrix_split[0])) max = k_led_matrix_split[0]; \
 | 
			
		||||
            if (!(is_keyboard_left()) && (min < k_led_matrix_split[0])) min = k_led_matrix_split[0];
 | 
			
		||||
#    else
 | 
			
		||||
#        define LED_MATRIX_USE_LIMITS(min, max)                        \
 | 
			
		||||
            uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter; \
 | 
			
		||||
            uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT;          \
 | 
			
		||||
            if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
 | 
			
		||||
#    endif
 | 
			
		||||
#else
 | 
			
		||||
#    define LED_MATRIX_USE_LIMITS(min, max) \
 | 
			
		||||
        uint8_t min = 0;                    \
 | 
			
		||||
        uint8_t max = DRIVER_LED_TOTAL;
 | 
			
		||||
#    if defined(LED_MATRIX_SPLIT)
 | 
			
		||||
#        define LED_MATRIX_USE_LIMITS(min, max)                                                   \
 | 
			
		||||
            uint8_t       min                   = 0;                                              \
 | 
			
		||||
            uint8_t       max                   = DRIVER_LED_TOTAL;                               \
 | 
			
		||||
            const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;                               \
 | 
			
		||||
            if (is_keyboard_left() && (max > k_led_matrix_split[0])) max = k_led_matrix_split[0]; \
 | 
			
		||||
            if (!(is_keyboard_left()) && (min < k_led_matrix_split[0])) min = k_led_matrix_split[0];
 | 
			
		||||
#    else
 | 
			
		||||
#        define LED_MATRIX_USE_LIMITS(min, max) \
 | 
			
		||||
            uint8_t min = 0;                    \
 | 
			
		||||
            uint8_t max = DRIVER_LED_TOTAL;
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define LED_MATRIX_TEST_LED_FLAGS() \
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +165,18 @@ typedef struct {
 | 
			
		|||
    void (*flush)(void);
 | 
			
		||||
} led_matrix_driver_t;
 | 
			
		||||
 | 
			
		||||
static inline bool led_matrix_check_finished_leds(uint8_t led_idx) {
 | 
			
		||||
#if defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    if (is_keyboard_left()) {
 | 
			
		||||
        uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
 | 
			
		||||
        return led_idx < k_led_matrix_split[0];
 | 
			
		||||
    } else
 | 
			
		||||
        return led_idx < DRIVER_LED_TOTAL;
 | 
			
		||||
#else
 | 
			
		||||
    return led_idx < DRIVER_LED_TOTAL;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern const led_matrix_driver_t led_matrix_driver;
 | 
			
		||||
 | 
			
		||||
extern led_eeconfig_t led_matrix_eeconfig;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,128 +26,123 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
#if defined(IS31FL3731) || defined(IS31FL3733)
 | 
			
		||||
 | 
			
		||||
#    include "i2c_master.h"
 | 
			
		||||
 | 
			
		||||
static void init(void) {
 | 
			
		||||
    i2c_init();
 | 
			
		||||
#    ifdef IS31FL3731
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
 | 
			
		||||
#    if defined(IS31FL3731)
 | 
			
		||||
    IS31FL3731_init(LED_DRIVER_ADDR_1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
    IS31FL3731_init(LED_DRIVER_ADDR_2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
    IS31FL3731_init(LED_DRIVER_ADDR_3);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
    IS31FL3731_init(LED_DRIVER_ADDR_4);
 | 
			
		||||
#        endif
 | 
			
		||||
#    else
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
#            ifndef LED_DRIVER_SYNC_1
 | 
			
		||||
#                define LED_DRIVER_SYNC_1 0
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
    IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
#            ifndef LED_DRIVER_SYNC_2
 | 
			
		||||
 | 
			
		||||
#    elif defined(IS31FL3733)
 | 
			
		||||
#        if !defined(LED_DRIVER_SYNC_1)
 | 
			
		||||
#            define LED_DRIVER_SYNC_1 0
 | 
			
		||||
#        endif
 | 
			
		||||
    IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
#            if !defined(LED_DRIVER_SYNC_2)
 | 
			
		||||
#                define LED_DRIVER_SYNC_2 0
 | 
			
		||||
#            endif
 | 
			
		||||
    IS31FL3733_init(LED_DRIVER_ADDR_2, LED_DRIVER_SYNC_2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
#            ifndef LED_DRIVER_SYNC_3
 | 
			
		||||
#                define LED_DRIVER_SYNC_3 0
 | 
			
		||||
#            endif
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
#                if !defined(LED_DRIVER_SYNC_3)
 | 
			
		||||
#                    define LED_DRIVER_SYNC_3 0
 | 
			
		||||
#                endif
 | 
			
		||||
    IS31FL3733_init(LED_DRIVER_ADDR_3, LED_DRIVER_SYNC_3);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
#            ifndef LED_DRIVER_SYNC_4
 | 
			
		||||
#                define LED_DRIVER_SYNC_4 0
 | 
			
		||||
#            endif
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
#                    if !defined(LED_DRIVER_SYNC_4)
 | 
			
		||||
#                        define LED_DRIVER_SYNC_4 0
 | 
			
		||||
#                    endif
 | 
			
		||||
    IS31FL3733_init(LED_DRIVER_ADDR_4, LED_DRIVER_SYNC_4);
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
 | 
			
		||||
#    ifdef IS31FL3731
 | 
			
		||||
#    if defined(IS31FL3731)
 | 
			
		||||
        IS31FL3731_set_led_control_register(index, true);
 | 
			
		||||
#    else
 | 
			
		||||
#    elif defined(IS31FL3733)
 | 
			
		||||
        IS31FL3733_set_led_control_register(index, true);
 | 
			
		||||
#    endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// This actually updates the LED drivers
 | 
			
		||||
#    ifdef IS31FL3731
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
#    if defined(IS31FL3731)
 | 
			
		||||
    IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
    IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
    IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
    IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
#    else
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
 | 
			
		||||
#    elif defined(IS31FL3733)
 | 
			
		||||
    IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
    IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
    IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
    IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    if defined(IS31FL3731)
 | 
			
		||||
static void flush(void) {
 | 
			
		||||
#    ifdef IS31FL3731
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
    IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
    IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
    IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
    IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
#    else
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_1
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_2
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_3
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef LED_DRIVER_ADDR_4
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const led_matrix_driver_t led_matrix_driver = {
 | 
			
		||||
    .init  = init,
 | 
			
		||||
    .flush = flush,
 | 
			
		||||
#    ifdef IS31FL3731
 | 
			
		||||
    .init          = init,
 | 
			
		||||
    .flush         = flush,
 | 
			
		||||
    .set_value     = IS31FL3731_set_value,
 | 
			
		||||
    .set_value_all = IS31FL3731_set_value_all,
 | 
			
		||||
#    else
 | 
			
		||||
    .set_value = IS31FL3733_set_value,
 | 
			
		||||
    .set_value_all = IS31FL3733_set_value_all,
 | 
			
		||||
#    endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#    elif defined(IS31FL3733)
 | 
			
		||||
static void flush(void) {
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
 | 
			
		||||
#        if defined(LED_DRIVER_ADDR_2)
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
 | 
			
		||||
#            if defined(LED_DRIVER_ADDR_3)
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
 | 
			
		||||
#                if defined(LED_DRIVER_ADDR_4)
 | 
			
		||||
    IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
 | 
			
		||||
#                endif
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const led_matrix_driver_t led_matrix_driver = {
 | 
			
		||||
    .init = init,
 | 
			
		||||
    .flush = flush,
 | 
			
		||||
    .set_value = IS31FL3733_set_value,
 | 
			
		||||
    .set_value_all = IS31FL3733_set_value_all,
 | 
			
		||||
};
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,82 +0,0 @@
 | 
			
		|||
/* Copyright 2021 QMK
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// to-do: remove this
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_ALPHAS_MODS
 | 
			
		||||
#    define ENABLE_LED_MATRIX_ALPHAS_MODS
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_BREATHING
 | 
			
		||||
#    define ENABLE_LED_MATRIX_BREATHING
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_BAND
 | 
			
		||||
#    define ENABLE_LED_MATRIX_BAND
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_BAND_PINWHEEL
 | 
			
		||||
#    define ENABLE_LED_MATRIX_BAND_PINWHEEL
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_BAND_SPIRAL
 | 
			
		||||
#    define ENABLE_LED_MATRIX_BAND_SPIRAL
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
 | 
			
		||||
#    define ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_CYCLE_UP_DOWN
 | 
			
		||||
#    define ENABLE_LED_MATRIX_CYCLE_UP_DOWN
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_CYCLE_OUT_IN
 | 
			
		||||
#    define ENABLE_LED_MATRIX_CYCLE_OUT_IN
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_DUAL_BEACON
 | 
			
		||||
#    define ENABLE_LED_MATRIX_DUAL_BEACON
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(LED_MATRIX_KEYREACTIVE_ENABLED)
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_SPLASH
 | 
			
		||||
#        define ENABLE_LED_MATRIX_SPLASH
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_LED_MATRIX_MULTISPLASH
 | 
			
		||||
#        define ENABLE_LED_MATRIX_MULTISPLASH
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
 | 
			
		||||
#    define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef DISABLE_LED_MATRIX_WAVE_UP_DOWN
 | 
			
		||||
#    define ENABLE_LED_MATRIX_WAVE_UP_DOWN
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +19,33 @@
 | 
			
		|||
void platform_setup(void);
 | 
			
		||||
 | 
			
		||||
void protocol_setup(void);
 | 
			
		||||
void protocol_init(void);
 | 
			
		||||
void protocol_task(void);
 | 
			
		||||
void protocol_pre_init(void);
 | 
			
		||||
void protocol_post_init(void);
 | 
			
		||||
void protocol_pre_task(void);
 | 
			
		||||
void protocol_post_task(void);
 | 
			
		||||
 | 
			
		||||
// Bodge as refactoring this area sucks....
 | 
			
		||||
void protocol_init(void) __attribute__((weak));
 | 
			
		||||
void protocol_init(void) {
 | 
			
		||||
    protocol_pre_init();
 | 
			
		||||
 | 
			
		||||
    keyboard_init();
 | 
			
		||||
 | 
			
		||||
    protocol_post_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void protocol_task(void) __attribute__((weak));
 | 
			
		||||
void protocol_task(void) {
 | 
			
		||||
    protocol_pre_task();
 | 
			
		||||
 | 
			
		||||
    keyboard_task();
 | 
			
		||||
 | 
			
		||||
    protocol_post_task();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DEFERRED_EXEC_ENABLE
 | 
			
		||||
void deferred_exec_task(void);
 | 
			
		||||
#endif  // DEFERRED_EXEC_ENABLE
 | 
			
		||||
 | 
			
		||||
/** \brief Main
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -30,12 +55,19 @@ int main(void) __attribute__((weak));
 | 
			
		|||
int main(void) {
 | 
			
		||||
    platform_setup();
 | 
			
		||||
    protocol_setup();
 | 
			
		||||
    keyboard_setup();
 | 
			
		||||
 | 
			
		||||
    protocol_init();
 | 
			
		||||
 | 
			
		||||
    /* Main loop */
 | 
			
		||||
    while (true) {
 | 
			
		||||
        protocol_task();
 | 
			
		||||
 | 
			
		||||
#ifdef DEFERRED_EXEC_ENABLE
 | 
			
		||||
        // Run deferred executions
 | 
			
		||||
        deferred_exec_task();
 | 
			
		||||
#endif  // DEFERRED_EXEC_ENABLE
 | 
			
		||||
 | 
			
		||||
        housekeeping_task();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ uint8_t thisHand, thatHand;
 | 
			
		|||
// user-defined overridable functions
 | 
			
		||||
__attribute__((weak)) void matrix_init_pins(void);
 | 
			
		||||
__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
 | 
			
		||||
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
 | 
			
		||||
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter);
 | 
			
		||||
#ifdef SPLIT_KEYBOARD
 | 
			
		||||
__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
 | 
			
		||||
__attribute__((weak)) void matrix_slave_scan_user(void) {}
 | 
			
		||||
| 
						 | 
				
			
			@ -113,10 +113,11 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[]
 | 
			
		|||
    // Start with a clear matrix row
 | 
			
		||||
    matrix_row_t current_row_value = 0;
 | 
			
		||||
 | 
			
		||||
    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
 | 
			
		||||
    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
 | 
			
		||||
    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
 | 
			
		||||
        pin_t pin = direct_pins[current_row][col_index];
 | 
			
		||||
        if (pin != NO_PIN) {
 | 
			
		||||
            current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index);
 | 
			
		||||
            current_row_value |= readPin(pin) ? 0 : row_shifter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,11 +170,12 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[]
 | 
			
		|||
    matrix_output_select_delay();
 | 
			
		||||
 | 
			
		||||
    // For each col...
 | 
			
		||||
    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
 | 
			
		||||
    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
 | 
			
		||||
    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
 | 
			
		||||
        uint8_t pin_state = readMatrixPin(col_pins[col_index]);
 | 
			
		||||
 | 
			
		||||
        // Populate the matrix row with the state of the col pin
 | 
			
		||||
        current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
 | 
			
		||||
        current_row_value |= pin_state ? 0 : row_shifter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Unselect row
 | 
			
		||||
| 
						 | 
				
			
			@ -217,7 +219,7 @@ __attribute__((weak)) void matrix_init_pins(void) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
 | 
			
		||||
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) {
 | 
			
		||||
    bool key_pressed = false;
 | 
			
		||||
 | 
			
		||||
    // Select col
 | 
			
		||||
| 
						 | 
				
			
			@ -231,11 +233,11 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[]
 | 
			
		|||
        // Check row pin state
 | 
			
		||||
        if (readMatrixPin(row_pins[row_index]) == 0) {
 | 
			
		||||
            // Pin LO, set col bit
 | 
			
		||||
            current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
 | 
			
		||||
            current_matrix[row_index] |= row_shifter;
 | 
			
		||||
            key_pressed = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Pin HI, clear col bit
 | 
			
		||||
            current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
 | 
			
		||||
            current_matrix[row_index] &= ~row_shifter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -288,10 +290,8 @@ void matrix_init(void) {
 | 
			
		|||
    matrix_init_pins();
 | 
			
		||||
 | 
			
		||||
    // initialize matrix state: all keys off
 | 
			
		||||
    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
 | 
			
		||||
        raw_matrix[i] = 0;
 | 
			
		||||
        matrix[i]     = 0;
 | 
			
		||||
    }
 | 
			
		||||
    memset(matrix, 0, sizeof(matrix));
 | 
			
		||||
    memset(raw_matrix, 0, sizeof(raw_matrix));
 | 
			
		||||
 | 
			
		||||
    debounce_init(ROWS_PER_HAND);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -312,24 +312,22 @@ __attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_mat
 | 
			
		|||
bool matrix_post_scan(void) {
 | 
			
		||||
    bool changed = false;
 | 
			
		||||
    if (is_keyboard_master()) {
 | 
			
		||||
        static bool  last_connected              = false;
 | 
			
		||||
        matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
 | 
			
		||||
        if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
 | 
			
		||||
            for (int i = 0; i < ROWS_PER_HAND; ++i) {
 | 
			
		||||
                if (matrix[thatHand + i] != slave_matrix[i]) {
 | 
			
		||||
                    matrix[thatHand + i] = slave_matrix[i];
 | 
			
		||||
                    changed              = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // reset other half if disconnected
 | 
			
		||||
            for (int i = 0; i < ROWS_PER_HAND; ++i) {
 | 
			
		||||
                matrix[thatHand + i] = 0;
 | 
			
		||||
                slave_matrix[i]      = 0;
 | 
			
		||||
            }
 | 
			
		||||
            changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;
 | 
			
		||||
 | 
			
		||||
            last_connected = true;
 | 
			
		||||
        } else if (last_connected) {
 | 
			
		||||
            // reset other half when disconnected
 | 
			
		||||
            memset(slave_matrix, 0, sizeof(slave_matrix));
 | 
			
		||||
            changed = true;
 | 
			
		||||
 | 
			
		||||
            last_connected = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));
 | 
			
		||||
 | 
			
		||||
        matrix_scan_quantum();
 | 
			
		||||
    } else {
 | 
			
		||||
        transport_slave(matrix + thatHand, matrix + thisHand);
 | 
			
		||||
| 
						 | 
				
			
			@ -351,8 +349,9 @@ uint8_t matrix_scan(void) {
 | 
			
		|||
    }
 | 
			
		||||
#elif (DIODE_DIRECTION == ROW2COL)
 | 
			
		||||
    // Set col, read rows
 | 
			
		||||
    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
 | 
			
		||||
        matrix_read_rows_on_col(curr_matrix, current_col);
 | 
			
		||||
    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
 | 
			
		||||
    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
 | 
			
		||||
        matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,601 +0,0 @@
 | 
			
		|||
MCU_ORIG := $(MCU)
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring MKL26Z64, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m0plus
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 6
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = KINETIS
 | 
			
		||||
  MCU_SERIES = KL2x
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= MKL26Z64
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= kl2x
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= PJRC_TEENSY_LC
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring MK20DX128, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = KINETIS
 | 
			
		||||
  MCU_SERIES = K20x
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= MK20DX128
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= k20x5
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= PJRC_TEENSY_3
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring MK20DX256, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = KINETIS
 | 
			
		||||
  MCU_SERIES = K20x
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= MK20DX256
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= k20x7
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= PJRC_TEENSY_3_1
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring MK66F18, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = KINETIS
 | 
			
		||||
  MCU_SERIES = MK66F18
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= MK66FX1M0
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= MK66F18
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= PJRC_TEENSY_3_6
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F042, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m0
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 6
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F0xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F042x6
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f0xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F042X6
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= no
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F0
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F072, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m0
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 6
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F0xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F072xB
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f0xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F072XB
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= no
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F0
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F103, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m3
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F1xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F103x8
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f1xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F103
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= no
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F1
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F303, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F3xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F303xC
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f3xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F303XC
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F3
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F401, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  ifeq ($(strip $(BOOTLOADER)), tinyuf2)
 | 
			
		||||
    MCU_LDSCRIPT ?= STM32F401xC_tinyuf2
 | 
			
		||||
    FIRMWARE_FORMAT ?= uf2
 | 
			
		||||
  else
 | 
			
		||||
    MCU_LDSCRIPT ?= STM32F401xC
 | 
			
		||||
  endif
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= BLACKPILL_STM32_F401
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F407, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F407xE
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F407XE
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F411, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  ifeq ($(strip $(BOOTLOADER)), tinyuf2)
 | 
			
		||||
    MCU_LDSCRIPT ?= STM32F411xE_tinyuf2
 | 
			
		||||
    FIRMWARE_FORMAT ?= uf2
 | 
			
		||||
  else
 | 
			
		||||
    MCU_LDSCRIPT ?= STM32F411xE
 | 
			
		||||
  endif
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= BLACKPILL_STM32_F411
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32F4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32F446, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32F4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32F446xE
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32f4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_F446XE
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32G431, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32G4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32G431xB
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32g4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_G431XB
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32G4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(findstring STM32G474, $(MCU)),)
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32G4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32G474xE
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32g4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_G474XE
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32G4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),STM32L433 STM32L443))
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32L4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32L432xC
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32l4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_L433XC
 | 
			
		||||
 | 
			
		||||
  PLATFORM_NAME ?= platform_l432
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32L4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),STM32L412 STM32L422))
 | 
			
		||||
  # Cortex version
 | 
			
		||||
  MCU = cortex-m4
 | 
			
		||||
 | 
			
		||||
  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
 | 
			
		||||
  ARMV = 7
 | 
			
		||||
 | 
			
		||||
  ## chip/board settings
 | 
			
		||||
  # - the next two should match the directories in
 | 
			
		||||
  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
 | 
			
		||||
  MCU_FAMILY = STM32
 | 
			
		||||
  MCU_SERIES = STM32L4xx
 | 
			
		||||
 | 
			
		||||
  # Linker script to use
 | 
			
		||||
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
 | 
			
		||||
  #   or <keyboard_dir>/ld/
 | 
			
		||||
  MCU_LDSCRIPT ?= STM32L412xB
 | 
			
		||||
 | 
			
		||||
  # Startup code to use
 | 
			
		||||
  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
 | 
			
		||||
  MCU_STARTUP ?= stm32l4xx
 | 
			
		||||
 | 
			
		||||
  # Board: it should exist either in <chibios>/os/hal/boards/,
 | 
			
		||||
  # <keyboard_dir>/boards/, or drivers/boards/
 | 
			
		||||
  BOARD ?= GENERIC_STM32_L412XB
 | 
			
		||||
 | 
			
		||||
  PLATFORM_NAME ?= platform_l432
 | 
			
		||||
 | 
			
		||||
  USE_FPU ?= yes
 | 
			
		||||
 | 
			
		||||
  # UF2 settings
 | 
			
		||||
  UF2_FAMILY ?= STM32L4
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
 | 
			
		||||
  PROTOCOL = LUFA
 | 
			
		||||
 | 
			
		||||
  # Processor frequency.
 | 
			
		||||
  #     This will define a symbol, F_CPU, in all source code files equal to the
 | 
			
		||||
  #     processor frequency in Hz. You can then use this symbol in your source code to
 | 
			
		||||
  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
 | 
			
		||||
  #     automatically to create a 32-bit value in your source code.
 | 
			
		||||
  #
 | 
			
		||||
  #     This will be an integer division of F_USB below, as it is sourced by
 | 
			
		||||
  #     F_USB after it has run through any CPU prescalers. Note that this value
 | 
			
		||||
  #     does not *change* the processor frequency - it should merely be updated to
 | 
			
		||||
  #     reflect the processor speed set externally so that the code can use accurate
 | 
			
		||||
  #     software delays.
 | 
			
		||||
  F_CPU ?= 16000000
 | 
			
		||||
 | 
			
		||||
  # LUFA specific
 | 
			
		||||
  #
 | 
			
		||||
  # Target architecture (see library "Board Types" documentation).
 | 
			
		||||
  ARCH = AVR8
 | 
			
		||||
 | 
			
		||||
  # Input clock frequency.
 | 
			
		||||
  #     This will define a symbol, F_USB, in all source code files equal to the
 | 
			
		||||
  #     input clock frequency (before any prescaling is performed) in Hz. This value may
 | 
			
		||||
  #     differ from F_CPU if prescaling is used on the latter, and is required as the
 | 
			
		||||
  #     raw input clock is fed directly to the PLL sections of the AVR for high speed
 | 
			
		||||
  #     clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
 | 
			
		||||
  #     at the end, this will be done automatically to create a 32-bit value in your
 | 
			
		||||
  #     source code.
 | 
			
		||||
  #
 | 
			
		||||
  #     If no clock division is performed on the input clock inside the AVR (via the
 | 
			
		||||
  #     CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
 | 
			
		||||
  F_USB ?= $(F_CPU)
 | 
			
		||||
 | 
			
		||||
  # Interrupt driven control endpoint task
 | 
			
		||||
  ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
 | 
			
		||||
    OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
 | 
			
		||||
  endif
 | 
			
		||||
  ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2))
 | 
			
		||||
    NO_I2C = yes
 | 
			
		||||
  endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),atmega32a))
 | 
			
		||||
  # MCU name for avrdude
 | 
			
		||||
  AVRDUDE_MCU = m32
 | 
			
		||||
 | 
			
		||||
  PROTOCOL = VUSB
 | 
			
		||||
 | 
			
		||||
  # Processor frequency.
 | 
			
		||||
  #     This will define a symbol, F_CPU, in all source code files equal to the
 | 
			
		||||
  #     processor frequency in Hz. You can then use this symbol in your source code to
 | 
			
		||||
  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
 | 
			
		||||
  #     automatically to create a 32-bit value in your source code.
 | 
			
		||||
  F_CPU ?= 12000000
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),atmega328p))
 | 
			
		||||
  # MCU name for avrdude
 | 
			
		||||
  AVRDUDE_MCU = m328p
 | 
			
		||||
 | 
			
		||||
  PROTOCOL = VUSB
 | 
			
		||||
 | 
			
		||||
  # Processor frequency.
 | 
			
		||||
  #     This will define a symbol, F_CPU, in all source code files equal to the
 | 
			
		||||
  #     processor frequency in Hz. You can then use this symbol in your source code to
 | 
			
		||||
  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
 | 
			
		||||
  #     automatically to create a 32-bit value in your source code.
 | 
			
		||||
  F_CPU ?= 16000000
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),atmega328))
 | 
			
		||||
  # MCU name for avrdude
 | 
			
		||||
  AVRDUDE_MCU = m328
 | 
			
		||||
 | 
			
		||||
  PROTOCOL = VUSB
 | 
			
		||||
 | 
			
		||||
  # Processor frequency.
 | 
			
		||||
  #     This will define a symbol, F_CPU, in all source code files equal to the
 | 
			
		||||
  #     processor frequency in Hz. You can then use this symbol in your source code to
 | 
			
		||||
  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
 | 
			
		||||
  #     automatically to create a 32-bit value in your source code.
 | 
			
		||||
  F_CPU ?= 16000000
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(MCU),attiny85))
 | 
			
		||||
  PROTOCOL = VUSB
 | 
			
		||||
 | 
			
		||||
  # Processor frequency.
 | 
			
		||||
  #     This will define a symbol, F_CPU, in all source code files equal to the
 | 
			
		||||
  #     processor frequency in Hz. You can then use this symbol in your source code to
 | 
			
		||||
  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
 | 
			
		||||
  #     automatically to create a 32-bit value in your source code.
 | 
			
		||||
  F_CPU ?= 16500000
 | 
			
		||||
endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +1,57 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
 | 
			
		||||
/* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
 | 
			
		||||
 * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 | 
			
		||||
 * Copyright 2021 Dasky (@daskygit)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "report.h"
 | 
			
		||||
#include "host.h"
 | 
			
		||||
#include "timer.h"
 | 
			
		||||
#include "print.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "pointing_device.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#ifdef MOUSEKEY_ENABLE
 | 
			
		||||
#    include "mousekey.h"
 | 
			
		||||
#endif
 | 
			
		||||
#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
 | 
			
		||||
#    error More than one rotation selected.  This is not supported.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static report_mouse_t mouseReport = {};
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return (new.buttons != old.buttons) || (new.x&& new.x != old.x) || (new.y&& new.y != old.y) || (new.h&& new.h != old.h) || (new.v&& new.v != old.v); }
 | 
			
		||||
extern const pointing_device_driver_t pointing_device_driver;
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); }
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void           pointing_device_init_kb(void) {}
 | 
			
		||||
__attribute__((weak)) void           pointing_device_init_user(void) {}
 | 
			
		||||
__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); }
 | 
			
		||||
__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; }
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
 | 
			
		||||
    if (pressed) {
 | 
			
		||||
        buttons |= 1 << (button);
 | 
			
		||||
    } else {
 | 
			
		||||
        buttons &= ~(1 << (button));
 | 
			
		||||
    }
 | 
			
		||||
    return buttons;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void pointing_device_init(void) {
 | 
			
		||||
    // initialize device, if that needs to be done.
 | 
			
		||||
    pointing_device_driver.init();
 | 
			
		||||
#ifdef POINTING_DEVICE_MOTION_PIN
 | 
			
		||||
    setPinInputHigh(POINTING_DEVICE_MOTION_PIN);
 | 
			
		||||
#endif
 | 
			
		||||
    pointing_device_init_kb();
 | 
			
		||||
    pointing_device_init_user();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void pointing_device_send(void) {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,20 +66,55 @@ __attribute__((weak)) void pointing_device_send(void) {
 | 
			
		|||
    mouseReport.y = 0;
 | 
			
		||||
    mouseReport.v = 0;
 | 
			
		||||
    mouseReport.h = 0;
 | 
			
		||||
    old_report    = mouseReport;
 | 
			
		||||
 | 
			
		||||
    memcpy(&old_report, &mouseReport, sizeof(mouseReport));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void pointing_device_task(void) {
 | 
			
		||||
    // gather info and put it in:
 | 
			
		||||
    // mouseReport.x = 127 max -127 min
 | 
			
		||||
    // mouseReport.y = 127 max -127 min
 | 
			
		||||
    // mouseReport.v = 127 max -127 min (scroll vertical)
 | 
			
		||||
    // mouseReport.h = 127 max -127 min (scroll horizontal)
 | 
			
		||||
    // mouseReport.buttons = 0x1F (decimal 31, binary 00011111) max (bitmask for mouse buttons 1-5, 1 is rightmost, 5 is leftmost) 0x00 min
 | 
			
		||||
    // send the report
 | 
			
		||||
    // Gather report info
 | 
			
		||||
#ifdef POINTING_DEVICE_MOTION_PIN
 | 
			
		||||
    if (!readPin(POINTING_DEVICE_MOTION_PIN))
 | 
			
		||||
#endif
 | 
			
		||||
        mouseReport = pointing_device_driver.get_report(mouseReport);
 | 
			
		||||
 | 
			
		||||
        // Support rotation of the sensor data
 | 
			
		||||
#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
 | 
			
		||||
    int8_t x = mouseReport.x, y = mouseReport.y;
 | 
			
		||||
#    if defined(POINTING_DEVICE_ROTATION_90)
 | 
			
		||||
    mouseReport.x = y;
 | 
			
		||||
    mouseReport.y = -x;
 | 
			
		||||
#    elif defined(POINTING_DEVICE_ROTATION_180)
 | 
			
		||||
    mouseReport.x = -x;
 | 
			
		||||
    mouseReport.y = -y;
 | 
			
		||||
#    elif defined(POINTING_DEVICE_ROTATION_270)
 | 
			
		||||
    mouseReport.x = -y;
 | 
			
		||||
    mouseReport.y = x;
 | 
			
		||||
#    else
 | 
			
		||||
#        error "How the heck did you get here?!"
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
    // Support Inverting the X and Y Axises
 | 
			
		||||
#if defined(POINTING_DEVICE_INVERT_X)
 | 
			
		||||
    mouseReport.x = -mouseReport.x;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(POINTING_DEVICE_INVERT_Y)
 | 
			
		||||
    mouseReport.y = -mouseReport.y;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // allow kb to intercept and modify report
 | 
			
		||||
    mouseReport = pointing_device_task_kb(mouseReport);
 | 
			
		||||
    // combine with mouse report to ensure that the combined is sent correctly
 | 
			
		||||
#ifdef MOUSEKEY_ENABLE
 | 
			
		||||
    report_mouse_t mousekey_report = mousekey_get_report();
 | 
			
		||||
    mouseReport.buttons            = mouseReport.buttons | mousekey_report.buttons;
 | 
			
		||||
#endif
 | 
			
		||||
    pointing_device_send();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
report_mouse_t pointing_device_get_report(void) { return mouseReport; }
 | 
			
		||||
 | 
			
		||||
void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
 | 
			
		||||
 | 
			
		||||
uint16_t pointing_device_get_cpi(void) { return pointing_device_driver.get_cpi(); }
 | 
			
		||||
 | 
			
		||||
void pointing_device_set_cpi(uint16_t cpi) { pointing_device_driver.set_cpi(cpi); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,9 +21,68 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#include "host.h"
 | 
			
		||||
#include "report.h"
 | 
			
		||||
 | 
			
		||||
#if defined(POINTING_DEVICE_DRIVER_adns5050)
 | 
			
		||||
#    include "drivers/sensors/adns5050.h"
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
 | 
			
		||||
#    include "spi_master.h"
 | 
			
		||||
#    include "drivers/sensors/adns9800.h"
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)
 | 
			
		||||
#    include "analog.h"
 | 
			
		||||
#    include "drivers/sensors/analog_joystick.h"
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
 | 
			
		||||
#    include "drivers/sensors/cirque_pinnacle.h"
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
 | 
			
		||||
#    include "i2c_master.h"
 | 
			
		||||
#    include "drivers/sensors/pimoroni_trackball.h"
 | 
			
		||||
// support for legacy pimoroni defines
 | 
			
		||||
#    ifdef PIMORONI_TRACKBALL_INVERT_X
 | 
			
		||||
#        define POINTING_DEVICE_INVERT_X
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef PIMORONI_TRACKBALL_INVERT_Y
 | 
			
		||||
#        define POINTING_DEVICE_INVERT_Y
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef PIMORONI_TRACKBALL_ROTATE
 | 
			
		||||
#        define POINTING_DEVICE_ROTATION_90
 | 
			
		||||
#    endif
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
 | 
			
		||||
#    include "spi_master.h"
 | 
			
		||||
#    include "drivers/sensors/pmw3360.h"
 | 
			
		||||
#else
 | 
			
		||||
void           pointing_device_driver_init(void);
 | 
			
		||||
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);
 | 
			
		||||
uint16_t       pointing_device_driver_get_cpi(void);
 | 
			
		||||
void           pointing_device_driver_set_cpi(uint16_t cpi);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    void (*init)(void);
 | 
			
		||||
    report_mouse_t (*get_report)(report_mouse_t mouse_report);
 | 
			
		||||
    void (*set_cpi)(uint16_t);
 | 
			
		||||
    uint16_t (*get_cpi)(void);
 | 
			
		||||
} pointing_device_driver_t;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    POINTING_DEVICE_BUTTON1,
 | 
			
		||||
    POINTING_DEVICE_BUTTON2,
 | 
			
		||||
    POINTING_DEVICE_BUTTON3,
 | 
			
		||||
    POINTING_DEVICE_BUTTON4,
 | 
			
		||||
    POINTING_DEVICE_BUTTON5,
 | 
			
		||||
    POINTING_DEVICE_BUTTON6,
 | 
			
		||||
    POINTING_DEVICE_BUTTON7,
 | 
			
		||||
    POINTING_DEVICE_BUTTON8,
 | 
			
		||||
} pointing_device_buttons_t;
 | 
			
		||||
 | 
			
		||||
void           pointing_device_init(void);
 | 
			
		||||
void           pointing_device_task(void);
 | 
			
		||||
void           pointing_device_send(void);
 | 
			
		||||
report_mouse_t pointing_device_get_report(void);
 | 
			
		||||
void           pointing_device_set_report(report_mouse_t newMouseReport);
 | 
			
		||||
bool           has_mouse_report_changed(report_mouse_t new, report_mouse_t old);
 | 
			
		||||
uint16_t       pointing_device_get_cpi(void);
 | 
			
		||||
void           pointing_device_set_cpi(uint16_t cpi);
 | 
			
		||||
 | 
			
		||||
void           pointing_device_init_kb(void);
 | 
			
		||||
void           pointing_device_init_user(void);
 | 
			
		||||
report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);
 | 
			
		||||
report_mouse_t pointing_device_task_user(report_mouse_t mouse_report);
 | 
			
		||||
uint8_t        pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										262
									
								
								quantum/pointing_device_drivers.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								quantum/pointing_device_drivers.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,262 @@
 | 
			
		|||
/* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
 | 
			
		||||
 * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 | 
			
		||||
 * Copyright 2021 Dasky (@daskygit)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "pointing_device.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
#include "timer.h"
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
// hid mouse reports cannot exceed -127 to 127, so constrain to that value
 | 
			
		||||
#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt)))
 | 
			
		||||
 | 
			
		||||
// get_report functions should probably be moved to their respective drivers.
 | 
			
		||||
#if defined(POINTING_DEVICE_DRIVER_adns5050)
 | 
			
		||||
report_mouse_t adns5050_get_report(report_mouse_t mouse_report) {
 | 
			
		||||
    report_adns5050_t data = adns5050_read_burst();
 | 
			
		||||
 | 
			
		||||
    if (data.dx != 0 || data.dy != 0) {
 | 
			
		||||
#    ifdef CONSOLE_ENABLE
 | 
			
		||||
        if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
        mouse_report.x = data.dx;
 | 
			
		||||
        mouse_report.y = data.dy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init         = adns5050_init,
 | 
			
		||||
    .get_report   = adns5050_get_report,
 | 
			
		||||
    .set_cpi      = adns5050_set_cpi,
 | 
			
		||||
    .get_cpi      = adns5050_get_cpi,
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
 | 
			
		||||
 | 
			
		||||
report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {
 | 
			
		||||
    report_adns9800_t sensor_report = adns9800_get_report();
 | 
			
		||||
 | 
			
		||||
    int8_t clamped_x = constrain_hid(sensor_report.x);
 | 
			
		||||
    int8_t clamped_y = constrain_hid(sensor_report.y);
 | 
			
		||||
 | 
			
		||||
    mouse_report.x = clamped_x;
 | 
			
		||||
    mouse_report.y = clamped_y;
 | 
			
		||||
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = adns9800_init,
 | 
			
		||||
    .get_report = adns9800_get_report_driver,
 | 
			
		||||
    .set_cpi    = adns9800_set_cpi,
 | 
			
		||||
    .get_cpi    = adns9800_get_cpi
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)
 | 
			
		||||
report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) {
 | 
			
		||||
    report_analog_joystick_t data = analog_joystick_read();
 | 
			
		||||
 | 
			
		||||
#    ifdef CONSOLE_ENABLE
 | 
			
		||||
    if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    mouse_report.x = data.x;
 | 
			
		||||
    mouse_report.y = data.y;
 | 
			
		||||
 | 
			
		||||
    mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1);
 | 
			
		||||
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = analog_joystick_init,
 | 
			
		||||
    .get_report = analog_joystick_get_report,
 | 
			
		||||
    .set_cpi    = NULL,
 | 
			
		||||
    .get_cpi    = NULL
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
 | 
			
		||||
#    ifndef CIRQUE_PINNACLE_TAPPING_TERM
 | 
			
		||||
#        ifdef TAPPING_TERM_PER_KEY
 | 
			
		||||
#            include "action.h"
 | 
			
		||||
#            include "action_tapping.h"
 | 
			
		||||
#            define CIRQUE_PINNACLE_TAPPING_TERM get_tapping_term(KC_BTN1, NULL)
 | 
			
		||||
#        else
 | 
			
		||||
#            ifdef TAPPING_TERM
 | 
			
		||||
#                define CIRQUE_PINNACLE_TAPPING_TERM TAPPING_TERM
 | 
			
		||||
#            else
 | 
			
		||||
#                define CIRQUE_PINNACLE_TAPPING_TERM 200
 | 
			
		||||
#            endif
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE
 | 
			
		||||
#        define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8)
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {
 | 
			
		||||
    pinnacle_data_t touchData = cirque_pinnacle_read_data();
 | 
			
		||||
    static uint16_t x = 0, y = 0, mouse_timer = 0;
 | 
			
		||||
    int8_t          report_x = 0, report_y = 0;
 | 
			
		||||
    static bool     is_z_down = false;
 | 
			
		||||
 | 
			
		||||
    cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale());  // Scale coordinates to arbitrary X, Y resolution
 | 
			
		||||
 | 
			
		||||
    if (x && y && touchData.xValue && touchData.yValue) {
 | 
			
		||||
        report_x = (int8_t)(touchData.xValue - x);
 | 
			
		||||
        report_y = (int8_t)(touchData.yValue - y);
 | 
			
		||||
    }
 | 
			
		||||
    x = touchData.xValue;
 | 
			
		||||
    y = touchData.yValue;
 | 
			
		||||
 | 
			
		||||
    if ((bool)touchData.zValue != is_z_down) {
 | 
			
		||||
        is_z_down = (bool)touchData.zValue;
 | 
			
		||||
        if (!touchData.zValue) {
 | 
			
		||||
            if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) {
 | 
			
		||||
                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);
 | 
			
		||||
                pointing_device_set_report(mouse_report);
 | 
			
		||||
                pointing_device_send();
 | 
			
		||||
#    if TAP_CODE_DELAY > 0
 | 
			
		||||
                wait_ms(TAP_CODE_DELAY);
 | 
			
		||||
#    endif
 | 
			
		||||
                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);
 | 
			
		||||
                pointing_device_set_report(mouse_report);
 | 
			
		||||
                pointing_device_send();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        mouse_timer = timer_read();
 | 
			
		||||
    }
 | 
			
		||||
    if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) {
 | 
			
		||||
        mouse_timer = 0;
 | 
			
		||||
    }
 | 
			
		||||
    mouse_report.x = report_x;
 | 
			
		||||
    mouse_report.y = report_y;
 | 
			
		||||
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = cirque_pinnacle_init,
 | 
			
		||||
    .get_report = cirque_pinnacle_get_report,
 | 
			
		||||
    .set_cpi    = cirque_pinnacle_set_scale,
 | 
			
		||||
    .get_cpi    = cirque_pinnacle_get_scale
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
 | 
			
		||||
report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
 | 
			
		||||
    static fast_timer_t throttle      = 0;
 | 
			
		||||
    static uint16_t     debounce      = 0;
 | 
			
		||||
    static uint8_t      error_count   = 0;
 | 
			
		||||
    pimoroni_data_t     pimoroni_data = {0};
 | 
			
		||||
    static int16_t      x_offset = 0, y_offset = 0;
 | 
			
		||||
 | 
			
		||||
    if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) {
 | 
			
		||||
        i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);
 | 
			
		||||
 | 
			
		||||
        if (status == I2C_STATUS_SUCCESS) {
 | 
			
		||||
            error_count = 0;
 | 
			
		||||
 | 
			
		||||
            if (!(pimoroni_data.click & 128)) {
 | 
			
		||||
                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);
 | 
			
		||||
                if (!debounce) {
 | 
			
		||||
                    x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE);
 | 
			
		||||
                    y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE);
 | 
			
		||||
                    pimoroni_trackball_adapt_values(&mouse_report.x, &x_offset);
 | 
			
		||||
                    pimoroni_trackball_adapt_values(&mouse_report.y, &y_offset);
 | 
			
		||||
                } else {
 | 
			
		||||
                    debounce--;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);
 | 
			
		||||
                debounce             = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            error_count++;
 | 
			
		||||
        }
 | 
			
		||||
        throttle = timer_read_fast();
 | 
			
		||||
    }
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = pimironi_trackball_device_init,
 | 
			
		||||
    .get_report = pimorono_trackball_get_report,
 | 
			
		||||
    .set_cpi    = NULL,
 | 
			
		||||
    .get_cpi    = NULL
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
 | 
			
		||||
 | 
			
		||||
static void init(void) { pmw3360_init(); }
 | 
			
		||||
 | 
			
		||||
report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) {
 | 
			
		||||
    report_pmw3360_t data        = pmw3360_read_burst();
 | 
			
		||||
    static uint16_t  MotionStart = 0;  // Timer for accel, 0 is resting state
 | 
			
		||||
 | 
			
		||||
    if (data.isOnSurface && data.isMotion) {
 | 
			
		||||
        // Reset timer if stopped moving
 | 
			
		||||
        if (!data.isMotion) {
 | 
			
		||||
            if (MotionStart != 0) MotionStart = 0;
 | 
			
		||||
            return mouse_report;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set timer if new motion
 | 
			
		||||
        if ((MotionStart == 0) && data.isMotion) {
 | 
			
		||||
#    ifdef CONSOLE_ENABLE
 | 
			
		||||
            if (debug_mouse) dprintf("Starting motion.\n");
 | 
			
		||||
#    endif
 | 
			
		||||
            MotionStart = timer_read();
 | 
			
		||||
        }
 | 
			
		||||
        mouse_report.x = constrain_hid(data.dx);
 | 
			
		||||
        mouse_report.y = constrain_hid(data.dy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mouse_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = init,
 | 
			
		||||
    .get_report = pmw3360_get_report,
 | 
			
		||||
    .set_cpi    = pmw3360_set_cpi,
 | 
			
		||||
    .get_cpi    = pmw3360_get_cpi
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#else
 | 
			
		||||
__attribute__((weak)) void           pointing_device_driver_init(void) {}
 | 
			
		||||
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; }
 | 
			
		||||
__attribute__((weak)) uint16_t       pointing_device_driver_get_cpi(void) { return 0; }
 | 
			
		||||
__attribute__((weak)) void           pointing_device_driver_set_cpi(uint16_t cpi) {}
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const pointing_device_driver_t pointing_device_driver = {
 | 
			
		||||
    .init       = pointing_device_driver_init,
 | 
			
		||||
    .get_report = pointing_device_driver_get_report,
 | 
			
		||||
    .get_cpi    = pointing_device_driver_get_cpi,
 | 
			
		||||
    .set_cpi    = pointing_device_driver_set_cpi
 | 
			
		||||
};
 | 
			
		||||
// clang-format on
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -18,14 +18,33 @@
 | 
			
		|||
 | 
			
		||||
#    include <stdbool.h>
 | 
			
		||||
#    include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#    include "process_auto_shift.h"
 | 
			
		||||
 | 
			
		||||
static uint16_t autoshift_time    = 0;
 | 
			
		||||
static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
 | 
			
		||||
static uint16_t autoshift_lastkey = KC_NO;
 | 
			
		||||
#    ifndef AUTO_SHIFT_DISABLED_AT_STARTUP
 | 
			
		||||
#        define AUTO_SHIFT_STARTUP_STATE true /* enabled */
 | 
			
		||||
#    else
 | 
			
		||||
#        define AUTO_SHIFT_STARTUP_STATE false /* disabled */
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
// Stores the last Auto Shift key's up or down time, for evaluation or keyrepeat.
 | 
			
		||||
static uint16_t autoshift_time = 0;
 | 
			
		||||
#    if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
// Stores the last key's up or down time, to replace autoshift_time so that Tap Hold times are accurate.
 | 
			
		||||
static uint16_t retroshift_time = 0;
 | 
			
		||||
// Stores a possibly Retro Shift key's up or down time, as retroshift_time needs
 | 
			
		||||
// to be set before the Retro Shift key is evaluated if it is interrupted by an
 | 
			
		||||
// Auto Shifted key.
 | 
			
		||||
static uint16_t last_retroshift_time;
 | 
			
		||||
#    endif
 | 
			
		||||
static uint16_t    autoshift_timeout = AUTO_SHIFT_TIMEOUT;
 | 
			
		||||
static uint16_t    autoshift_lastkey = KC_NO;
 | 
			
		||||
static keyrecord_t autoshift_lastrecord;
 | 
			
		||||
// Keys take 8 bits if modifiers are excluded. This records the shift state
 | 
			
		||||
// when pressed for each key, so that can be passed to the release function
 | 
			
		||||
// and it knows which key needs to be released (if shifted is different base).
 | 
			
		||||
static uint16_t autoshift_shift_states[((1 << 8) + 15) / 16];
 | 
			
		||||
static struct {
 | 
			
		||||
    // Whether autoshift is enabled.
 | 
			
		||||
    // Whether Auto Shift is enabled.
 | 
			
		||||
    bool enabled : 1;
 | 
			
		||||
    // Whether the last auto-shifted key was released after the timeout.  This
 | 
			
		||||
    // is used to replicate the last key for a tap-then-hold.
 | 
			
		||||
| 
						 | 
				
			
			@ -34,43 +53,157 @@ static struct {
 | 
			
		|||
    bool in_progress : 1;
 | 
			
		||||
    // Whether the auto-shifted keypress has been registered.
 | 
			
		||||
    bool holding_shift : 1;
 | 
			
		||||
} autoshift_flags = {true, false, false, false};
 | 
			
		||||
    // Whether the user is holding a shift and we removed it.
 | 
			
		||||
    bool cancelling_lshift : 1;
 | 
			
		||||
    bool cancelling_rshift : 1;
 | 
			
		||||
    // clang-format wants to remove the true for some reason.
 | 
			
		||||
    // clang-format off
 | 
			
		||||
} autoshift_flags = {AUTO_SHIFT_STARTUP_STATE, false, false, false, false, false};
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
/** \brief Called on physical press, returns whether key should be added to Auto Shift */
 | 
			
		||||
__attribute__((weak)) bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { return false; }
 | 
			
		||||
 | 
			
		||||
/** \brief Called on physical press, returns whether is Auto Shift key */
 | 
			
		||||
__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_ALPHA
 | 
			
		||||
        case AUTO_SHIFT_ALPHA:
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_NUMERIC
 | 
			
		||||
        case AUTO_SHIFT_NUMERIC:
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_SPECIAL
 | 
			
		||||
        case AUTO_SHIFT_SPECIAL:
 | 
			
		||||
#    endif
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return get_custom_auto_shifted_key(keycode, record);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \brief Called to check whether defines should apply if PER_KEY is set for it */
 | 
			
		||||
__attribute__((weak)) bool get_auto_shift_repeat(uint16_t keycode, keyrecord_t *record) { return true; }
 | 
			
		||||
__attribute__((weak)) bool get_auto_shift_no_auto_repeat(uint16_t keycode, keyrecord_t *record) { return true; }
 | 
			
		||||
 | 
			
		||||
/** \brief Called when an Auto Shift key needs to be pressed */
 | 
			
		||||
__attribute__((weak)) void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) {
 | 
			
		||||
    if (shifted) {
 | 
			
		||||
        add_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
    }
 | 
			
		||||
    register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \brief Called when an Auto Shift key needs to be released */
 | 
			
		||||
__attribute__((weak)) void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) { unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); }
 | 
			
		||||
 | 
			
		||||
/** \brief Sets the shift state to use when keyrepeating, required by custom shifts */
 | 
			
		||||
void set_autoshift_shift_state(uint16_t keycode, bool shifted) {
 | 
			
		||||
    keycode = keycode & 0xFF;
 | 
			
		||||
    if (shifted) {
 | 
			
		||||
        autoshift_shift_states[keycode / 16] |= (uint16_t)1 << keycode % 16;
 | 
			
		||||
    } else {
 | 
			
		||||
        autoshift_shift_states[keycode / 16] &= ~((uint16_t)1 << keycode % 16);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \brief Gets the shift state to use when keyrepeating, required by custom shifts */
 | 
			
		||||
bool get_autoshift_shift_state(uint16_t keycode) {
 | 
			
		||||
    keycode = keycode & 0xFF;
 | 
			
		||||
    return (autoshift_shift_states[keycode / 16] & (uint16_t)1 << keycode % 16) != (uint16_t)0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \brief Restores the shift key if it was cancelled by Auto Shift */
 | 
			
		||||
static void autoshift_flush_shift(void) {
 | 
			
		||||
    autoshift_flags.holding_shift = false;
 | 
			
		||||
    del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
    if (autoshift_flags.cancelling_lshift) {
 | 
			
		||||
        autoshift_flags.cancelling_lshift = false;
 | 
			
		||||
        add_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
    }
 | 
			
		||||
    if (autoshift_flags.cancelling_rshift) {
 | 
			
		||||
        autoshift_flags.cancelling_rshift = false;
 | 
			
		||||
        add_mods(MOD_BIT(KC_RSFT));
 | 
			
		||||
    }
 | 
			
		||||
    send_keyboard_report();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \brief Record the press of an autoshiftable key
 | 
			
		||||
 *
 | 
			
		||||
 *  \return Whether the record should be further processed.
 | 
			
		||||
 */
 | 
			
		||||
static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) {
 | 
			
		||||
    if (!autoshift_flags.enabled) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // clang-format off
 | 
			
		||||
    if ((get_mods()
 | 
			
		||||
#    if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
            | get_oneshot_mods()
 | 
			
		||||
#    endif
 | 
			
		||||
        ) & (~MOD_BIT(KC_LSFT))
 | 
			
		||||
    ) {
 | 
			
		||||
        // clang-format on
 | 
			
		||||
        // Prevents keyrepeating unshifted value of key after using it in a key combo.
 | 
			
		||||
        autoshift_lastkey = KC_NO;
 | 
			
		||||
#    ifndef AUTO_SHIFT_MODIFIERS
 | 
			
		||||
    if (get_mods()) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
        // We can't return true here anymore because custom unshifted values are
 | 
			
		||||
        // possible and there's no good way to tell whether the press returned
 | 
			
		||||
        // true upon release.
 | 
			
		||||
        set_autoshift_shift_state(keycode, false);
 | 
			
		||||
        autoshift_press_user(keycode, false, record);
 | 
			
		||||
#        if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
        set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));
 | 
			
		||||
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
 | 
			
		||||
#        endif
 | 
			
		||||
        return false;
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef AUTO_SHIFT_REPEAT
 | 
			
		||||
    const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
 | 
			
		||||
#        ifndef AUTO_SHIFT_NO_AUTO_REPEAT
 | 
			
		||||
    if (!autoshift_flags.lastshifted) {
 | 
			
		||||
#        endif
 | 
			
		||||
        if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) {
 | 
			
		||||
            // Allow a tap-then-hold for keyrepeat.
 | 
			
		||||
            if (!autoshift_flags.lastshifted) {
 | 
			
		||||
                register_code(autoshift_lastkey);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Simulate pressing the shift key.
 | 
			
		||||
                add_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
                register_code(autoshift_lastkey);
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
#        ifndef AUTO_SHIFT_NO_AUTO_REPEAT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store record to be sent to user functions if there's no release record then.
 | 
			
		||||
    autoshift_lastrecord               = *record;
 | 
			
		||||
    autoshift_lastrecord.event.pressed = false;
 | 
			
		||||
    autoshift_lastrecord.event.time    = 0;
 | 
			
		||||
    // clang-format off
 | 
			
		||||
#    if defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)
 | 
			
		||||
    if (keycode == autoshift_lastkey &&
 | 
			
		||||
#        ifdef AUTO_SHIFT_REPEAT_PER_KEY
 | 
			
		||||
        get_auto_shift_repeat(autoshift_lastkey, record) &&
 | 
			
		||||
#        endif
 | 
			
		||||
#        if !defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY)
 | 
			
		||||
        (
 | 
			
		||||
            !autoshift_flags.lastshifted
 | 
			
		||||
#            ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY
 | 
			
		||||
            || get_auto_shift_no_auto_repeat(autoshift_lastkey, record)
 | 
			
		||||
#            endif
 | 
			
		||||
        ) &&
 | 
			
		||||
#        endif
 | 
			
		||||
        TIMER_DIFF_16(now, autoshift_time) <
 | 
			
		||||
#        ifdef TAPPING_TERM_PER_KEY
 | 
			
		||||
        get_tapping_term(autoshift_lastkey, record)
 | 
			
		||||
#        else
 | 
			
		||||
        TAPPING_TERM
 | 
			
		||||
#        endif
 | 
			
		||||
    ) {
 | 
			
		||||
        // clang-format on
 | 
			
		||||
        // Allow a tap-then-hold for keyrepeat.
 | 
			
		||||
        if (get_mods() & MOD_BIT(KC_LSFT)) {
 | 
			
		||||
            autoshift_flags.cancelling_lshift = true;
 | 
			
		||||
            del_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
        }
 | 
			
		||||
        if (get_mods() & MOD_BIT(KC_RSFT)) {
 | 
			
		||||
            autoshift_flags.cancelling_rshift = true;
 | 
			
		||||
            del_mods(MOD_BIT(KC_RSFT));
 | 
			
		||||
        }
 | 
			
		||||
        // autoshift_shift_state doesn't need to be changed.
 | 
			
		||||
        autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    // Use physical shift state of press event to be more like normal typing.
 | 
			
		||||
#    if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
    autoshift_flags.lastshifted = (get_mods() | get_oneshot_mods()) & MOD_BIT(KC_LSFT);
 | 
			
		||||
    set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));
 | 
			
		||||
#    else
 | 
			
		||||
    autoshift_flags.lastshifted = get_mods() & MOD_BIT(KC_LSFT);
 | 
			
		||||
#    endif
 | 
			
		||||
    // Record the keycode so we can simulate it later.
 | 
			
		||||
    autoshift_lastkey           = keycode;
 | 
			
		||||
    autoshift_time              = now;
 | 
			
		||||
| 
						 | 
				
			
			@ -84,51 +217,70 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record)
 | 
			
		|||
 | 
			
		||||
/** \brief Registers an autoshiftable key under the right conditions
 | 
			
		||||
 *
 | 
			
		||||
 * If the autoshift delay has elapsed, register a shift and the key.
 | 
			
		||||
 * If autoshift_timeout has elapsed, register a shift and the key.
 | 
			
		||||
 *
 | 
			
		||||
 * If the autoshift key is released before the delay has elapsed, register the
 | 
			
		||||
 * If the Auto Shift key is released before the delay has elapsed, register the
 | 
			
		||||
 * key without a shift.
 | 
			
		||||
 *
 | 
			
		||||
 * Called on key down with keycode=KC_NO, auto-shifted key up, and timeout.
 | 
			
		||||
 */
 | 
			
		||||
static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
 | 
			
		||||
    // Called on key down with KC_NO, auto-shifted key up, and timeout.
 | 
			
		||||
    if (autoshift_flags.in_progress) {
 | 
			
		||||
static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger, keyrecord_t *record) {
 | 
			
		||||
    if (autoshift_flags.in_progress && (keycode == autoshift_lastkey || keycode == KC_NO)) {
 | 
			
		||||
        // Process the auto-shiftable key.
 | 
			
		||||
        autoshift_flags.in_progress = false;
 | 
			
		||||
 | 
			
		||||
        // Time since the initial press was recorded.
 | 
			
		||||
        const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
 | 
			
		||||
        if (elapsed < autoshift_timeout) {
 | 
			
		||||
            register_code(autoshift_lastkey);
 | 
			
		||||
            autoshift_flags.lastshifted = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Simulate pressing the shift key.
 | 
			
		||||
            add_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
            register_code(autoshift_lastkey);
 | 
			
		||||
            autoshift_flags.lastshifted = true;
 | 
			
		||||
#    if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT)
 | 
			
		||||
            if (matrix_trigger) {
 | 
			
		||||
                // Prevents release.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        autoshift_flags.lastshifted =
 | 
			
		||||
            autoshift_flags.lastshifted
 | 
			
		||||
            || TIMER_DIFF_16(now, autoshift_time) >=
 | 
			
		||||
#    ifdef AUTO_SHIFT_TIMEOUT_PER_KEY
 | 
			
		||||
                get_autoshift_timeout(autoshift_lastkey, record)
 | 
			
		||||
#    else
 | 
			
		||||
                autoshift_timeout
 | 
			
		||||
#    endif
 | 
			
		||||
        ;
 | 
			
		||||
        // clang-format on
 | 
			
		||||
        set_autoshift_shift_state(autoshift_lastkey, autoshift_flags.lastshifted);
 | 
			
		||||
        if (get_mods() & MOD_BIT(KC_LSFT)) {
 | 
			
		||||
            autoshift_flags.cancelling_lshift = true;
 | 
			
		||||
            del_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
        }
 | 
			
		||||
        if (get_mods() & MOD_BIT(KC_RSFT)) {
 | 
			
		||||
            autoshift_flags.cancelling_rshift = true;
 | 
			
		||||
            del_mods(MOD_BIT(KC_RSFT));
 | 
			
		||||
        }
 | 
			
		||||
        autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
 | 
			
		||||
 | 
			
		||||
        // clang-format off
 | 
			
		||||
#    if (defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)) && (!defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY))
 | 
			
		||||
        if (matrix_trigger
 | 
			
		||||
#        ifdef AUTO_SHIFT_REPEAT_PER_KEY
 | 
			
		||||
            && get_auto_shift_repeat(autoshift_lastkey, record)
 | 
			
		||||
#        endif
 | 
			
		||||
#        ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY
 | 
			
		||||
            && !get_auto_shift_no_auto_repeat(autoshift_lastkey, record)
 | 
			
		||||
#        endif
 | 
			
		||||
        ) {
 | 
			
		||||
            // Prevents release.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
#    endif
 | 
			
		||||
        // clang-format on
 | 
			
		||||
#    if TAP_CODE_DELAY > 0
 | 
			
		||||
        wait_ms(TAP_CODE_DELAY);
 | 
			
		||||
#    endif
 | 
			
		||||
        unregister_code(autoshift_lastkey);
 | 
			
		||||
        del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
 | 
			
		||||
        autoshift_release_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
 | 
			
		||||
        autoshift_flush_shift();
 | 
			
		||||
    } else {
 | 
			
		||||
        // Release after keyrepeat.
 | 
			
		||||
        unregister_code(keycode);
 | 
			
		||||
        autoshift_release_user(keycode, get_autoshift_shift_state(keycode), record);
 | 
			
		||||
        if (keycode == autoshift_lastkey) {
 | 
			
		||||
            // This will only fire when the key was the last auto-shiftable
 | 
			
		||||
            // pressed. That prevents aaaaBBBB then releasing a from unshifting
 | 
			
		||||
            // later Bs (if B wasn't auto-shiftable).
 | 
			
		||||
            del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
            // pressed. That prevents 'aaaaBBBB' then releasing a from unshifting
 | 
			
		||||
            // later 'B's (if 'B' wasn't auto-shiftable).
 | 
			
		||||
            autoshift_flush_shift();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    send_keyboard_report();  // del_weak_mods doesn't send one.
 | 
			
		||||
    // Roll the autoshift_time forward for detecting tap-and-hold.
 | 
			
		||||
    autoshift_time = now;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -141,24 +293,29 @@ static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
 | 
			
		|||
 */
 | 
			
		||||
void autoshift_matrix_scan(void) {
 | 
			
		||||
    if (autoshift_flags.in_progress) {
 | 
			
		||||
        const uint16_t now     = timer_read();
 | 
			
		||||
        const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
 | 
			
		||||
        if (elapsed >= autoshift_timeout) {
 | 
			
		||||
            autoshift_end(autoshift_lastkey, now, true);
 | 
			
		||||
        const uint16_t now = timer_read();
 | 
			
		||||
        if (TIMER_DIFF_16(now, autoshift_time) >=
 | 
			
		||||
#    ifdef AUTO_SHIFT_TIMEOUT_PER_KEY
 | 
			
		||||
            get_autoshift_timeout(autoshift_lastkey, &autoshift_lastrecord)
 | 
			
		||||
#    else
 | 
			
		||||
            autoshift_timeout
 | 
			
		||||
#    endif
 | 
			
		||||
        ) {
 | 
			
		||||
            autoshift_end(autoshift_lastkey, now, true, &autoshift_lastrecord);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void autoshift_toggle(void) {
 | 
			
		||||
    autoshift_flags.enabled = !autoshift_flags.enabled;
 | 
			
		||||
    del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
    autoshift_flush_shift();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void autoshift_enable(void) { autoshift_flags.enabled = true; }
 | 
			
		||||
 | 
			
		||||
void autoshift_disable(void) {
 | 
			
		||||
    autoshift_flags.enabled = false;
 | 
			
		||||
    del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
    autoshift_flush_shift();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    ifndef AUTO_SHIFT_NO_SETUP
 | 
			
		||||
| 
						 | 
				
			
			@ -173,76 +330,158 @@ void autoshift_timer_report(void) {
 | 
			
		|||
 | 
			
		||||
bool get_autoshift_state(void) { return autoshift_flags.enabled; }
 | 
			
		||||
 | 
			
		||||
uint16_t get_autoshift_timeout(void) { return autoshift_timeout; }
 | 
			
		||||
uint16_t                       get_generic_autoshift_timeout() { return autoshift_timeout; }
 | 
			
		||||
__attribute__((weak)) uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) { return autoshift_timeout; }
 | 
			
		||||
 | 
			
		||||
void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; }
 | 
			
		||||
 | 
			
		||||
bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
 | 
			
		||||
    // Note that record->event.time isn't reliable, see:
 | 
			
		||||
    // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550
 | 
			
		||||
    const uint16_t now = timer_read();
 | 
			
		||||
    // clang-format off
 | 
			
		||||
    const uint16_t now =
 | 
			
		||||
#    if !defined(RETRO_SHIFT) || defined(NO_ACTION_TAPPING)
 | 
			
		||||
        timer_read()
 | 
			
		||||
#    else
 | 
			
		||||
        (record->event.pressed) ? retroshift_time : timer_read()
 | 
			
		||||
#    endif
 | 
			
		||||
    ;
 | 
			
		||||
    // clang-format on
 | 
			
		||||
 | 
			
		||||
    if (record->event.pressed) {
 | 
			
		||||
        if (autoshift_flags.in_progress) {
 | 
			
		||||
            // Evaluate previous key if there is one. Doing this elsewhere is
 | 
			
		||||
            // more complicated and easier to break.
 | 
			
		||||
            autoshift_end(KC_NO, now, false);
 | 
			
		||||
            // Evaluate previous key if there is one.
 | 
			
		||||
            autoshift_end(KC_NO, now, false, &autoshift_lastrecord);
 | 
			
		||||
        }
 | 
			
		||||
        // For pressing another key while keyrepeating shifted autoshift.
 | 
			
		||||
        del_weak_mods(MOD_BIT(KC_LSFT));
 | 
			
		||||
 | 
			
		||||
        switch (keycode) {
 | 
			
		||||
            case KC_ASTG:
 | 
			
		||||
                autoshift_toggle();
 | 
			
		||||
                return true;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ASON:
 | 
			
		||||
                autoshift_enable();
 | 
			
		||||
                return true;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ASOFF:
 | 
			
		||||
                autoshift_disable();
 | 
			
		||||
                return true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
#    ifndef AUTO_SHIFT_NO_SETUP
 | 
			
		||||
            case KC_ASUP:
 | 
			
		||||
                autoshift_timeout += 5;
 | 
			
		||||
                return true;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ASDN:
 | 
			
		||||
                autoshift_timeout -= 5;
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ASRP:
 | 
			
		||||
                autoshift_timer_report();
 | 
			
		||||
                return true;
 | 
			
		||||
                break;
 | 
			
		||||
#    endif
 | 
			
		||||
        }
 | 
			
		||||
        // If Retro Shift is disabled, possible custom actions shouldn't happen.
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        if (IS_RETRO(keycode)
 | 
			
		||||
#    if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
            // Not tapped or #defines mean that rolls should use hold action.
 | 
			
		||||
            && (
 | 
			
		||||
                record->tap.count == 0
 | 
			
		||||
#        ifdef RETRO_TAPPING_PER_KEY
 | 
			
		||||
                || !get_retro_tapping(keycode, record)
 | 
			
		||||
#        endif
 | 
			
		||||
                || (record->tap.interrupted && (IS_LT(keycode)
 | 
			
		||||
#        if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
 | 
			
		||||
#            ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
 | 
			
		||||
                    ? get_hold_on_other_key_press(keycode, record)
 | 
			
		||||
#            else
 | 
			
		||||
                    ? true
 | 
			
		||||
#            endif
 | 
			
		||||
#        else
 | 
			
		||||
                    ? false
 | 
			
		||||
#        endif
 | 
			
		||||
#        if defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
 | 
			
		||||
#            ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
 | 
			
		||||
                    : !get_ignore_mod_tap_interrupt(keycode, record)
 | 
			
		||||
#            else
 | 
			
		||||
                    : false
 | 
			
		||||
#            endif
 | 
			
		||||
#        else
 | 
			
		||||
                    : true
 | 
			
		||||
#        endif
 | 
			
		||||
                ))
 | 
			
		||||
            )
 | 
			
		||||
#    endif
 | 
			
		||||
        ) {
 | 
			
		||||
            // clang-format on
 | 
			
		||||
            autoshift_lastkey = KC_NO;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (keycode == KC_LSFT) {
 | 
			
		||||
            autoshift_flags.cancelling_lshift = false;
 | 
			
		||||
        } else if (keycode == KC_RSFT) {
 | 
			
		||||
            autoshift_flags.cancelling_rshift = false;
 | 
			
		||||
        }
 | 
			
		||||
        // Same as above (for pressed), additional checks are not needed because
 | 
			
		||||
        // tap.count gets set to 0 in process_action
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        else if (IS_RETRO(keycode)
 | 
			
		||||
#    if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
            && (
 | 
			
		||||
                record->tap.count == 0
 | 
			
		||||
#        ifdef RETRO_TAPPING_PER_KEY
 | 
			
		||||
                || !get_retro_tapping(keycode, record)
 | 
			
		||||
#        endif
 | 
			
		||||
            )
 | 
			
		||||
#    endif
 | 
			
		||||
        ) {
 | 
			
		||||
            // Fixes modifiers not being applied to rolls with AUTO_SHIFT_MODIFIERS set.
 | 
			
		||||
#    if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
 | 
			
		||||
            if (autoshift_flags.in_progress
 | 
			
		||||
#        ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
 | 
			
		||||
                && !get_ignore_mod_tap_interrupt(keycode, record)
 | 
			
		||||
#        endif
 | 
			
		||||
            ) {
 | 
			
		||||
                autoshift_end(KC_NO, now, false, &autoshift_lastrecord);
 | 
			
		||||
            }
 | 
			
		||||
#    endif
 | 
			
		||||
            // clang-format on
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!autoshift_flags.enabled) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (get_auto_shifted_key(keycode, record)) {
 | 
			
		||||
        if (record->event.pressed) {
 | 
			
		||||
            return autoshift_press(keycode, now, record);
 | 
			
		||||
        } else {
 | 
			
		||||
            autoshift_end(keycode, now, false);
 | 
			
		||||
            autoshift_end(keycode, now, false, record);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Prevent keyrepeating of older keys upon non-AS key event.
 | 
			
		||||
    // Not commented at above returns but they serve the same function.
 | 
			
		||||
    if (record->event.pressed) {
 | 
			
		||||
        autoshift_lastkey = KC_NO;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_ALPHA
 | 
			
		||||
        case KC_A ... KC_Z:
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_NUMERIC
 | 
			
		||||
        case KC_1 ... KC_0:
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef NO_AUTO_SHIFT_SPECIAL
 | 
			
		||||
        case KC_TAB:
 | 
			
		||||
        case KC_MINUS ... KC_SLASH:
 | 
			
		||||
        case KC_NONUS_BSLASH:
 | 
			
		||||
#    endif
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
#    if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
 | 
			
		||||
// Called to record time before possible delays by action_tapping_process.
 | 
			
		||||
void retroshift_poll_time(keyevent_t *event) {
 | 
			
		||||
    last_retroshift_time = retroshift_time;
 | 
			
		||||
    retroshift_time      = timer_read();
 | 
			
		||||
}
 | 
			
		||||
// Used to swap the times of Retro Shifted key and Auto Shift key that interrupted it.
 | 
			
		||||
void retroshift_swap_times() {
 | 
			
		||||
    if (last_retroshift_time != 0 && autoshift_flags.in_progress) {
 | 
			
		||||
        uint16_t temp        = retroshift_time;
 | 
			
		||||
        retroshift_time      = last_retroshift_time;
 | 
			
		||||
        last_retroshift_time = temp;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,13 +22,31 @@
 | 
			
		|||
#    define AUTO_SHIFT_TIMEOUT 175
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define IS_LT(kc) ((kc) >= QK_LAYER_TAP && (kc) <= QK_LAYER_TAP_MAX)
 | 
			
		||||
#define IS_MT(kc) ((kc) >= QK_MOD_TAP && (kc) <= QK_MOD_TAP_MAX)
 | 
			
		||||
#define IS_RETRO(kc) (IS_MT(kc) || IS_LT(kc))
 | 
			
		||||
#define DO_GET_AUTOSHIFT_TIMEOUT(keycode, record, ...) record
 | 
			
		||||
// clang-format off
 | 
			
		||||
#define AUTO_SHIFT_ALPHA KC_A ... KC_Z
 | 
			
		||||
#define AUTO_SHIFT_NUMERIC KC_1 ... KC_0
 | 
			
		||||
#define AUTO_SHIFT_SPECIAL          \
 | 
			
		||||
             KC_TAB:                \
 | 
			
		||||
        case KC_MINUS ... KC_SLASH: \
 | 
			
		||||
        case KC_NONUS_BSLASH
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
bool process_auto_shift(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
void retroshift_poll_time(keyevent_t *event);
 | 
			
		||||
void retroshift_swap_times(void);
 | 
			
		||||
 | 
			
		||||
void     autoshift_enable(void);
 | 
			
		||||
void     autoshift_disable(void);
 | 
			
		||||
void     autoshift_toggle(void);
 | 
			
		||||
bool     get_autoshift_state(void);
 | 
			
		||||
uint16_t get_autoshift_timeout(void);
 | 
			
		||||
uint16_t get_generic_autoshift_timeout(void);
 | 
			
		||||
// clang-format off
 | 
			
		||||
uint16_t (get_autoshift_timeout)(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
void     set_autoshift_timeout(uint16_t timeout);
 | 
			
		||||
void     autoshift_matrix_scan(void);
 | 
			
		||||
bool     get_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
bool     get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
// clang-format on
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,10 +18,9 @@
 | 
			
		|||
#include "process_combo.h"
 | 
			
		||||
#include "action_tapping.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef COMBO_COUNT
 | 
			
		||||
__attribute__((weak)) combo_t  key_combos[COMBO_COUNT];
 | 
			
		||||
uint16_t COMBO_LEN = COMBO_COUNT;
 | 
			
		||||
__attribute__((weak)) combo_t key_combos[COMBO_COUNT];
 | 
			
		||||
uint16_t                      COMBO_LEN = COMBO_COUNT;
 | 
			
		||||
#else
 | 
			
		||||
extern combo_t  key_combos[];
 | 
			
		||||
extern uint16_t COMBO_LEN;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,64 +45,86 @@ __attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef COMBO_NO_TIMER
 | 
			
		||||
static uint16_t timer                 = 0;
 | 
			
		||||
static uint16_t timer = 0;
 | 
			
		||||
#endif
 | 
			
		||||
static bool     b_combo_enable        = true;  // defaults to enabled
 | 
			
		||||
static uint16_t longest_term          = 0;
 | 
			
		||||
static bool     b_combo_enable = true;  // defaults to enabled
 | 
			
		||||
static uint16_t longest_term   = 0;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    keyrecord_t record;
 | 
			
		||||
    uint16_t combo_index;
 | 
			
		||||
    uint16_t keycode;
 | 
			
		||||
    uint16_t    combo_index;
 | 
			
		||||
    uint16_t    keycode;
 | 
			
		||||
} queued_record_t;
 | 
			
		||||
static uint8_t key_buffer_size = 0;
 | 
			
		||||
static uint8_t         key_buffer_size = 0;
 | 
			
		||||
static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH];
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint16_t combo_index;
 | 
			
		||||
} queued_combo_t;
 | 
			
		||||
static uint8_t combo_buffer_write= 0;
 | 
			
		||||
static uint8_t combo_buffer_read = 0;
 | 
			
		||||
static uint8_t        combo_buffer_write = 0;
 | 
			
		||||
static uint8_t        combo_buffer_read  = 0;
 | 
			
		||||
static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];
 | 
			
		||||
 | 
			
		||||
#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH
 | 
			
		||||
 | 
			
		||||
#define COMBO_KEY_POS ((keypos_t){.col=254, .row=254})
 | 
			
		||||
 | 
			
		||||
#define COMBO_KEY_POS ((keypos_t){.col = 254, .row = 254})
 | 
			
		||||
 | 
			
		||||
#ifndef EXTRA_SHORT_COMBOS
 | 
			
		||||
/* flags are their own elements in combo_t struct. */
 | 
			
		||||
#    define COMBO_ACTIVE(combo)   (combo->active)
 | 
			
		||||
#    define COMBO_ACTIVE(combo) (combo->active)
 | 
			
		||||
#    define COMBO_DISABLED(combo) (combo->disabled)
 | 
			
		||||
#    define COMBO_STATE(combo)    (combo->state)
 | 
			
		||||
#    define COMBO_STATE(combo) (combo->state)
 | 
			
		||||
 | 
			
		||||
#    define ACTIVATE_COMBO(combo)    do {combo->active = true;}while(0)
 | 
			
		||||
#    define DEACTIVATE_COMBO(combo)  do {combo->active = false;}while(0)
 | 
			
		||||
#    define DISABLE_COMBO(combo)     do {combo->disabled = true;}while(0)
 | 
			
		||||
#    define RESET_COMBO_STATE(combo) do { \
 | 
			
		||||
    combo->disabled = false; \
 | 
			
		||||
    combo->state = 0; \
 | 
			
		||||
}while(0)
 | 
			
		||||
#    define ACTIVATE_COMBO(combo) \
 | 
			
		||||
        do {                      \
 | 
			
		||||
            combo->active = true; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define DEACTIVATE_COMBO(combo) \
 | 
			
		||||
        do {                        \
 | 
			
		||||
            combo->active = false;  \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define DISABLE_COMBO(combo)    \
 | 
			
		||||
        do {                        \
 | 
			
		||||
            combo->disabled = true; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define RESET_COMBO_STATE(combo) \
 | 
			
		||||
        do {                         \
 | 
			
		||||
            combo->disabled = false; \
 | 
			
		||||
            combo->state    = 0;     \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#else
 | 
			
		||||
/* flags are at the two high bits of state. */
 | 
			
		||||
#    define COMBO_ACTIVE(combo)   (combo->state & 0x80)
 | 
			
		||||
#    define COMBO_ACTIVE(combo) (combo->state & 0x80)
 | 
			
		||||
#    define COMBO_DISABLED(combo) (combo->state & 0x40)
 | 
			
		||||
#    define COMBO_STATE(combo)    (combo->state & 0x3F)
 | 
			
		||||
#    define COMBO_STATE(combo) (combo->state & 0x3F)
 | 
			
		||||
 | 
			
		||||
#    define ACTIVATE_COMBO(combo)    do {combo->state |= 0x80;}while(0)
 | 
			
		||||
#    define DEACTIVATE_COMBO(combo)  do {combo->state &= ~0x80;}while(0)
 | 
			
		||||
#    define DISABLE_COMBO(combo)     do {combo->state |= 0x40;}while(0)
 | 
			
		||||
#    define RESET_COMBO_STATE(combo) do {combo->state &= ~0x7F;}while(0)
 | 
			
		||||
#    define ACTIVATE_COMBO(combo) \
 | 
			
		||||
        do {                      \
 | 
			
		||||
            combo->state |= 0x80; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define DEACTIVATE_COMBO(combo) \
 | 
			
		||||
        do {                        \
 | 
			
		||||
            combo->state &= ~0x80;  \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define DISABLE_COMBO(combo)  \
 | 
			
		||||
        do {                      \
 | 
			
		||||
            combo->state |= 0x40; \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#    define RESET_COMBO_STATE(combo) \
 | 
			
		||||
        do {                         \
 | 
			
		||||
            combo->state &= ~0x7F;   \
 | 
			
		||||
        } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline void release_combo(uint16_t combo_index, combo_t *combo) {
 | 
			
		||||
    if (combo->keycode) {
 | 
			
		||||
        keyrecord_t record = {
 | 
			
		||||
            .event = {
 | 
			
		||||
                .key = COMBO_KEY_POS,
 | 
			
		||||
                .time = timer_read()|1,
 | 
			
		||||
                .pressed = false,
 | 
			
		||||
            },
 | 
			
		||||
            .event =
 | 
			
		||||
                {
 | 
			
		||||
                    .key     = COMBO_KEY_POS,
 | 
			
		||||
                    .time    = timer_read() | 1,
 | 
			
		||||
                    .pressed = false,
 | 
			
		||||
                },
 | 
			
		||||
            .keycode = combo->keycode,
 | 
			
		||||
        };
 | 
			
		||||
#ifndef NO_ACTION_TAPPING
 | 
			
		||||
| 
						 | 
				
			
			@ -123,18 +144,17 @@ static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
#elif defined(COMBO_MUST_HOLD_PER_COMBO)
 | 
			
		||||
    return get_combo_must_hold(combo_index, combo);
 | 
			
		||||
#elif defined(COMBO_MUST_HOLD_MODS)
 | 
			
		||||
    return (KEYCODE_IS_MOD(combo->keycode) ||
 | 
			
		||||
            (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
 | 
			
		||||
    return (KEYCODE_IS_MOD(combo->keycode) || (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
 | 
			
		||||
#endif
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
 | 
			
		||||
static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo) {
 | 
			
		||||
    if (_get_combo_must_hold(combo_index, combo)
 | 
			
		||||
#ifdef COMBO_MUST_TAP_PER_COMBO
 | 
			
		||||
            || get_combo_must_tap(combo_index, combo)
 | 
			
		||||
        || get_combo_must_tap(combo_index, combo)
 | 
			
		||||
#endif
 | 
			
		||||
       ) {
 | 
			
		||||
    ) {
 | 
			
		||||
        if (longest_term < COMBO_HOLD_TERM) {
 | 
			
		||||
            return COMBO_HOLD_TERM;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -144,9 +164,8 @@ static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
 | 
			
		||||
 | 
			
		||||
#if defined(COMBO_TERM_PER_COMBO)
 | 
			
		||||
        return get_combo_term(combo_index, combo);
 | 
			
		||||
    return get_combo_term(combo_index, combo);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return COMBO_TERM;
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +173,7 @@ static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
 | 
			
		||||
void clear_combos(void) {
 | 
			
		||||
    uint16_t index = 0;
 | 
			
		||||
    longest_term = 0;
 | 
			
		||||
    longest_term   = 0;
 | 
			
		||||
    for (index = 0; index < COMBO_LEN; ++index) {
 | 
			
		||||
        combo_t *combo = &key_combos[index];
 | 
			
		||||
        if (!COMBO_ACTIVE(combo)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +194,7 @@ static inline void dump_key_buffer(void) {
 | 
			
		|||
        key_buffer_next = key_buffer_i + 1;
 | 
			
		||||
 | 
			
		||||
        queued_record_t *qrecord = &key_buffer[key_buffer_i];
 | 
			
		||||
        keyrecord_t *record = &qrecord->record;
 | 
			
		||||
        keyrecord_t *    record  = &qrecord->record;
 | 
			
		||||
 | 
			
		||||
        if (IS_NOEVENT(record->event)) {
 | 
			
		||||
            continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -185,9 +204,9 @@ static inline void dump_key_buffer(void) {
 | 
			
		|||
            process_combo_event(qrecord->combo_index, true);
 | 
			
		||||
        } else {
 | 
			
		||||
#ifndef NO_ACTION_TAPPING
 | 
			
		||||
        action_tapping_process(*record);
 | 
			
		||||
            action_tapping_process(*record);
 | 
			
		||||
#else
 | 
			
		||||
        process_record(record);
 | 
			
		||||
            process_record(record);
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
        record->event.time = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +261,9 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
    /* Apply combo's result keycode to the last chord key of the combo and
 | 
			
		||||
     * disable the other keys. */
 | 
			
		||||
 | 
			
		||||
    if (COMBO_DISABLED(combo)) { return; }
 | 
			
		||||
    if (COMBO_DISABLED(combo)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // state to check against so we find the last key of the combo from the buffer
 | 
			
		||||
#if defined(EXTRA_EXTRA_LONG_COMBOS)
 | 
			
		||||
| 
						 | 
				
			
			@ -254,12 +275,11 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
    for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
 | 
			
		||||
 | 
			
		||||
        queued_record_t *qrecord = &key_buffer[key_buffer_i];
 | 
			
		||||
        keyrecord_t *record = &qrecord->record;
 | 
			
		||||
        uint16_t keycode = qrecord->keycode;
 | 
			
		||||
        keyrecord_t *    record  = &qrecord->record;
 | 
			
		||||
        uint16_t         keycode = qrecord->keycode;
 | 
			
		||||
 | 
			
		||||
        uint8_t key_count = 0;
 | 
			
		||||
        uint8_t  key_count = 0;
 | 
			
		||||
        uint16_t key_index = -1;
 | 
			
		||||
        _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -271,7 +291,7 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
        KEY_STATE_DOWN(state, key_index);
 | 
			
		||||
        if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {
 | 
			
		||||
            // this in the end executes the combo when the key_buffer is dumped.
 | 
			
		||||
            record->keycode = combo->keycode;
 | 
			
		||||
            record->keycode   = combo->keycode;
 | 
			
		||||
            record->event.key = COMBO_KEY_POS;
 | 
			
		||||
 | 
			
		||||
            qrecord->combo_index = combo_index;
 | 
			
		||||
| 
						 | 
				
			
			@ -283,19 +303,15 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
 | 
			
		|||
            // by making it a TICK event.
 | 
			
		||||
            record->event.time = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    drop_combo_from_buffer(combo_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void apply_combos(void) {
 | 
			
		||||
    // Apply all buffered normal combos.
 | 
			
		||||
    for (uint8_t i = combo_buffer_read;
 | 
			
		||||
            i != combo_buffer_write;
 | 
			
		||||
            INCREMENT_MOD(i)) {
 | 
			
		||||
 | 
			
		||||
    for (uint8_t i = combo_buffer_read; i != combo_buffer_write; INCREMENT_MOD(i)) {
 | 
			
		||||
        queued_combo_t *buffered_combo = &combo_buffer[i];
 | 
			
		||||
        combo_t *combo = &key_combos[buffered_combo->combo_index];
 | 
			
		||||
        combo_t *       combo          = &key_combos[buffered_combo->combo_index];
 | 
			
		||||
 | 
			
		||||
#ifdef COMBO_MUST_TAP_PER_COMBO
 | 
			
		||||
        if (get_combo_must_tap(buffered_combo->combo_index, combo)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -310,15 +326,15 @@ static inline void apply_combos(void) {
 | 
			
		|||
    clear_combos();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
 | 
			
		||||
combo_t *overlaps(combo_t *combo1, combo_t *combo2) {
 | 
			
		||||
    /* Checks if the combos overlap and returns the combo that should be
 | 
			
		||||
     * dropped from the combo buffer.
 | 
			
		||||
     * The combo that has less keys will be dropped. If they have the same
 | 
			
		||||
     * amount of keys, drop combo1. */
 | 
			
		||||
 | 
			
		||||
    uint8_t idx1 = 0, idx2 = 0;
 | 
			
		||||
    uint8_t  idx1 = 0, idx2 = 0;
 | 
			
		||||
    uint16_t key1, key2;
 | 
			
		||||
    bool overlaps = false;
 | 
			
		||||
    bool     overlaps = false;
 | 
			
		||||
 | 
			
		||||
    while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {
 | 
			
		||||
        idx2 = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -335,7 +351,7 @@ combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
 | 
			
		||||
    uint8_t key_count = 0;
 | 
			
		||||
    uint8_t  key_count = 0;
 | 
			
		||||
    uint16_t key_index = -1;
 | 
			
		||||
    _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -369,12 +385,9 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
 | 
			
		|||
 | 
			
		||||
                // disable readied combos that overlap with this combo
 | 
			
		||||
                combo_t *drop = NULL;
 | 
			
		||||
                for (uint8_t combo_buffer_i = combo_buffer_read;
 | 
			
		||||
                        combo_buffer_i != combo_buffer_write;
 | 
			
		||||
                        INCREMENT_MOD(combo_buffer_i)) {
 | 
			
		||||
 | 
			
		||||
                    queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
 | 
			
		||||
                    combo_t *buffered_combo = &key_combos[qcombo->combo_index];
 | 
			
		||||
                for (uint8_t combo_buffer_i = combo_buffer_read; combo_buffer_i != combo_buffer_write; INCREMENT_MOD(combo_buffer_i)) {
 | 
			
		||||
                    queued_combo_t *qcombo         = &combo_buffer[combo_buffer_i];
 | 
			
		||||
                    combo_t *       buffered_combo = &key_combos[qcombo->combo_index];
 | 
			
		||||
 | 
			
		||||
                    if ((drop = overlaps(buffered_combo, combo))) {
 | 
			
		||||
                        DISABLE_COMBO(drop);
 | 
			
		||||
| 
						 | 
				
			
			@ -387,21 +400,19 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
 | 
			
		|||
                            INCREMENT_MOD(combo_buffer_read);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (drop != combo) {
 | 
			
		||||
                    // save this combo to buffer
 | 
			
		||||
                    combo_buffer[combo_buffer_write] = (queued_combo_t){
 | 
			
		||||
                        .combo_index=combo_index,
 | 
			
		||||
                        .combo_index = combo_index,
 | 
			
		||||
                    };
 | 
			
		||||
                    INCREMENT_MOD(combo_buffer_write);
 | 
			
		||||
 | 
			
		||||
                    // get possible longer waiting time for tap-/hold-only combos.
 | 
			
		||||
                    longest_term = _get_wait_time(combo_index, combo);
 | 
			
		||||
                }
 | 
			
		||||
            } // if timer elapsed end
 | 
			
		||||
 | 
			
		||||
            }  // if timer elapsed end
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        // chord releases
 | 
			
		||||
| 
						 | 
				
			
			@ -416,7 +427,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
 | 
			
		|||
            else if (get_combo_must_tap(combo_index, combo)) {
 | 
			
		||||
                // immediately apply tap-only combo
 | 
			
		||||
                apply_combo(combo_index, combo);
 | 
			
		||||
                apply_combos(); // also apply other prepared combos and dump key buffer
 | 
			
		||||
                apply_combos();  // also apply other prepared combos and dump key buffer
 | 
			
		||||
#    ifdef COMBO_PROCESS_KEY_RELEASE
 | 
			
		||||
                if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
 | 
			
		||||
                    release_combo(combo_index, combo);
 | 
			
		||||
| 
						 | 
				
			
			@ -424,10 +435,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
 | 
			
		|||
#    endif
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
        } else if (COMBO_ACTIVE(combo)
 | 
			
		||||
                && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo))
 | 
			
		||||
                && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
 | 
			
		||||
                ) {
 | 
			
		||||
        } else if (COMBO_ACTIVE(combo) && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo)) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {
 | 
			
		||||
            /* last key released */
 | 
			
		||||
            release_combo(combo_index, combo);
 | 
			
		||||
            key_is_part_of_combo = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -435,9 +443,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
 | 
			
		|||
#ifdef COMBO_PROCESS_KEY_RELEASE
 | 
			
		||||
            process_combo_key_release(combo_index, combo, key_index, keycode);
 | 
			
		||||
#endif
 | 
			
		||||
        } else if (COMBO_ACTIVE(combo)
 | 
			
		||||
                && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
 | 
			
		||||
                ) {
 | 
			
		||||
        } else if (COMBO_ACTIVE(combo) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {
 | 
			
		||||
            /* first or middle key released */
 | 
			
		||||
            key_is_part_of_combo = true;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -489,21 +495,21 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
 | 
			
		||||
    if (record->event.pressed && is_combo_key) {
 | 
			
		||||
#ifndef COMBO_NO_TIMER
 | 
			
		||||
#   ifdef COMBO_STRICT_TIMER
 | 
			
		||||
#    ifdef COMBO_STRICT_TIMER
 | 
			
		||||
        if (!timer) {
 | 
			
		||||
            // timer is set only on the first key
 | 
			
		||||
            timer = timer_read();
 | 
			
		||||
        }
 | 
			
		||||
#   else
 | 
			
		||||
#    else
 | 
			
		||||
        timer = timer_read();
 | 
			
		||||
#   endif
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
 | 
			
		||||
            key_buffer[key_buffer_size++] = (queued_record_t){
 | 
			
		||||
                .record = *record,
 | 
			
		||||
                .keycode = keycode,
 | 
			
		||||
                .combo_index = -1, // this will be set when applying combos
 | 
			
		||||
                .record      = *record,
 | 
			
		||||
                .keycode     = keycode,
 | 
			
		||||
                .combo_index = -1,  // this will be set when applying combos
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -532,7 +538,7 @@ void combo_task(void) {
 | 
			
		|||
        if (combo_buffer_read != combo_buffer_write) {
 | 
			
		||||
            apply_combos();
 | 
			
		||||
            longest_term = 0;
 | 
			
		||||
            timer = 0;
 | 
			
		||||
            timer        = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            dump_key_buffer();
 | 
			
		||||
            timer = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -546,9 +552,9 @@ void combo_enable(void) { b_combo_enable = true; }
 | 
			
		|||
 | 
			
		||||
void combo_disable(void) {
 | 
			
		||||
#ifndef COMBO_NO_TIMER
 | 
			
		||||
    timer                      = 0;
 | 
			
		||||
    timer = 0;
 | 
			
		||||
#endif
 | 
			
		||||
    b_combo_enable = false;
 | 
			
		||||
    b_combo_enable    = false;
 | 
			
		||||
    combo_buffer_read = combo_buffer_write;
 | 
			
		||||
    clear_combos();
 | 
			
		||||
    dump_key_buffer();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,8 +43,8 @@ typedef struct {
 | 
			
		|||
#ifdef EXTRA_SHORT_COMBOS
 | 
			
		||||
    uint8_t state;
 | 
			
		||||
#else
 | 
			
		||||
    bool disabled;
 | 
			
		||||
    bool active;
 | 
			
		||||
    bool     disabled;
 | 
			
		||||
    bool     active;
 | 
			
		||||
#    if defined(EXTRA_EXTRA_LONG_COMBOS)
 | 
			
		||||
    uint32_t state;
 | 
			
		||||
#    elif defined(EXTRA_LONG_COMBOS)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								quantum/process_keycode/process_dynamic_tapping_term.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								quantum/process_keycode/process_dynamic_tapping_term.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/* Copyright 2020 Vladislav Kucheriavykh
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
#include "process_dynamic_tapping_term.h"
 | 
			
		||||
 | 
			
		||||
#ifndef DYNAMIC_TAPPING_TERM_INCREMENT
 | 
			
		||||
#    define DYNAMIC_TAPPING_TERM_INCREMENT 5
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void tapping_term_report(void) {
 | 
			
		||||
    const char *tapping_term_str = get_u16_str(g_tapping_term, ' ');
 | 
			
		||||
    // Skip padding spaces
 | 
			
		||||
    while (*tapping_term_str == ' ') {
 | 
			
		||||
        tapping_term_str++;
 | 
			
		||||
    }
 | 
			
		||||
    send_string(tapping_term_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record) {
 | 
			
		||||
    if (record->event.pressed) {
 | 
			
		||||
        switch (keycode) {
 | 
			
		||||
            case DT_PRNT:
 | 
			
		||||
                tapping_term_report();
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            case DT_UP:
 | 
			
		||||
                g_tapping_term += DYNAMIC_TAPPING_TERM_INCREMENT;
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            case DT_DOWN:
 | 
			
		||||
                g_tapping_term -= DYNAMIC_TAPPING_TERM_INCREMENT;
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
/* Copyright 2017 Fred Sundvik
 | 
			
		||||
/* Copyright 2020 Vladislav Kucheriavykh
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
| 
						 | 
				
			
			@ -16,8 +16,11 @@
 | 
			
		|||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "action.h"
 | 
			
		||||
 | 
			
		||||
#ifdef LCD_ENABLE
 | 
			
		||||
extern const uint8_t resource_lcd_logo[];
 | 
			
		||||
#ifndef DYNAMIC_TAPPING_TERM_INCREMENT
 | 
			
		||||
#    define DYNAMIC_TAPPING_TERM_INCREMENT 5
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record);
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
#include "process_haptic.h"
 | 
			
		||||
#include "quantum_keycodes.h"
 | 
			
		||||
#include "action_tapping.h"
 | 
			
		||||
#include "usb_device_state.h"
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
 | 
			
		||||
    switch (keycode) {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +31,9 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t
 | 
			
		|||
        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
 | 
			
		||||
            if (record->tap.count == 0) return false;
 | 
			
		||||
            break;
 | 
			
		||||
        case KC_LCTRL ... KC_RGUI:
 | 
			
		||||
        case KC_LEFT_CTRL ... KC_RIGHT_GUI:
 | 
			
		||||
        case QK_MOMENTARY ... QK_MOMENTARY_MAX:
 | 
			
		||||
        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef NO_HAPTIC_FN
 | 
			
		||||
        case KC_FN0 ... KC_FN31:
 | 
			
		||||
| 
						 | 
				
			
			@ -42,34 +44,34 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t
 | 
			
		|||
#ifdef NO_HAPTIC_PUNCTUATION
 | 
			
		||||
        case KC_ENTER:
 | 
			
		||||
        case KC_ESCAPE:
 | 
			
		||||
        case KC_BSPACE:
 | 
			
		||||
        case KC_BACKSPACE:
 | 
			
		||||
        case KC_SPACE:
 | 
			
		||||
        case KC_MINUS:
 | 
			
		||||
        case KC_EQUAL:
 | 
			
		||||
        case KC_LBRACKET:
 | 
			
		||||
        case KC_RBRACKET:
 | 
			
		||||
        case KC_BSLASH:
 | 
			
		||||
        case KC_LEFT_BRACKET:
 | 
			
		||||
        case KC_RIGHT_BRACKET:
 | 
			
		||||
        case KC_BACKSLASH:
 | 
			
		||||
        case KC_NONUS_HASH:
 | 
			
		||||
        case KC_SCOLON:
 | 
			
		||||
        case KC_SEMICOLON:
 | 
			
		||||
        case KC_QUOTE:
 | 
			
		||||
        case KC_GRAVE:
 | 
			
		||||
        case KC_COMMA:
 | 
			
		||||
        case KC_SLASH:
 | 
			
		||||
        case KC_DOT:
 | 
			
		||||
        case KC_NONUS_BSLASH:
 | 
			
		||||
        case KC_NONUS_BACKSLASH:
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef NO_HAPTIC_LOCKKEYS
 | 
			
		||||
        case KC_CAPSLOCK:
 | 
			
		||||
        case KC_SCROLLLOCK:
 | 
			
		||||
        case KC_NUMLOCK:
 | 
			
		||||
        case KC_CAPS_LOCK:
 | 
			
		||||
        case KC_SCROLL_LOCK:
 | 
			
		||||
        case KC_NUM_LOCK:
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef NO_HAPTIC_NAV
 | 
			
		||||
        case KC_PSCREEN:
 | 
			
		||||
        case KC_PRINT_SCREEN:
 | 
			
		||||
        case KC_PAUSE:
 | 
			
		||||
        case KC_INSERT:
 | 
			
		||||
        case KC_DELETE:
 | 
			
		||||
        case KC_PGDOWN:
 | 
			
		||||
        case KC_PGUP:
 | 
			
		||||
        case KC_PAGE_DOWN:
 | 
			
		||||
        case KC_PAGE_UP:
 | 
			
		||||
        case KC_LEFT:
 | 
			
		||||
        case KC_UP:
 | 
			
		||||
        case KC_RIGHT:
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +132,7 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (haptic_get_enable()) {
 | 
			
		||||
    if (haptic_get_enable() && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED))) {
 | 
			
		||||
        if (record->event.pressed) {
 | 
			
		||||
            // keypress
 | 
			
		||||
            if (haptic_get_feedback() < 2 && get_haptic_enabled_key(keycode, record)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
        switch (keycode) {
 | 
			
		||||
            case MAGIC_SWAP_CONTROL_CAPSLOCK ... MAGIC_TOGGLE_ALT_GUI:
 | 
			
		||||
            case MAGIC_SWAP_LCTL_LGUI ... MAGIC_EE_HANDS_RIGHT:
 | 
			
		||||
            case MAGIC_TOGGLE_GUI:
 | 
			
		||||
                /* keymap config */
 | 
			
		||||
                keymap_config.raw = eeconfig_read_keymap();
 | 
			
		||||
                switch (keycode) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
 | 
			
		||||
    if (music_activated || midi_activated) {
 | 
			
		||||
        if (record->event.pressed) {
 | 
			
		||||
            if (keycode == KC_LCTL) {  // Start recording
 | 
			
		||||
            if (keycode == KC_LEFT_CTRL) {  // Start recording
 | 
			
		||||
                music_all_notes_off();
 | 
			
		||||
                music_sequence_recording = true;
 | 
			
		||||
                music_sequence_recorded  = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (keycode == KC_LALT) {  // Stop recording/playing
 | 
			
		||||
            if (keycode == KC_LEFT_ALT) {  // Stop recording/playing
 | 
			
		||||
                music_all_notes_off();
 | 
			
		||||
                if (music_sequence_recording) {  // was recording
 | 
			
		||||
                    music_sequence_recorded = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +165,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (keycode == KC_LGUI && music_sequence_recorded) {  // Start playing
 | 
			
		||||
            if (keycode == KC_LEFT_GUI && music_sequence_recorded) {  // Start playing
 | 
			
		||||
                music_all_notes_off();
 | 
			
		||||
                music_sequence_recording = false;
 | 
			
		||||
                music_sequence_playing   = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0
 | 
			
		|||
 | 
			
		||||
// uint8_t keycode_to_ascii[0xFF][2];
 | 
			
		||||
 | 
			
		||||
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
 | 
			
		||||
// keycode_to_ascii[KC_MINUS] = {0x2D, 0x5F};
 | 
			
		||||
 | 
			
		||||
void print_char(char c) {
 | 
			
		||||
    USB_Disable();
 | 
			
		||||
| 
						 | 
				
			
			@ -90,8 +90,8 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
            case KC_PIPE:
 | 
			
		||||
            case KC_TILD:
 | 
			
		||||
                keycode &= 0xFF;
 | 
			
		||||
            case KC_LSFT:
 | 
			
		||||
            case KC_RSFT:
 | 
			
		||||
            case KC_LEFT_SHIFT:
 | 
			
		||||
            case KC_RIGHT_SHIFT:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    character_shift++;
 | 
			
		||||
                } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -107,13 +107,13 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                    print_box_string("This is a line of text!");
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
            case KC_ESC:
 | 
			
		||||
            case KC_ESCAPE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    print_char(0x1B);
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_SPC:
 | 
			
		||||
            case KC_SPACE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    print_char(0x20);
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ENT:
 | 
			
		||||
            case KC_ENTER:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x0C);
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_BSPC:
 | 
			
		||||
            case KC_BACKSPACE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x18);
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +169,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_COMM:
 | 
			
		||||
            case KC_COMMA:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x3C);
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +179,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_SLSH:
 | 
			
		||||
            case KC_SLASH:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x3F);
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +189,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_QUOT:
 | 
			
		||||
            case KC_QUOTE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x22);
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +199,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_GRV:
 | 
			
		||||
            case KC_GRAVE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7E);
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +209,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_MINS:
 | 
			
		||||
            case KC_MINUS:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x5F);
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +219,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_EQL:
 | 
			
		||||
            case KC_EQUAL:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x2B);
 | 
			
		||||
| 
						 | 
				
			
			@ -229,7 +229,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_LBRC:
 | 
			
		||||
            case KC_LEFT_BRACKET:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7B);
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +239,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_RBRC:
 | 
			
		||||
            case KC_RIGHT_BRACKET:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7D);
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +249,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_BSLS:
 | 
			
		||||
            case KC_BACKSLASH:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7C);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0
 | 
			
		|||
 | 
			
		||||
// uint8_t keycode_to_ascii[0xFF][2];
 | 
			
		||||
 | 
			
		||||
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
 | 
			
		||||
// keycode_to_ascii[KC_MINUS] = {0x2D, 0x5F};
 | 
			
		||||
 | 
			
		||||
void print_char(char c) {
 | 
			
		||||
    uint8_t b = 8;
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +84,8 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
            case KC_PIPE:
 | 
			
		||||
            case KC_TILD:
 | 
			
		||||
                keycode &= 0xFF;
 | 
			
		||||
            case KC_LSFT:
 | 
			
		||||
            case KC_RSFT:
 | 
			
		||||
            case KC_LEFT_SHIFT:
 | 
			
		||||
            case KC_RIGHT_SHIFT:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    character_shift++;
 | 
			
		||||
                } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,13 +101,13 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                    print_string("This is a line of text!\n\n\n");
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
            case KC_ESC:
 | 
			
		||||
            case KC_ESCAPE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    print_char(0x1B);
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_SPC:
 | 
			
		||||
            case KC_SPACE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    print_char(0x20);
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +133,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_ENT:
 | 
			
		||||
            case KC_ENTER:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x0C);
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +143,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_BSPC:
 | 
			
		||||
            case KC_BACKSPACE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x18);
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +163,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_COMM:
 | 
			
		||||
            case KC_COMMA:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x3C);
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +173,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_SLSH:
 | 
			
		||||
            case KC_SLASH:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x3F);
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_QUOT:
 | 
			
		||||
            case KC_QUOTE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x22);
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +193,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_GRV:
 | 
			
		||||
            case KC_GRAVE:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7E);
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +203,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_MINS:
 | 
			
		||||
            case KC_MINUS:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x5F);
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +213,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_EQL:
 | 
			
		||||
            case KC_EQUAL:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x2B);
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_LBRC:
 | 
			
		||||
            case KC_LEFT_BRACKET:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7B);
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +233,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_RBRC:
 | 
			
		||||
            case KC_RIGHT_BRACKET:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7D);
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +243,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
 | 
			
		|||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
                break;
 | 
			
		||||
            case KC_BSLS:
 | 
			
		||||
            case KC_BACKSLASH:
 | 
			
		||||
                if (record->event.pressed) {
 | 
			
		||||
                    if (character_shift) {
 | 
			
		||||
                        print_char(0x7C);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue