flex_kneron_ui · universalguilibrary kl730_renderer · MT-LB DVE / Periscope board
| Module | Source file(s) | Role |
|---|---|---|
| main.cpp | app/src/main.cpp | Entry point. Loads XML config, connects to pipeline daemon (TCP 9001), builds widget tree, initialises KL730 backend + camera thread, subscribes to BGS/battery events via ListenerProvider (MQTT 127.0.0.1:1883), runs render loop at 30 fps. Handles SIGINT/SIGTERM for clean shutdown. |
| XmlSettings | app/src/utils/XmlSettings.cpp app/inc/utils/XmlSettings.hpp |
Parses GUISettings.xml via pugixml. Resolves config path: /mnt/flash/GUISettings.xml (runtime override) → /atn/GUISettings.xml (factory). Produces canvas dimensions, controller list, and widget tree specification. |
| WidgetFactory | app/src/utils/WidgetFactory.cpp | Reads widget spec from XmlSettings. Constructs all IWidget instances (MainWidget, InfobarWidget, MainMenuWidget, ReticleWidget). Wires widgets to their controllers. Returns the root widget pointer. |
| IWidget | app/inc/widgets/IWidget.hpp | Abstract widget interface: render(Canvas&) and handle_event(Event). Concrete implementations: MainWidget (root compositor), InfobarWidget (status bar — battery, EPS, mode indicators), MainMenuWidget (F1–F4 navigation), ReticleWidget (aiming reticle overlay). |
| Canvas | app/src/utils/Canvas.cpp app/inc/utils/Canvas.hpp |
RGBA draw surface (1920×1200). On device, bound directly to Kl730Backend's MemBroker DMA buffer via bind_rgba() — zero-copy. Exposes fill, draw_rect, draw_text, draw_icon, draw_png. On HOST_MOCK, owns its own heap buffer. |
| WidgetsMediator | app/src/utils/WidgetsMediator.cpp | Observer/mediator pattern for widget-to-widget notifications (e.g., menu open/close toggling InfobarWidget visibility). Decouples widget interdependencies. |
| PipelineClient | app/src/utils/PipelineClient.cpp | TCP client to the pipeline daemon on port 9001. Sends zoom / day-night / recording commands; receives the pipeline-ready handshake at startup. Blocks with 5-second timeout on wait_pipeline_ready(). |
| DayNightController | app/src/controllers/DayNightController.cpp | Tracks day/night mode state. On mode change: sends command to pipeline daemon via PipelineClient, notifies widgets via WidgetsMediator. |
| ZoomController | app/src/controllers/ZoomController.cpp | Manages digital zoom level. Sends zoom step command to pipeline daemon on button events. |
| ExposureController | app/src/controllers/ExposureController.cpp | Manages CMOS sensor exposure settings. Interacts with pipeline daemon for exposure adjustments. |
| RecordingController | app/src/controllers/RecordingController.cpp | Start/stop recording control. Sends recording commands to pipeline daemon; updates recording-state indicator in widgets. |
| Icon | app/src/utils/Icon.cpp app/inc/utils/Icon.hpp |
PNG icon loader using lodepng. Caches decoded RGBA icon bitmaps in memory. Used by Canvas::draw_icon(). Icons installed at /atn/resources/icons/. |
| LanguageManager | app/src/utils/LanguageManager.cpp | Loads language XML files (e.g., /atn/languages/EN.xml) for localised UI strings. Provides string lookup by key. |
| UserSettingsStore | app/src/utils/UserSettingsStore.cpp | Persists user preferences to /mnt/flash/ (UBIFS, SPI-NAND). Guards all writes with /proc/mounts check — returns error if flash is not mounted. |
| CameraThread | app/src/utils/CameraThread.cpp app/inc/utils/CameraThread.hpp |
Dedicated thread owning the VMF VSRC camera pipeline. Opens IMX678 sensor via imx678_3840x2160_ch0.cfg; IFP scales to landscape 1920×1200 (ISP_MODE_DISABLE). Blocking SSM read on gui_vsrc_ifp_0. Hardware DMA_2D copy to MemBroker double-buffer (4096-byte aligned). Atomically flips m_read_idx after each frame. Buffer constants: c_out_w=1920, c_out_h=1200, c_out_stride=1984. |
| Kl730Backend | universalguilibrary/lib/kl730_renderer/src/Kl730Backend.cpp universalguilibrary/lib/kl730_renderer/inc/Kl730Backend.hpp |
Platform seam implementing the GLA_RenderContext interface (render_api.h). end_frame(): NEON-accelerated BT.601 RGBA→YUV420 conversion with 90°CW transpose (landscape→portrait), per-pixel camera composite (alpha-blend + sensor V-flip correction), then VMF_VDISP_ProcessOneFrame(). Allocates portrait DMA double-buffer (PORT_W=1200, PORT_H=1920, PORT_STRIDE=1216) via MemBroker. Exposes lifecycle API: kl730_backend_init(), kl730_backend_destroy(), kl730_backend_set_camera(), kl730_backend_get_rgba_buf(). |
| ListenerProvider (BGS) | external: liblistener_provider_lib | UDP/local MQTT subscriber. Receives: BUTTON_PRESSED / BUTTON_LONG_PRESSED (F1=Up, F2=Down, F3=Set, F4=Back, keycodes 59–62 from BGS), BATTERY_LEVEL (JSON: bat_level1/bat_level2 from TI BQ27546 fuel gauges on I2C-0/I2C-2), EPS_STATUS (BQ25895 charger — 1=charging). Callbacks push to a mutex-protected queue; render loop drains each tick. |
| lodepng | third_party/lodepng/lodepng.cpp | Single-file PNG decode library. Used exclusively by the Icon module to decode PNG icons into RGBA bitmaps at load time. |
| HOST_MOCK (dev only) | mock/src/DisplaySDL.cpp mock/src/ButtonServer.cpp |
x86 development build only (compiled with -DHOST_MOCK). SDL2 window renders the Canvas RGBA buffer at native size. ButtonServer listens on UDP 9002 for panel_emulator button injection. Not shipped on device. |
main() loads GUISettings.xml → XmlSettings.
Connects to pipeline daemon (TCP 9001, 5 s timeout).
WidgetFactory builds the IWidget tree.
kl730_backend_init() allocates RGBA working buffer (1920×1200×4 = ~9 MB heap)
and portrait YUV420 DMA double-buffer (1216×1920×1.5 ≈ 3.5 MB MemBroker each slot).
Canvas is bound to the RGBA buffer — zero copy from this point.
CameraThread::start("gui_vsrc", "/kneron/Resource/VIC/0/imx678_3840x2160_ch0.cfg").
VMF VSRC opens sensor, IFP downscales 3840×2160 → 1920×1200 landscape (ISP_MODE_DISABLE).
SSM reader opens gui_vsrc_ifp_0.
DMA_2D descriptor configured (Y + Cb + Cr planes, stride 1984).
Frame loop: SSM receive → DMA_2D copy to write slot → atomic index flip.
Render thread reads latest slot non-blocking via get_frame().
Event structs to btn_queue (mutex-protected).
Render loop swaps the queue atomically, then calls root→handle_event(ev) for each:
canvas.fill(Color::transparent()) — zeroes the RGBA buffer (all alpha = 0, camera shows through by default).root→render(canvas) — widget tree draws into the RGBA buffer:
InfobarWidget paints status icons, MainMenuWidget paints menu (if open), ReticleWidget paints reticle crosshair, MainWidget composites children.
Alpha = 0 pixels remain transparent → camera behind.
Alpha = 255 pixels are opaque GUI → occludes camera.
m_read_idx atomically → gets latest camera Y/U/V plane pointers and strides.lr = 0..1199 (landscape row) → portrait col pc = 1199 − lrcam_y + lr × cam_stride (sensor V-flip correction: row = lr not pc)vld4q_u8 loads 16 RGBA pixels; vld1q_u8 loads 16 cam_yvbsl overrides: α=0 → cam, α=255 → GUIdst_y[lc × PORT_STRIDE + pc] (90°CW: portrait row = lc)VMF_VDISP_ProcessOneFrame() → VOC0 DMA → FT8201P MIPI DSI.
| Buffer | Dimensions | Stride | Allocator |
|---|---|---|---|
| RGBA working buffer (GUI) | 1920 × 1200 × 4 B | 7680 B/row | heap (new u8[]) |
| Camera landscape Y | 1984 × 1200 | 1984 B/row | MemBroker (DMA-mapped) |
| Camera landscape UV | 992 × 600 each | 992 B/row | MemBroker (DMA-mapped) |
| Portrait display Y | 1216 × 1920 | 1216 B/row | MemBroker (4096-aligned) |
| Portrait display UV | 608 × 960 each | 608 B/row | MemBroker (4096-aligned) |
dst_y[lc × PORT_STRIDE + (GUI_H−1−lr)].
Portrait row = landscape column; portrait column = (GUI_H−1) − landscape row.
GLA_RenderContext seam (render_api.h) from universalguilibrary is used, but the ARS45 HAL backend from the Artosyn TTBN-6 project is not portable to KL730. A new kl730_renderer backend was implemented from scratch targeting VMF VSRC + VMF VDISP._isp_0 never initialised. The rotation is done in end_frame() as a transposed write loop. NEON acceleration brings the Y-plane cost to ~1 ms for 1920×1200.1−m_read_idx and flips m_read_idx atomically after each DMA_2D completes. Render thread reads the current slot without any mutex — zero stall on the 30 fps render thread.canvas.bind_rgba(kl730_backend_get_rgba_buf(), ...) gives Canvas direct access to the MemBroker DMA buffer. Widget draw calls write directly to composited memory — no intermediate buffer copy before end_frame().lr (landscape row iteration variable) instead of pc (GUI_H−1−lr). Column unchanged. UV: row = lr/2.lo interface (127.0.0.1). BGS and battery-general-service communicate via mosquitto on lo:1883. S20network init script must run before mosquitto./mnt/flash/ (UBIFS, SPI-NAND MX35LF4GE4AD). UserSettingsStore verifies /proc/mounts before writing.| Channel | Transport | Direction | Messages |
|---|---|---|---|
| Pipeline daemon | TCP 127.0.0.1:9001 | Bidirectional | PIPELINE_READY, ZOOM_SET, DAYNIGHT_SET, RECORDING_START, RECORDING_STOP |
| BGS (buttons) | MQTT lo:1883 (ListenerProvider) | Subscribe | BUTTON_PRESSED, BUTTON_LONG_PRESSED → F1/F2/F3/F4 (Up/Down/Set/Back) |
| Battery service | MQTT lo:1883 (ListenerProvider) | Subscribe | BATTERY_LEVEL {bat_level1, bat_level2}, EPS_STATUS {0|1} |
| VMF VSRC | SSM shared memory | Subscribe (camera frames) | YUV420 frames on pin gui_vsrc_ifp_0, 1920×1200, ~30 fps |
| VMF VDISP | VMF internal (VOC0) | Push (display output) | YUV420 portrait frames 1200×1920 → FT8201P MIPI DSI panel |