From a1ba1efc9698eccdb385feb049eb40c10916026f Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Tue, 19 May 2026 09:32:52 +0200 Subject: [PATCH 1/8] feat: optional key lock feature --- .../firmwares/edgetx/yaml_generalsettings.cpp | 2 + companion/src/firmwares/generalsettings.h | 1 + companion/src/generaledit/generalsetup.cpp | 9 ++ companion/src/generaledit/generalsetup.h | 1 + companion/src/generaledit/generalsetup.ui | 14 ++ radio/src/datastructs_private.h | 8 +- radio/src/gui/128x64/radio_setup.cpp | 16 ++ radio/src/gui/212x64/radio_setup.cpp | 16 ++ .../colorlcd/setup_menus/key_shortcuts.cpp | 13 ++ radio/src/gui/common/stdlcd/feature_control.h | 6 + radio/src/hal/key_driver.h | 5 + radio/src/keys.cpp | 59 +++++++- radio/src/keys.h | 6 + radio/src/main.cpp | 51 +++++++ .../storage/yaml/yaml_datastructs_128x64.cpp | 2 +- .../src/storage/yaml/yaml_datastructs_f16.cpp | 3 +- .../storage/yaml/yaml_datastructs_gx12.cpp | 2 +- .../storage/yaml/yaml_datastructs_nb4p.cpp | 3 +- .../storage/yaml/yaml_datastructs_nv14.cpp | 3 +- .../storage/yaml/yaml_datastructs_pa01.cpp | 3 +- .../storage/yaml/yaml_datastructs_pl18.cpp | 3 +- .../storage/yaml/yaml_datastructs_pl18u.cpp | 3 +- .../storage/yaml/yaml_datastructs_st16.cpp | 3 +- .../src/storage/yaml/yaml_datastructs_t15.cpp | 3 +- .../storage/yaml/yaml_datastructs_t15pro.cpp | 3 +- .../src/storage/yaml/yaml_datastructs_t20.cpp | 2 +- .../src/storage/yaml/yaml_datastructs_t22.cpp | 3 +- .../storage/yaml/yaml_datastructs_tpro.cpp | 2 +- .../storage/yaml/yaml_datastructs_tx15.cpp | 3 +- .../yaml/yaml_datastructs_tx16smk3.cpp | 3 +- .../src/storage/yaml/yaml_datastructs_x10.cpp | 3 +- .../src/storage/yaml/yaml_datastructs_x9d.cpp | 3 +- .../yaml/yaml_datastructs_x9dp2019.cpp | 3 +- .../src/storage/yaml/yaml_datastructs_x9e.cpp | 3 +- .../storage/yaml/yaml_datastructs_xlite.cpp | 2 +- .../storage/yaml/yaml_datastructs_xlites.cpp | 2 +- radio/src/targets/horus/hal.h | 16 ++ radio/src/targets/stm32h7s78-dk/hal.h | 5 + radio/src/targets/t15pro/hal.h | 5 + radio/src/targets/taranis/hal.h | 23 +++ radio/src/targets/tx15/hal.h | 5 + radio/src/targets/tx16smk3/hal.h | 5 + radio/src/tests/keys_lock.cpp | 142 ++++++++++++++++++ radio/src/translations/i18n/cn.h | 4 + radio/src/translations/i18n/cz.h | 4 + radio/src/translations/i18n/da.h | 4 + radio/src/translations/i18n/de.h | 4 + radio/src/translations/i18n/en.h | 4 + radio/src/translations/i18n/es.h | 4 + radio/src/translations/i18n/fi.h | 4 + radio/src/translations/i18n/fr.h | 4 + radio/src/translations/i18n/he.h | 4 + radio/src/translations/i18n/it.h | 4 + radio/src/translations/i18n/jp.h | 4 + radio/src/translations/i18n/ko.h | 4 + radio/src/translations/i18n/nl.h | 4 + radio/src/translations/i18n/pl.h | 4 + radio/src/translations/i18n/pt.h | 4 + radio/src/translations/i18n/ru.h | 4 + radio/src/translations/i18n/se.h | 4 + radio/src/translations/i18n/tw.h | 4 + radio/src/translations/i18n/ua.h | 4 + radio/src/translations/sim_string_list.h | 4 + radio/src/translations/string_list.h | 4 + 64 files changed, 526 insertions(+), 26 deletions(-) create mode 100644 radio/src/tests/keys_lock.cpp diff --git a/companion/src/firmwares/edgetx/yaml_generalsettings.cpp b/companion/src/firmwares/edgetx/yaml_generalsettings.cpp index a085d935b3f..bbf58c7c206 100644 --- a/companion/src/firmwares/edgetx/yaml_generalsettings.cpp +++ b/companion/src/firmwares/edgetx/yaml_generalsettings.cpp @@ -399,6 +399,7 @@ Node convert::encode(const GeneralSettings& rhs) } node["oneLogPerDay"] = (int)rhs.oneLogPerDay; + node["keyLockEnabled"] = (int)rhs.keyLockEnabled; return node; } @@ -737,6 +738,7 @@ bool convert::decode(const Node& node, GeneralSettings& rhs) } node["oneLogPerDay"] >> rhs.oneLogPerDay; + node["keyLockEnabled"] >> rhs.keyLockEnabled; // override critical settings after import // TODO: for consistency move up call stack to use existing eeprom and profile conversions diff --git a/companion/src/firmwares/generalsettings.h b/companion/src/firmwares/generalsettings.h index 7ef2e901e3d..c36fefe19b3 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -458,6 +458,7 @@ class GeneralSettings { unsigned int keyShortcuts[MAX_KEYSHORTCUTS]; unsigned int qmFavorites[MAX_QMFAVOURITES]; bool oneLogPerDay; + bool keyLockEnabled; void switchConfigClear(); diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 9ba789d74f9..2efc8bb3f00 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -635,6 +635,7 @@ void GeneralSetupPanel::setValues() ui->modelQuickSelect_CB->setChecked(generalSettings.modelQuickSelect); ui->chkOneLogPerDay->setChecked(generalSettings.oneLogPerDay); + ui->chkKeyLockEnabled->setChecked(generalSettings.keyLockEnabled); if (Boards::getCapability(board, Board::HasColorLcd)) { ui->modelSelectLayout_CB->setCurrentIndex(generalSettings.modelSelectLayout); @@ -1141,3 +1142,11 @@ void GeneralSetupPanel::on_chkOneLogPerDay_stateChanged(int) emit modified(); } } + +void GeneralSetupPanel::on_chkKeyLockEnabled_stateChanged(int) +{ + if (!lock) { + generalSettings.keyLockEnabled = ui->chkKeyLockEnabled->isChecked(); + emit modified(); + } +} diff --git a/companion/src/generaledit/generalsetup.h b/companion/src/generaledit/generalsetup.h index 55b51a815a9..d0d3249122c 100644 --- a/companion/src/generaledit/generalsetup.h +++ b/companion/src/generaledit/generalsetup.h @@ -105,6 +105,7 @@ class GeneralSetupPanel : public GeneralPanel void on_pwrOffIfInactiveSB_editingFinished(); void on_chkOneLogPerDay_stateChanged(int); + void on_chkKeyLockEnabled_stateChanged(int); private: Ui::GeneralSetup *ui; diff --git a/companion/src/generaledit/generalsetup.ui b/companion/src/generaledit/generalsetup.ui index 0b1c42d9ca5..f02a75325a1 100644 --- a/companion/src/generaledit/generalsetup.ui +++ b/companion/src/generaledit/generalsetup.ui @@ -2544,6 +2544,20 @@ Mode 4: + + + + Key lock + + + + + + + + + + diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index e291ddad4ea..2e644f4d9b5 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -1161,12 +1161,14 @@ PACK(struct RadioData { NOBACKUP(uint8_t oneLogPerDay:1); #if defined(COLORLCD) - NOBACKUP(uint8_t spare:4 SKIP); + NOBACKUP(uint8_t keyLockEnabled:1); + NOBACKUP(uint8_t spare:3 SKIP); #elif LCD_W == 128 uint8_t invertLCD:1; // Invert B&W LCD display - NOBACKUP(uint8_t spare:1 SKIP); + NOBACKUP(uint8_t keyLockEnabled:1); #else - NOBACKUP(uint8_t spare:2 SKIP); + NOBACKUP(uint8_t keyLockEnabled:1); + NOBACKUP(uint8_t spare:1 SKIP); #endif NOBACKUP(uint8_t pwrOffIfInactive); diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index bea0b3509e3..2f08827f6ea 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -83,6 +83,7 @@ enum { CASE_BACKLIGHT(ITEM_RADIO_SETUP_BRIGHTNESS) CASE_CONTRAST(ITEM_RADIO_SETUP_CONTRAST) CASE_BACKLIGHT(ITEM_RADIO_SETUP_FLASH_BEEP) + CASE_KEY_LOCK(ITEM_RADIO_SETUP_KEY_LOCK) ITEM_RADIO_ONE_LOG_PER_DAY, CASE_SPLASH_PARAM(ITEM_RADIO_SETUP_DISABLE_SPLASH) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_ON_SPEED) @@ -218,6 +219,7 @@ void menuRadioSetup(event_t event) CASE_BACKLIGHT(0) CASE_CONTRAST(0) CASE_BACKLIGHT(0) + CASE_KEY_LOCK(0) 0, CASE_SPLASH_PARAM(0) CASE_PWR_BUTTON_PRESS(0) @@ -594,6 +596,20 @@ void menuRadioSetup(event_t event) break; } +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + case ITEM_RADIO_SETUP_KEY_LOCK: { + static char lbl[24]; + const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); + const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); + snprintf(lbl, sizeof(lbl), STR_KEY_LOCK_FMT, + k1 ? k1 : "?", k2 ? k2 : "?"); + lcdDrawTextAlignedLeft(y, lbl); + g_eeGeneral.keyLockEnabled = + editCheckBox(g_eeGeneral.keyLockEnabled, LCD_W - 9, y, nullptr, attr, event); + break; + } +#endif + case ITEM_RADIO_SETUP_DISABLE_SPLASH: { lcdDrawTextAlignedLeft(y, STR_SPLASHSCREEN); diff --git a/radio/src/gui/212x64/radio_setup.cpp b/radio/src/gui/212x64/radio_setup.cpp index 9fa7e1baf3f..649068ae53e 100644 --- a/radio/src/gui/212x64/radio_setup.cpp +++ b/radio/src/gui/212x64/radio_setup.cpp @@ -80,6 +80,7 @@ enum MenuRadioSetupItems { ITEM_RADIO_SETUP_CONTRAST, CASE_PCBX9E_PCBX9DP(ITEM_RADIO_SETUP_BACKLIGHT_COLOR) ITEM_RADIO_SETUP_FLASH_BEEP, + CASE_KEY_LOCK(ITEM_RADIO_SETUP_KEY_LOCK) ITEM_RADIO_ONE_LOG_PER_DAY, CASE_SPLASH_PARAM(ITEM_RADIO_SETUP_DISABLE_SPLASH) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_ON_SPEED) @@ -218,6 +219,7 @@ void menuRadioSetup(event_t event) 0, // contrast CASE_PCBX9E_PCBX9DP(0) // backlight color 0, // flash beep + CASE_KEY_LOCK(0) // key lock 0, CASE_SPLASH_PARAM(0) // disable splash CASE_PWR_BUTTON_PRESS(0) // pwr on speed @@ -577,6 +579,20 @@ void menuRadioSetup(event_t event) break; } +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + case ITEM_RADIO_SETUP_KEY_LOCK: { + static char lbl[24]; + const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); + const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); + snprintf(lbl, sizeof(lbl), STR_KEY_LOCK_FMT, + k1 ? k1 : "?", k2 ? k2 : "?"); + lcdDrawTextAlignedLeft(y, lbl); + g_eeGeneral.keyLockEnabled = + editCheckBox(g_eeGeneral.keyLockEnabled, RADIO_SETUP_2ND_COLUMN, y, nullptr, attr, event); + break; + } +#endif + case ITEM_RADIO_SETUP_DISABLE_SPLASH: lcdDrawTextAlignedLeft(y, STR_SPLASHSCREEN); if (SPLASH_NEEDED()) { diff --git a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp index 659f18a4ff5..9c8f93f0a33 100644 --- a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp +++ b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp @@ -22,6 +22,7 @@ #include "key_shortcuts.h" #include "edgetx.h" #include "pagegroup.h" +#include "toggleswitch.h" #define SET_DIRTY() storageDirty(EE_GENERAL) @@ -67,6 +68,18 @@ QMKeyShortcutsPage::QMKeyShortcutsPage(): { std::vector qmPages = QuickMenu::menuPageNames(false); +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + static char keyLockLabel[32]; + const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); + const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); + snprintf(keyLockLabel, sizeof(keyLockLabel), STR_KEY_LOCK_FMT, + k1 ? k1 : "?", k2 ? k2 : "?"); + setupLine(keyLockLabel, [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.keyLockEnabled)); + }); +#endif + setupLine(STR_SHORT_PRESS, nullptr); addKey(EVT_KEY_BREAK(KEY_SYS), qmPages, "SYS"); addKey(EVT_KEY_BREAK(KEY_MODEL), qmPages, "MDL"); diff --git a/radio/src/gui/common/stdlcd/feature_control.h b/radio/src/gui/common/stdlcd/feature_control.h index 9aeda6d4fe7..244f7c75fb6 100644 --- a/radio/src/gui/common/stdlcd/feature_control.h +++ b/radio/src/gui/common/stdlcd/feature_control.h @@ -47,6 +47,12 @@ #define CASE_BACKLIGHT(x) #endif +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) +#define CASE_KEY_LOCK(x) x, +#else +#define CASE_KEY_LOCK(x) +#endif + #if defined(RTCLOCK) #define CASE_RTCLOCK(x) x, #else diff --git a/radio/src/hal/key_driver.h b/radio/src/hal/key_driver.h index 0919f2570e6..e53a8975b2d 100644 --- a/radio/src/hal/key_driver.h +++ b/radio/src/hal/key_driver.h @@ -65,8 +65,13 @@ uint32_t readTrims(); // Init GPIO ports void keysInit(); +// returns a bit field with each supported key uint32_t keysGetSupported(); +static inline bool keyIsSupported(EnumKeys key) { + return keysGetSupported() & (1 << key); +} + uint8_t keysGetMaxKeys(); uint8_t keysGetMaxTrims(); diff --git a/radio/src/keys.cpp b/radio/src/keys.cpp index af4a93f908d..36a2fe7dce7 100644 --- a/radio/src/keys.cpp +++ b/radio/src/keys.cpp @@ -28,7 +28,7 @@ #include "hal/rotary_encoder.h" #include "dataconstants.h" -#if !defined(BOOT) && (defined(USE_HATS_AS_KEYS) || defined(PCBXLITE)) +#if !defined(BOOT) #include "edgetx.h" #endif @@ -183,6 +183,26 @@ void Key::killEvents() static Key keys[MAX_KEYS]; static Key trim_keys[MAX_TRIMS * 2]; +#if !defined(BOOT) +// Per-target key combo (from hal.h KEYS_LOCK_KEY1/KEY2) long-pressed together +// toggles a software key lock. +static bool s_keys_locked = false; +static bool s_keys_lock_notify = false; +static uint8_t s_keys_lock_combo_cnt = 0; + +bool areKeysLocked() { return s_keys_locked; } + +bool consumeKeysLockToggleEvent() +{ + if (!s_keys_lock_notify) return false; + s_keys_lock_notify = false; + return true; +} +#else +bool areKeysLocked() { return false; } +bool consumeKeysLockToggleEvent() { return false; } +#endif + /** * @brief returns true if there is an event waiting. * @@ -491,11 +511,48 @@ bool keysPollingCycle() trims_input = READ_TRIMS(); #endif +#if !defined(BOOT) && defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + // Combo keys (from hal.h) long-pressed together toggle the key lock; killing + // the per-key events while both are held prevents their individual actions + // from firing. + if (g_eeGeneral.keyLockEnabled && + keyIsSupported(KEYS_LOCK_KEY1) && keyIsSupported(KEYS_LOCK_KEY2)) { + bool both = (keys_input & (1u << KEYS_LOCK_KEY1)) && + (keys_input & (1u << KEYS_LOCK_KEY2)); + if (both) { + keys[KEYS_LOCK_KEY1].killEvents(); + keys[KEYS_LOCK_KEY2].killEvents(); + if (s_keys_lock_combo_cnt < 0xFF) s_keys_lock_combo_cnt++; + if (s_keys_lock_combo_cnt == KEY_LONG_DELAY) { + s_keys_locked = !s_keys_locked; + s_keys_lock_notify = true; + } + } else { + s_keys_lock_combo_cnt = 0; + } + } else { + // Feature disabled: never leave keys stuck locked. + s_keys_locked = false; + s_keys_lock_combo_cnt = 0; + } +#endif + for (int i = 0; i < MAX_KEYS; i++) { event_t evt = keys[i].input(keys_input & (1 << i)); if (evt) { evt = keyMapping(evt | i); +#if !defined(BOOT) + if (evt) { + if (!s_keys_locked) { + pushEvent(evt); + } else if ((evt & _MSK_KEY_FLAGS) == _MSK_KEY_FIRST) { + // Re-show the "Keys locked" toast on each new key press while locked. + s_keys_lock_notify = true; + } + } +#else if (evt) pushEvent(evt); +#endif } } diff --git a/radio/src/keys.h b/radio/src/keys.h index 44cef3af146..cb04f2447d3 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -186,6 +186,12 @@ uint8_t keysGetTrimState(uint8_t trim); bool keysPollingCycle(); bool rotaryEncoderPollingCycle(); +// Holding the per-target combo (KEYS_LOCK_KEY1 + KEYS_LOCK_KEY2 from hal.h) +// for KEY_LONG_DELAY toggles the key lock. While locked, key events are +// suppressed; trims/sticks/switches still work. +bool areKeysLocked(); +bool consumeKeysLockToggleEvent(); + #if defined(USE_HATS_AS_KEYS) void setHatsAsKeys(bool val); bool getHatsAsKeys(); diff --git a/radio/src/main.cpp b/radio/src/main.cpp index b0b2c24c5ee..8b6dbffc2ce 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -254,6 +254,54 @@ void checkHatsAsKeys() } #endif +#if !defined(COLORLCD) +// Tick count at which to clear our key-lock message. 0 = no message active. +static tmr10ms_t s_keysLockMsgUntil = 0; +static const char* s_keysLockMsg = nullptr; +#endif + +void checkKeysLock() +{ + if (consumeKeysLockToggleEvent()) { + audioKeyPress(); + const char* lockedMsg = STR_KEYS_LOCKED; +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + static char lockedBuf[40]; + const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); + const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); + snprintf(lockedBuf, sizeof(lockedBuf), STR_KEYS_LOCKED_FMT, + k1 ? k1 : "?", k2 ? k2 : "?"); + lockedMsg = lockedBuf; +#endif +#if defined(COLORLCD) + POPUP_BUBBLE(areKeysLocked() ? lockedMsg : STR_KEYS_UNLOCKED, 1500); +#else + // Can't use a modal POPUP_INFORMATION: keys may be locked, so the user + // couldn't dismiss it. Keep our own deadline and re-arm POPUP_WAIT every + // tick below — that survives other code clearing warningText (e.g. the + // GVAR display in view_main.cpp). + s_keysLockMsg = areKeysLocked() ? lockedMsg : STR_KEYS_UNLOCKED; + s_keysLockMsgUntil = get_tmr10ms() + 150; +#endif + } + +#if !defined(COLORLCD) + if (s_keysLockMsgUntil) { + if ((int32_t)(get_tmr10ms() - s_keysLockMsgUntil) >= 0) { + s_keysLockMsgUntil = 0; + // Only clear if our message is still the active one; another popup + // may have replaced it in the meantime. + if (warningText == s_keysLockMsg) CLEAR_POPUP(); + s_keysLockMsg = nullptr; + } else { + // Keep the popup fresh every tick — another path (GVAR, etc.) may + // have cleared warningText; just re-arm it. + POPUP_WAIT(s_keysLockMsg); + } + } +#endif +} + void checkStorageUpdate() { #if defined(RTC_BACKUP_RAM) && !defined(SIMU) @@ -508,6 +556,9 @@ void perMain() checkHatsAsKeys(); #endif + checkKeysLock(); + + #if defined(RTC_BACKUP_RAM) if (UNEXPECTED_SHUTDOWN()) { #if defined(COLORLCD) diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index b91a4f92b8f..4534dfb68de 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -383,7 +383,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_f16.cpp b/radio/src/storage/yaml/yaml_datastructs_f16.cpp index 81c271903ed..737ba38f92f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_f16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_f16.cpp @@ -448,7 +448,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp index 160b3258036..db48637f29c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp @@ -407,7 +407,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp index 8f0fa5a8e3e..5955ec54b07 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp @@ -438,7 +438,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 07ba8e7721e..a96e30cf133 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -445,7 +445,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp index f1fb8b77671..846201b7fba 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp @@ -471,7 +471,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index afcda6d4261..d00d18f1aa6 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -445,7 +445,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp index 619b433a945..6793a80d279 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp @@ -438,7 +438,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_st16.cpp b/radio/src/storage/yaml/yaml_datastructs_st16.cpp index 50a8fbddb2f..058aa58aa03 100644 --- a/radio/src/storage/yaml/yaml_datastructs_st16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_st16.cpp @@ -471,7 +471,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15.cpp b/radio/src/storage/yaml/yaml_datastructs_t15.cpp index f9ad5884f91..59e9418a3e7 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15.cpp @@ -456,7 +456,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp index 50a8fbddb2f..058aa58aa03 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp @@ -471,7 +471,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index 42e3775cb7f..a367e9949ae 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -392,7 +392,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_t22.cpp b/radio/src/storage/yaml/yaml_datastructs_t22.cpp index 4b51ff36f19..253887ecec5 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t22.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t22.cpp @@ -471,7 +471,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 6900f12ef57..470781ecf3d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -392,7 +392,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp index 50a8fbddb2f..058aa58aa03 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp @@ -471,7 +471,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp index 5a91f4c6ac6..c083b188376 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp @@ -472,7 +472,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index 8cc22f3eb1d..93aff2958ed 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -447,7 +447,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index b3ecaa27930..86d0afca491 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -382,7 +382,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 2 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp index b3ecaa27930..86d0afca491 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp @@ -382,7 +382,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 2 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp index 55808994c0b..5def72c269e 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -382,7 +382,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 2 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp index fcca68e690e..3d336f7c8be 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -383,7 +383,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index 6c2e3fe3507..df4bd703dec 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -388,7 +388,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), - YAML_PADDING( 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index b5d5c6258c3..4143114907a 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -1267,3 +1267,19 @@ #define LCD_PHYS_H LCD_H #define LCD_PHYS_W LCD_W #define LCD_DEPTH 16 + +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#if defined(PCBX12S) +#define KEYS_LOCK_KEY1 KEY_PAGEUP +#define KEYS_LOCK_KEY2 KEY_ENTER +#elif defined(RADIO_X10) || defined(RADIO_X10E) +#define KEYS_LOCK_KEY1 KEY_PAGEDN +#define KEYS_LOCK_KEY2 KEY_SYS +#elif defined(RADIO_T15) +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_TELE +#else +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL +#endif diff --git a/radio/src/targets/stm32h7s78-dk/hal.h b/radio/src/targets/stm32h7s78-dk/hal.h index 1b8540a0751..fc349590334 100644 --- a/radio/src/targets/stm32h7s78-dk/hal.h +++ b/radio/src/targets/stm32h7s78-dk/hal.h @@ -369,4 +369,9 @@ #define LCD_DEPTH 16 // #define LCD_CONTRAST_DEFAULT 20 +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL + #endif // _HAL_H_ diff --git a/radio/src/targets/t15pro/hal.h b/radio/src/targets/t15pro/hal.h index a8b5804a0c9..6c64dff6ce1 100644 --- a/radio/src/targets/t15pro/hal.h +++ b/radio/src/targets/t15pro/hal.h @@ -498,4 +498,9 @@ USART6: EXTMODULE_USART #define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_PAGEDN +#define KEYS_LOCK_KEY2 KEY_ENTER + #endif // _HAL_H_ diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 181ec2c3556..450b712a68b 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2962,3 +2962,26 @@ #define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) #define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn #define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler + +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#if defined(RADIO_ZORRO) || defined(RADIO_BOXER) || defined(RADIO_GX12) || \ + defined(RADIO_TX12) || defined(RADIO_TX12MK2) || defined(RADIO_MT12) || \ + defined(RADIO_POCKET) || defined(RADIO_T8) || defined(RADIO_COMMANDO8) || \ + defined(PCBXLITE) || defined(PCBXLITES) +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL +#elif defined(PCBX7) +#define KEYS_LOCK_KEY1 KEY_MENU +#define KEYS_LOCK_KEY2 KEY_ENTER +#elif defined(RADIO_T12MAX) +#define KEYS_LOCK_KEY1 KEY_PAGEDN +#define KEYS_LOCK_KEY2 KEY_ENTER +#elif defined(RADIO_FAMILY_JUMPER_T12) +// T12, TLITE, LR3PRO — only have EXIT/ENTER/UP/DOWN/LEFT/RIGHT +#define KEYS_LOCK_KEY1 KEY_ENTER +#define KEYS_LOCK_KEY2 KEY_RIGHT +#else +#define KEYS_LOCK_KEY1 KEY_MENU +#define KEYS_LOCK_KEY2 KEY_EXIT +#endif diff --git a/radio/src/targets/tx15/hal.h b/radio/src/targets/tx15/hal.h index 48fad2e6024..67f1f7cfbdc 100644 --- a/radio/src/targets/tx15/hal.h +++ b/radio/src/targets/tx15/hal.h @@ -617,4 +617,9 @@ SAI1_Block_A: PDM_CLOCK (1 MHz bit-clock output on SAI1_CK1 / PE5, !FLYSKY_GIMBA #define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL + #endif // _HAL_H_ diff --git a/radio/src/targets/tx16smk3/hal.h b/radio/src/targets/tx16smk3/hal.h index 7383be64b82..39c6444b367 100644 --- a/radio/src/targets/tx16smk3/hal.h +++ b/radio/src/targets/tx16smk3/hal.h @@ -654,4 +654,9 @@ USART6: INTMODULE_USART #define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL + #endif // _HAL_H_ diff --git a/radio/src/tests/keys_lock.cpp b/radio/src/tests/keys_lock.cpp new file mode 100644 index 00000000000..d11ba25d4a2 --- /dev/null +++ b/radio/src/tests/keys_lock.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) EdgeTX + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include "gtests.h" + +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + +// keys.cpp local; must match. +static constexpr int LOCK_POLL_DELAY = 32; +// A few extra polling cycles past the threshold to be safe against off-by-ones. +static constexpr int LOCK_POLL_HOLD = LOCK_POLL_DELAY + 4; + +static void releaseAllKeys() +{ + for (int i = 0; i < MAX_KEYS; i++) simuSetKey(i, false); + // Drain any pending events from prior tests. + while (getEvent()) {} + // Two polling cycles to let the Key debouncers see "released". + keysPollingCycle(); + keysPollingCycle(); + while (getEvent()) {} + (void)consumeKeysLockToggleEvent(); +} + +static void holdComboFor(int cycles) +{ + simuSetKey(KEYS_LOCK_KEY1, true); + simuSetKey(KEYS_LOCK_KEY2, true); + for (int i = 0; i < cycles; i++) keysPollingCycle(); +} + +class KeyLockTest : public EdgeTxTest +{ + protected: + void SetUp() override + { + EdgeTxTest::SetUp(); + g_eeGeneral.keyLockEnabled = 1; + releaseAllKeys(); + } +}; + +TEST_F(KeyLockTest, ComboTogglesLock) +{ + EXPECT_FALSE(areKeysLocked()); + + holdComboFor(LOCK_POLL_HOLD); + EXPECT_TRUE(areKeysLocked()); + EXPECT_TRUE(consumeKeysLockToggleEvent()); + EXPECT_FALSE(consumeKeysLockToggleEvent()); // consumed once + + releaseAllKeys(); + + holdComboFor(LOCK_POLL_HOLD); + EXPECT_FALSE(areKeysLocked()); + EXPECT_TRUE(consumeKeysLockToggleEvent()); +} + +TEST_F(KeyLockTest, BriefComboDoesNotToggle) +{ + // Hold for fewer cycles than the long-press threshold. + holdComboFor(LOCK_POLL_DELAY - 2); + EXPECT_FALSE(areKeysLocked()); + EXPECT_FALSE(consumeKeysLockToggleEvent()); +} + +TEST_F(KeyLockTest, DisabledFlagPreventsLocking) +{ + g_eeGeneral.keyLockEnabled = 0; + holdComboFor(LOCK_POLL_HOLD); + EXPECT_FALSE(areKeysLocked()); + EXPECT_FALSE(consumeKeysLockToggleEvent()); +} + +TEST_F(KeyLockTest, DisablingFlagWhileLockedReleasesLock) +{ + holdComboFor(LOCK_POLL_HOLD); + ASSERT_TRUE(areKeysLocked()); + (void)consumeKeysLockToggleEvent(); + releaseAllKeys(); + + // Turning the feature off must never leave the radio stuck locked. + g_eeGeneral.keyLockEnabled = 0; + keysPollingCycle(); + EXPECT_FALSE(areKeysLocked()); +} + +// Find a key not part of the lock combo to use for input-suppression tests. +static int pickProbeKey() +{ + for (int i = 0; i < MAX_KEYS; i++) { + if (i == KEYS_LOCK_KEY1 || i == KEYS_LOCK_KEY2) continue; + if (keyIsSupported((EnumKeys)i)) return i; + } + return -1; +} + +TEST_F(KeyLockTest, LockedKeyPressIsSuppressed) +{ + int probe = pickProbeKey(); + if (probe < 0) GTEST_SKIP() << "no spare key to probe with"; + + // Sanity check: while unlocked, a key press emits an event. + simuSetKey(probe, true); + keysPollingCycle(); + simuSetKey(probe, false); + keysPollingCycle(); + bool sawEvent = false; + while (event_t e = getEvent()) { (void)e; sawEvent = true; } + EXPECT_TRUE(sawEvent); + + // Lock the keys. + holdComboFor(LOCK_POLL_HOLD); + ASSERT_TRUE(areKeysLocked()); + (void)consumeKeysLockToggleEvent(); + releaseAllKeys(); + + // Now key presses must not push any events. + simuSetKey(probe, true); + keysPollingCycle(); + simuSetKey(probe, false); + keysPollingCycle(); + EXPECT_EQ(getEvent(), (event_t)0); + + // A key press while locked re-arms the toggle-event notification so the + // UI can re-show the "Keys locked" toast. + EXPECT_TRUE(consumeKeysLockToggleEvent()); +} + +#endif // KEYS_LOCK_KEY1 && KEYS_LOCK_KEY2 diff --git a/radio/src/translations/i18n/cn.h b/radio/src/translations/i18n/cn.h index b444e6e340f..cd09250998e 100644 --- a/radio/src/translations/i18n/cn.h +++ b/radio/src/translations/i18n/cn.h @@ -570,6 +570,10 @@ #define TR_KEYS_BACKLIGHT "按键背光" #define TR_BLCOLOR "颜色" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "开机图片" #define TR_PLAY_HELLO "开机语音" #define TR_PWR_ON_DELAY "开机延迟" diff --git a/radio/src/translations/i18n/cz.h b/radio/src/translations/i18n/cz.h index d0ad92f2801..6fc8ee1a8ff 100644 --- a/radio/src/translations/i18n/cz.h +++ b/radio/src/translations/i18n/cz.h @@ -573,6 +573,10 @@ #define TR_KEYS_BACKLIGHT "Podsvětlení kláves" #define TR_BLCOLOR "Barva" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN TR("úvodní logo", "Zobrazit úvodní logo") #define TR_PLAY_HELLO "Zvuk při spuštění" #define TR_PWR_ON_DELAY "Zpoždění zapnutí" diff --git a/radio/src/translations/i18n/da.h b/radio/src/translations/i18n/da.h index ddb608cdb63..24528f91f93 100644 --- a/radio/src/translations/i18n/da.h +++ b/radio/src/translations/i18n/da.h @@ -575,6 +575,10 @@ #define TR_KEYS_BACKLIGHT "Taster klarhed" #define TR_BLCOLOR "Farve" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Start skærm" #define TR_PLAY_HELLO "Startop lyd" #define TR_PWR_ON_DELAY "Forsinkelse ved tænd" diff --git a/radio/src/translations/i18n/de.h b/radio/src/translations/i18n/de.h index dded51e14af..c023c1bec1f 100644 --- a/radio/src/translations/i18n/de.h +++ b/radio/src/translations/i18n/de.h @@ -571,6 +571,10 @@ #define TR_KEYS_BACKLIGHT "Tastenbeleucht." #define TR_BLCOLOR "Farbe" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN TR("Startbild Ein", "Startbild Anzeigedauer") #define TR_PLAY_HELLO "Startton abspielen" #define TR_PWR_ON_DELAY TR("PWR EIN Verzög.", "Einschaltverzögerung") diff --git a/radio/src/translations/i18n/en.h b/radio/src/translations/i18n/en.h index 09aa435ce82..afeb36a80ad 100644 --- a/radio/src/translations/i18n/en.h +++ b/radio/src/translations/i18n/en.h @@ -571,6 +571,10 @@ #define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR "Color" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Splash screen" #define TR_PLAY_HELLO "Startup Sound" #define TR_PWR_ON_DELAY "Pwr On delay" diff --git a/radio/src/translations/i18n/es.h b/radio/src/translations/i18n/es.h index 448b57ad55e..a6442768c42 100644 --- a/radio/src/translations/i18n/es.h +++ b/radio/src/translations/i18n/es.h @@ -569,6 +569,10 @@ #define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR "Color" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Pantalla inicio" #define TR_PLAY_HELLO "Startup Sound" #define TR_PWR_ON_DELAY TR("Atraso On", "Atraso encendido") diff --git a/radio/src/translations/i18n/fi.h b/radio/src/translations/i18n/fi.h index 13fca2ab9fd..e0d38978cdc 100644 --- a/radio/src/translations/i18n/fi.h +++ b/radio/src/translations/i18n/fi.h @@ -570,6 +570,10 @@ #define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR "Color" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Splash screen" #define TR_PLAY_HELLO "Startup Sound" #define TR_PWR_ON_DELAY "Pwr On delay" diff --git a/radio/src/translations/i18n/fr.h b/radio/src/translations/i18n/fr.h index 5cbf215161b..a7ce7be96c5 100644 --- a/radio/src/translations/i18n/fr.h +++ b/radio/src/translations/i18n/fr.h @@ -574,6 +574,10 @@ #define TR_KEYS_BACKLIGHT "Rétroéclairage touches" #define TR_BLCOLOR "Couleur" #define TR_ONE_LOG_PER_DAY "Un log par jour" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Écran d'accueil" #define TR_PLAY_HELLO "Son de démarrage" #define TR_PWR_ON_DELAY "Délai btn ON" diff --git a/radio/src/translations/i18n/he.h b/radio/src/translations/i18n/he.h index 6fdf5072622..0054bc6f5ab 100644 --- a/radio/src/translations/i18n/he.h +++ b/radio/src/translations/i18n/he.h @@ -574,6 +574,10 @@ #define TR_KEYS_BACKLIGHT "תאורת לחצנים" #define TR_BLCOLOR "צבע" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "מסך פתיחה" #define TR_PLAY_HELLO "צליל אתחול" #define TR_PWR_ON_DELAY "השהיית הפעלה פעיל" diff --git a/radio/src/translations/i18n/it.h b/radio/src/translations/i18n/it.h index 4d07ce8491e..29beaf6fbff 100644 --- a/radio/src/translations/i18n/it.h +++ b/radio/src/translations/i18n/it.h @@ -571,6 +571,10 @@ #define TR_KEYS_BACKLIGHT "Luce tasti" #define TR_BLCOLOR "Colore" #define TR_ONE_LOG_PER_DAY "Un log al giorno" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN TR("Sch. avvio", "Schermata d'avvio") #define TR_PLAY_HELLO "Suono all'accensione" #define TR_PWR_ON_DELAY "Rit. accens." diff --git a/radio/src/translations/i18n/jp.h b/radio/src/translations/i18n/jp.h index e967f597b1c..19c762041fc 100644 --- a/radio/src/translations/i18n/jp.h +++ b/radio/src/translations/i18n/jp.h @@ -570,6 +570,10 @@ #define TR_KEYS_BACKLIGHT "キー バックライト" #define TR_BLCOLOR "カラー" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "起動イメージ" #define TR_PLAY_HELLO "起動時サウンド" #define TR_PWR_ON_DELAY "電源ON遅延" diff --git a/radio/src/translations/i18n/ko.h b/radio/src/translations/i18n/ko.h index c200015707e..daeded79943 100644 --- a/radio/src/translations/i18n/ko.h +++ b/radio/src/translations/i18n/ko.h @@ -580,6 +580,10 @@ #define TR_KEYS_BACKLIGHT "키 백라이트" #define TR_BLCOLOR "색상" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "시작화면" #define TR_PLAY_HELLO "시작 사운드" diff --git a/radio/src/translations/i18n/nl.h b/radio/src/translations/i18n/nl.h index 8b25d0f4ba2..6716d80e40c 100644 --- a/radio/src/translations/i18n/nl.h +++ b/radio/src/translations/i18n/nl.h @@ -572,6 +572,10 @@ #define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR "Kleur" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Startscherm Aan" #define TR_PLAY_HELLO "Startup Sound" #define TR_PWR_ON_DELAY "Pwr On delay" diff --git a/radio/src/translations/i18n/pl.h b/radio/src/translations/i18n/pl.h index abeffe3e675..f3288da5d7c 100644 --- a/radio/src/translations/i18n/pl.h +++ b/radio/src/translations/i18n/pl.h @@ -570,6 +570,10 @@ #define TR_KEYS_BACKLIGHT "Podśw. przycisków" #define TR_BLCOLOR "Kolor" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Logo ekranu" #define TR_PLAY_HELLO "Dźwięk uruchomienia" #define TR_PWR_ON_DELAY "Pwr On delay" diff --git a/radio/src/translations/i18n/pt.h b/radio/src/translations/i18n/pt.h index 4fac68f62ce..d5f597c6770 100644 --- a/radio/src/translations/i18n/pt.h +++ b/radio/src/translations/i18n/pt.h @@ -571,6 +571,10 @@ #define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR "Cor" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Tela abertura" #define TR_PLAY_HELLO "Som ao ligar" #define TR_PWR_ON_DELAY "Delay para LIGA" diff --git a/radio/src/translations/i18n/ru.h b/radio/src/translations/i18n/ru.h index fa16867fdc1..2c4003c1133 100644 --- a/radio/src/translations/i18n/ru.h +++ b/radio/src/translations/i18n/ru.h @@ -573,6 +573,10 @@ #define TR_KEYS_BACKLIGHT "Подсветка клавиш" #define TR_BLCOLOR "Цвет" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Заставка" #define TR_PLAY_HELLO "Звук запуска" #define TR_PWR_ON_DELAY "Время включ" diff --git a/radio/src/translations/i18n/se.h b/radio/src/translations/i18n/se.h index 68ee79fc6b5..beb27fd3cdc 100644 --- a/radio/src/translations/i18n/se.h +++ b/radio/src/translations/i18n/se.h @@ -573,6 +573,10 @@ #define TR_BLOFFBRIGHTNESS "Ljusstyrka av" #define TR_KEYS_BACKLIGHT "Tangentbelysning" #define TR_ONE_LOG_PER_DAY "En logg per dag" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Startbild" #define TR_PLAY_HELLO "Startljud" #define TR_PWR_ON_DELAY "Fördröj start" diff --git a/radio/src/translations/i18n/tw.h b/radio/src/translations/i18n/tw.h index 36012452a2a..29140891611 100644 --- a/radio/src/translations/i18n/tw.h +++ b/radio/src/translations/i18n/tw.h @@ -568,6 +568,10 @@ #define TR_KEYS_BACKLIGHT "按鍵背光" #define TR_BLCOLOR "顏色" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "開機圖片" #define TR_PLAY_HELLO "開機語音" #define TR_PWR_ON_DELAY "開機延遲" diff --git a/radio/src/translations/i18n/ua.h b/radio/src/translations/i18n/ua.h index 28049d395f7..f517cbe2438 100644 --- a/radio/src/translations/i18n/ua.h +++ b/radio/src/translations/i18n/ua.h @@ -572,6 +572,10 @@ #define TR_KEYS_BACKLIGHT "Яскравість кнопки" #define TR_BLCOLOR "Колір" #define TR_ONE_LOG_PER_DAY "One log per day" +#define TR_KEY_LOCK_FMT "Key lock (%s+%s hold)" +#define TR_KEYS_LOCKED "Keys locked" +#define TR_KEYS_LOCKED_FMT "Keys locked (%s+%s)" +#define TR_KEYS_UNLOCKED "Keys unlocked" #define TR_SPLASHSCREEN "Заставка" #define TR_PLAY_HELLO "Звук запуску" #define TR_PWR_ON_DELAY "Затримка увімкн." diff --git a/radio/src/translations/sim_string_list.h b/radio/src/translations/sim_string_list.h index 872abe0b443..a35b7861d8f 100644 --- a/radio/src/translations/sim_string_list.h +++ b/radio/src/translations/sim_string_list.h @@ -1165,3 +1165,7 @@ #define STR_VVARIOCENTER currentLangStrings->STR_VVARIOCENTER #define STR_XJT_ACCST_RF_PROTOCOLS currentLangStrings->STR_XJT_ACCST_RF_PROTOCOLS #define STR_ONE_LOG_PER_DAY currentLangStrings->STR_ONE_LOG_PER_DAY +#define STR_KEY_LOCK_FMT currentLangStrings->STR_KEY_LOCK_FMT +#define STR_KEYS_LOCKED currentLangStrings->STR_KEYS_LOCKED +#define STR_KEYS_LOCKED_FMT currentLangStrings->STR_KEYS_LOCKED_FMT +#define STR_KEYS_UNLOCKED currentLangStrings->STR_KEYS_UNLOCKED diff --git a/radio/src/translations/string_list.h b/radio/src/translations/string_list.h index c219893d851..3a624410ba2 100644 --- a/radio/src/translations/string_list.h +++ b/radio/src/translations/string_list.h @@ -953,6 +953,10 @@ STR(SPECTRUM_ANALYSER_EXT) STR(SPECTRUM_ANALYSER_INT) STR(SPLASHSCREEN) STR(ONE_LOG_PER_DAY) +STR(KEY_LOCK_FMT) +STR(KEYS_LOCKED) +STR(KEYS_LOCKED_FMT) +STR(KEYS_UNLOCKED) STR(SPORT_UPDATE_POWER_MODE) STR(SRC_BATT) STR(SRC_GPS) From 116f78818658f70ef429d139bd8fd134393fc93b Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Sat, 23 May 2026 13:24:03 +0200 Subject: [PATCH 2/8] chore: increase some variables --- radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp | 2 +- radio/src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp index 9c8f93f0a33..681ce65122c 100644 --- a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp +++ b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp @@ -69,7 +69,7 @@ QMKeyShortcutsPage::QMKeyShortcutsPage(): std::vector qmPages = QuickMenu::menuPageNames(false); #if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) - static char keyLockLabel[32]; + static char keyLockLabel[45]; const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); snprintf(keyLockLabel, sizeof(keyLockLabel), STR_KEY_LOCK_FMT, diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 8b6dbffc2ce..7ae1b24812e 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -266,7 +266,7 @@ void checkKeysLock() audioKeyPress(); const char* lockedMsg = STR_KEYS_LOCKED; #if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) - static char lockedBuf[40]; + static char lockedBuf[45]; const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); snprintf(lockedBuf, sizeof(lockedBuf), STR_KEYS_LOCKED_FMT, From 10eada3942fb7c6a7b07a4959c78c442663e96ca Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Sun, 24 May 2026 10:13:24 +0200 Subject: [PATCH 3/8] fix: bw oddities --- radio/src/targets/taranis/hal.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 450b712a68b..253d5c2d886 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2965,15 +2965,14 @@ // Keys held together to toggle the keyboard lock. Override either define // in this file to map the combo to a different pair on this target. -#if defined(RADIO_ZORRO) || defined(RADIO_BOXER) || defined(RADIO_GX12) || \ +#if defined(PCBXLITE) || defined(PCBXLITES) || \ + defined(RADIO_V12) || defined(RADIO_V14) +// no combo defined +#elif defined(RADIO_ZORRO) || defined(RADIO_BOXER) || defined(RADIO_GX12) || \ defined(RADIO_TX12) || defined(RADIO_TX12MK2) || defined(RADIO_MT12) || \ - defined(RADIO_POCKET) || defined(RADIO_T8) || defined(RADIO_COMMANDO8) || \ - defined(PCBXLITE) || defined(PCBXLITES) + defined(RADIO_POCKET) || defined(RADIO_T8) || defined(RADIO_COMMANDO8) #define KEYS_LOCK_KEY1 KEY_SYS #define KEYS_LOCK_KEY2 KEY_MODEL -#elif defined(PCBX7) -#define KEYS_LOCK_KEY1 KEY_MENU -#define KEYS_LOCK_KEY2 KEY_ENTER #elif defined(RADIO_T12MAX) #define KEYS_LOCK_KEY1 KEY_PAGEDN #define KEYS_LOCK_KEY2 KEY_ENTER @@ -2981,6 +2980,9 @@ // T12, TLITE, LR3PRO — only have EXIT/ENTER/UP/DOWN/LEFT/RIGHT #define KEYS_LOCK_KEY1 KEY_ENTER #define KEYS_LOCK_KEY2 KEY_RIGHT +#elif defined(PCBX7) +#define KEYS_LOCK_KEY1 KEY_MENU +#define KEYS_LOCK_KEY2 KEY_ENTER #else #define KEYS_LOCK_KEY1 KEY_MENU #define KEYS_LOCK_KEY2 KEY_EXIT From 33650b429ce25b599bc17817c4567446682cc0bb Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:11:00 +0000 Subject: [PATCH 4/8] feat: add key lock combos for c14, t22, st16, v14, v14lcd Ports the equivalent combo assignments from PR 7383 (main branch) so these targets get the key-lock feature: c14/v14/v14lcd use SYS+MODEL, t22 uses PAGEDN+ENTER, st16 uses EXIT+ENTER. V12 is left without a combo since it lacks a KEY_MENU to safely fall back to. Co-Authored-By: Claude Sonnet 5 --- radio/src/targets/c14/hal.h | 5 +++++ radio/src/targets/st16/hal.h | 5 +++++ radio/src/targets/t22/hal.h | 7 ++++++- radio/src/targets/taranis/hal.h | 6 +++--- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/radio/src/targets/c14/hal.h b/radio/src/targets/c14/hal.h index f85cfa27753..b788b6eb188 100644 --- a/radio/src/targets/c14/hal.h +++ b/radio/src/targets/c14/hal.h @@ -478,3 +478,8 @@ UART5: TELEMETRY_USART #define USE_EXTI4_IRQ #define EXTI4_IRQ_Priority 6 #endif + +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_SYS +#define KEYS_LOCK_KEY2 KEY_MODEL diff --git a/radio/src/targets/st16/hal.h b/radio/src/targets/st16/hal.h index 1448483d69f..1d6fa0f17a0 100644 --- a/radio/src/targets/st16/hal.h +++ b/radio/src/targets/st16/hal.h @@ -554,3 +554,8 @@ #define LCD_PHYS_H LCD_W #define LCD_DEPTH 16 + +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_EXIT +#define KEYS_LOCK_KEY2 KEY_ENTER diff --git a/radio/src/targets/t22/hal.h b/radio/src/targets/t22/hal.h index 376412d7ccf..d53ae0c7fc7 100644 --- a/radio/src/targets/t22/hal.h +++ b/radio/src/targets/t22/hal.h @@ -526,4 +526,9 @@ TIM17: ROTARY_ENCODER_TIMER #define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH -#endif // _HAL_H_ \ No newline at end of file +// Keys held together to toggle the keyboard lock. Override either define +// in this file to map the combo to a different pair on this target. +#define KEYS_LOCK_KEY1 KEY_PAGEDN +#define KEYS_LOCK_KEY2 KEY_ENTER + +#endif // _HAL_H_ diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 253d5c2d886..e53440a17f2 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2965,12 +2965,12 @@ // Keys held together to toggle the keyboard lock. Override either define // in this file to map the combo to a different pair on this target. -#if defined(PCBXLITE) || defined(PCBXLITES) || \ - defined(RADIO_V12) || defined(RADIO_V14) +#if defined(PCBXLITE) || defined(PCBXLITES) || defined(RADIO_V12) // no combo defined #elif defined(RADIO_ZORRO) || defined(RADIO_BOXER) || defined(RADIO_GX12) || \ defined(RADIO_TX12) || defined(RADIO_TX12MK2) || defined(RADIO_MT12) || \ - defined(RADIO_POCKET) || defined(RADIO_T8) || defined(RADIO_COMMANDO8) + defined(RADIO_POCKET) || defined(RADIO_T8) || defined(RADIO_COMMANDO8) || \ + defined(RADIO_V14) || defined(RADIO_V14LCD) #define KEYS_LOCK_KEY1 KEY_SYS #define KEYS_LOCK_KEY2 KEY_MODEL #elif defined(RADIO_T12MAX) From f68ccd6251f0782f8fa2c780cfa6b2ededae69d1 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 23 Jul 2026 06:09:43 +0000 Subject: [PATCH 5/8] fix(keys-lock): fix c14/v12 keyLockEnabled persistence, B&W label truncation, and struct dedup - yaml_datastructs_c14.cpp and yaml_datastructs_v12.cpp were missing the keyLockEnabled mapping entirely, so the setting silently failed to persist across save/load on those radios. - The B&W 128x64/212x64 key-lock menu label buffer (lbl[24]) was too small for STR_KEY_LOCK_FMT's worst-case output (up to 28 bytes for combos like PAGE<+PAGE>), causing truncation. Bumped to lbl[45], matching the ColorLCD equivalent. - Hoisted keyLockEnabled out of the per-flavor #if defined(COLORLCD)/ #elif LCD_W==128/#else block in datastructs_private.h into the shared prefix (alongside modelQuickSelect/oneLogPerDay), removing the 3x duplication that let the c14/v12 YAML omissions happen in the first place. Verified sizeof(RadioData) is unchanged for all three flavors (COLORLCD/LCD_W==128/else) via chksize.h's static_assert, and regenerated every target's yaml_datastructs via tools/generate-yaml.sh (only field order/padding shifted, no fields added or removed elsewhere). Mirrors the fixes applied to the same feature on main in EdgeTX/edgetx#7569. --- radio/src/datastructs_private.h | 4 +--- radio/src/gui/128x64/radio_setup.cpp | 2 +- radio/src/gui/212x64/radio_setup.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_128x64.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_c14.cpp | 3 ++- radio/src/storage/yaml/yaml_datastructs_gx12.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_t20.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_tpro.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_v12.cpp | 3 ++- radio/src/storage/yaml/yaml_datastructs_xlite.cpp | 2 +- radio/src/storage/yaml/yaml_datastructs_xlites.cpp | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index 2e644f4d9b5..3fa6b75692c 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -1159,15 +1159,13 @@ PACK(struct RadioData { NOBACKUP(uint8_t modelQuickSelect:1); NOBACKUP(uint8_t oneLogPerDay:1); + NOBACKUP(uint8_t keyLockEnabled:1); #if defined(COLORLCD) - NOBACKUP(uint8_t keyLockEnabled:1); NOBACKUP(uint8_t spare:3 SKIP); #elif LCD_W == 128 uint8_t invertLCD:1; // Invert B&W LCD display - NOBACKUP(uint8_t keyLockEnabled:1); #else - NOBACKUP(uint8_t keyLockEnabled:1); NOBACKUP(uint8_t spare:1 SKIP); #endif diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index 2f08827f6ea..9c71270169d 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -598,7 +598,7 @@ void menuRadioSetup(event_t event) #if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) case ITEM_RADIO_SETUP_KEY_LOCK: { - static char lbl[24]; + static char lbl[45]; const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); snprintf(lbl, sizeof(lbl), STR_KEY_LOCK_FMT, diff --git a/radio/src/gui/212x64/radio_setup.cpp b/radio/src/gui/212x64/radio_setup.cpp index 649068ae53e..b7640ef19b7 100644 --- a/radio/src/gui/212x64/radio_setup.cpp +++ b/radio/src/gui/212x64/radio_setup.cpp @@ -581,7 +581,7 @@ void menuRadioSetup(event_t event) #if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) case ITEM_RADIO_SETUP_KEY_LOCK: { - static char lbl[24]; + static char lbl[45]; const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); snprintf(lbl, sizeof(lbl), STR_KEY_LOCK_FMT, diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index 4534dfb68de..007a02f3652 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -382,8 +382,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_c14.cpp b/radio/src/storage/yaml/yaml_datastructs_c14.cpp index 634be7f7379..4732c417ee3 100644 --- a/radio/src/storage/yaml/yaml_datastructs_c14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_c14.cpp @@ -447,7 +447,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp index db48637f29c..4a3a8c029b8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp @@ -406,8 +406,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index a367e9949ae..47acf972e53 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -391,8 +391,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 470781ecf3d..07f24dd8995 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -391,8 +391,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_v12.cpp b/radio/src/storage/yaml/yaml_datastructs_v12.cpp index 97328f3dade..bfc5a885c94 100644 --- a/radio/src/storage/yaml/yaml_datastructs_v12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_v12.cpp @@ -466,7 +466,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 4 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 3 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp index 3d336f7c8be..a079cbb8632 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -382,8 +382,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index df4bd703dec..30ce8aed82b 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -387,8 +387,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; From 60061d7622df6b61f7ef1b40b77c98488366c0dc Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:08:21 +0000 Subject: [PATCH 6/8] fix(cpn): key lock checkbox overlapping PPM units row in general setup The port of #7383 placed the new key lock label/checkbox at row 30 in gridLayout_2, which was already occupied by the PPM units label/combobox. Move key lock to row 31 and shift the trailing vertical spacer to row 32. Co-Authored-By: Claude Sonnet 5 --- companion/src/generaledit/generalsetup.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/companion/src/generaledit/generalsetup.ui b/companion/src/generaledit/generalsetup.ui index f02a75325a1..c17ef1e1aac 100644 --- a/companion/src/generaledit/generalsetup.ui +++ b/companion/src/generaledit/generalsetup.ui @@ -1727,7 +1727,7 @@ p, li { white-space: pre-wrap; } - + Qt::Orientation::Vertical @@ -2544,14 +2544,14 @@ Mode 4: - + Key lock - + From 36e01af4e828c212dd3ebd7e2a090b1300535f4b Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 6 Aug 2026 02:05:18 +0000 Subject: [PATCH 7/8] fix(cpn): disable key lock checkbox on boards with no combo defined PR #7383 disabled the General Setup key-lock checkbox on boards whose hw_defs JSON has no key_lock_combo, via BoardJson::hasKeyLockCombo(). 2.12 predates the JSON hw_defs system, so this backport never gained that gating and always showed the checkbox enabled, even on boards where the firmware compiles out the feature entirely for lack of a KEYS_LOCK_KEY1/KEY2 combo: V12, XLite/XLites, PA01, and the whole PL18 family (NV14, EL18, PL18, PL18EV, PL18U, NB4P). Add Board::HasKeyLockCombo, backed by the existing IS_* board-family helpers instead of JSON, and gate the checkbox/label the same way generalsetup.cpp already gates other per-board UI elements. Co-Authored-By: Claude Sonnet 5 --- companion/src/firmwares/boards.cpp | 6 ++++++ companion/src/firmwares/boards.h | 1 + companion/src/generaledit/generalsetup.cpp | 11 ++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index a736c406649..4cf3e287403 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -396,6 +396,12 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return ((IS_FAMILY_HORUS_OR_T16(board) && getCapability(board, HasAuxSerialMode)) || IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_TX16SMK3(board)); + case HasKeyLockCombo: + return !(IS_HELLORADIOSKY_V12(board) || IS_TARANIS_XLITE(board) || + IS_FLYSKY_PA01(board) || IS_FLYSKY_NV14(board) || + IS_FLYSKY_EL18(board) || IS_FAMILY_PL18(board) || + board == Board::BOARD_FLYSKY_NB4P); + case HasLedStripGPIO: return (IS_RADIOMASTER_MT12(board) || IS_FAMILY_PL18(board) || IS_HELLORADIOSKY_V16(board)); diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 0788792a545..21490b9f914 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -237,6 +237,7 @@ namespace Board { HasInternalGPS, HasInternalModuleSupport, HasIntModuleHeartbeatGPIO, + HasKeyLockCombo, HasLedStripGPIO, HasRTC, HasSDCard, diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 2efc8bb3f00..c67aff9e680 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -635,7 +635,16 @@ void GeneralSetupPanel::setValues() ui->modelQuickSelect_CB->setChecked(generalSettings.modelQuickSelect); ui->chkOneLogPerDay->setChecked(generalSettings.oneLogPerDay); - ui->chkKeyLockEnabled->setChecked(generalSettings.keyLockEnabled); + { + bool hasCombo = Boards::getCapability(board, Board::HasKeyLockCombo); + ui->chkKeyLockEnabled->setChecked(hasCombo && generalSettings.keyLockEnabled); + ui->chkKeyLockEnabled->setEnabled(hasCombo); + ui->label_keyLockEnabled->setEnabled(hasCombo); + if (!hasCombo) { + ui->chkKeyLockEnabled->setToolTip(tr("Not available on this radio (no key combo defined)")); + ui->label_keyLockEnabled->setToolTip(tr("Not available on this radio (no key combo defined)")); + } + } if (Boards::getCapability(board, Board::HasColorLcd)) { ui->modelSelectLayout_CB->setCurrentIndex(generalSettings.modelSelectLayout); From 72409ef02aeaa94d0a4a3a71c9dcd69ba0931951 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Tue, 11 Aug 2026 08:11:50 +0000 Subject: [PATCH 8/8] fix(color): move key lock toggle to main Radio Setup screen for 2.12 The toggle was added to QMKeyShortcutsPage, but that whole page is compiled out on 2.12 (radio_setup.cpp gates its "Key Shortcuts" button behind #if VERSION_MAJOR > 2, unrelated to this feature), leaving the toggle unreachable on colorlcd targets. Move it to RadioSetupPage's main screen instead, positioned above "One log per day" to match the B&W layout. --- radio/src/gui/colorlcd/radio/radio_setup.cpp | 23 +++++++++++++++++++ .../colorlcd/setup_menus/key_shortcuts.cpp | 13 ----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/radio/src/gui/colorlcd/radio/radio_setup.cpp b/radio/src/gui/colorlcd/radio/radio_setup.cpp index 7ffb91f25ed..5b46f6d9c71 100644 --- a/radio/src/gui/colorlcd/radio/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio/radio_setup.cpp @@ -718,6 +718,25 @@ class ManageModelsSetupPage : public SubPage Window* favSelectMatch = nullptr; }; +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) +// Combo-specific label ("Lock keys (SYS+MDL)"), so built dynamically +// rather than as a SetupLineDef - can't be a plain STR_TYP entry. +static coord_t addKeyLockLine(Window* window, coord_t y, coord_t col2, PaddingSize padding) +{ + static char keyLockLabel[45]; + const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); + const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); + snprintf(keyLockLabel, sizeof(keyLockLabel), STR_KEY_LOCK_FMT, + k1 ? k1 : "?", k2 ? k2 : "?"); + Window* w = new SetupLine(window, y, col2, padding, keyLockLabel, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.keyLockEnabled)); + }); + return y + w->height() + padding; +} +#endif + static SetupLineDef setupLines[] = { { // Have only one log per day @@ -1013,5 +1032,9 @@ void RadioSetupPage::build(Window* window) }, BTN_H); y += w->height() + padding; +#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) + y = addKeyLockLine(window, y, SubPage::EDT_X, padding); +#endif + SetupLine::showLines(window, y, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); } diff --git a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp index 681ce65122c..659f18a4ff5 100644 --- a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp +++ b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp @@ -22,7 +22,6 @@ #include "key_shortcuts.h" #include "edgetx.h" #include "pagegroup.h" -#include "toggleswitch.h" #define SET_DIRTY() storageDirty(EE_GENERAL) @@ -68,18 +67,6 @@ QMKeyShortcutsPage::QMKeyShortcutsPage(): { std::vector qmPages = QuickMenu::menuPageNames(false); -#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2) - static char keyLockLabel[45]; - const char* k1 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY1); - const char* k2 = keysGetLabel((EnumKeys)KEYS_LOCK_KEY2); - snprintf(keyLockLabel, sizeof(keyLockLabel), STR_KEY_LOCK_FMT, - k1 ? k1 : "?", k2 ? k2 : "?"); - setupLine(keyLockLabel, [=](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, 0}, - GET_SET_DEFAULT(g_eeGeneral.keyLockEnabled)); - }); -#endif - setupLine(STR_SHORT_PRESS, nullptr); addKey(EVT_KEY_BREAK(KEY_SYS), qmPages, "SYS"); addKey(EVT_KEY_BREAK(KEY_MODEL), qmPages, "MDL");