Compare commits

...

2 commits

Author SHA1 Message Date
Marcus Kida
975661689a Build: report per-region memory usage after each build
Pass --print-memory-usage to the linker and re-print its FLASH/RAM/CCMRAM
table in the build-script summary with a computed Free column, so the
headroom for further features is visible at the end of every build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 10:49:22 +02:00
Marcus Kida
c614ba8096 Themes: add 'Red' and multi-colour 'Neon 80s' presets
Red mirrors the two-tone Terminal/Vertexic presets (black-on-red day,
red-on-black night). Neon 80s is a synthwave palette that assigns distinct
neon colours (cyan, magenta, violet, green, yellow, red, orange) to the
individual theme items over a dark background, demonstrating that presets
are not limited to two colours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 10:40:37 +02:00
6 changed files with 97 additions and 3 deletions

View file

@ -7,7 +7,10 @@ single running list until the first versioned release.
## Unreleased
_Nothing yet._
- **Two new theme presets** (Display Options → "Theme"): **Red** (black-on-red
day / red-on-black night) and **Neon 80s**, an 80s-synthwave palette that maps
several neon colours (cyan, magenta, violet, green, yellow) across the
individual UI elements over a dark background.
## 0.7.1

View file

@ -277,6 +277,8 @@ typedef enum
THEME_PRESET_BASIC80S, // Commodore 64 BASIC (light blue on blue)
THEME_PRESET_TERMINAL, // green terminal (green-on-black night / black-on-green day)
THEME_PRESET_VERTEXIC, // amber/orange (black-on-amber day / amber-on-black night)
THEME_PRESET_RED, // red (black-on-red day / red-on-black night)
THEME_PRESET_NEON80S, // 80s synthwave: cyan/magenta/violet/green neon on dark
NUM_THEME_PRESETS
} themePreset_t;

View file

