Add side-key action picker (F + hold side key)

This commit is contained in:
Armel FAUVEAU committed 2026-08-11 22:12:25 +02:00
1 parent a38f611bd5
commit c65db46bad
7 files changed
+221 -36

No files matched your search

+6
View File
@@ -214,6 +214,12 @@ enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG
enable_feature(ENABLE_FEAT_F4HWN_FOXHUNT
app/foxhunt.c
)
enable_feature(ENABLE_FEAT_F4HWN_ACTION_PICKER)
if(ENABLE_FEAT_F4HWN_ACTION_PICKER AND NOT ENABLE_FEAT_F4HWN)
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_ACTION_PICKER requires ENABLE_FEAT_F4HWN.")
endif()
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP)
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER)
+124 -35
View File
@@ -51,6 +51,9 @@
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
#include "app/foxhunt.h"
#endif
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
#include "ui/menu.h"
#endif
#if defined(ENABLE_FMRADIO)
static void ACTION_Scan_FM(bool bRestart);
@@ -299,6 +302,120 @@ void ACTION_SwitchDemodul(void)
}
#ifdef ENABLE_FMRADIO
inline static bool ACTION_IsBlockedInFM(uint8_t action)
{
switch (action) {
case ACTION_OPT_POWER:
case ACTION_OPT_MONITOR:
case ACTION_OPT_A_B:
case ACTION_OPT_VFO_MR:
case ACTION_OPT_SWITCH_DEMODUL:
#ifdef ENABLE_VOX
case ACTION_OPT_VOX:
#endif
#ifdef ENABLE_FEAT_F4HWN
case ACTION_OPT_RXMODE:
case ACTION_OPT_MAINONLY:
case ACTION_OPT_WN:
#ifdef ENABLE_FEAT_F4HWN_AUDIO
case ACTION_OPT_RXA:
#endif
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
case ACTION_OPT_POWER_HIGH:
case ACTION_OPT_REMOVE_OFFSET:
#endif
#endif
#ifdef ENABLE_FEAT_F4HWN_BEAM
case ACTION_OPT_BEAM:
#endif
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
case ACTION_OPT_FOXHUNT:
#endif
return true;
default:
return false;
}
}
#endif
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
static void ACTION_Execute(uint8_t action)
{
if (action >= ACTION_OPT_LEN || action_opt_table[action] == NULL) {
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
}
#ifdef ENABLE_FMRADIO
if (gFmRadioMode && ACTION_IsBlockedInFM(action)) {
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
}
#endif
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
action_opt_table[action]();
}
uint8_t gActionPickerKey;
uint8_t gActionPickerSelection[2] = {1, 1};
uint8_t gActionPickerTimeout_500ms;
bool ACTION_PickerProcessKey(KEY_Code_t key, bool isPressed, bool isHeld)
{
if (gActionPickerKey == 0)
return false;
if (isPressed)
gActionPickerTimeout_500ms = ACTION_PICKER_TIMEOUT_500MS;
uint8_t *selection = &gActionPickerSelection[gActionPickerKey - 1];
switch (key) {
case KEY_UP:
case KEY_DOWN:
if (isPressed && !isHeld) {
if (key == KEY_UP) {
if (--*selection == 0)
*selection = gSubMenu_SIDEFUNCTIONS_size - 1;
}
else if (++*selection >= gSubMenu_SIDEFUNCTIONS_size) {
*selection = 1;
}
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
gUpdateDisplay = true;
}
return true;
case KEY_MENU:
if (!isPressed && !isHeld) {
const uint8_t action = gSubMenu_SIDEFUNCTIONS[*selection].id;
gActionPickerKey = 0;
gUpdateDisplay = true;
ACTION_Execute(action);
}
return true;
case KEY_EXIT:
case KEY_F:
if (!isPressed) {
gActionPickerKey = 0;
gUpdateDisplay = true;
}
return true;
case KEY_PTT:
gActionPickerKey = 0;
gUpdateDisplay = true;
return false;
default:
return true;
}
}
#endif
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
HideFKeyIcon();
@@ -360,48 +477,20 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
}
// held or released after short press
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
ACTION_Execute(func);
#else
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
#ifdef ENABLE_FMRADIO
if (gFmRadioMode) { // do not run these actions in FM radio mode
switch (func) {
case ACTION_OPT_POWER:
case ACTION_OPT_MONITOR:
case ACTION_OPT_A_B:
case ACTION_OPT_VFO_MR:
case ACTION_OPT_SWITCH_DEMODUL:
#ifdef ENABLE_VOX
case ACTION_OPT_VOX:
#endif
#ifdef ENABLE_FEAT_F4HWN
case ACTION_OPT_RXMODE:
case ACTION_OPT_MAINONLY:
case ACTION_OPT_WN:
#ifdef ENABLE_FEAT_F4HWN_AUDIO
case ACTION_OPT_RXA:
#endif
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
case ACTION_OPT_POWER_HIGH:
case ACTION_OPT_REMOVE_OFFSET:
#endif
#endif
#ifdef ENABLE_FEAT_F4HWN_BEAM
case ACTION_OPT_BEAM:
#endif
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
case ACTION_OPT_FOXHUNT:
#endif
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
default:
break;
}
if (gFmRadioMode && ACTION_IsBlockedInFM(func)) {
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
}
#endif
action_opt_table[func]();
#endif
}
+7
View File
@@ -55,6 +55,13 @@ void ACTION_SwitchDemodul(void);
#endif
#endif
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
#define ACTION_PICKER_TIMEOUT_500MS 10u
extern uint8_t gActionPickerKey;
extern uint8_t gActionPickerSelection[2];
extern uint8_t gActionPickerTimeout_500ms;
bool ACTION_PickerProcessKey(KEY_Code_t key, bool isPressed, bool isHeld);
#endif
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
#endif
+33 -1
View File
@@ -1611,6 +1611,13 @@ void APP_TimeSlice10ms(void)
if (gCurrentFunction != FUNCTION_POWER_SAVE || !gRxIdleMode)
CheckRadioInterrupts();
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
if (gActionPickerKey != 0 && FUNCTION_IsRx()) {
gActionPickerKey = 0;
gUpdateDisplay = true;
}
#endif
if (gCurrentFunction == FUNCTION_TRANSMIT)
{ // transmitting
@@ -1810,6 +1817,15 @@ void cancelUserInputModes(void)
void APP_TimeSlice500ms(void)
{
gNextTimeslice_500ms = false;
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
if (gActionPickerKey != 0 && gActionPickerTimeout_500ms > 0 &&
--gActionPickerTimeout_500ms == 0)
{
gActionPickerKey = 0;
gUpdateDisplay = true;
}
#endif
bool exit_menu = false;
// Skipped authentic device check
@@ -2029,6 +2045,10 @@ void APP_TimeSlice500ms(void)
{
gEeprom.KEY_LOCK = true; // lock the keyboard
gUpdateStatus = true; // lock symbol needs showing
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
gActionPickerKey = 0;
gUpdateDisplay = true;
#endif
}
if (exit_menu) {
@@ -2269,7 +2289,11 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
BACKLIGHT_TurnOn();
}
if (Key == KEY_EXIT && bKeyHeld) { // exit key held pressed
if (Key == KEY_EXIT && bKeyHeld
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
&& gActionPickerKey == 0
#endif
) { // exit key held pressed
// clear the live DTMF decoder
if (gDTMF_RX_live[0] != 0) {
DTMF_clear_input_box_memory();
@@ -2306,6 +2330,14 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
}
bool lowBatPopup = gLowBattery && !gLowBatteryConfirmed && gScreenToDisplay == DISPLAY_MAIN;
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
if (gActionPickerKey != 0 &&
(gEeprom.KEY_LOCK || lowBatPopup || gScreenToDisplay != DISPLAY_MAIN))
gActionPickerKey = 0;
if (ACTION_PickerProcessKey(Key, bKeyPressed, bKeyHeld))
goto Skip;
#endif
#ifdef ENABLE_FEAT_F4HWN // Disable PTT if KEY_LOCK
bool lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
+10
View File
@@ -455,6 +455,16 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gRequestDisplayScreen = DISPLAY_MAIN;
}
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
if (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
gActionPickerKey = (Key == KEY_SIDE1) ? 1 : 2;
gActionPickerTimeout_500ms = ACTION_PICKER_TIMEOUT_500MS;
gUpdateDisplay = true;
HideFKeyIcon();
return;
}
#endif
HideFKeyIcon();
processFKeyFunction(Key, true);
+39
View File
@@ -18,6 +18,9 @@
#include <stdlib.h> // abs()
#include "app/app.h"
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
#include "app/action.h"
#endif
#include "app/chFrScanner.h"
#include "app/dtmf.h"
@@ -1322,6 +1325,23 @@ static void UI_PrintScanRangeCss(char *String, uint8_t LabelX, uint8_t ValueX, u
}
#endif
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
static void UI_PrintActionPickerLabel(uint8_t index, uint8_t line, bool big)
{
char label[20];
strcpy(label, gSubMenu_SIDEFUNCTIONS[index].name);
char *newline = strchr(label, '\n');
if (newline != NULL)
*newline = ' ';
if (big)
UI_PrintString(label, 0, LCD_WIDTH, line, 8);
else
UI_PrintStringSmallNormal(label, 0, LCD_WIDTH, line);
}
#endif
void UI_DisplayMain(void)
{
char String[22];
@@ -1342,6 +1362,25 @@ void UI_DisplayMain(void)
return;
}
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
if (gActionPickerKey != 0) {
const uint8_t selection = gActionPickerSelection[gActionPickerKey - 1];
uint8_t previous = selection - 1;
uint8_t next = selection + 1;
if (previous == 0)
previous = gSubMenu_SIDEFUNCTIONS_size - 1;
if (next >= gSubMenu_SIDEFUNCTIONS_size)
next = 1;
UI_PrintActionPickerLabel(previous, 1, false);
UI_PrintActionPickerLabel(selection, 2, true);
UI_PrintActionPickerLabel(next, 4, false);
ST7565_BlitFullScreen();
return;
}
#endif
#ifndef ENABLE_FEAT_F4HWN
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
{ // tell user how to unlock the keyboard
+2
View File
@@ -74,6 +74,7 @@
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": false,
"ENABLE_FEAT_F4HWN_BEAM": false,
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
"ENABLE_FEAT_F4HWN_ACTION_PICKER": false,
"ENABLE_FEAT_F4HWN_RXTX_LOG": false,
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": false,
@@ -210,6 +211,7 @@
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true,
"ENABLE_FEAT_F4HWN_BEAM": true,
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
"ENABLE_FEAT_F4HWN_ACTION_PICKER": true,
"ENABLE_FEAT_F4HWN_RXTX_LOG": true,
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": true,