diff --git a/companion/src/firmwares/boardjson.cpp b/companion/src/firmwares/boardjson.cpp index b2e62f9db..460391252 100644 --- a/companion/src/firmwares/boardjson.cpp +++ b/companion/src/firmwares/boardjson.cpp @@ -978,7 +978,7 @@ bool BoardJson::loadDefinition() if (m_board == Board::BOARD_UNKNOWN) return true; - if (!loadFile(m_board, m_hwdefn, m_inputs, m_switches, m_keys, m_trims, m_display, m_cfs, m_hardware)) + if (!loadFile(m_board, m_hwdefn, m_inputs, m_switches, m_keys, m_trims, m_display, m_cfs, m_hardware, m_hasKeyLockCombo)) return false; afterLoadFixups(m_board, m_inputs, m_switches, m_keys, m_trims); @@ -1015,7 +1015,7 @@ bool BoardJson::loadDefinition() // static bool BoardJson::loadFile(Board::Type board, QString hwdefn, InputsTable * inputs, SwitchesTable * switches, KeysTable * keys, TrimsTable * trims, DisplayDefn & display, CustomSwitchesDefn & cfs, - HardwareDefn & hardware) + HardwareDefn & hardware, bool & hasKeyLockCombo) { if (board == Board::BOARD_UNKNOWN) { return false; @@ -1202,6 +1202,9 @@ bool BoardJson::loadFile(Board::Type board, QString hwdefn, InputsTable * inputs } } + hasKeyLockCombo = obj.value("key_lock_combo").isArray() && + obj.value("key_lock_combo").toArray().size() == 2; + if (obj.value("trims").isArray()) { const QJsonArray &trms = obj.value("trims").toArray(); @@ -1315,4 +1318,4 @@ const QString BoardJson::getCapabilityStr(const Board::Capability capability) co default: return QString(); } -} \ No newline at end of file +} diff --git a/companion/src/firmwares/boardjson.h b/companion/src/firmwares/boardjson.h index de3bd74c5..051f8ae2c 100644 --- a/companion/src/firmwares/boardjson.h +++ b/companion/src/firmwares/boardjson.h @@ -167,6 +167,7 @@ class BoardJson const Board::KeyInfo getKeyInfo(int index) const; const int getKeyIndex(const QString key) const; + bool hasKeyLockCombo() const { return m_hasKeyLockCombo; } const int getSwitchIndex(const QString val, Board::LookupValueType lvt) const; const int getCFSIndexForSwitch(int sw) const; @@ -201,6 +202,7 @@ private: DisplayDefn m_display; CustomSwitchesDefn m_cfs; HardwareDefn m_hardware; + bool m_hasKeyLockCombo = false; struct InputCounts { unsigned int flexGyroAxes; @@ -226,7 +228,7 @@ private: static bool loadFile(Board::Type board, QString hwdefn, InputsTable * inputs, SwitchesTable * switches, KeysTable * keys, TrimsTable * trims, DisplayDefn & lcd, CustomSwitchesDefn & cfs, - HardwareDefn & hardware); + HardwareDefn & hardware, bool & hasKeyLockCombo); static void afterLoadFixups(Board::Type board, InputsTable * inputs, SwitchesTable * switches, KeysTable * keys, TrimsTable * trims); diff --git a/companion/src/firmwares/edgetx/yaml_generalsettings.cpp b/companion/src/firmwares/edgetx/yaml_generalsettings.cpp index 551621e5d..b148af4d1 100644 --- a/companion/src/firmwares/edgetx/yaml_generalsettings.cpp +++ b/companion/src/firmwares/edgetx/yaml_generalsettings.cpp @@ -417,6 +417,7 @@ Node convert::encode(const GeneralSettings& rhs) } node["oneLogPerDay"] = (int)rhs.oneLogPerDay; + node["keyLockEnabled"] = (int)rhs.keyLockEnabled; return node; } @@ -780,6 +781,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 05d89b8f1..e8873f90c 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -459,6 +459,7 @@ class GeneralSettings { unsigned int qmFavorites[MAX_QMFAVOURITES]; char* qmFavoritesTools[MAX_QMFAVOURITES]; bool oneLogPerDay; + bool keyLockEnabled; void switchConfigClear(); diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 2e0b935c6..abd60277c 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -26,6 +26,7 @@ #include "autocombobox.h" #include "namevalidator.h" #include "helpers.h" +#include "boardjson.h" constexpr char FIM_HATSMODE[] {"Hats Mode"}; constexpr char FIM_STICKMODE[] {"Stick Mode"}; @@ -474,6 +475,17 @@ void GeneralSetupPanel::setValues() ui->startSoundCB->setChecked(!generalSettings.dontPlayHello); ui->modelQuickSelect_CB->setChecked(generalSettings.modelQuickSelect); ui->chkOneLogPerDay->setChecked(generalSettings.oneLogPerDay); + { + BoardJson* bj = Boards::getBoardJson(board); + bool hasCombo = bj && bj->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); @@ -951,3 +963,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 232e03730..0aed9c927 100644 --- a/companion/src/generaledit/generalsetup.h +++ b/companion/src/generaledit/generalsetup.h @@ -103,6 +103,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 310b12bec..b50148ce0 100644 --- a/companion/src/generaledit/generalsetup.ui +++ b/companion/src/generaledit/generalsetup.ui @@ -1326,6 +1326,20 @@ p, li { white-space: pre-wrap; } + + + + Key lock + + + + + + + + + + diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 8d4cd2146..b01752104 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -146,9 +146,16 @@ AddHWGenTarget(${HW_DESC_JSON} stm32_pwm_inputs stm32_pwm_inputs.inc) AddHWGenTarget(${HW_DESC_JSON} simu_keys simu_keys.inc) AddHWGenTarget(${HW_DESC_JSON} simu_switches simu_switches.inc) +AddHWGenTarget(${HW_DESC_JSON} hal_keys_lock hal_keys_lock.h) +add_custom_target(hal_keys_lock_hdr DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/hal_keys_lock.h) +if(TARGET board) + add_dependencies(board hal_keys_lock_hdr) +endif() + add_dependencies(board hal_settings hal_keys hal_adc_inputs stm32_keys stm32_switches stm32_adc_inputs stm32_pwm_inputs yaml_inputs lua_inputs lua_mixsrc lua_keys) add_dependencies(board_bl hal_settings hal_keys stm32_keys) + include(hal/CMakeLists.txt) add_subdirectory(bitmaps) diff --git a/radio/src/boards/generic_stm32/CMakeLists.txt b/radio/src/boards/generic_stm32/CMakeLists.txt index 67c3a70c0..80adf7821 100644 --- a/radio/src/boards/generic_stm32/CMakeLists.txt +++ b/radio/src/boards/generic_stm32/CMakeLists.txt @@ -24,9 +24,10 @@ set(BOARD_LIB_SRC add_library(minimal_board_lib OBJECT EXCLUDE_FROM_ALL ${MINIMAL_BOARD_LIB_SRC}) add_dependencies(minimal_board_lib stm32_keys) +add_dependencies(minimal_board_lib hal_keys_lock_hdr) add_library(board_lib OBJECT EXCLUDE_FROM_ALL ${BOARD_LIB_SRC}) -add_dependencies(board_lib minimal_board_lib) +add_dependencies(board_lib minimal_board_lib hal_keys_lock_hdr) if(DEBUG) target_compile_definitions(board_lib PRIVATE DEBUG) diff --git a/radio/src/boards/hw_defs/boxer.json b/radio/src/boards/hw_defs/boxer.json index 578c5d531..4885b100a 100644 --- a/radio/src/boards/hw_defs/boxer.json +++ b/radio/src/boards/hw_defs/boxer.json @@ -354,5 +354,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/bumblebee.json b/radio/src/boards/hw_defs/bumblebee.json index e552ec0da..65e2771d7 100644 --- a/radio/src/boards/hw_defs/bumblebee.json +++ b/radio/src/boards/hw_defs/bumblebee.json @@ -425,5 +425,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/c14.json b/radio/src/boards/hw_defs/c14.json index 1550f88b1..a399c29c1 100644 --- a/radio/src/boards/hw_defs/c14.json +++ b/radio/src/boards/hw_defs/c14.json @@ -376,5 +376,9 @@ "mixer_scheduler_timer_freq": "(PERI1_FREQUENCY * TIMER_MULT_APB1)", "mixer_scheduler_timer_irqn": "TIM8_BRK_TIM12_IRQn", "mixer_scheduler_timer_irqhandler": "TIM8_BRK_TIM12_IRQHandler" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/commando8.json b/radio/src/boards/hw_defs/commando8.json index 4b38465b2..189024b8f 100644 --- a/radio/src/boards/hw_defs/commando8.json +++ b/radio/src/boards/hw_defs/commando8.json @@ -300,5 +300,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/f16.json b/radio/src/boards/hw_defs/f16.json index ae80ad34e..4b07ac2a0 100644 --- a/radio/src/boards/hw_defs/f16.json +++ b/radio/src/boards/hw_defs/f16.json @@ -460,5 +460,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/gx12.json b/radio/src/boards/hw_defs/gx12.json index 95e5dfa7c..0a369eb50 100644 --- a/radio/src/boards/hw_defs/gx12.json +++ b/radio/src/boards/hw_defs/gx12.json @@ -482,5 +482,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/lr3pro.json b/radio/src/boards/hw_defs/lr3pro.json index 12ae15c6b..be437024a 100644 --- a/radio/src/boards/hw_defs/lr3pro.json +++ b/radio/src/boards/hw_defs/lr3pro.json @@ -282,5 +282,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_ENTER", + "KEY_RIGHT" + ] } diff --git a/radio/src/boards/hw_defs/mt12.json b/radio/src/boards/hw_defs/mt12.json index 05c1264f9..b8fe8c854 100644 --- a/radio/src/boards/hw_defs/mt12.json +++ b/radio/src/boards/hw_defs/mt12.json @@ -347,5 +347,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/pocket.json b/radio/src/boards/hw_defs/pocket.json index f74a509a8..55d980a5c 100644 --- a/radio/src/boards/hw_defs/pocket.json +++ b/radio/src/boards/hw_defs/pocket.json @@ -317,5 +317,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/st16.json b/radio/src/boards/hw_defs/st16.json index b705dd88d..c529de227 100644 --- a/radio/src/boards/hw_defs/st16.json +++ b/radio/src/boards/hw_defs/st16.json @@ -477,5 +477,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_EXIT", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t12.json b/radio/src/boards/hw_defs/t12.json index ee2ceb1ad..36bcae2d9 100644 --- a/radio/src/boards/hw_defs/t12.json +++ b/radio/src/boards/hw_defs/t12.json @@ -356,5 +356,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_ENTER", + "KEY_RIGHT" + ] } diff --git a/radio/src/boards/hw_defs/t12max.json b/radio/src/boards/hw_defs/t12max.json index e0a314623..0e22b3fd6 100644 --- a/radio/src/boards/hw_defs/t12max.json +++ b/radio/src/boards/hw_defs/t12max.json @@ -317,5 +317,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_PAGEDN", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t14.json b/radio/src/boards/hw_defs/t14.json index 2600c553d..9b0b5ef55 100644 --- a/radio/src/boards/hw_defs/t14.json +++ b/radio/src/boards/hw_defs/t14.json @@ -332,5 +332,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t15.json b/radio/src/boards/hw_defs/t15.json index c85848bec..5cea79754 100644 --- a/radio/src/boards/hw_defs/t15.json +++ b/radio/src/boards/hw_defs/t15.json @@ -394,5 +394,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC1E | TIM_CCER_CC1NE", "haptic_timer_mode": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR1" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_TELE" + ] } diff --git a/radio/src/boards/hw_defs/t15pro.json b/radio/src/boards/hw_defs/t15pro.json index eec541388..2a9013694 100644 --- a/radio/src/boards/hw_defs/t15pro.json +++ b/radio/src/boards/hw_defs/t15pro.json @@ -444,5 +444,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_PAGEDN", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t16.json b/radio/src/boards/hw_defs/t16.json index d57a5eb7f..554d99d5b 100644 --- a/radio/src/boards/hw_defs/t16.json +++ b/radio/src/boards/hw_defs/t16.json @@ -474,5 +474,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/t18.json b/radio/src/boards/hw_defs/t18.json index 031d372eb..ef166d47f 100644 --- a/radio/src/boards/hw_defs/t18.json +++ b/radio/src/boards/hw_defs/t18.json @@ -475,5 +475,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/t20.json b/radio/src/boards/hw_defs/t20.json index 74f20a250..26b13a08b 100644 --- a/radio/src/boards/hw_defs/t20.json +++ b/radio/src/boards/hw_defs/t20.json @@ -555,5 +555,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t20v2.json b/radio/src/boards/hw_defs/t20v2.json index b6c261817..9ae5ef893 100644 --- a/radio/src/boards/hw_defs/t20v2.json +++ b/radio/src/boards/hw_defs/t20v2.json @@ -558,5 +558,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t22.json b/radio/src/boards/hw_defs/t22.json index d089261c8..81c235f38 100644 --- a/radio/src/boards/hw_defs/t22.json +++ b/radio/src/boards/hw_defs/t22.json @@ -545,5 +545,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_PAGEDN", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/t8.json b/radio/src/boards/hw_defs/t8.json index f9daf48f7..5de60ee2b 100644 --- a/radio/src/boards/hw_defs/t8.json +++ b/radio/src/boards/hw_defs/t8.json @@ -295,5 +295,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/tlite.json b/radio/src/boards/hw_defs/tlite.json index 112b10f41..3a3003df4 100644 --- a/radio/src/boards/hw_defs/tlite.json +++ b/radio/src/boards/hw_defs/tlite.json @@ -280,5 +280,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_ENTER", + "KEY_RIGHT" + ] } diff --git a/radio/src/boards/hw_defs/tpro.json b/radio/src/boards/hw_defs/tpro.json index 5a75748bf..d81bed1a2 100644 --- a/radio/src/boards/hw_defs/tpro.json +++ b/radio/src/boards/hw_defs/tpro.json @@ -386,5 +386,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/tpros.json b/radio/src/boards/hw_defs/tpros.json index e5633eb99..7b3ed0205 100644 --- a/radio/src/boards/hw_defs/tpros.json +++ b/radio/src/boards/hw_defs/tpros.json @@ -319,5 +319,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/tprov2.json b/radio/src/boards/hw_defs/tprov2.json index c17afc30e..60a8438e6 100644 --- a/radio/src/boards/hw_defs/tprov2.json +++ b/radio/src/boards/hw_defs/tprov2.json @@ -416,5 +416,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/tx12.json b/radio/src/boards/hw_defs/tx12.json index dd3b474a8..c71a71cc7 100644 --- a/radio/src/boards/hw_defs/tx12.json +++ b/radio/src/boards/hw_defs/tx12.json @@ -366,5 +366,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/tx12mk2.json b/radio/src/boards/hw_defs/tx12mk2.json index 63f23e4fc..4d6c3c8c1 100644 --- a/radio/src/boards/hw_defs/tx12mk2.json +++ b/radio/src/boards/hw_defs/tx12mk2.json @@ -346,5 +346,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/tx15.json b/radio/src/boards/hw_defs/tx15.json index cd035f662..e65ce67fd 100644 --- a/radio/src/boards/hw_defs/tx15.json +++ b/radio/src/boards/hw_defs/tx15.json @@ -430,5 +430,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/tx16s.json b/radio/src/boards/hw_defs/tx16s.json index 2eaf411e2..ad2738c83 100644 --- a/radio/src/boards/hw_defs/tx16s.json +++ b/radio/src/boards/hw_defs/tx16s.json @@ -482,5 +482,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/tx16smk3.json b/radio/src/boards/hw_defs/tx16smk3.json index 8f0edf9c3..e65b5e38a 100644 --- a/radio/src/boards/hw_defs/tx16smk3.json +++ b/radio/src/boards/hw_defs/tx16smk3.json @@ -544,5 +544,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/v14.json b/radio/src/boards/hw_defs/v14.json index 29efa82b5..7d61618a6 100644 --- a/radio/src/boards/hw_defs/v14.json +++ b/radio/src/boards/hw_defs/v14.json @@ -394,5 +394,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/v14lcd.json b/radio/src/boards/hw_defs/v14lcd.json index d1df9446d..11fc24b6e 100644 --- a/radio/src/boards/hw_defs/v14lcd.json +++ b/radio/src/boards/hw_defs/v14lcd.json @@ -393,5 +393,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/v16.json b/radio/src/boards/hw_defs/v16.json index 901daabc4..4bee5a3e4 100644 --- a/radio/src/boards/hw_defs/v16.json +++ b/radio/src/boards/hw_defs/v16.json @@ -493,5 +493,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/x10.json b/radio/src/boards/hw_defs/x10.json index 2b2660eeb..1f48a9e52 100644 --- a/radio/src/boards/hw_defs/x10.json +++ b/radio/src/boards/hw_defs/x10.json @@ -479,5 +479,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_PAGEDN", + "KEY_SYS" + ] } diff --git a/radio/src/boards/hw_defs/x10express.json b/radio/src/boards/hw_defs/x10express.json index 2b2660eeb..b85b8e4e0 100644 --- a/radio/src/boards/hw_defs/x10express.json +++ b/radio/src/boards/hw_defs/x10express.json @@ -479,5 +479,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC2E", "haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/boards/hw_defs/x12s.json b/radio/src/boards/hw_defs/x12s.json index 23be1bce6..248574fca 100644 --- a/radio/src/boards/hw_defs/x12s.json +++ b/radio/src/boards/hw_defs/x12s.json @@ -468,5 +468,9 @@ "haptic_timer_output_enable": "TIM_CCER_CC1E", "haptic_timer_mode": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR1" - } + }, + "key_lock_combo": [ + "KEY_PAGEUP", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/x7.json b/radio/src/boards/hw_defs/x7.json index 9c70904dd..6a913b526 100644 --- a/radio/src/boards/hw_defs/x7.json +++ b/radio/src/boards/hw_defs/x7.json @@ -344,5 +344,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/x7access.json b/radio/src/boards/hw_defs/x7access.json index 9185f5868..7f7d07fd6 100644 --- a/radio/src/boards/hw_defs/x7access.json +++ b/radio/src/boards/hw_defs/x7access.json @@ -329,5 +329,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR3", "haptic_ccmr2": "TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2", "haptic_ccer": "TIM_CCER_CC3E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_ENTER" + ] } diff --git a/radio/src/boards/hw_defs/x9d+.json b/radio/src/boards/hw_defs/x9d+.json index 38faffdee..21a106e0e 100644 --- a/radio/src/boards/hw_defs/x9d+.json +++ b/radio/src/boards/hw_defs/x9d+.json @@ -393,5 +393,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/x9d+2019.json b/radio/src/boards/hw_defs/x9d+2019.json index bd99ff181..06042ac22 100644 --- a/radio/src/boards/hw_defs/x9d+2019.json +++ b/radio/src/boards/hw_defs/x9d+2019.json @@ -387,5 +387,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR3", "haptic_ccmr2": "TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2", "haptic_ccer": "TIM_CCER_CC3E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/x9d.json b/radio/src/boards/hw_defs/x9d.json index 45e5de657..e6bcb972b 100644 --- a/radio/src/boards/hw_defs/x9d.json +++ b/radio/src/boards/hw_defs/x9d.json @@ -372,5 +372,9 @@ }, "haptic": { "haptic_gpio": "GPIO_PIN(GPIOC, 14)" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/x9e.json b/radio/src/boards/hw_defs/x9e.json index 443ded67c..60857f7db 100644 --- a/radio/src/boards/hw_defs/x9e.json +++ b/radio/src/boards/hw_defs/x9e.json @@ -585,5 +585,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR1", "haptic_ccmr1": "TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2", "haptic_ccer": "TIM_CCER_CC1E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/x9lite.json b/radio/src/boards/hw_defs/x9lite.json index ce5fdde13..ca187c8ca 100644 --- a/radio/src/boards/hw_defs/x9lite.json +++ b/radio/src/boards/hw_defs/x9lite.json @@ -292,5 +292,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/x9lites.json b/radio/src/boards/hw_defs/x9lites.json index 78d75f43b..cfac6137a 100644 --- a/radio/src/boards/hw_defs/x9lites.json +++ b/radio/src/boards/hw_defs/x9lites.json @@ -319,5 +319,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_MENU", + "KEY_EXIT" + ] } diff --git a/radio/src/boards/hw_defs/zorro.json b/radio/src/boards/hw_defs/zorro.json index cf71f58d0..8888f65c6 100644 --- a/radio/src/boards/hw_defs/zorro.json +++ b/radio/src/boards/hw_defs/zorro.json @@ -370,5 +370,9 @@ "haptic_counter_register": "HAPTIC_TIMER->CCR2", "haptic_ccmr1": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2", "haptic_ccer": "TIM_CCER_CC2E" - } + }, + "key_lock_combo": [ + "KEY_SYS", + "KEY_MODEL" + ] } diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 4c22283b6..b0942a59d 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -79,12 +79,12 @@ static inline void check_struct() #if defined(PCBXLITES) CHKSIZE(RadioData, 951); #elif defined(RADIO_ST16) || defined(PCBPA01) || defined(RADIO_TX15) || defined(RADIO_T15PRO) || defined(RADIO_TX16SMK3) || defined(RADIO_T22) - CHKSIZE(RadioData, 1182); + CHKSIZE(RadioData, 1183); #elif defined(COLORLCD) #if defined(IMU) - CHKSIZE(RadioData, 1062); + CHKSIZE(RadioData, 1063); #else - CHKSIZE(RadioData, 1061); + CHKSIZE(RadioData, 1062); #endif #elif defined(RADIO_GX12) CHKSIZE(RadioData, 1068); diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index 41475b424..2f5f32513 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -1163,13 +1163,16 @@ PACK(struct RadioData { NOBACKUP(uint8_t modelSelectLayout:2); NOBACKUP(uint8_t radioThemesDisabled:1); NOBACKUP(uint8_t oneLogPerDay:1); + NOBACKUP(uint8_t keyLockEnabled:1); #elif LCD_W == 128 uint8_t invertLCD:1; // Invert B&W LCD display NOBACKUP(uint8_t oneLogPerDay:1); - NOBACKUP(uint8_t spare:5 SKIP); + NOBACKUP(uint8_t keyLockEnabled:1); + NOBACKUP(uint8_t spare:4 SKIP); #else NOBACKUP(uint8_t oneLogPerDay:1); - NOBACKUP(uint8_t spare:6 SKIP); + NOBACKUP(uint8_t keyLockEnabled:1); + NOBACKUP(uint8_t spare:5 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 c6c39279a..76102831d 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -87,6 +87,7 @@ enum { CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_SOURCE) CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_SOURCE_OVERRIDE) 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) @@ -228,7 +229,6 @@ void menuRadioSetup(event_t event) CASE_BACKLIGHT(0) CASE_BACKLIGHT(BACKLIGHT_WARNING_ROW(LABEL(0))) CASE_BACKLIGHT(0) - 0, CASE_SPLASH_PARAM(0) CASE_PWR_BUTTON_PRESS(0) CASE_PWR_BUTTON_PRESS(0) @@ -628,6 +628,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 454606ddf..f1f1acbbf 100644 --- a/radio/src/gui/212x64/radio_setup.cpp +++ b/radio/src/gui/212x64/radio_setup.cpp @@ -90,6 +90,7 @@ enum MenuRadioSetupItems { ITEM_RADIO_SETUP_BACKLIGHT_SOURCE, ITEM_RADIO_SETUP_BACKLIGHT_SOURCE_OVERRIDE, 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) @@ -235,6 +236,7 @@ void menuRadioSetup(event_t event) BACKLIGHT_WARNING_ROW(LABEL(0)), // backlight control override warning 0, // flash beep 0, + CASE_KEY_LOCK(0) // key lock CASE_SPLASH_PARAM(0) // disable splash CASE_PWR_BUTTON_PRESS(0) // pwr on speed CASE_PWR_BUTTON_PRESS(0) // pwr off speed @@ -619,6 +621,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 7d8e9d175..0ab462cdb 100644 --- a/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp +++ b/radio/src/gui/colorlcd/setup_menus/key_shortcuts.cpp @@ -28,6 +28,7 @@ #include "pagegroup.h" #include "qmpagechoice.h" #include "radio_tools.h" +#include "toggleswitch.h" #define SET_DIRTY() storageDirty(EE_GENERAL) @@ -87,6 +88,18 @@ QMKeyShortcutsPage::QMKeyShortcutsPage(): { auto 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"); diff --git a/radio/src/gui/common/stdlcd/feature_control.h b/radio/src/gui/common/stdlcd/feature_control.h index 817c21db3..f28135e50 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/keys.cpp b/radio/src/keys.cpp index 9cd90a61f..6fd15aa1a 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. * @@ -494,11 +514,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 44cef3af1..8ad712378 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -25,6 +25,9 @@ #include "edgetx_types.h" #include "hal/key_driver.h" +#if !defined(BOOT) && !defined(SIMU) +#include "hal_keys_lock.h" +#endif constexpr event_t EVT_REFRESH = 0x1000; constexpr event_t EVT_ENTRY = 0x1001; @@ -186,6 +189,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 0906531ad..ae0b95eb3 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -282,6 +282,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[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, + 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) @@ -547,6 +595,8 @@ void perMain() checkHatsAsKeys(); #endif + checkKeysLock(); + #if defined(COLORLCD) MainWindow::instance()->run(); #endif diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index 9246fa136..45eee4f1f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -385,7 +385,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), 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 8e437024e..933a19b46 100644 --- a/radio/src/storage/yaml/yaml_datastructs_c14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_c14.cpp @@ -457,6 +457,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_f16.cpp b/radio/src/storage/yaml/yaml_datastructs_f16.cpp index ba914c983..348070c2c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_f16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_f16.cpp @@ -458,6 +458,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp index f1ec725f2..5a3bd9ba8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp @@ -409,7 +409,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), 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 0612d9857..e61833055 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp @@ -448,6 +448,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 0d558b01c..b15aa9069 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -455,6 +455,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp index 2e7bb9c16..956beb578 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp @@ -481,6 +481,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 0c3c3ded1..67c792eeb 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -455,6 +455,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp index 7727001f6..448c19d3a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp @@ -448,6 +448,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_st16.cpp b/radio/src/storage/yaml/yaml_datastructs_st16.cpp index 9039a1cd3..cf3701ad0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_st16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_st16.cpp @@ -481,6 +481,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15.cpp b/radio/src/storage/yaml/yaml_datastructs_t15.cpp index 92eca63de..a0bfc3f14 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15.cpp @@ -466,6 +466,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp index 9039a1cd3..cf3701ad0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp @@ -481,6 +481,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index 36b0c22eb..7db2dd1e1 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -394,7 +394,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), 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 1e86c2f41..e8cc28bc9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t22.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t22.cpp @@ -481,6 +481,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 773e72c43..29c269aa2 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -394,7 +394,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), 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 9039a1cd3..cf3701ad0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp @@ -481,6 +481,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp index 5992e5ec4..d536a7b27 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp @@ -482,6 +482,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index e15e3a323..2ed42e5ab 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -457,6 +457,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelSelectLayout", 2 ), YAML_UNSIGNED( "radioThemesDisabled", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_ARRAY("keyShortcuts", 8, 6, struct_KeyShortcut, NULL), YAML_ARRAY("qmFavorites", 8, 12, struct_QMFavorite, NULL), diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index 24f9395ca..54992e240 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -384,7 +384,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 6 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 5 ), 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 24f9395ca..54992e240 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp @@ -384,7 +384,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 6 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 5 ), 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 d6c6527e0..20ec6ec9d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -384,7 +384,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED( "disablePwrOnOffHaptic", 1 ), YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 6 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 5 ), 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 9246fa136..45eee4f1f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -385,7 +385,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), 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 a5a3b3b4a..f8daba2e8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -390,7 +390,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "modelQuickSelect", 1 ), YAML_UNSIGNED( "invertLCD", 1 ), YAML_UNSIGNED( "oneLogPerDay", 1 ), - YAML_PADDING( 5 ), + YAML_UNSIGNED( "keyLockEnabled", 1 ), + YAML_PADDING( 4 ), YAML_UNSIGNED( "pwrOffIfInactive", 8 ), YAML_END }; diff --git a/radio/src/tests/keys_lock.cpp b/radio/src/tests/keys_lock.cpp new file mode 100644 index 000000000..d11ba25d4 --- /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 dde0c244a..483fa0ce4 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 66643fb14..3a25c3fc0 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 1cc9463de..0c54b1408 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 9bc721b37..bca8b70f9 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 f2447958f..f7b47e1f4 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 c3827cbd5..97354276c 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 ded201024..0ac929af4 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 4c9955cf7..8438e5c04 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 29cb8109a..1aa90014f 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 84fdcab93..ff20e47be 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 578cfbf09..ebf7c5cd1 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 52dca5776..8165f851b 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 98d140a46..c710d2b26 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 db4e25958..c0ac974c9 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 de982cd0d..8681b6978 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 290a6ef63..6199d12f9 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 ceb0ad581..0fe1bb248 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 218060332..ccb47c840 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 e7955c959..78e78f1e5 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 5fdac83b0..7ab2adb6e 100644 --- a/radio/src/translations/sim_string_list.h +++ b/radio/src/translations/sim_string_list.h @@ -1170,3 +1170,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 146fcf847..6313b0943 100644 --- a/radio/src/translations/string_list.h +++ b/radio/src/translations/string_list.h @@ -959,6 +959,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) diff --git a/radio/util/hw_defs/generator.py b/radio/util/hw_defs/generator.py index 172d003c2..504645c52 100644 --- a/radio/util/hw_defs/generator.py +++ b/radio/util/hw_defs/generator.py @@ -95,6 +95,7 @@ def generate_from_template(json_filename, template_filename, target): imu=hw_def.imu, rotenc=hw_def.rotenc, haptic=hw_def.haptic, + key_lock_combo=hw_def.key_lock_combo, ) try: diff --git a/radio/util/hw_defs/hal_keys_lock.jinja b/radio/util/hw_defs/hal_keys_lock.jinja new file mode 100644 index 000000000..aee60f4ed --- /dev/null +++ b/radio/util/hw_defs/hal_keys_lock.jinja @@ -0,0 +1,11 @@ +// +// WARNING: DO NOT EDIT THIS FILE +// This file has been generated from the target's JSON hardware description +// + +#pragma once + +{% if key_lock_combo %} +#define KEYS_LOCK_KEY1 {{ key_lock_combo[0] }} +#define KEYS_LOCK_KEY2 {{ key_lock_combo[1] }} +{% endif %} diff --git a/radio/util/hw_defs/models.py b/radio/util/hw_defs/models.py index e01a539e2..03169c95a 100644 --- a/radio/util/hw_defs/models.py +++ b/radio/util/hw_defs/models.py @@ -423,6 +423,30 @@ class HardwareDefinition(BaseModel): imu: Optional[IMU] = None rotenc: Optional[RotEnc] = None haptic: Optional[Haptic] = None + key_lock_combo: Optional[List[KeyEnum]] = None + + @model_validator(mode="after") + def check_key_lock_combo(self: "HardwareDefinition") -> "HardwareDefinition": + if self.key_lock_combo is None: + return self + if len(self.key_lock_combo) != 2: + raise PydanticCustomError( + "KeyLockComboError", + "'key_lock_combo' must list exactly 2 keys", + ) + if self.key_lock_combo[0] == self.key_lock_combo[1]: + raise PydanticCustomError( + "KeyLockComboError", + "'key_lock_combo' entries must be distinct", + ) + defined = {k.key for k in self.keys} + for k in self.key_lock_combo: + if k not in defined: + raise PydanticCustomError( + "KeyLockComboError", + f"'key_lock_combo' references {k} which is not in 'keys'", + ) + return self @staticmethod def from_json(data: Union[str, bytes, bytearray]) -> "HardwareDefinition":