feat(radio): optional key lock feature (#7383)

This commit is contained in:
3djc
2026-07-16 18:54:50 +10:00
committed by GitHub
parent 640fc189e7
commit e184cf3976
111 changed files with 750 additions and 66 deletions
+6 -3
View File
@@ -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();
}
}
}
+3 -1
View File
@@ -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);
@@ -417,6 +417,7 @@ Node convert<GeneralSettings>::encode(const GeneralSettings& rhs)
}
node["oneLogPerDay"] = (int)rhs.oneLogPerDay;
node["keyLockEnabled"] = (int)rhs.keyLockEnabled;
return node;
}
@@ -780,6 +781,7 @@ bool convert<GeneralSettings>::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
@@ -459,6 +459,7 @@ class GeneralSettings {
unsigned int qmFavorites[MAX_QMFAVOURITES];
char* qmFavoritesTools[MAX_QMFAVOURITES];
bool oneLogPerDay;
bool keyLockEnabled;
void switchConfigClear();
@@ -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();
}
}
+1
View File
@@ -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;
+14
View File
@@ -1326,6 +1326,20 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="30" column="0">
<widget class="QLabel" name="label_keyLockEnabled">
<property name="text">
<string>Key lock</string>
</property>
</widget>
</item>
<item row="30" column="1">
<widget class="QCheckBox" name="chkKeyLockEnabled">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
+7
View File
@@ -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)
@@ -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)
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -372,5 +372,9 @@
},
"haptic": {
"haptic_gpio": "GPIO_PIN(GPIOC, 14)"
}
},
"key_lock_combo": [
"KEY_MENU",
"KEY_EXIT"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+5 -1
View File
@@ -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"
]
}
+3 -3
View File
@@ -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);
+5 -2
View File
@@ -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);
+15 -1
View File
@@ -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);
+16
View File
@@ -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()) {
@@ -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");
@@ -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
+58 -1
View File
@@ -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
}
}
+9
View File
@@ -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();
+50
View File
@@ -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
@@ -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
};
@@ -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),
@@ -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),
@@ -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
};
@@ -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),
@@ -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),
@@ -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),
@@ -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),
@@ -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),
@@ -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),
@@ -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),
@@ -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),
@@ -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
};
@@ -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),
@@ -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
};
@@ -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),
@@ -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),
@@ -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),
@@ -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
};
@@ -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
};
@@ -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
};
@@ -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
};
@@ -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
};
+142
View File
@@ -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
+4
View File
@@ -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 "开机延迟"
+4
View File
@@ -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í"
+4
View File
@@ -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"
+4
View File
@@ -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")
+4
View File
@@ -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"
+4
View File
@@ -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")
+4
View File
@@ -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"
+4
View File
@@ -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"
+4
View File
@@ -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 "השהיית הפעלה פעיל"
+4
View File
@@ -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."
+4
View File
@@ -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遅延"
+4
View File
@@ -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 "시작 사운드"
+4
View File
@@ -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"

Some files were not shown because too many files have changed in this diff Show More