diff options
Diffstat (limited to 'quantum')
25 files changed, 734 insertions, 207 deletions
diff --git a/quantum/action.c b/quantum/action.c index a39631ba3e..be85192d25 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -47,7 +47,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. int tp_buttons; #if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) -int retro_tapping_counter = 0; +bool retro_tap_primed = false; +uint16_t retro_tap_curr_key = 0; +# if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) +uint8_t retro_tap_curr_mods = 0; +uint8_t retro_tap_next_mods = 0; +# endif #endif #if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) @@ -77,7 +82,13 @@ void action_exec(keyevent_t event) { debug_event(event); ac_dprintf("\n"); #if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) - retro_tapping_counter++; + uint16_t event_keycode = get_event_keycode(event, false); + if (event.pressed) { + retro_tap_primed = false; + retro_tap_curr_key = event_keycode; + } else if (retro_tap_curr_key == event_keycode) { + retro_tap_primed = true; + } #endif } @@ -531,7 +542,8 @@ void process_action(keyrecord_t *record, action_t action) { # if defined(RETRO_TAPPING) && defined(DUMMY_MOD_NEUTRALIZER_KEYCODE) // Send a dummy keycode to neutralize flashing modifiers // if the key was held and then released with no interruptions. - if (retro_tapping_counter == 2) { + uint16_t ev_kc = get_event_keycode(event, false); + if (retro_tap_primed && retro_tap_curr_key == ev_kc) { neutralize_flashing_modifiers(get_mods()); } # endif @@ -817,6 +829,10 @@ void process_action(keyrecord_t *record, action_t action) { case ACT_LAYER_TAP_EXT: # endif led_set(host_keyboard_leds()); +# ifndef NO_ACTION_ONESHOT + // don't release the key + do_release_oneshot = false; +# endif break; default: break; @@ -825,30 +841,44 @@ void process_action(keyrecord_t *record, action_t action) { #ifndef NO_ACTION_TAPPING # 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 { + if (is_tap_action(action)) { if (event.pressed) { if (tap_count > 0) { - retro_tapping_counter = 0; + retro_tap_primed = false; + } else { +# if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) + retro_tap_curr_mods = retro_tap_next_mods; + retro_tap_next_mods = get_mods(); +# endif } } else { + uint16_t event_keycode = get_event_keycode(event, false); +# if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) + uint8_t curr_mods = get_mods(); +# endif if (tap_count > 0) { - retro_tapping_counter = 0; - } else { + retro_tap_primed = false; + } else if (retro_tap_curr_key == event_keycode) { if ( # ifdef RETRO_TAPPING_PER_KEY - get_retro_tapping(get_event_keycode(record->event, false), record) && + get_retro_tapping(event_keycode, record) && # endif - retro_tapping_counter == 2) { + retro_tap_primed) { # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) process_auto_shift(action.layer_tap.code, record); # else + register_mods(retro_tap_curr_mods); + wait_ms(TAP_CODE_DELAY); tap_code(action.layer_tap.code); + wait_ms(TAP_CODE_DELAY); + unregister_mods(retro_tap_curr_mods); # endif } - retro_tapping_counter = 0; + retro_tap_primed = false; } +# if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) + retro_tap_next_mods = curr_mods; +# endif } } # endif diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c index 8f238490f2..e42a98554d 100644 --- a/quantum/action_tapping.c +++ b/quantum/action_tapping.c @@ -49,6 +49,45 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re } # endif +# if defined(CHORDAL_HOLD) +extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM; + +# define REGISTERED_TAPS_SIZE 8 +// Array of tap-hold keys that have been settled as tapped but not yet released. +static keypos_t registered_taps[REGISTERED_TAPS_SIZE] = {}; +static uint8_t num_registered_taps = 0; + +/** Adds `key` to the registered_taps array. */ +static void registered_taps_add(keypos_t key); +/** Returns the index of `key` in registered_taps, or -1 if not found. */ +static int8_t registered_tap_find(keypos_t key); +/** Removes index `i` from the registered_taps array. */ +static void registered_taps_del_index(uint8_t i); +/** Logs the registered_taps array for debugging. */ +static void debug_registered_taps(void); + +/** \brief Finds which queued events should be held according to Chordal Hold. + * + * In a situation with multiple unsettled tap-hold key presses, scan the queue + * up until the first release, non-tap-hold, or one-shot event and find the + * latest event in the queue that settles as held according to + * get_chordal_hold(). + * + * \return Index of the first tap, or equivalently, one past the latest hold. + */ +static uint8_t waiting_buffer_find_chordal_hold_tap(void); + +/** Processes queued events up to and including `key` as tapped. */ +static void waiting_buffer_chordal_hold_taps_until(keypos_t key); + +/** \brief Processes and pops buffered events until the first tap-hold event. */ +static void waiting_buffer_process_regular(void); + +static bool is_mt_or_lt(uint16_t keycode) { + return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode); +} +# endif // CHORDAL_HOLD + # ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; @@ -166,6 +205,20 @@ void action_tapping_process(keyrecord_t record) { bool process_tapping(keyrecord_t *keyp) { const keyevent_t event = keyp->event; +# if defined(CHORDAL_HOLD) + if (!event.pressed) { + const int8_t i = registered_tap_find(event.key); + if (i != -1) { + // If a tap-hold key was previously settled as tapped, set its + // tap.count correspondingly on release. + keyp->tap.count = 1; + registered_taps_del_index(i); + ac_dprintf("Found tap release for [%d]\n", i); + debug_registered_taps(); + } + } +# endif // CHORDAL_HOLD + // state machine is in the "reset" state, no tapping key is to be // processed if (IS_NOEVENT(tapping_key.event)) { @@ -188,7 +241,7 @@ bool process_tapping(keyrecord_t *keyp) { return true; } -# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(CHORDAL_HOLD) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) TAP_DEFINE_KEYCODE; # endif @@ -199,6 +252,7 @@ bool process_tapping(keyrecord_t *keyp) { // early return for tick events return true; } + if (tapping_key.tap.count == 0) { if (IS_TAPPING_RECORD(keyp) && !event.pressed) { // first tap! @@ -212,6 +266,25 @@ bool process_tapping(keyrecord_t *keyp) { // enqueue return false; } +# if defined(CHORDAL_HOLD) + else if (is_mt_or_lt(tapping_keycode) && !event.pressed && waiting_buffer_typed(event) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) { + // Key release that is not a chord with the tapping key. + // Settle the tapping key and any other pending tap-hold + // keys preceding the press of this key as tapped. + + ac_dprintf("Tapping: End. Chord considered a tap\n"); + tapping_key.tap.count = 1; + registered_taps_add(tapping_key.event.key); + process_record(&tapping_key); + tapping_key = (keyrecord_t){0}; + + waiting_buffer_chordal_hold_taps_until(event.key); + debug_registered_taps(); + debug_waiting_buffer(); + // enqueue + return false; + } +# endif // CHORDAL_HOLD /* Process a key typed within TAPPING_TERM * This can register the key before settlement of tapping, * useful for long TAPPING_TERM but may prevent fast typing. @@ -229,6 +302,22 @@ bool process_tapping(keyrecord_t *keyp) { // clang-format on ac_dprintf("Tapping: End. No tap. Interfered by typing key\n"); process_record(&tapping_key); + +# if defined(CHORDAL_HOLD) + uint8_t first_tap = waiting_buffer_find_chordal_hold_tap(); + ac_dprintf("first_tap = %u\n", first_tap); + if (first_tap < WAITING_BUFFER_SIZE) { + for (; waiting_buffer_tail != first_tap; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { + ac_dprintf("Processing [%u]\n", waiting_buffer_tail); + process_record(&waiting_buffer[waiting_buffer_tail]); + } + } + + waiting_buffer_chordal_hold_taps_until(event.key); + debug_registered_taps(); + debug_waiting_buffer(); +# endif // CHORDAL_HOLD + tapping_key = (keyrecord_t){0}; debug_tapping_key(); // enqueue @@ -237,6 +326,19 @@ bool process_tapping(keyrecord_t *keyp) { /* Process release event of a key pressed before tapping starts * Without this unexpected repeating will occur with having fast repeating setting * https://github.com/tmk/tmk_keyboard/issues/60 + * + * NOTE: This workaround causes events to process out of order, + * e.g. in a rolled press of three tap-hold keys like + * + * "A down, B down, C down, A up, B up, C up" + * + * events are processed as + * + * "A down, B down, A up, B up, C down, C up" + * + * It seems incorrect to process keyp before the tapping key. + * This workaround is old, from 2013. This might no longer + * be needed for the original problem it was meant to address. */ else if (!event.pressed && !waiting_buffer_typed(event)) { // Modifier/Layer should be retained till end of this tapping. @@ -271,19 +373,52 @@ bool process_tapping(keyrecord_t *keyp) { // set interrupted flag when other key pressed during tapping if (event.pressed) { tapping_key.tap.interrupted = true; - if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS + +# if defined(CHORDAL_HOLD) + if (is_mt_or_lt(tapping_keycode) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) { + // In process_action(), HOLD_ON_OTHER_KEY_PRESS + // will revert interrupted events to holds, so + // this needs to be set false. + tapping_key.tap.interrupted = false; + + if (!is_tap_record(keyp)) { + ac_dprintf("Tapping: End. Chord considered a tap\n"); + tapping_key.tap.count = 1; + registered_taps_add(tapping_key.event.key); + debug_registered_taps(); + process_record(&tapping_key); + tapping_key = (keyrecord_t){0}; + } + } else +# endif // CHORDAL_HOLD + if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) - // Auto Shift cannot evaluate this early - // Retro Shift uses the hold action for all nested taps even without HOLD_ON_OTHER_KEY_PRESS, so this is fine to skip - && !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp)) + // Auto Shift cannot evaluate this early + // Retro Shift uses the hold action for all nested taps even without HOLD_ON_OTHER_KEY_PRESS, so this is fine to skip + && !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp)) # endif - ) { + ) { + // Settle the tapping key as *held*, since + // HOLD_ON_OTHER_KEY_PRESS is enabled for this key. ac_dprintf("Tapping: End. No tap. Interfered by pressed key\n"); process_record(&tapping_key); - tapping_key = (keyrecord_t){0}; + +# if defined(CHORDAL_HOLD) + if (waiting_buffer_tail != waiting_buffer_head && is_tap_record(&waiting_buffer[waiting_buffer_tail])) { + tapping_key = waiting_buffer[waiting_buffer_tail]; + // Pop tail from the queue. + waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE; + debug_waiting_buffer(); + } else +# endif // CHORDAL_HOLD + { + tapping_key = (keyrecord_t){0}; + } debug_tapping_key(); - // enqueue - return false; + +# if defined(CHORDAL_HOLD) + waiting_buffer_process_regular(); +# endif // CHORDAL_HOLD } } // enqueue @@ -520,26 +655,125 @@ void waiting_buffer_scan_tap(void) { } } -/** \brief Tapping key debug print - * - * FIXME: Needs docs - */ +# ifdef CHORDAL_HOLD +__attribute__((weak)) bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record) { + return get_chordal_hold_default(tap_hold_record, other_record); +} + +bool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record) { + if (tap_hold_record->event.type != KEY_EVENT || other_record->event.type != KEY_EVENT) { + return true; // Return true on combos or other non-key events. + } + + char tap_hold_hand = chordal_hold_handedness(tap_hold_record->event.key); + if (tap_hold_hand == '*') { + return true; + } + char other_hand = chordal_hold_handedness(other_record->event.key); + return other_hand == '*' || tap_hold_hand != other_hand; +} + +__attribute__((weak)) char chordal_hold_handedness(keypos_t key) { + return (char)pgm_read_byte(&chordal_hold_layout[key.row][key.col]); +} + +static void registered_taps_add(keypos_t key) { + if (num_registered_taps >= REGISTERED_TAPS_SIZE) { + ac_dprintf("TAPS OVERFLOW: CLEAR ALL STATES\n"); + clear_keyboard(); + num_registered_taps = 0; + } + + registered_taps[num_registered_taps] = key; + ++num_registered_taps; +} + +static int8_t registered_tap_find(keypos_t key) { + for (int8_t i = 0; i < num_registered_taps; ++i) { + if (KEYEQ(registered_taps[i], key)) { + return i; + } + } + return -1; +} + +static void registered_taps_del_index(uint8_t i) { + if (i < num_registered_taps) { + --num_registered_taps; + if (i < num_registered_taps) { + registered_taps[i] = registered_taps[num_registered_taps]; + } + } +} + +static void debug_registered_taps(void) { + ac_dprintf("registered_taps = { "); + for (int8_t i = 0; i < num_registered_taps; ++i) { + ac_dprintf("%02X%02X ", registered_taps[i].row, registered_taps[i].col); + } + ac_dprintf("}\n"); +} + +static uint8_t waiting_buffer_find_chordal_hold_tap(void) { + keyrecord_t *prev = &tapping_key; + uint16_t prev_keycode = get_record_keycode(&tapping_key, false); + uint8_t first_tap = WAITING_BUFFER_SIZE; + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + keyrecord_t * cur = &waiting_buffer[i]; + const uint16_t cur_keycode = get_record_keycode(cur, false); + if (!cur->event.pressed || !is_mt_or_lt(prev_keycode)) { + break; + } else if (get_chordal_hold(prev_keycode, prev, cur_keycode, cur)) { + first_tap = i; // Track one index past the latest hold. + } + prev = cur; + prev_keycode = cur_keycode; + } + return first_tap; +} + +static void waiting_buffer_chordal_hold_taps_until(keypos_t key) { + while (waiting_buffer_tail != waiting_buffer_head) { + keyrecord_t *record = &waiting_buffer[waiting_buffer_tail]; + ac_dprintf("waiting_buffer_chordal_hold_taps_until: processing [%u]\n", waiting_buffer_tail); + if (record->event.pressed && is_tap_record(record)) { + record->tap.count = 1; + registered_taps_add(record->event.key); + } + process_record(record); + waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE; + + if (KEYEQ(key, record->event.key) && record->event.pressed) { + break; + } + } +} + +static void waiting_buffer_process_regular(void) { + for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { + if (is_tap_record(&waiting_buffer[waiting_buffer_tail])) { + break; // Stop once a tap-hold key event is reached. + } + ac_dprintf("waiting_buffer_process_regular: processing [%u]\n", waiting_buffer_tail); + process_record(&waiting_buffer[waiting_buffer_tail]); + } + debug_waiting_buffer(); +} +# endif // CHORDAL_HOLD + +/** \brief Logs tapping key if ACTION_DEBUG is enabled. */ static void debug_tapping_key(void) { ac_dprintf("TAPPING_KEY="); debug_record(tapping_key); ac_dprintf("\n"); } -/** \brief Waiting buffer debug print - * - * FIXME: Needs docs - */ +/** \brief Logs waiting buffer if ACTION_DEBUG is enabled. */ static void debug_waiting_buffer(void) { - ac_dprintf("{ "); + ac_dprintf("{"); for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - ac_dprintf("[%u]=", i); + ac_dprintf(" [%u]=", i); debug_record(waiting_buffer[i]); - ac_dprintf(" "); } ac_dprintf("}\n"); } diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h index 6b518b8298..c3c7b999ec 100644 --- a/quantum/action_tapping.h +++ b/quantum/action_tapping.h @@ -46,6 +46,71 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record); bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record); +#ifdef CHORDAL_HOLD +/** + * Callback to say when a key chord before the tapping term may be held. + * + * In keymap.c, define the callback + * + * bool get_chordal_hold(uint16_t tap_hold_keycode, + * keyrecord_t* tap_hold_record, + * uint16_t other_keycode, + * keyrecord_t* other_record) { + * // Conditions... + * } + * + * This callback is called when: + * + * 1. `tap_hold_keycode` is pressed. + * 2. `other_keycode` is pressed while `tap_hold_keycode` is still held, + * provided `other_keycode` is *not* also a tap-hold key and it is pressed + * before the tapping term. + * + * If false is returned, this has the effect of immediately settling the + * tap-hold key as tapped. If true is returned, the tap-hold key is still + * unsettled, and may be settled as held depending on configuration and + * subsequent events. + * + * @param tap_hold_keycode Keycode of the tap-hold key. + * @param tap_hold_record Record from the tap-hold press event. + * @param other_keycode Keycode of the other key. + * @param other_record Record from the other key's press event. + * @return True if the tap-hold key may be considered held; false if tapped. + */ +bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record); + +/** + * Default "opposite hands rule" for whether a key chord may settle as held. + * + * This function returns true when the tap-hold key and other key are on + * "opposite hands." In detail, handedness of the two keys are compared. If + * handedness values differ, or if either handedness is '*', the function + * returns true, indicating that it may be held. Otherwise, it returns false, + * in which case the tap-hold key is immediately settled at tapped. + * + * @param tap_hold_record Record of the active tap-hold key press. + * @param other_record Record of the other, interrupting key press. + * @return True if the tap-hold key may be considered held; false if tapped. + */ +bool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record); + +/** + * Gets the handedness of a key. + * + * This function returns: + * 'L' for keys pressed by the left hand, + * 'R' for keys on the right hand, + * '*' for keys exempt from the "opposite hands rule." This could be used + * perhaps on thumb keys or keys that might be pressed by either hand. + * + * @param key A key matrix position. + * @return Handedness value. + */ +char chordal_hold_handedness(keypos_t key); + +extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM; +#endif + #ifdef DYNAMIC_TAPPING_TERM_ENABLE extern uint16_t g_tapping_term; #endif diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c index b2611c5f09..2cc3c2d661 100644 --- a/quantum/audio/audio.c +++ b/quantum/audio/audio.c @@ -258,11 +258,10 @@ void audio_stop_tone(float pitch) { for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) { found = (tones[i].pitch == pitch); if (found) { - tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) { - tones[j] = tones[j + 1]; - tones[j + 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; + tones[j] = tones[j + 1]; } + tones[AUDIO_TONE_STACKSIZE - 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; break; } } diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk index bbc362d4c7..d38b1cd342 100644 --- a/quantum/debounce/tests/rules.mk +++ b/quantum/debounce/tests/rules.mk @@ -16,6 +16,7 @@ DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5 DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \ + $(PLATFORM_PATH)/timer.c \ $(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c debounce_none_DEFS := $(DEBOUNCE_COMMON_DEFS) diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 3c22bbd445..beb7f9d18f 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -245,6 +245,17 @@ void dynamic_keymap_macro_set_buffer(uint16_t offset, uint16_t size, uint8_t *da } } +typedef struct send_string_eeprom_state_t { + const uint8_t *ptr; +} send_string_eeprom_state_t; + +char send_string_get_next_eeprom(void *arg) { + send_string_eeprom_state_t *state = (send_string_eeprom_state_t *)arg; + char ret = eeprom_read_byte(state->ptr); + state->ptr++; + return ret; +} + void dynamic_keymap_macro_reset(void) { void *p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR); void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE); @@ -284,57 +295,6 @@ void dynamic_keymap_macro_send(uint8_t id) { ++p; } - // Send the macro string by making a temporary string. - char data[8] = {0}; - // We already checked there was a null at the end of - // the buffer, so this cannot go past the end - while (1) { - data[0] = eeprom_read_byte(p++); - data[1] = 0; - // Stop at the null terminator of this macro string - if (data[0] == 0) { - break; - } - if (data[0] == SS_QMK_PREFIX) { - // Get the code - data[1] = eeprom_read_byte(p++); - // Unexpected null, abort. - if (data[1] == 0) { - return; - } - if (data[1] == SS_TAP_CODE || data[1] == SS_DOWN_CODE || data[1] == SS_UP_CODE) { - // Get the keycode - data[2] = eeprom_read_byte(p++); - // Unexpected null, abort. - if (data[2] == 0) { - return; - } - // Null terminate - data[3] = 0; - } else if (data[1] == SS_DELAY_CODE) { - // Get the number and '|' - // At most this is 4 digits plus '|' - uint8_t i = 2; - while (1) { - data[i] = eeprom_read_byte(p++); - // Unexpected null, abort - if (data[i] == 0) { - return; - } - // Found '|', send it - if (data[i] == '|') { - data[i + 1] = 0; - break; - } - // If haven't found '|' by i==6 then - // number too big, abort - if (i == 6) { - return; - } - ++i; - } - } - } - send_string_with_delay(data, DYNAMIC_KEYMAP_MACRO_DELAY); - } + send_string_eeprom_state_t state = {p}; + send_string_with_delay_impl(send_string_get_next_eeprom, &state, DYNAMIC_KEYMAP_MACRO_DELAY); } diff --git a/quantum/keymap_extras/keymap_eurkey.h b/quantum/keymap_extras/keymap_eurkey.h new file mode 100644 index 0000000000..20f7f58683 --- /dev/null +++ b/quantum/keymap_extras/keymap_eurkey.h @@ -0,0 +1,166 @@ +// Copyright 2025 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ + +#pragma once +#include "keycodes.h" +// clang-format off + +// Aliases +#define EU_GRV KC_GRV // ` +#define EU_1 KC_1 // 1 +#define EU_2 KC_2 // 2 +#define EU_3 KC_3 // 3 +#define EU_4 KC_4 // 4 +#define EU_5 KC_5 // 5 +#define EU_6 KC_6 // 6 +#define EU_7 KC_7 // 7 +#define EU_8 KC_8 // 8 +#define EU_9 KC_9 // 9 +#define EU_0 KC_0 // 0 +#define EU_MINS KC_MINS // - +#define EU_EQL KC_EQL // = +#define EU_Q KC_Q // Q +#define EU_W KC_W // W +#define EU_E KC_E // E +#define EU_R KC_R // R +#define EU_T KC_T // T +#define EU_Y KC_Y // Y +#define EU_U KC_U // U +#define EU_I KC_I // I +#define EU_O KC_O // O +#define EU_P KC_P // P +#define EU_LBRC KC_LBRC // [ +#define EU_RBRC KC_RBRC // ] +#define EU_BSLS KC_BSLS // (backslash) +#define EU_A KC_A // A +#define EU_S KC_S // S +#define EU_D KC_D // D +#define EU_F KC_F // F +#define EU_G KC_G // G +#define EU_H KC_H // H +#define EU_J KC_J // J +#define EU_K KC_K // K +#define EU_L KC_L // L +#define EU_SCLN KC_SCLN // ; +#define EU_QUOT KC_QUOT // ' +#define EU_Z KC_Z // Z +#define EU_X KC_X // X +#define EU_C KC_C // C +#define EU_V KC_V // V +#define EU_B KC_B // B +#define EU_N KC_N // N +#define EU_M KC_M // M +#define EU_COMM KC_COMM // , +#define EU_DOT KC_DOT // . +#define EU_SLSH KC_SLSH // / +#define EU_TILD S(EU_GRV) // ~ +#define EU_EXLM S(EU_1) // ! +#define EU_AT S(EU_2) // @ +#define EU_HASH S(EU_3) // # +#define EU_DLR S(EU_4) // $ +#define EU_PERC S(EU_5) // % +#define EU_CIRC S(EU_6) // ^ +#define EU_AMPR S(EU_7) // & +#define EU_ASTR S(EU_8) // * +#define EU_LPRN S(EU_9) // ( +#define EU_RPRN S(EU_0) // ) +#define EU_UNDS S(EU_MINS) // _ +#define EU_PLUS S(EU_EQL) // + +#define EU_LCBR S(EU_LBRC) // { +#define EU_RCBR S(EU_RBRC) // } +#define EU_PIPE S(EU_BSLS) // | +#define EU_COLN S(EU_SCLN) // : +#define EU_DQUO S(EU_QUOT) // " +#define EU_LABK S(EU_COMM) // < +#define EU_RABK S(EU_DOT) // > +#define EU_QUES S(EU_SLSH) // ? +#define EU_DGRV ALGR(EU_GRV) // ` (dead) +#define EU_IEXL ALGR(EU_1) // ¡ +#define EU_FORD ALGR(EU_2) // ª +#define EU_MORD ALGR(EU_3) // º +#define EU_PND ALGR(EU_4) // £ +#define EU_EURO ALGR(EU_5) // € +#define EU_DCIR ALGR(EU_6) // ^ (dead) +#define EU_RNGA ALGR(EU_7) // ˚ (dead) +#define EU_DLQU ALGR(EU_8) // „ +#define EU_LDQU ALGR(EU_9) // “ +#define EU_RDQU ALGR(EU_0) // ” +#define EU_NDSH ALGR(EU_MINS) // – +#define EU_MUL ALGR(EU_EQL) // × +#define EU_AE ALGR(EU_Q) // æ +#define EU_ARNG ALGR(EU_W) // Å +#define EU_EDIA ALGR(EU_E) // Ë +#define EU_YACU ALGR(EU_R) // Ý +#define EU_THRN ALGR(EU_T) // Þ +#define EU_YDIA ALGR(EU_Y) // Ÿ +#define EU_UDIA ALGR(EU_U) // Ü +#define EU_IDIA ALGR(EU_I) // Ï +#define EU_ODIA ALGR(EU_O) // Ö +#define EU_OE ALGR(EU_P) // Œ +#define EU_LDAQ ALGR(EU_LBRC) // « +#define EU_RDAQ ALGR(EU_RBRC) // » +#define EU_NOT ALGR(EU_BSLS) // ¬ +#define EU_ADIA ALGR(EU_A) // Ä +#define EU_SS ALGR(EU_S) // ß +#define EU_ETH ALGR(EU_D) // Ð +#define EU_EGRV ALGR(EU_F) // È +#define EU_EACU ALGR(EU_G) // É +#define EU_UGRV ALGR(EU_H) // Ù +#define EU_UACU ALGR(EU_J) // Ú +#define EU_IJ ALGR(EU_K) // IJ +#define EU_OSTR ALGR(EU_L) // Ø +#define EU_DEG ALGR(EU_SCLN) // ° +#define EU_ACUT ALGR(EU_QUOT) // ´ (dead) +#define EU_AGRV ALGR(EU_Z) // À +#define EU_AACU ALGR(EU_X) // Á +#define EU_CCED ALGR(EU_C) // Ç +#define EU_IGRV ALGR(EU_V) // Ì +#define EU_IACU ALGR(EU_B) // Í +#define EU_NTIL ALGR(EU_N) // Ñ +#define EU_DGRK ALGR(EU_M) // μ (dead Greek key) +#define EU_OGRV ALGR(EU_COMM) // Ò +#define EU_OACU ALGR(EU_DOT) // Ó +#define EU_IQUE ALGR(EU_SLSH) // ¿ +#define EU_DTIL ALGR(EU_TILD) // ~ (dead) +#define EU_SUP1 S(ALGR(EU_1)) // ¹ +#define EU_SUP2 S(ALGR(EU_2)) // ² +#define EU_SUP3 S(ALGR(EU_3)) // ³ +#define EU_YEN ALGR(EU_DLR) // ¥ +#define EU_CENT S(EU_EURO) // ¢ +#define EU_CARN S(EU_DCIR) // ˇ (dead) +#define EU_MACR S(ALGR(EU_7)) // ¯ (dead) +#define EU_SLQU S(EU_DLQU) // ‚ +#define EU_LSQU S(EU_LDQU) // ‘ +#define EU_RSQU S(EU_RDQU) // ’ +#define EU_MDSH S(EU_NDSH) // — +#define EU_DIV S(EU_MUL) // ÷ +#define EU_LSAQ S(EU_LDAQ) // ‹ +#define EU_RSAQ S(EU_RDAQ) // › +#define EU_BRKP S(ALGR(EU_BSLS)) // ¦ +#define EU_SECT S(ALGR(EU_S)) // § +#define EU_MDDT S(ALGR(EU_SCLN)) // · +#define EU_DIAE ALGR(EU_DQUO) // ¨ (dead) +#define EU_ELLP ALGR(EU_QUES) // … + diff --git a/quantum/keymap_extras/keymap_farsi.h b/quantum/keymap_extras/keymap_farsi.h index 8e16188a8f..4f34b08d92 100644 --- a/quantum/keymap_extras/keymap_farsi.h +++ b/quantum/keymap_extras/keymap_farsi.h @@ -150,7 +150,7 @@ #define FA_DQT ALGR(FA_GAF) // " #define FA_MINA ALGR(FA_BSLS) // - #define FA_PIPE ALGR(FA_ZA) // | -#define FA_SUBA ALGR(FA_RA) // ٖ +#define FA_SUBA ALGR(FA_RE) // ٖ #define FA_HMZB ALGR(FA_DAL) // ء #define FA_ELLP ALGR(FA_PE) // … #define FA_COMM ALGR(FA_WAW) // , diff --git a/quantum/leader.c b/quantum/leader.c index 272609ad0c..23e5e8cd6d 100644 --- a/quantum/leader.c +++ b/quantum/leader.c @@ -21,6 +21,10 @@ __attribute__((weak)) void leader_start_user(void) {} __attribute__((weak)) void leader_end_user(void) {} +__attribute__((weak)) bool leader_add_user(uint16_t keycode) { + return false; +} + void leader_start(void) { if (leading) { return; @@ -61,6 +65,9 @@ bool leader_sequence_add(uint16_t keycode) { leader_sequence[leader_sequence_size] = keycode; leader_sequence_size++; + if (leader_add_user(keycode)) { + leader_end(); + } return true; } diff --git a/quantum/leader.h b/quantum/leader.h index 3177fcd196..fba6b287ba 100644 --- a/quantum/leader.h +++ b/quantum/leader.h @@ -22,6 +22,15 @@ void leader_start_user(void); void leader_end_user(void); /** + * \brief User callback, invoked when a keycode is added to the leader sequence. + * + * \param keycode The keycode added to the leader sequence. + * + * \return `true` to finish the key sequence, `false` to continue. + */ +bool leader_add_user(uint16_t keycode); + +/** * Begin the leader sequence, resetting the buffer and timer. */ void leader_start(void); diff --git a/quantum/os_detection/tests/rules.mk b/quantum/os_detection/tests/rules.mk index 1b69b71ba9..2f31f5e391 100644 --- a/quantum/os_detection/tests/rules.mk +++ b/quantum/os_detection/tests/rules.mk @@ -4,4 +4,5 @@ os_detection_DEFS += -DOS_DETECTION_DEBOUNCE=50 os_detection_SRC := \ $(QUANTUM_PATH)/os_detection/tests/os_detection.cpp \ $(QUANTUM_PATH)/os_detection.c \ + $(PLATFORM_PATH)/timer.c \ $(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c diff --git a/quantum/painter/qgf.c b/quantum/painter/qgf.c index bc2df94933..07c3f80314 100644 --- a/quantum/painter/qgf.c +++ b/quantum/painter/qgf.c @@ -255,10 +255,10 @@ bool qgf_validate_stream(qp_stream_t *stream) { // Read and validate all the frames (automatically validates the frame offset descriptor in the process) for (uint16_t i = 0; i < frame_count; ++i) { // Validate the frame descriptor block - uint8_t bpp; - bool has_palette; - bool is_panel_native; - bool has_delta; + uint8_t bpp = 0; + bool has_palette = false; + bool is_panel_native = false; + bool has_delta = false; if (!qgf_validate_frame_descriptor(stream, i, &bpp, &has_palette, &is_panel_native, &has_delta)) { return false; } diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h index 3dc77b42cf..f7fdb02789 100644 --- a/quantum/painter/qp.h +++ b/quantum/painter/qp.h @@ -557,6 +557,12 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai # define SH1106_NUM_DEVICES 0 #endif // QUANTUM_PAINTER_SH1106_ENABLE +#ifdef QUANTUM_PAINTER_SH1107_ENABLE +# include "qp_sh1107.h" +#else // QUANTUM_PAINTER_SH1107_ENABLE +# define SH1107_NUM_DEVICES 0 +#endif // QUANTUM_PAINTER_SH1107_ENABLE + #ifdef QUANTUM_PAINTER_LD7032_ENABLE # include "qp_ld7032.h" #else // QUANTUM_PAINTER_LD7032_ENABLE diff --git a/quantum/painter/qp_draw_image.c b/quantum/painter/qp_draw_image.c index 18fa38cb19..a225039af4 100644 --- a/quantum/painter/qp_draw_image.c +++ b/quantum/painter/qp_draw_image.c @@ -318,9 +318,9 @@ static deferred_token qp_render_animation_state(animation_state_t *state, uint16 } static uint32_t animation_callback(uint32_t trigger_time, void *cb_arg) { - animation_state_t *state = (animation_state_t *)cb_arg; - uint16_t delay_ms; - bool ret = qp_render_animation_state(state, &delay_ms); + animation_state_t *state = (animation_state_t *)cb_arg; + uint16_t delay_ms = 0; + bool ret = qp_render_animation_state(state, &delay_ms); if (!ret) { // Setting the device to NULL clears the animation slot state->device = NULL; diff --git a/quantum/painter/qp_internal.c b/quantum/painter/qp_internal.c index 5097edfa07..24b881bd09 100644 --- a/quantum/painter/qp_internal.c +++ b/quantum/painter/qp_internal.c @@ -19,6 +19,7 @@ enum { + (GC9107_NUM_DEVICES) // GC9107 + (SSD1351_NUM_DEVICES) // SSD1351 + (SH1106_NUM_DEVICES) // SH1106 + + (SH1107_NUM_DEVICES) // SH1107 + (LD7032_NUM_DEVICES) // LD7032 }; diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk index b773dd091c..10c2698092 100644 --- a/quantum/painter/rules.mk +++ b/quantum/painter/rules.mk @@ -18,6 +18,8 @@ VALID_QUANTUM_PAINTER_DRIVERS := \ ssd1351_spi \ sh1106_i2c \ sh1106_spi \ + sh1107_i2c \ + sh1107_spi \ ld7032_i2c \ ld7032_spi @@ -184,6 +186,29 @@ define handle_quantum_painter_driver $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \ $(DRIVER_PATH)/painter/sh1106/qp_sh1106.c + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1107_spi) + QUANTUM_PAINTER_NEEDS_SURFACE := yes + QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes + QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes + OPT_DEFS += -DQUANTUM_PAINTER_SH1107_ENABLE -DQUANTUM_PAINTER_SH1107_SPI_ENABLE + COMMON_VPATH += \ + $(DRIVER_PATH)/painter/oled_panel \ + $(DRIVER_PATH)/painter/sh1107 + SRC += \ + $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \ + $(DRIVER_PATH)/painter/sh1107/qp_sh1107.c + + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1107_i2c) + QUANTUM_PAINTER_NEEDS_SURFACE := yes + QUANTUM_PAINTER_NEEDS_COMMS_I2C := yes + OPT_DEFS += -DQUANTUM_PAINTER_SH1107_ENABLE -DQUANTUM_PAINTER_SH1107_I2C_ENABLE + COMMON_VPATH += \ + $(DRIVER_PATH)/painter/oled_panel \ + $(DRIVER_PATH)/painter/sh1107 + SRC += \ + $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \ + $(DRIVER_PATH)/painter/sh1107/qp_sh1107.c + else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ld7032_spi) QUANTUM_PAINTER_NEEDS_SURFACE := yes QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes diff --git a/quantum/quantum.c b/quantum/quantum.c index d4ebd58e7f..b63300add8 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -329,13 +329,13 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef HAPTIC_ENABLE process_haptic(keycode, record) && #endif -#if defined(VIA_ENABLE) - process_record_via(keycode, record) && -#endif #if defined(POINTING_DEVICE_ENABLE) && defined(POINTING_DEVICE_AUTO_MOUSE_ENABLE) process_auto_mouse(keycode, record) && #endif process_record_kb(keycode, record) && +#if defined(VIA_ENABLE) + process_record_via(keycode, record) && +#endif #if defined(SECURE_ENABLE) process_secure(keycode, record) && #endif diff --git a/quantum/rgb_matrix/animations/hue_breathing_anim.h b/quantum/rgb_matrix/animations/hue_breathing_anim.h index 5316e92dbd..60d7426cfc 100644 --- a/quantum/rgb_matrix/animations/hue_breathing_anim.h +++ b/quantum/rgb_matrix/animations/hue_breathing_anim.h @@ -2,21 +2,17 @@ RGB_MATRIX_EFFECT(HUE_BREATHING) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -// Change huedelta to adjust range of hue change. 0-255. // Hue Breathing - All LED's light up +hsv_t HUE_BREATHING_math(hsv_t hsv, uint8_t i, uint8_t time) { + // Adjust delta between 0-255 to change hue range + uint8_t delta = 12; + hsv.h = hsv.h + scale8(abs8(sin8(time / 2) - 128) * 2, delta); + return hsv; +} + bool HUE_BREATHING(effect_params_t* params) { - RGB_MATRIX_USE_LIMITS(led_min, led_max); - uint8_t huedelta = 12; - hsv_t hsv = rgb_matrix_config.hsv; - uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); - hsv.h = hsv.h + scale8(abs8(sin8(time) - 128) * 2, huedelta); - rgb_t rgb = hsv_to_rgb(hsv); - for (uint8_t i = led_min; i < led_max; i++) { - RGB_MATRIX_TEST_LED_FLAGS(); - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); - } - return rgb_matrix_check_finished_leds(led_max); + return effect_runner_i(params, &HUE_BREATHING_math); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_RGB_HUE_BREATHING +#endif // ENABLE_RGB_MATRIX_HUE_BREATHING diff --git a/quantum/rgb_matrix/animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h index a682156da2..d4f79adb56 100644 --- a/quantum/rgb_matrix/animations/raindrops_anim.h +++ b/quantum/rgb_matrix/animations/raindrops_anim.h @@ -2,35 +2,42 @@ RGB_MATRIX_EFFECT(RAINDROPS) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -static void raindrops_set_color(int i, effect_params_t* params) { +static void raindrops_set_color(uint8_t i, effect_params_t* params) { if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; - hsv_t hsv = {0, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v}; + hsv_t hsv = rgb_matrix_config.hsv; // Take the shortest path between hues - int16_t deltaH = ((rgb_matrix_config.hsv.h + 180) % 360 - rgb_matrix_config.hsv.h) / 4; + int16_t deltaH = ((hsv.h + 180) % 360 - hsv.h) / 4; if (deltaH > 127) { deltaH -= 256; } else if (deltaH < -127) { deltaH += 256; } - hsv.h = rgb_matrix_config.hsv.h + (deltaH * (random8() & 0x03)); + hsv.h += (deltaH * random8_max(3)); rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } bool RAINDROPS(effect_params_t* params) { + static uint16_t index = RGB_MATRIX_LED_COUNT + 1; + + // Periodic trigger for LED change + if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0)) { + index = random8_max(RGB_MATRIX_LED_COUNT); + } + RGB_MATRIX_USE_LIMITS(led_min, led_max); - if (!params->init) { - // Change one LED every tick, make sure speed is not 0 - if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) { - raindrops_set_color(random8_max(RGB_MATRIX_LED_COUNT), params); - } - } else { - for (int i = led_min; i < led_max; i++) { + if (params->init) { + for (uint8_t i = led_min; i < led_max; i++) { raindrops_set_color(i, params); } } + // Change LED once and set index out of range till next trigger + else if (led_min <= index && index < led_max) { + raindrops_set_color(index, params); + index = RGB_MATRIX_LED_COUNT + 1; + } return rgb_matrix_check_finished_leds(led_max); } diff --git a/quantum/rgb_matrix/animations/starlight_anim.h b/quantum/rgb_matrix/animations/starlight_anim.h index 742e843b57..c4f943c5ce 100644 --- a/quantum/rgb_matrix/animations/starlight_anim.h +++ b/quantum/rgb_matrix/animations/starlight_anim.h @@ -2,7 +2,9 @@ RGB_MATRIX_EFFECT(STARLIGHT) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -void set_starlight_color(uint8_t i, effect_params_t* params) { +static void set_starlight_color(uint8_t i, effect_params_t* params) { + if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; + uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); hsv_t hsv = rgb_matrix_config.hsv; hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); @@ -11,21 +13,26 @@ void set_starlight_color(uint8_t i, effect_params_t* params) { } bool STARLIGHT(effect_params_t* params) { - if (!params->init) { - if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0) { - uint8_t rand_led = random8_max(RGB_MATRIX_LED_COUNT); - set_starlight_color(rand_led, params); - } - return false; + static uint16_t index = RGB_MATRIX_LED_COUNT + 1; + + // Periodic trigger for LED change + if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) { + index = random8_max(RGB_MATRIX_LED_COUNT); } RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - RGB_MATRIX_TEST_LED_FLAGS(); - set_starlight_color(i, params); + if (params->init) { + for (uint8_t i = led_min; i < led_max; i++) { + set_starlight_color(i, params); + } + } + // Change LED once and set index out of range till next trigger + else if (led_min <= index && index < led_max) { + set_starlight_color(index, params); + index = RGB_MATRIX_LED_COUNT + 1; } return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT
\ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT diff --git a/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h b/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h index 50ce5d6ab4..276b8c3fdf 100644 --- a/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h +++ b/quantum/rgb_matrix/animations/starlight_dual_hue_anim.h @@ -2,31 +2,38 @@ RGB_MATRIX_EFFECT(STARLIGHT_DUAL_HUE) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -void set_starlight_dual_hue_color(uint8_t i, effect_params_t* params) { +static void set_starlight_dual_hue_color(uint8_t i, effect_params_t* params) { + if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; + uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); hsv_t hsv = rgb_matrix_config.hsv; hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); - hsv.h = hsv.h + random8_max((30 + 1 - -30) + -30); + hsv.h = hsv.h + random8_max(31); rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } bool STARLIGHT_DUAL_HUE(effect_params_t* params) { - if (!params->init) { - if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0) { - uint8_t rand_led = random8_max(RGB_MATRIX_LED_COUNT); - set_starlight_dual_hue_color(rand_led, params); - } - return false; + static uint16_t index = RGB_MATRIX_LED_COUNT + 1; + + // Periodic trigger for LED change + if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) { + index = random8_max(RGB_MATRIX_LED_COUNT); } RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - RGB_MATRIX_TEST_LED_FLAGS(); - set_starlight_dual_hue_color(i, params); + if (params->init) { + for (uint8_t i = led_min; i < led_max; i++) { + set_starlight_dual_hue_color(i, params); + } + } + // Change LED once and set index out of range till next trigger + else if (led_min <= index && index < led_max) { + set_starlight_dual_hue_color(index, params); + index = RGB_MATRIX_LED_COUNT + 1; } return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE
\ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE diff --git a/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h b/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h index 6def4eea09..e063658982 100644 --- a/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h +++ b/quantum/rgb_matrix/animations/starlight_dual_sat_anim.h @@ -2,31 +2,38 @@ RGB_MATRIX_EFFECT(STARLIGHT_DUAL_SAT) # ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS -void set_starlight_dual_sat_color(uint8_t i, effect_params_t* params) { +static void set_starlight_dual_sat_color(uint8_t i, effect_params_t* params) { + if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; + uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); hsv_t hsv = rgb_matrix_config.hsv; hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); - hsv.s = hsv.s + random8_max((30 + 1 - -30) + -30); + hsv.s = hsv.s + random8_max(31); rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } bool STARLIGHT_DUAL_SAT(effect_params_t* params) { - if (!params->init) { - if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0) { - uint8_t rand_led = random8_max(RGB_MATRIX_LED_COUNT); - set_starlight_dual_sat_color(rand_led, params); - } - return false; + static uint16_t index = RGB_MATRIX_LED_COUNT + 1; + + // Periodic trigger for LED change + if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) { + index = random8_max(RGB_MATRIX_LED_COUNT); } RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - RGB_MATRIX_TEST_LED_FLAGS(); - set_starlight_dual_sat_color(i, params); + if (params->init) { + for (uint8_t i = led_min; i < led_max; i++) { + set_starlight_dual_sat_color(i, params); + } + } + // Change LED once and set index out of range till next trigger + else if (led_min <= index && index < led_max) { + set_starlight_dual_sat_color(index, params); + index = RGB_MATRIX_LED_COUNT + 1; } return rgb_matrix_check_finished_leds(led_max); } # endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT
\ No newline at end of file +#endif // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT diff --git a/quantum/send_string/send_string.c b/quantum/send_string/send_string.c index 44c5ec5ab9..602f24dabe 100644 --- a/quantum/send_string/send_string.c +++ b/quantum/send_string/send_string.c @@ -150,48 +150,65 @@ void send_string(const char *string) { send_string_with_delay(string, TAP_CODE_DELAY); } -void send_string_with_delay(const char *string, uint8_t interval) { +void send_string_with_delay_impl(char (*getter)(void *), void *arg, uint8_t interval) { while (1) { - char ascii_code = *string; + char ascii_code = getter(arg); if (!ascii_code) break; if (ascii_code == SS_QMK_PREFIX) { - ascii_code = *(++string); + ascii_code = getter(arg); if (ascii_code == SS_TAP_CODE) { // tap - uint8_t keycode = *(++string); + uint8_t keycode = getter(arg); tap_code(keycode); } else if (ascii_code == SS_DOWN_CODE) { // down - uint8_t keycode = *(++string); + uint8_t keycode = getter(arg); register_code(keycode); } else if (ascii_code == SS_UP_CODE) { // up - uint8_t keycode = *(++string); + uint8_t keycode = getter(arg); unregister_code(keycode); } else if (ascii_code == SS_DELAY_CODE) { // delay - int ms = 0; - uint8_t keycode = *(++string); + int ms = 0; + ascii_code = getter(arg); - while (isdigit(keycode)) { + while (isdigit(ascii_code)) { ms *= 10; - ms += keycode - '0'; - keycode = *(++string); + ms += ascii_code - '0'; + ascii_code = getter(arg); } wait_ms(ms); } wait_ms(interval); + + // if we had a delay that terminated with a null, we're done + if (ascii_code == 0) break; } else { send_char_with_delay(ascii_code, interval); } - - ++string; } } +typedef struct send_string_memory_state_t { + const char *string; +} send_string_memory_state_t; + +char send_string_get_next_ram(void *arg) { + send_string_memory_state_t *state = (send_string_memory_state_t *)arg; + char ret = *state->string; + state->string++; + return ret; +} + +void send_string_with_delay(const char *string, uint8_t interval) { + send_string_memory_state_t state = {string}; + send_string_with_delay_impl(send_string_get_next_ram, &state, interval); +} + void send_char(char ascii_code) { send_char_with_delay(ascii_code, TAP_CODE_DELAY); } @@ -297,42 +314,15 @@ void send_string_P(const char *string) { send_string_with_delay_P(string, TAP_CODE_DELAY); } -void send_string_with_delay_P(const char *string, uint8_t interval) { - while (1) { - char ascii_code = pgm_read_byte(string); - if (!ascii_code) break; - if (ascii_code == SS_QMK_PREFIX) { - ascii_code = pgm_read_byte(++string); - - if (ascii_code == SS_TAP_CODE) { - // tap - uint8_t keycode = pgm_read_byte(++string); - tap_code(keycode); - } else if (ascii_code == SS_DOWN_CODE) { - // down - uint8_t keycode = pgm_read_byte(++string); - register_code(keycode); - } else if (ascii_code == SS_UP_CODE) { - // up - uint8_t keycode = pgm_read_byte(++string); - unregister_code(keycode); - } else if (ascii_code == SS_DELAY_CODE) { - // delay - int ms = 0; - uint8_t keycode = pgm_read_byte(++string); - - while (isdigit(keycode)) { - ms *= 10; - ms += keycode - '0'; - keycode = pgm_read_byte(++string); - } - wait_ms(ms); - } - } else { - send_char_with_delay(ascii_code, interval); - } +char send_string_get_next_progmem(void *arg) { + send_string_memory_state_t *state = (send_string_memory_state_t *)arg; + char ret = pgm_read_byte(state->string); + state->string++; + return ret; +} - ++string; - } +void send_string_with_delay_P(const char *string, uint8_t interval) { + send_string_memory_state_t state = {string}; + send_string_with_delay_impl(send_string_get_next_progmem, &state, interval); } #endif diff --git a/quantum/send_string/send_string.h b/quantum/send_string/send_string.h index f727ec507d..4f91252075 100644 --- a/quantum/send_string/send_string.h +++ b/quantum/send_string/send_string.h @@ -161,4 +161,12 @@ void send_string_with_delay_P(const char *string, uint8_t interval); */ #define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) +/** + * \brief Actual implementation function that iterates and sends the string returned by the getter function. + * + * The getter assumes that the next byte is available to be read, and returns it. `arg` is passed in and can be whatever + * makes most sense for the getter -- each invocation of `getter` must advance its position in the source. + */ +void send_string_with_delay_impl(char (*getter)(void *), void *arg, uint8_t interval); + /** \} */ diff --git a/quantum/sequencer/tests/rules.mk b/quantum/sequencer/tests/rules.mk index 611459e060..74b680ae31 100644 --- a/quantum/sequencer/tests/rules.mk +++ b/quantum/sequencer/tests/rules.mk @@ -8,4 +8,5 @@ sequencer_SRC := \ $(QUANTUM_PATH)/sequencer/tests/midi_mock.c \ $(QUANTUM_PATH)/sequencer/tests/sequencer_tests.cpp \ $(QUANTUM_PATH)/sequencer/sequencer.c \ + $(PLATFORM_PATH)/timer.c \ $(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c |