diff --git a/App/apps/app_menu.c b/App/apps/app_menu.c index b77f6cd0..8ea8f942 100644 --- a/App/apps/app_menu.c +++ b/App/apps/app_menu.c @@ -25,6 +25,9 @@ #include "driver/system.h" #include "driver/gpio.h" #include "ui/helper.h" +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER +#include "k5viewer.h" +#endif /* "F4HWN APPS" banner and the same thin separator used by the multiboot * selector. Bit 3 leaves room for the selected-row capsule's top edge. */ @@ -75,6 +78,12 @@ static KEY_Code_t app_get_key(void) { for (;;) { +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + /* APP_MenuOpen() is modal and does not return to APP_Update(). Keep + * serial key injection and the viewer connection alive while waiting. */ + K5VIEWER_ParseInput(); + K5VIEWER_Update(false); +#endif KEY_Code_t key = KEYBOARD_Poll(); if (key != KEY_INVALID) { @@ -179,6 +188,15 @@ _Static_assert(APP_MENU_SLOT_COUNT <= APP_SLOT_COUNT, void APP_MenuOpen(void) { +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + /* Detach the modal selector from the key state that triggered F+7. The + * normal K5Viewer updater suppresses frames while a key is held; without + * clearing this stale state, the selector could never publish its first + * frame to the viewer. */ + gKeyReading0 = KEY_INVALID; + gKeyReading1 = KEY_INVALID; +#endif + /* Apps are installed from UV Studio (0x073x) into physical slots 0..N-1, * shown here as 1..N. Keep empty slots in the list so their location is * visible and selectable while scrolling. */ @@ -245,6 +263,9 @@ void APP_MenuOpen(void) ST7565_BlitStatusLine(); ST7565_BlitFullScreen(); +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + K5VIEWER_Update(false); +#endif const KEY_Code_t key = app_get_key(); if (key == KEY_EXIT) diff --git a/App/apps/app_overlay.c b/App/apps/app_overlay.c index 499ea1d0..4291e6da 100644 --- a/App/apps/app_overlay.c +++ b/App/apps/app_overlay.c @@ -33,6 +33,9 @@ #include "driver/st7565.h" #include "driver/system.h" #include "driver/backlight.h" +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER +#include "k5viewer.h" +#endif #include "app/app.h" #include "ui/helper.h" #include "ui/main.h" @@ -50,7 +53,25 @@ _Static_assert(sizeof(app_header_t) == 64, "app_header_t must be 64 bytes"); /* ---- ABI wrappers: the few resident calls that are not a direct signature match ---- */ -static uint8_t app_get_key(void) { return (uint8_t)KEYBOARD_GetKey(); } +static uint8_t app_get_key(void) +{ +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + /* Overlay apps run synchronously outside APP_Update(). Keep serial key + * injection alive while an app owns the foreground loop. */ + K5VIEWER_ParseInput(); +#endif + return (uint8_t)KEYBOARD_GetKey(); +} + +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER +static void app_blit_full(void) +{ + ST7565_BlitFullScreen(); + /* The normal loop mirrors completed frames after drawing. Overlay apps + * bypass that loop, so publish the frame from this ABI wrapper. */ + K5VIEWER_Update(false); +} +#endif static int8_t app_nav_dir(uint8_t key) { int8_t direction; @@ -635,7 +656,11 @@ static const app_api_t app_api = { .draw_rect = UI_DrawRectangleBuffer, .print_bold = UI_PrintStringSmallBold, .print_tiny = GUI_DisplaySmallest, +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + .blit_full = app_blit_full, +#else .blit_full = ST7565_BlitFullScreen, +#endif .blit_line = ST7565_BlitLine, .blit_status = ST7565_BlitStatusLine, .get_key = app_get_key, @@ -730,6 +755,14 @@ uint8_t APP_LaunchOverlay(uint8_t slot) app_fm_dirty = false; #endif +#ifdef ENABLE_FEAT_F4HWN_K5VIEWER + /* The caller enters from a debounced key event, so the resident key state + * still contains that trigger while the modal app is running. Clear it so + * K5Viewer is allowed to mirror overlay frames immediately. */ + gKeyReading0 = KEY_INVALID; + gKeyReading1 = KEY_INVALID; +#endif + /* Pin RX to the user-selected VFO before the app runs. Under dual watch * gRxVfo is whichever VFO the receiver was parked on when F+7 was pressed, * so an RF app (FoxHunt, a future S-meter, ...) would measure and display a