diff --git a/CHANGELOG.md b/CHANGELOG.md index 42cdccb..16cc0ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ single running list until the first versioned release. ## Unreleased +- **Theme presets** — selectable on the radio in Display Options → "Theme": + **Custom** (CPS-editable, default), **BASIC 80s** (Commodore 64 blue/light-blue), + and **Terminal** (green-on-black night / black-on-green day). The choice is stored + in the codeplug custom-data area (migration-safe; defaults to Custom), and CPS + theme edits only ever change Custom. - **Fix:** green LED **flickering** between the red flashes while muted during a DMR QSO. The DMR RX handler re-asserts the green LED as soon as it reads it off, racing the main-loop mute clear; the green LED is now **held off at the driver** for the diff --git a/MANUAL.md b/MANUAL.md index 7e3924e..8c99540 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -58,6 +58,17 @@ Optionally dims the backlight after inactivity to **100 / 50 / 30 / 15 %** of th configured brightness (only in **Auto** backlight mode). Any activity restores full brightness. Set it in **Menu → Options → Display Options → Screen Dim**. +### Theme presets — 🟢 [FreeTRX] +Pick a colour theme on the radio at **Menu → Options → Display Options → "Theme"** +(◀/▶ to change; the screen updates live): + +- **Custom** — the theme edited via the CPS or the on-radio colour editor (default). +- **BASIC 80s** — a Commodore-64 look (light blue on blue). +- **Terminal** — a green terminal (green-on-black at night, black-on-green by day). + +Editing colours in the CPS only affects **Custom**; the two built-in presets are +fixed. Every element (S-meter, frequencies, menus) follows the selected theme. + ### Main-menu icons — 🟢 [FreeTRX] Each top-level menu entry has a small icon next to it. diff --git a/MDUV380_firmware/application/include/functions/codeplug.h b/MDUV380_firmware/application/include/functions/codeplug.h index 98cfab6..36f180c 100644 --- a/MDUV380_firmware/application/include/functions/codeplug.h +++ b/MDUV380_firmware/application/include/functions/codeplug.h @@ -353,6 +353,7 @@ typedef enum CODEPLUG_CUSTOM_DATA_TYPE_SATELLITE_TLE, CODEPLUG_CUSTOM_DATA_TYPE_THEME_DAY, CODEPLUG_CUSTOM_DATA_TYPE_THEME_NIGHT, + CODEPLUG_CUSTOM_DATA_TYPE_THEME_PRESET, // FreeTRX: 1 byte, selected theme preset (themePreset_t) } CodeplugCustomDataType_t; diff --git a/MDUV380_firmware/application/include/hardware/HX8353E.h b/MDUV380_firmware/application/include/hardware/HX8353E.h index ea920e0..60342a4 100644 --- a/MDUV380_firmware/application/include/hardware/HX8353E.h +++ b/MDUV380_firmware/application/include/hardware/HX8353E.h @@ -269,8 +269,21 @@ void displayDrawFrequencySevenSeg(int16_t x, int16_t y, int16_t cellW, int16_t c #endif #if defined(HAS_COLOURS) +// FreeTRX selectable theme presets. CUSTOM is the CPS-editable theme (default); +// the others are fixed palettes baked into the firmware. +typedef enum +{ + THEME_PRESET_CUSTOM = 0, // editable via CPS / on-device colour editor + THEME_PRESET_BASIC80S, // Commodore 64 BASIC (light blue on blue) + THEME_PRESET_TERMINAL, // green terminal (green-on-black night / black-on-green day) + NUM_THEME_PRESETS +} themePreset_t; + void themeInitToDefaultValues(DayTime_t daytime, bool invert); void themeInit(bool SPIFlashAvailable); +void themeApplyPreset(uint8_t preset); // rebuild themeItems[] for the given preset +uint8_t themeGetPreset(void); // read the stored preset (default CUSTOM) +void themeSetPreset(uint8_t preset, bool save); // apply, and optionally persist to the codeplug void displayThemeApply(themeItem_t fgItem, themeItem_t bgItem); void displayThemeResetToDefault(void); bool displayThemeIsForegroundColourEqualTo(themeItem_t fgItem); diff --git a/MDUV380_firmware/application/source/hardware/HX8353E_display.c b/MDUV380_firmware/application/source/hardware/HX8353E_display.c index a9836cb..7613cda 100644 --- a/MDUV380_firmware/application/source/hardware/HX8353E_display.c +++ b/MDUV380_firmware/application/source/hardware/HX8353E_display.c @@ -1510,28 +1510,104 @@ void themeInitToDefaultValues(DayTime_t daytime, bool invert) themeItems[daytime][THEME_ITEM_FG_BD_COLOUR] = PLATFORM_COLOUR_FORMAT_SWAP_BYTES(RGB888_TO_PLATFORM_COLOUR_FORMAT(0xFF0000U)); } -void themeInit(bool SPIFlashAvailable) +// Set every theme item to fgRGB, then override the background items with bgRGB, for +// a cohesive single-palette look. The selected-menu-item background is intentionally +// left as the foreground colour so the highlighted row still inverts. +static void themeSetAllItems(DayTime_t daytime, uint32_t fgRGB, uint32_t bgRGB) { + uint16_t fg = PLATFORM_COLOUR_FORMAT_SWAP_BYTES(RGB888_TO_PLATFORM_COLOUR_FORMAT(fgRGB)); + uint16_t bg = PLATFORM_COLOUR_FORMAT_SWAP_BYTES(RGB888_TO_PLATFORM_COLOUR_FORMAT(bgRGB)); + + for (int i = 0; i < THEME_ITEM_MAX; i++) + { + themeItems[daytime][i] = fg; + } + themeItems[daytime][THEME_ITEM_BG] = bg; + themeItems[daytime][THEME_ITEM_BG_SPLASHSCREEN] = bg; + themeItems[daytime][THEME_ITEM_BG_NOTIFICATION] = bg; + themeItems[daytime][THEME_ITEM_BG_MENU_NAME] = bg; + themeItems[daytime][THEME_ITEM_BG_HEADER_TEXT] = bg; + // THEME_ITEM_BG_MENU_ITEM_SELECTED intentionally left = fg (inverts the selected row). +} + +void themeApplyPreset(uint8_t preset) +{ + // Start from the monochrome defaults, then overlay the chosen preset. themeInitToDefaultValues(NIGHT, true); themeInitToDefaultValues(DAY, false); - // Read and apply user's theme, if any + switch (preset) + { + case THEME_PRESET_BASIC80S: // Commodore 64 BASIC: light blue on blue (day & night) + themeSetAllItems(DAY, 0x7C71D5, 0x3E31A2); + themeSetAllItems(NIGHT, 0x7C71D5, 0x3E31A2); + break; + + case THEME_PRESET_TERMINAL: + themeSetAllItems(DAY, 0x000000, 0x00A000); // black on green + themeSetAllItems(NIGHT, 0x00E000, 0x000000); // green on black + break; + + case THEME_PRESET_CUSTOM: + default: + // Overlay the user's Custom theme from the codeplug, if present (CPS edits + // only ever touch this). + { + uint16_t themingTmp[THEME_ITEM_MAX]; + + if (codeplugGetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_DAY, (uint8_t *) &themingTmp)) + { + memcpy(&themeItems[DAY], &themingTmp, sizeof(themingTmp)); + } + if (codeplugGetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_NIGHT, (uint8_t *) &themingTmp)) + { + memcpy(&themeItems[NIGHT], &themingTmp, sizeof(themingTmp)); + } + } + break; + } + + foregroundColour = themeItems[DAY][THEME_ITEM_FG_DEFAULT]; + backgroundColour = themeItems[DAY][THEME_ITEM_BG]; +} + +uint8_t themeGetPreset(void) +{ + uint8_t buf[4] = { THEME_PRESET_CUSTOM, 0, 0, 0 }; + + if (codeplugGetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_PRESET, buf)) + { + return (buf[0] < NUM_THEME_PRESETS) ? buf[0] : THEME_PRESET_CUSTOM; + } + return THEME_PRESET_CUSTOM; +} + +void themeSetPreset(uint8_t preset, bool save) +{ + if (preset >= NUM_THEME_PRESETS) + { + preset = THEME_PRESET_CUSTOM; + } + + themeApplyPreset(preset); + + if (save) + { + uint8_t buf[1] = { preset }; + codeplugSetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_PRESET, buf, sizeof(buf)); + } +} + +void themeInit(bool SPIFlashAvailable) +{ if (SPIFlashAvailable) { - uint16_t themingTmp[THEME_ITEM_MAX]; - - if (codeplugGetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_DAY, (uint8_t *) &themingTmp)) - { - memcpy(&themeItems[DAY], &themingTmp, sizeof(themingTmp)); - - foregroundColour = themeItems[DAY][THEME_ITEM_FG_DEFAULT]; - backgroundColour = themeItems[DAY][THEME_ITEM_BG]; - } - - if (codeplugGetOpenGD77CustomData(CODEPLUG_CUSTOM_DATA_TYPE_THEME_NIGHT, (uint8_t *) &themingTmp)) - { - memcpy(&themeItems[NIGHT], &themingTmp, sizeof(themingTmp)); - } + themeApplyPreset(themeGetPreset()); + } + else + { + themeInitToDefaultValues(NIGHT, true); + themeInitToDefaultValues(DAY, false); } if (settingsIsOptionBitSet(BIT_AUTO_NIGHT_OVERRIDE) && (uiDataGlobal.daytimeOverridden == NIGHT)) diff --git a/MDUV380_firmware/application/source/user_interface/menuDisplayOptions.c b/MDUV380_firmware/application/source/user_interface/menuDisplayOptions.c index 68bfe26..f791474 100644 --- a/MDUV380_firmware/application/source/user_interface/menuDisplayOptions.c +++ b/MDUV380_firmware/application/source/user_interface/menuDisplayOptions.c @@ -104,6 +104,9 @@ enum DISPLAY_SHOW_DISTANCE, DISPLAY_DMR_LAST_TALKER_ON_SCREEN, DISPLAY_SCREEN_DIM, +#if defined(HAS_COLOURS) + DISPLAY_THEME_PRESET, +#endif NUM_DISPLAY_MENU_ITEMS }; @@ -269,6 +272,18 @@ static void updateScreen(bool isFirstRun) } break; +#if defined(HAS_COLOURS) + case DISPLAY_THEME_PRESET: + { + // Preset names are plain literals -> rightSideVar (voice-safe). + const char *presetNames[] = { "Custom", "BASIC 80s", "Terminal" }; + uint8_t p = themeGetPreset(); + leftSide = "Theme"; + snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%s", presetNames[(p < NUM_THEME_PRESETS) ? p : 0]); + } + break; +#endif + #if ! defined(PLATFORM_GD77S) case DISPLAY_AUTO_NIGHT: leftSide = currentLanguage->auto_night; @@ -630,6 +645,19 @@ static void handleEvent(uiEvent_t *ev) } break; +#if defined(HAS_COLOURS) + case DISPLAY_THEME_PRESET: + { + uint8_t p = themeGetPreset(); + if (p < (NUM_THEME_PRESETS - 1)) + { + themeSetPreset(p + 1, true); // apply + persist, live preview + displayThemeResetToDefault(); + } + } + break; +#endif + #if ! defined(PLATFORM_GD77S) case DISPLAY_AUTO_NIGHT: if (settingsIsOptionBitSet(BIT_AUTO_NIGHT) == false) @@ -839,6 +867,19 @@ static void handleEvent(uiEvent_t *ev) } break; +#if defined(HAS_COLOURS) + case DISPLAY_THEME_PRESET: + { + uint8_t p = themeGetPreset(); + if (p > THEME_PRESET_CUSTOM) + { + themeSetPreset(p - 1, true); // apply + persist, live preview + displayThemeResetToDefault(); + } + } + break; +#endif + #if ! defined(PLATFORM_GD77S) case DISPLAY_AUTO_NIGHT: if (settingsIsOptionBitSet(BIT_AUTO_NIGHT)) diff --git a/README.md b/README.md index 5c5eb3b..8181293 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ FreeTRX keeps OpenGD77's core (DMR/FM, hotspot, CPS compatibility) and adds: - **7-segment "LED" frequency font** on the VFO and the SK1-held frequency view. - **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 / 15 % after inactivity (Auto backlight mode); new setting in Display Options. +- **Theme presets** — pick a colour theme on the radio: **Custom** (CPS-editable, + default), **BASIC 80s** (Commodore 64), or **Terminal** (green-on-black). CPS edits + only affect Custom. - **On-radio channel management** — create and delete channels directly from the Channel quick menu, no PC/CPS required (see [Managing channels](#managing-channels-on-the-radio)). - **Main-menu icons** next to each top-level entry.