FreeTRX/MDUV380_firmware/application/include/user_interface/menuSystem.h
2026-07-06 14:41:43 +02:00

487 lines
17 KiB
C

/*
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
* Daniel Caujolle-Bert, F1RMB
*
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _OPENGD77_MENUSYSTEM_H_
#define _OPENGD77_MENUSYSTEM_H_
#include "main.h"
#include "user_interface/uiGlobals.h"
#include "functions/sound.h"
#include "functions/settings.h"
#include "functions/voicePrompts.h"
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
#if defined(PLATFORM_VARIANT_DM1701)
#define MENU_MAX_DISPLAYED_ENTRIES 6
#else
#define MENU_MAX_DISPLAYED_ENTRIES 7
#endif
#else
// Black and white smaller height screeens (MD9600 and GD77 etc)
#define MENU_MAX_DISPLAYED_ENTRIES 3
#endif
// These macros are taking care of the odd/even value of MENU_MAX_DISPLAYED_ENTRIES (-x..0..+x)
#define MENU_START_ITERATION_VALUE ((MENU_MAX_DISPLAYED_ENTRIES & 0x01) ? (1 - ((MENU_MAX_DISPLAYED_ENTRIES - 1) / 2) - 1) : (1 - (MENU_MAX_DISPLAYED_ENTRIES / 2)))
#define MENU_END_ITERATION_VALUE ((MENU_MAX_DISPLAYED_ENTRIES & 0x01) ? (MENU_MAX_DISPLAYED_ENTRIES - ((MENU_MAX_DISPLAYED_ENTRIES - 1) / 2) - 1) : (MENU_MAX_DISPLAYED_ENTRIES - (MENU_MAX_DISPLAYED_ENTRIES / 2)))
// Short press event
#define BUTTONCHECK_SHORTUP(e, sk) (((e)->keys.key == 0) && ((e)->buttons & sk ## _SHORT_UP))
// Long press event
#define BUTTONCHECK_LONGDOWN(e, sk) (((e)->keys.key == 0) && ((e)->buttons & sk ## _LONG_DOWN))
// Extra long press event
#define BUTTONCHECK_EXTRALONGDOWN(e, sk) (((e)->keys.key == 0) && ((e)->buttons & sk ## _EXTRA_LONG_DOWN))
// SK*/ORANGE button is down, regardless event status
#define BUTTONCHECK_DOWN(e, sk) (((e)->buttons & sk))
// SK*/ORANGE button is the only one down, regardless event status.
#define BUTTONCHECK_UNIQUE_DOWN(e, sk) (((e)->buttons & sk) && (((e)->buttons & ~(sk | sk ## _SHORT_UP | sk ## _LONG_DOWN | sk ## _EXTRA_LONG_DOWN)) == 0))
typedef enum
{
NO_EVENT = 0,
KEY_EVENT = (1 << 0),
BUTTON_EVENT = (1 << 1),
FUNCTION_EVENT = (1 << 2),
ROTARY_EVENT = (1 << 3),
SYNTHETIC_EVENT = (1 << 7)
} uiEventInput_t;
typedef enum
{
CPS2UI_COMMAND_CLEARBUF = 0,
CPS2UI_COMMAND_PRINT,
CPS2UI_COMMAND_RENDER_DISPLAY,
CPS2UI_COMMAND_BACKLIGHT,
CPS2UI_COMMAND_GREEN_LED,
CPS2UI_COMMAND_RED_LED,
CPS2UI_COMMAND_END
} uiCPSCommand_t;
typedef enum
{
MENU_STATUS_SUCCESS = 0,
MENU_STATUS_ERROR = (1 << 0),
MENU_STATUS_LIST_TYPE = (1 << 1),
MENU_STATUS_INPUT_TYPE = (1 << 2),
MENU_STATUS_FORCE_FIRST = (1 << 3)
} menuStatus_t;
typedef enum
{
NOTIFICATION_TYPE_SQUELCH = 0,
NOTIFICATION_TYPE_POWER,
#if defined(HAS_SOFT_VOLUME)
NOTIFICATION_TYPE_VOLUME,
#endif
NOTIFICATION_TYPE_MESSAGE,
NOTIFICATION_TYPE_BEARING,
NOTIFICATION_TYPE_MAX
} uiNotificationType_t;
typedef enum
{
NOTIFICATION_ID_NONE = 0,
NOTIFICATION_ID_SQUELCH,
#if defined(HAS_SOFT_VOLUME)
NOTIFICATION_ID_VOLUME,
#endif
NOTIFICATION_ID_POWER,
NOTIFICATION_ID_MESSAGE,
NOTIFICATION_ID_BEARING,
NOTIFICATION_ID_TX_INHIBIT,
NOTIFICATION_ID_USER /* Could use _USER, _USER + 1, + 2, etc... */
} uiNotificationID_t;
typedef struct
{
double bearing;
int32_t distanceX10;
} uiNotificationBearingInfo_t;
#define NOTIFICATION_ID_USER_APO (NOTIFICATION_ID_USER)
typedef struct
{
uint32_t buttons;
keyboardCode_t keys;
uint32_t rotary;
uint16_t function;
uiEventInput_t events;
bool hasEvent;
uint32_t time;
} uiEvent_t;
typedef menuStatus_t (*menuFunctionPointer_t)(uiEvent_t *, bool); // Typedef for menu function pointers. Functions are passed the key, the button and the event data. Event can be a Key or a button or both. Last arg is for when the function is only called to initialise and display its screen.
typedef void (*menuExitCallback_t)(void *data);
typedef struct
{
const menuFunctionPointer_t function;
menuExitCallback_t exitCallback;
void *data;
int lastItemIndex;
} menuFunctionData_t;
typedef struct
{
int currentMenuNumber;
int stackPosition;
int stack[16];
} menuControlDataStruct_t;
typedef struct
{
const int stringOffset; // String offset in stringsTable_t
const int menuNum;
} menuItemNewData_t;
typedef struct
{
const int numItems;
const menuItemNewData_t *items;
} menuItemsList_t;
void menuDisplayTitle(const char *title);
void menuDisplayEntry(int loopOffset, int focusedItem, const char *entryText, int32_t optStart, themeItem_t fgItem, themeItem_t fgOptItem, themeItem_t bgItem);
#if defined(HAS_COLOURS)
void menuSetNextEntryIconByMenuNum(int menuNum); // sets the icon the next menuDisplayEntry() will draw (0/none for menus without an icon)
#endif
// menuGetMenuOffset() return values
#define MENU_OFFSET_BEFORE_FIRST_ENTRY -1
#define MENU_OFFSET_AFTER_LAST_ENTRY -2
int menuGetMenuOffset(int maxMenuItems, int loopOffset);
void uiChannelModeUpdateScreen(int txTimeSecs);
void uiChannelModeLoadChannelData(bool useChannelDataInMemory, bool loadVoicePromptAnnouncement);
void uiChannelModeColdStart(void);
void uiVFOModeUpdateScreen(int txTimeSecs);
void uiVFOModeLoadChannelData(bool forceAPRSReset);
void uiVFOLoadContact(CodeplugContact_t *contact);
bool uiVFOModeIsTXFocused(void);
void uiVFOModeStopScanning(void);
bool uiVFOModeIsScanning(void);
bool uiVFOModeDualWatchIsScanning(void);
bool uiVFOModeSweepScanning(bool includePaused);
void uiVFOSweepScanModePause(bool pause, bool forceDigitalOnPause);
bool uiVFOModeFrequencyScanningIsActiveAndEnabled(uint32_t *lowFreq, uint32_t *highFreq);
void uiChannelModeStopScanning(void);
bool uiChannelModeIsScanning(void);
void uiChannelInitializeCurrentZone(void);
void uiCPSUpdate(uiCPSCommand_t command, int x, int y, ucFont_t fontSize, ucTextAlign_t alignment, bool isInverted, char *szMsg);
void menuSystemInit(time_t_custom UTCdateTimeInSecs);
void menuSystemLanguageHasChanged(void);
void displayLightTrigger(bool fromKeyEvent);
void displayLightOverrideTimeout(int timeout);
int menuSystemGetLastItemIndex(int stackPos);
void menuSystemCallExitCallback(void);
void menuSystemRegisterExitCallback(menuExitCallback_t f, void *data);
void menuSystemPushNewMenu(int menuNumber);
void menuSystemSetCurrentMenu(int menuNumber);
int menuSystemGetCurrentMenuNumber(void);
int menuSystemGetPreviousMenuNumber(void);
int menuSystemGetPreviouslyPushedMenuNumber(bool keepIt);
int menuSystemGetRootMenuNumber(void);
void menuSystemPopPreviousMenu(void);
void menuSystemPopAllAndDisplayRootMenu(void);
void menuSystemPopAllAndDisplaySpecificRootMenu(int newRootMenu, bool resetKeyboard);
void menuSystemCallCurrentMenuTick(uiEvent_t *ev);
int menuGetKeypadKeyValue(uiEvent_t *ev, bool digitsOnly);
void menuUpdateCursor(int pos, bool moved, bool render);
void moveCursorLeftInString(char *str, int *pos, bool delete);
void moveCursorRightInString(char *str, int *pos, int max, bool insert);
void menuRadioInfosInit(void);
void menuRadioInfosPushBackVoltage(int32_t voltage);
void menuLockScreenPop(void);
void menuLastHeardUpdateScreen(bool showTitleOrHeader, bool displayDetails, bool isFirstRun);
void menuLastHeardInit(void); // Only used in uiTXScreen
void menuLastHeardHandleEvent(uiEvent_t *ev); // Only used in uiTXScreen
void menuPrivateCallClear(void);
void menuPrivateCallDismiss(void);
void menuHotspotRestoreSettings(void);
bool menuTxScreenDisplaysLastHeard(void);
void menuTxScreenHandleTxTermination(uiEvent_t *ev, txTerminationReason_t reason);
void menuSatelliteScreenClearPredictions(bool reloadKeps);
bool menuSatelliteIsDisplayingHeader(void);
void menuSatelliteTxScreen(uint32_t txTimeSecs);
void menuSatelliteSetFullReload(void);
void menuSystemMenuIncrement(int32_t *O, int32_t M);
void menuSystemMenuDecrement(int32_t *O, int32_t M);
// For QuickKeys
void menuDisplaySettingOption(const char *entryText, const char *valueText);
#if defined(PLATFORM_GD77S)
bool uiChannelModeTransmitDTMFContactForGD77S(void);
void uiChannelModeHeartBeatActivityForGD77S(uiEvent_t *ev);
#endif
void uiNotificationShow(uiNotificationType_t type, uiNotificationID_t id, uint32_t msTimeout, const char *message, bool immediateRender);
void uiNotificationBearingShow(uiNotificationBearingInfo_t *bearing, uint32_t msTimeout, bool immediateRender);
void uiNotificationRefresh(void);
bool uiNotificationHasTimedOut(void);
bool uiNotificationIsVisible(void);
void uiNotificationHide(bool immediateRender);
uiNotificationID_t uiNotificationGetId(void);
#if defined(HAS_GPS)
extern const uint8_t MENU_GENERAL_OPTIONS_GPS_ENTRY_NUMBER;
#endif
//
// ---- Menu internals ----
//
/*
* ---------------------- IMPORTANT ----------------------------
*
* These enums must match the menuFunctions array in menuSystem.c
*
* ---------------------- IMPORTANT ----------------------------
*/
enum MENU_SCREENS
{
MENU_EMPTY = -1,
MENU_ANY = MENU_EMPTY,
MENU_MAIN_MENU = 0,
MENU_CONTACTS_MENU,
MENU_ZONE_LIST,
MENU_RADIO_INFOS,
MENU_RSSI_SCREEN,
MENU_LAST_HEARD,
MENU_OPTIONS,// Top level menu 'Options'
MENU_GENERAL,// General options
MENU_RADIO,// Radio options
MENU_DISPLAY,// Display options
MENU_SOUND,// Sound options
MENU_SATELLITE,
#if defined(HAS_GPS)
MENU_GPS,
#endif
MENU_CONTACT_LIST,
MENU_DTMF_CONTACT_LIST,
MENU_CONTACT_QUICKLIST,
MENU_CONTACT_LIST_SUBMENU,
MENU_CONTACT_DETAILS,
MENU_LANGUAGE,
MENU_CALIBRATION,
#if defined(HAS_COLOURS)
MENU_THEME,
#endif
UI_CHANNEL_QUICK_MENU,
UI_VFO_QUICK_MENU,
MENU_CHANNEL_DETAILS,
MENU_FIRMWARE_INFO,
#if !defined(PLATFORM_GD77S)
MENU_APRS,
#endif
// *** Add new menus to be accessed using quickkey (ID: 0..31) above this line ***
UI_MESSAGE_BOX,
UI_HOTSPOT_MODE,
UI_CPS,
MENU_NUMERICAL_ENTRY,
UI_TX_SCREEN,
UI_SPLASH_SCREEN,
UI_POWER_OFF,
UI_VFO_MODE,
UI_CHANNEL_MODE,
UI_LOCK_SCREEN,
UI_PRIVATE_CALL,
MENU_CONTACT_NEW,
#if defined(HAS_COLOURS)
MENU_THEME_ITEMS_BROWSER,
MENU_COLOUR_PICKER,
#endif
NUM_MENU_ENTRIES
};
// QuickMenus
enum CHANNEL_SCREEN_QUICK_MENU_ITEMS
{
CH_SCREEN_QUICK_MENU_COPY2VFO = 0,
CH_SCREEN_QUICK_MENU_COPY_FROM_VFO,
CH_SCREEN_QUICK_MENU_FILTER_FM,
CH_SCREEN_QUICK_MENU_FILTER_DMR,
CH_SCREEN_QUICK_MENU_DMR_CC_SCAN,
CH_SCREEN_QUICK_MENU_FILTER_DMR_TS,
CH_SCREEN_QUICK_MENU_TALKAROUND,
CH_SCREEN_QUICK_MENU_ROAMING,
CH_SCREEN_QUICK_MENU_AUDIO_MUTE,
NUM_CH_SCREEN_QUICK_MENU_ITEMS
};
enum VFO_SCREEN_QUICK_MENU_ITEMS
{
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
VFO_SCREEN_QUICK_MENU_VFO_A_B = 0,
VFO_SCREEN_QUICK_MENU_TX_SWAP_RX,
#elif defined(PLATFORM_DM1801)
VFO_SCREEN_QUICK_MENU_TX_SWAP_RX = 0,
#endif
VFO_SCREEN_QUICK_MENU_BOTH_TO_RX,
VFO_SCREEN_QUICK_MENU_BOTH_TO_TX,
VFO_SCREEN_QUICK_MENU_FILTER_FM,
VFO_SCREEN_QUICK_MENU_FILTER_DMR,
VFO_SCREEN_QUICK_MENU_DMR_CC_SCAN,
VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS,
VFO_SCREEN_QUICK_MENU_VFO_TO_NEW,
VFO_SCREEN_QUICK_MENU_TONE_SCAN,
VFO_SCREEN_QUICK_MENU_DUAL_SCAN,
VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE,
VFO_SCREEN_QUICK_MENU_AUDIO_MUTE,
NUM_VFO_SCREEN_QUICK_MENU_ITEMS
};
// QuickKeys
#define QUICKKEY_CONTACT 0x0
#define QUICKKEY_MENU 0x1
#define QUICKKEY_TYPE(f) ((f>>15) & 0x1) // 1 bits
#define QUICKKEY_MENUID(f) ((f>>10) & 0x1f) // 5 bits
#define QUICKKEY_ENTRYID(f) ((f>>5) & 0x1f) // 5 bits
#define QUICKKEY_FUNCTIONID(f) (f & 0x1f) // 5 bits
#define QUICKKEY_LONGENTRYID(f) (f & 0x3ff) // 10 bits (Zones)
#define QUICKKEY_MENUVALUE(m, e, f) ((QUICKKEY_MENU << 15) | ((m & 0x1f) << 10) | ((e & 0x1f) << 5) | (f & 0x1f))
#define QUICKKEY_MENULONGVALUE(m, e) ((QUICKKEY_MENU << 15) | ((m & 0x1f) << 10) | (e & 0x1ff))
#define QUICKKEY_CONTACTVALUE(c) (c)
enum QUICK_FUNCTIONS
{
FUNC_START_SCANNING = QUICKKEY_MENUVALUE(0, 0, 1),
FUNC_RIGHT = 0x11,
FUNC_LEFT = 0x12,
FUNC_TOGGLE_TORCH = QUICKKEY_MENUVALUE(0, 0, 2),
FUNC_REDRAW = QUICKKEY_MENUVALUE(0, 0, 3)
};
// This is used to store current position in menus. The system keeps track of its value, e.g entering in
// a submenu, it will be restored exiting that submenu. It's up to the menuFunction() to override its
// value when isFirstRun == true.
typedef struct
{
int32_t currentItemIndex;
int32_t startIndex;
int32_t numItems;
int lightTimer;
menuItemNewData_t *currentMenuList;
menuControlDataStruct_t controlData;
char menuOptionsSetQuickkey;
int menuOptionsTimeout;
bool newOptionSelected;
const menuItemsList_t *data[];
} menuDataGlobal_t;
enum
{
RADIO_INFOS_BATTERY_LEVEL = 0,
RADIO_INFOS_CURRENT_TIME,
RADIO_INFOS_DATE,
RADIO_INFOS_LOCATION,
RADIO_INFOS_TEMPERATURE_LEVEL,
RADIO_INFOS_BATTERY_GRAPH,
NUM_RADIO_INFOS_MENU_ITEMS,
RADIO_INFOS_UP_TIME,
RADIO_INFOS_TIME_ALARM
};
extern menuDataGlobal_t menuDataGlobal;
extern const menuItemsList_t menuDataMainMenu;
extern const menuItemsList_t menuDataContact;
extern const menuItemsList_t menuDataOptions;
extern bool lockscreenIsRearming;
menuStatus_t uiVFOMode(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiVFOModeQuickMenu(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiChannelMode(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiChannelModeQuickMenu(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiCPS(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiSplashScreen(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiPowerOff(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuZoneList(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuDisplayMenuList(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuRadioInfos(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuFirmwareInfoScreen(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuNumericalEntry(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuTxScreen(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuRSSIScreen(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuLastHeard(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuGeneralOptions(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuRadioOptions(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuDisplayOptions(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuSoundOptions(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuCredits(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuChannelDetails(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuHotspotMode(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuLockScreen(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuContactList(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuContactListSubMenu(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuContactDetails(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuLanguage(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuPrivateCall(uiEvent_t *event, bool isFirstRun);
menuStatus_t uiMessageBox(uiEvent_t *event, bool isFirstRun);
menuStatus_t menuSatelliteScreen(uiEvent_t *ev, bool isFirstRun);
#if defined(HAS_GPS)
menuStatus_t menuGPS(uiEvent_t *event, bool isFirstRun);
#endif
menuStatus_t menuCalibration(uiEvent_t *ev, bool isFirstRun);
#if !defined(PLATFORM_GD77S)
menuStatus_t menuAPRSOptions(uiEvent_t *ev, bool isFirstRun);
#endif
#if defined(HAS_COLOURS)
menuStatus_t menuThemeOptions(uiEvent_t *ev, bool isFirstRun);
menuStatus_t menuThemeItemsBrowser(uiEvent_t *ev, bool isFirstRun);
menuStatus_t menuColourPicker(uiEvent_t *ev, bool isFirstRun);
#endif
#endif