@ -1530,6 +1530,60 @@ static void themeSetAllItems(DayTime_t daytime, uint32_t fgRGB, uint32_t bgRGB)
// THEME_ITEM_BG_MENU_ITEM_SELECTED intentionally left = fg (inverts the selected row).
}
static void themeSetItem(DayTime_t daytime, themeItem_t item, uint32_t rgb)
{
themeItems[daytime][item] = PLATFORM_COLOUR_FORMAT_SWAP_BYTES(RGB888_TO_PLATFORM_COLOUR_FORMAT(rgb));
}
// 80s synthwave: a dark background with several neon accents mapped onto the
// individual UI elements. Both day and night use the same neon palette so the
// look is preserved; only the background darkens for night.
static void themeSetNeon80s(DayTime_t daytime, uint32_t bg)
{
const uint32_t cyan = 0x00F0FF;
const uint32_t magenta = 0xFF2CA5;
const uint32_t green = 0x39FF14;
const uint32_t violet = 0xB16CFF;
const uint32_t yellow = 0xFFE600;
const uint32_t red = 0xFF3131;
const uint32_t orange = 0xFF7A18;
// Base: cyan text on the dark background, everything else overridden below.
themeSetAllItems(daytime, cyan, bg);
themeSetItem(daytime, THEME_ITEM_FG_DECORATION, magenta); // borders / separators / drop shadow
themeSetItem(daytime, THEME_ITEM_FG_TEXT_INPUT, green);
themeSetItem(daytime, THEME_ITEM_FG_SPLASHSCREEN, magenta);
themeSetItem(daytime, THEME_ITEM_FG_NOTIFICATION, cyan);
themeSetItem(daytime, THEME_ITEM_FG_WARNING_NOTIFICATION, yellow);
themeSetItem(daytime, THEME_ITEM_FG_ERROR_NOTIFICATION, red);
themeSetItem(daytime, THEME_ITEM_FG_MENU_NAME, magenta); // menu header
themeSetItem(daytime, THEME_ITEM_BG_MENU_ITEM_SELECTED, magenta); // selected row = magenta bar
themeSetItem(daytime, THEME_ITEM_FG_OPTIONS_VALUE, green);
themeSetItem(daytime, THEME_ITEM_FG_HEADER_TEXT, violet); // mode/PWR/battery header
themeSetItem(daytime, THEME_ITEM_FG_RSSI_BAR, cyan);
themeSetItem(daytime, THEME_ITEM_FG_RSSI_BAR_S9P, magenta); // over S9 turns hot pink
themeSetItem(daytime, THEME_ITEM_FG_CHANNEL_NAME, yellow);
themeSetItem(daytime, THEME_ITEM_FG_CHANNEL_CONTACT, magenta);
themeSetItem(daytime, THEME_ITEM_FG_CHANNEL_CONTACT_INFO, cyan);
themeSetItem(daytime, THEME_ITEM_FG_ZONE_NAME, violet);
themeSetItem(daytime, THEME_ITEM_FG_RX_FREQ, cyan);
themeSetItem(daytime, THEME_ITEM_FG_TX_FREQ, red);
themeSetItem(daytime, THEME_ITEM_FG_CSS_SQL_VALUES, green);
themeSetItem(daytime, THEME_ITEM_FG_TX_COUNTER, yellow);
themeSetItem(daytime, THEME_ITEM_FG_POLAR_DRAWING, violet);
themeSetItem(daytime, THEME_ITEM_FG_SATELLITE_COLOUR, magenta);
themeSetItem(daytime, THEME_ITEM_FG_GPS_NUMBER, cyan);
themeSetItem(daytime, THEME_ITEM_FG_GPS_COLOUR, green);
themeSetItem(daytime, THEME_ITEM_FG_BD_COLOUR, orange);
}
void themeApplyPreset(uint8_t preset)
{
// Start from the monochrome defaults, then overlay the chosen preset.
@ -1553,6 +1607,16 @@ void themeApplyPreset(uint8_t preset)
themeSetAllItems(NIGHT, 0xFF6A00, 0x000000); // orange on black
break;
case THEME_PRESET_RED:
themeSetAllItems(DAY, 0x000000, 0xFF0000); // black on red
themeSetAllItems(NIGHT, 0xFF0000, 0x000000); // red on black
break;
case THEME_PRESET_NEON80S: // 80s synthwave neon on dark
themeSetNeon80s(DAY, 0x1B0B2E); // dark indigo
themeSetNeon80s(NIGHT, 0x08040F); // near-black purple
break;
case THEME_PRESET_CUSTOM:
default:
// Overlay the user's Custom theme from the codeplug, if present (CPS edits

View file

@ -276,7 +276,7 @@ static void updateScreen(bool isFirstRun)
case DISPLAY_THEME_PRESET:
{
// Preset names are plain literals -> rightSideVar (voice-safe).
const char *presetNames[] = { "Custom", "BASIC 80s", "Terminal", "Vertexic" };
const char *presetNames[] = { "Custom", "BASIC 80s", "Terminal", "Vertexic", "Red", "Neon 80s" };
uint8_t p = themeGetPreset();
leftSide = "Theme";
snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%s", presetNames[(p < NUM_THEME_PRESETS) ? p : 0]);

View file

@ -22,14 +22,38 @@ echo "==> Building toolchain image ($IMAGE)"
docker build --platform linux/amd64 -t "$IMAGE" "$REPO_DIR/docker"
echo "==> Running build in container ($PLATFORM)"
BUILD_LOG="$(mktemp)"
trap 'rm -f "$BUILD_LOG"' EXIT
# Stream to the terminal and capture a copy so we can re-show the linker's
# memory-usage table at the end (pipefail makes a build failure still abort).
docker run --rm --platform linux/amd64 \
-v "$REPO_DIR":/work \
"$IMAGE" \
bash /work/docker/build.sh "$PLATFORM"
bash /work/docker/build.sh "$PLATFORM" 2>&1 | tee "$BUILD_LOG"
echo
echo "==> Firmware ready:"
echo " $REPO_DIR/MDUV380_firmware/build/${OUT}.bin"
# Re-print the linker's per-region memory usage (emitted by --print-memory-usage
# during the link) with the free bytes worked out for each region.
if grep -q "Memory region" "$BUILD_LOG"; then
echo
echo "==> Memory usage (FLASH = code budget, RAM/CCMRAM = data budget):"
grep -A3 "Memory region" "$BUILD_LOG" | awk '
function toBytes(n, unit) {
if (unit == "KB") return n * 1024
if (unit == "MB") return n * 1024 * 1024
if (unit == "GB") return n * 1024 * 1024 * 1024
return n # already bytes
}
NR == 1 { printf " %-9s %11s %12s %8s %11s\n", "Region", "Used", "Size", "Used%", "Free"; next }
/:/ {
gsub(":", "", $1)
used = toBytes($2, $3); size = toBytes($4, $5); free = size - used
printf " %-9s %9d B %10d B %7s %9d B\n", $1, used, size, $6, free
}'
fi
echo
echo "Flash it (with the radio in firmware-update mode) using:"
echo " ./MDUV380_firmware/tools/opengd77_stm32_firmware_loader.py \\"

View file

@ -63,6 +63,7 @@ ASFLAGS := $(CPU) -x assembler-with-cpp -DDEBUG -g3 -MMD -MP
LDSCRIPT := ../STM32F405VGTX_FLASH.ld
LDFLAGS := $(CPU) -T$(LDSCRIPT) --specs=nano.specs \
-Wl,-Map=$(TARGET).map,--cref -Wl,--gc-sections -static \
-Wl,--print-memory-usage \
-Wl,--start-group -lc -lm -Wl,--end-group
# ---- Sources --------------------------------------------------------------