diff --git a/App/app/action.c b/App/app/action.c index 9462bd7f..65affa67 100644 --- a/App/app/action.c +++ b/App/app/action.c @@ -502,7 +502,7 @@ void ACTION_FoxHunt(void) APP_RunFoxHunt(); GUI_SelectNextDisplay(DISPLAY_MAIN); #else - if (APP_LaunchOverlayByName("FoxHunt") != APP_OK) + if (APP_LaunchOverlayShortcut(APP_SHORTCUT_FOXHUNT) != APP_OK) gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; #endif } @@ -515,7 +515,7 @@ void ACTION_Beacon(void) APP_RunBeacon(); GUI_SelectNextDisplay(DISPLAY_MAIN); #else - if (APP_LaunchOverlayByName("Beacon") != APP_OK) + if (APP_LaunchOverlayShortcut(APP_SHORTCUT_BEACON) != APP_OK) gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; #endif } @@ -526,7 +526,7 @@ void ACTION_Beacon(void) void ACTION_FM(void) { #ifdef ENABLE_FEAT_F4HWN_OVERLAY_APPS - if (APP_LaunchOverlayByName("Broadcast FM") != APP_OK) + if (APP_LaunchOverlayShortcut(APP_SHORTCUT_FM) != APP_OK) gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; return; #else diff --git a/App/app/fm.c b/App/app/fm.c index 78f5c9a1..0d31fbf1 100644 --- a/App/app/fm.c +++ b/App/app/fm.c @@ -52,8 +52,9 @@ const uint8_t BUTTON_EVENT_HELD = BUTTON_STATE_PRESSED | BUTTON_STATE_HELD; const uint8_t BUTTON_EVENT_SHORT = 0; const uint8_t BUTTON_EVENT_LONG = BUTTON_STATE_HELD; - +#ifndef ENABLE_FEAT_F4HWN_OVERLAY_APPS static void Key_FUNC(KEY_Code_t Key, uint8_t state); +#endif bool FM_CheckValidChannel(uint8_t Channel) { @@ -94,6 +95,7 @@ int FM_ConfigureChannelState(void) return 0; } +#ifndef ENABLE_FEAT_F4HWN_OVERLAY_APPS void FM_SetFrequency(void) { BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/); @@ -196,6 +198,7 @@ void FM_PlayAndUpdate(void) BACKLIGHT_TurnOn(); FM_AudioPathOn(); } +#endif int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit) { @@ -240,6 +243,7 @@ int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit) return 0; } +#ifndef ENABLE_FEAT_F4HWN_OVERLAY_APPS static void Key_DIGITS(KEY_Code_t Key, uint8_t state) { enum { STATE_FREQ_MODE, STATE_MR_MODE, STATE_SAVE }; @@ -662,5 +666,31 @@ void FM_Start(void) SETTINGS_WriteCurrentState(); #endif } +#else +/* + * Broadcast FM is sovereign in Overlay builds: its app owns tuning, scanning, + * audio and persistence through app_api. Keep only the shared channel/state + * loader and FM_CheckFrequencyLock above; these tiny compatibility entry points + * satisfy generic firmware paths which cannot become active because Overlay + * builds never set gFmRadioMode or gFM_ScanState. + */ +void FM_TurnOff(void) {} +void FM_EraseChannels(void) {} +void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag) +{ + (void)Frequency; + (void)Step; + (void)bFlag; +} +void FM_PlayAndUpdate(void) {} +void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) +{ + (void)Key; + (void)bKeyPressed; + (void)bKeyHeld; +} +void FM_Play(void) {} +void FM_Start(void) {} +#endif #endif diff --git a/App/apps/app_overlay.c b/App/apps/app_overlay.c index 787214f9..3b19c5e1 100644 --- a/App/apps/app_overlay.c +++ b/App/apps/app_overlay.c @@ -671,11 +671,11 @@ static bool app_shortcuts_cached; static uint8_t app_shortcut_mask; static uint8_t app_shortcut_slots[3]; -static int8_t app_shortcut_index(const char *name) +static int8_t app_shortcut_index(uint8_t shortcut) { - if (strncmp(name, "Broadcast FM", APP_NAME_LEN) == 0) return 0; - if (strncmp(name, "FoxHunt", APP_NAME_LEN) == 0) return 1; - if (strncmp(name, "Beacon", APP_NAME_LEN) == 0) return 2; + if (shortcut == APP_SHORTCUT_FM) return 0; + if (shortcut == APP_SHORTCUT_FOXHUNT) return 1; + if (shortcut == APP_SHORTCUT_BEACON) return 2; return -1; } @@ -692,11 +692,12 @@ static void app_cache_shortcuts(void) if (APP_ValidateSlot(slot, &h) != APP_OK || h.link_vma != overlay_vma) continue; - const int8_t index = app_shortcut_index(h.name); + const uint8_t shortcut = (uint8_t)((h.flags & APP_FLAG_SHORTCUT_MASK) >> + APP_FLAG_SHORTCUT_SHIFT); + const int8_t index = app_shortcut_index(shortcut); if (index >= 0) { - const uint8_t bit = (uint8_t)(1u << index); - if (!(app_shortcut_mask & bit)) { - app_shortcut_mask |= bit; + if (!(app_shortcut_mask & shortcut)) { + app_shortcut_mask |= shortcut; app_shortcut_slots[index] = slot; } } @@ -711,25 +712,16 @@ uint8_t APP_OverlayShortcutMask(void) return app_shortcut_mask; } -uint8_t APP_LaunchOverlayByName(const char *name) +uint8_t APP_LaunchOverlayShortcut(uint8_t shortcut) { - const int8_t index = app_shortcut_index(name); - if (index >= 0) { - app_cache_shortcuts(); - return (app_shortcut_mask & (1u << index)) - ? APP_LaunchOverlay(app_shortcut_slots[index]) - : APP_ERR_MAGIC; - } + const int8_t index = app_shortcut_index(shortcut); + if (index < 0) + return APP_ERR_MAGIC; - app_header_t h; - - for (uint8_t slot = 0; slot < APP_SLOT_COUNT; slot++) { - if (APP_ValidateSlot(slot, &h) == APP_OK && - strncmp(h.name, name, APP_NAME_LEN) == 0) - return APP_LaunchOverlay(slot); - } - - return APP_ERR_MAGIC; + app_cache_shortcuts(); + return (app_shortcut_mask & shortcut) + ? APP_LaunchOverlay(app_shortcut_slots[index]) + : APP_ERR_MAGIC; } uint8_t APP_SlotInfo(uint8_t slot, app_header_t *out_header) diff --git a/App/apps/app_overlay.h b/App/apps/app_overlay.h index 3b0881df..93e5174c 100644 --- a/App/apps/app_overlay.h +++ b/App/apps/app_overlay.h @@ -68,12 +68,14 @@ #define APP_HDR_VERSION 1u #define APP_FLAG_COMMITTED 0x0001u #define APP_FLAG_SCREEN_SAVER 0x0002u +#define APP_FLAG_SHORTCUT_SHIFT 8u +#define APP_FLAG_SHORTCUT_MASK 0x0700u #define APP_NAME_LEN 16 #define APP_VERSION_LEN 16 -#define APP_SHORTCUT_FM (1u << 0) -#define APP_SHORTCUT_FOXHUNT (1u << 1) -#define APP_SHORTCUT_BEACON (1u << 2) +#define APP_SHORTCUT_FM 0x01u +#define APP_SHORTCUT_FOXHUNT 0x02u +#define APP_SHORTCUT_BEACON 0x04u typedef struct __attribute__((packed)) { uint32_t magic; /* APP_MAGIC */ @@ -108,8 +110,8 @@ uint8_t APP_ValidateSlot(uint8_t slot, app_header_t *out_header); * Returns when the app exits; the internal flash is never touched. */ uint8_t APP_LaunchOverlay(uint8_t slot); -/* Find the first valid app with this exact header name and launch it. */ -uint8_t APP_LaunchOverlayByName(const char *name); +/* Launch the first installed app advertising this shortcut bit. */ +uint8_t APP_LaunchOverlayShortcut(uint8_t shortcut); /* Cached availability of apps exposed as resident quick actions. */ uint8_t APP_OverlayShortcutMask(void); diff --git a/App/apps/beacon/build.sh b/App/apps/beacon/build.sh index 1168ee7a..905866b6 100755 --- a/App/apps/beacon/build.sh +++ b/App/apps/beacon/build.sh @@ -29,7 +29,8 @@ trap 'printf "\r ❌ %-13s build failed \n" "$APP_NAME"' ERR step 1 compile ; "$CC" $CFLAGS $LDFLAGS "${APP}_app.c" -lgcc -o "${APP}.elf" step 2 objcopy ; "$OBJCOPY" -O binary "${APP}.elf" "${APP}.bin" step 3 pack ; python3 ../pack_app.py "${APP}.bin" "${OUT}.app" \ - --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" >/dev/null + --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" \ + --shortcut beacon >/dev/null trap - ERR BYTES=$(wc -c < "${APP}.bin") diff --git a/App/apps/fm/build.sh b/App/apps/fm/build.sh index a35b18da..2b452893 100755 --- a/App/apps/fm/build.sh +++ b/App/apps/fm/build.sh @@ -29,7 +29,8 @@ trap 'printf "\r ❌ %-13s build failed \n" "$APP_NAME"' ERR step 1 compile ; "$CC" $CFLAGS $LDFLAGS "${APP}_app.c" -lgcc -o "${APP}.elf" step 2 objcopy ; "$OBJCOPY" -O binary "${APP}.elf" "${APP}.bin" step 3 pack ; python3 ../pack_app.py "${APP}.bin" "${OUT}.app" \ - --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" --screensaver >/dev/null + --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" \ + --shortcut fm --screensaver >/dev/null trap - ERR BYTES=$(wc -c < "${APP}.bin") diff --git a/App/apps/foxhunt/build.sh b/App/apps/foxhunt/build.sh index 24a90d49..fa4a86e8 100755 --- a/App/apps/foxhunt/build.sh +++ b/App/apps/foxhunt/build.sh @@ -29,7 +29,8 @@ trap 'printf "\r ❌ %-13s build failed \n" "$APP_NAME"' ERR step 1 compile ; "$CC" $CFLAGS $LDFLAGS "${APP}_app.c" -lgcc -o "${APP}.elf" step 2 objcopy ; "$OBJCOPY" -O binary "${APP}.elf" "${APP}.bin" step 3 pack ; python3 ../pack_app.py "${APP}.bin" "${OUT}.app" \ - --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" >/dev/null + --name "$APP_NAME" --ver "$APP_VER" --vma "${APP_VMA}" \ + --shortcut foxhunt >/dev/null trap - ERR BYTES=$(wc -c < "${APP}.bin") diff --git a/App/apps/pack_app.py b/App/apps/pack_app.py index 4ae9575f..eaa4a10c 100644 --- a/App/apps/pack_app.py +++ b/App/apps/pack_app.py @@ -32,6 +32,13 @@ ABI_VERSION = cdefine("app_api.h", "APP_ABI_VERSION") OVERLAY_MAX = cdefine("app_overlay.h", "APP_OVERLAY_MAX") # 4 KiB overlay budget FLAG_COMMITTED = cdefine("app_overlay.h", "APP_FLAG_COMMITTED") FLAG_SCREEN_SAVER = cdefine("app_overlay.h", "APP_FLAG_SCREEN_SAVER") +FLAG_SHORTCUT_SHIFT = cdefine("app_overlay.h", "APP_FLAG_SHORTCUT_SHIFT") +SHORTCUTS = { + "none": 0, + "fm": cdefine("app_overlay.h", "APP_SHORTCUT_FM"), + "foxhunt": cdefine("app_overlay.h", "APP_SHORTCUT_FOXHUNT"), + "beacon": cdefine("app_overlay.h", "APP_SHORTCUT_BEACON"), +} def field(s: str, n: int) -> bytes: b = s.encode("ascii", "strict")[: n - 1] @@ -48,6 +55,8 @@ def main(): help="RAM VMA the app was linked at (must match the firmware overlay)") ap.add_argument("--screensaver", action="store_true", help="allow the resident BLTime screen saver while this app is idle") + ap.add_argument("--shortcut", choices=SHORTCUTS, default="none", + help="resident quick action advertised by this app") a = ap.parse_args() code = open(a.infile, "rb").read() @@ -57,7 +66,9 @@ def main(): sys.exit(f"code {len(code)} B exceeds overlay budget {OVERLAY_MAX} B") crc = zlib.crc32(code) & 0xFFFFFFFF - flags = FLAG_COMMITTED | (FLAG_SCREEN_SAVER if a.screensaver else 0) + flags = (FLAG_COMMITTED | + (FLAG_SCREEN_SAVER if a.screensaver else 0) | + (SHORTCUTS[a.shortcut] << FLAG_SHORTCUT_SHIFT)) header = struct.pack( "<4sHHIIHH16s16sI8s", MAGIC, HDR_VERSION, ABI_VERSION,