Fix One Shot Modifiers getting stuck or releasing early

This commit is contained in:
Steven Wilde 2025-06-19 01:06:20 -05:00
parent 096696d86d
commit 2fce0f2ab1
5 changed files with 127 additions and 9 deletions

View File

@ -112,7 +112,7 @@ void action_exec(keyevent_t event) {
if (has_oneshot_layer_timed_out()) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
if (has_oneshot_mods_timed_out()) {
if (has_oneshot_mods_timed_out() && !get_oneshot_mods_fired()) {
clear_oneshot_mods();
}
# ifdef SWAP_HANDS_ENABLE

View File

@ -46,9 +46,13 @@ extern inline void clear_keys(void);
#ifndef NO_ACTION_ONESHOT
static uint8_t oneshot_mods = 0;
static uint8_t oneshot_locked_mods = 0;
static bool oneshot_mods_fired = false;
uint8_t get_oneshot_locked_mods(void) {
return oneshot_locked_mods;
}
bool get_oneshot_mods_fired(void) {
return oneshot_mods_fired;
}
void add_oneshot_locked_mods(uint8_t mods) {
if ((oneshot_locked_mods & mods) != mods) {
oneshot_locked_mods |= mods;
@ -260,16 +264,15 @@ static uint8_t get_mods_for_report(void) {
#ifndef NO_ACTION_ONESHOT
if (oneshot_mods) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
if (has_oneshot_mods_timed_out()) {
dprintf("Oneshot: timeout\n");
// Fire oneshots when other keys are pressed
// Release oneshots when everything else is released
if (has_anykey() || mods) {
oneshot_mods_fired = true;
} else if (oneshot_mods_fired) {
oneshot_mods_fired = false;
clear_oneshot_mods();
}
# endif
mods |= oneshot_mods;
if (has_anykey()) {
clear_oneshot_mods();
}
}
#endif

View File

@ -68,6 +68,7 @@ void clear_oneshot_mods(void);
bool has_oneshot_mods_timed_out(void);
uint8_t get_oneshot_locked_mods(void);
bool get_oneshot_mods_fired(void);
void add_oneshot_locked_mods(uint8_t mods);
void set_oneshot_locked_mods(uint8_t mods);
void clear_oneshot_locked_mods(void);

View File

@ -17,3 +17,5 @@
#pragma once
#include "test_common.h"
#define ONESHOT_TAP_TOGGLE 5

View File

@ -254,7 +254,7 @@ TEST_F(OneShot, OSMHoldNotLockingOSMs) {
/* Press and release regular key */
EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code, regular_key.report_code)).Times(1);
EXPECT_REPORT(driver, (osm_key2.report_code)).Times(1);
EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code)).Times(1);
tap_key(regular_key);
VERIFY_AND_CLEAR(driver);
@ -277,6 +277,118 @@ TEST_F(OneShot, OSMHoldNotLockingOSMs) {
VERIFY_AND_CLEAR(driver);
}
TEST_F(OneShot, OSMHoldRelease) {
TestDriver driver;
InSequence s;
KeymapKey osm_key = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};
set_keymap({osm_key});
/* Press and release OSM */
EXPECT_NO_REPORT(driver);
tap_key(osm_key);
idle_for(TAPPING_TERM);
VERIFY_AND_CLEAR(driver);
/* Press and hold OSM */
EXPECT_REPORT(driver, (osm_key.report_code)).Times(1);
osm_key.press();
run_one_scan_loop();
idle_for(TAPPING_TERM);
VERIFY_AND_CLEAR(driver);
/* Release OSM */
EXPECT_EMPTY_REPORT(driver);
osm_key.release();
run_one_scan_loop();
VERIFY_AND_CLEAR(driver);
}
TEST_F(OneShot, OSMHoldReleaseTwoOSMs) {
TestDriver driver;
InSequence s;
KeymapKey osm_key1 = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};
KeymapKey osm_key2 = KeymapKey{0, 0, 1, OSM(MOD_LCTL), KC_LCTL};
set_keymap({osm_key1, osm_key2});
/* Press and release OSM1 */
EXPECT_NO_REPORT(driver);
tap_key(osm_key1);
VERIFY_AND_CLEAR(driver);
/* Press and release OSM2 */
EXPECT_NO_REPORT(driver);
tap_key(osm_key2);
idle_for(TAPPING_TERM);
VERIFY_AND_CLEAR(driver);
/* Press and hold OSM1 */
EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code)).Times(1);
osm_key1.press();
run_one_scan_loop();
idle_for(TAPPING_TERM);
VERIFY_AND_CLEAR(driver);
/* Release OSM1 */
EXPECT_EMPTY_REPORT(driver);
osm_key1.release();
run_one_scan_loop();
VERIFY_AND_CLEAR(driver);
}
TEST_F(OneShot, OSMNonOSMMod) {
TestDriver driver;
InSequence s;
KeymapKey osm_key = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};
KeymapKey regular_mod = KeymapKey{0, 1, 0, KC_LCTL};
set_keymap({osm_key, regular_mod});
/* Press and release OSM */
EXPECT_NO_REPORT(driver);
tap_key(osm_key);
VERIFY_AND_CLEAR(driver);
/* Press regular mod */
EXPECT_REPORT(driver, (osm_key.report_code, regular_mod.report_code)).Times(1);
regular_mod.press();
run_one_scan_loop();
idle_for(TAPPING_TERM);
VERIFY_AND_CLEAR(driver);
/* Release regular mod */
EXPECT_EMPTY_REPORT(driver);
regular_mod.release();
run_one_scan_loop();
VERIFY_AND_CLEAR(driver);
}
TEST_F(OneShot, OSMLockRelease) {
TestDriver driver;
InSequence s;
KeymapKey osm_key = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};
set_keymap({osm_key});
EXPECT_NO_REPORT(driver);
for (int i = 1; i < ONESHOT_TAP_TOGGLE; i++) {
/* Press and release OSM */
tap_key(osm_key);
}
VERIFY_AND_CLEAR(driver);
/* Lock OSM */
EXPECT_REPORT(driver, (osm_key.report_code)).Times(1);
tap_key(osm_key);
VERIFY_AND_CLEAR(driver);
/* Unlock OSM */
EXPECT_NO_REPORT(driver);
tap_key(osm_key);
VERIFY_AND_CLEAR(driver);
}
TEST_F(OneShot, OSLWithAdditionalKeypress) {
TestDriver driver;
InSequence s;