Skip to content
Xila
Demonstration

Graphics

The Graphics module hosts LVGL 9.6 and exposes window/display/input services through an explicitly owned manager.

Last updatedEdit page

The Graphics module hosts LVGL 9.6 and exposes window/display/input services through an explicitly owned manager.

  • Owns LVGL initialization and timer handler loop integration.
  • Bridges platform screen/input character devices into LVGL display/input objects.
  • Provides window lifecycle APIs and event retrieval for application runtimes.

In scope

  • Manager construction and LVGL runtime lifecycle.
  • Display/input device registration and runtime polling.
  • Window creation, enumeration, focus/maximize operations, and close requests.
  • Theme initialization and updates.

Out of scope

  • Full safe Rust wrapper coverage for all LVGL symbols.
  • Application-level UI architecture decisions.
  • Owned Manager returned by graphics::initialize(time_manager, ...), with:
    • inner: RwLock<Inner> storing display/input vectors and window parent object,
    • global_lock: Mutex<()> used to serialize LVGL-critical sections.
  • Initialization calls lv_init and registers the supplied time manager for the C tick callback. graphics::set_ffi_manager installs a static graphics reference for callbacks that cannot carry Rust references.
  • Manager::loop drives lv_timer_handler() and periodic display resize checks.
  • Window wraps lv_obj_t*, stores user data queue, and maps LVGL events into Xila event types.
graph TD
    Clients@{ shape: processes, label: "Executables / modules" }
    Init[graphics::initialize(...)]
    Manager[Explicit Graphics Manager]
    LoopTask[Graphics loop task]
    LVGL[LVGL runtime]
    ScreenDev[Screen DirectCharacterDevice]
    InputDev[Input DirectCharacterDevice(s)]
    TimeMod[Supplied time manager]

    Clients -->|window/theme/input APIs| Manager
    Init --> Manager
    LoopTask -->|Manager::loop()| LVGL
    Manager -->|display flush| ScreenDev
    Manager -->|input poll| InputDev
    TimeMod -->|tick callback| LVGL
    Manager -->|global lock| LVGL
  1. Initialize Time first.
  2. Initialize graphics with the time manager, screen/input devices, and buffer configuration; retain the returned manager and register it with set_ffi_manager where needed.
  3. Spawn graphics loop task calling Manager::loop(...).
  4. Applications create windows and process queued events.
  5. Optional runtime updates: add inputs, update theme, close/maximize windows.
  • Input devices -> LVGL event callbacks -> per-window event queues.
  • Manager loop polls LVGL timers and triggers display maintenance.
  • Host bindings may call graphics APIs under manager lock (WASM path).
  • LVGL-sensitive operations are protected by manager-level mutex lock.
  • Metadata collections use RwLock for async read/write access.
  • Window/user-data pointers rely on explicit ownership conventions and from_raw/into_raw usage discipline.
  • Depends on Time for tick callback.
  • Interacts with Bindings and VM runtime in WASM host path.
  • Uses platform DirectCharacterDevice implementations for concrete display/input IO.
  • Initialization failures panic in current manager construction path.
  • Runtime operations return typed graphics::Error for invalid window identifiers and object failures.
  • Event queue and object pointer misuse can surface as runtime failures if ownership rules are violated.

The manager installs graphics-owned virtual pointer and keypad devices even without a physical input device. This enables diagnostics and UI automation through the same LVGL runtime as normal applications.

  • get_resolution() reads the active display resolution. inject_pointer_move(point) rejects negative positions and coordinates beyond the right or bottom edge.
  • inject_pointer_button(state), inject_key(key, state) and inject_text(text) enqueue input events; text injection is limited to printable ASCII.
  • inspect_ui_tree(max_elements, max_depth) returns a bounded tree snapshot with a truncation indicator. Its element IDs are snapshot-local indexes, not persistent LVGL object handles.
  • capture_screen() captures the active screen into an owned RGBA8888 snapshot. Inspection and capture report NotAvailable when their required UI/display state is missing.

These operations acquire the graphics lock. Callers should neither retain raw LVGL pointers across calls nor reuse snapshot indexes after a new snapshot.

let resolution = graphics_manager.get_resolution().await?;
let snapshot = graphics_manager.inspect_ui_tree(128, 8).await?;
let screenshot = graphics_manager.capture_screen().await?;

This snippet assumes an initialized graphics_manager with an active display. The graphics loop must still be scheduled to process LVGL timers and queued input. See Rustdoc for the concrete snapshot types.

The runtime graphics crate explicitly enables lvgl_rust_sys/build-lvgl to compile the native LVGL C archive, while guest/bindgen-only dependencies need not compile LVGL. The graphics build.rs uses the selected locale and fallback to generate font C sources and compile them into a separate font_assets static archive in Cargo OUT_DIR. Font-only changes can therefore rebuild the font archive without rebuilding LVGL C sources. The old standalone fonts_generator executable and eager generate-fonts task have been removed. This separation landed in Core #505; #506 restored the runtime C linkage and corrected upstream bindgen include paths.

Since Core #508, the Rust font build cache generates four deduplicated Latin range variants and archives each font/variant in a separate native object. src/fonts.rs selects the active six symbols at compile time using link aliases: the font archive may hold 24 objects, but unselected font variants are not linked. Chinese builds additionally subset Source Han Sans SC to glyphs occurring in localized catalog values, avoiding the entire CJK range (Core #456). The cache accounts for input fonts, converter/tool versions, locale ranges and generator options, and regenerates missing or corrupted results. Use mise run test:fonts to check cache and link behavior; there is no runtime font registry.

The generated fonts are compile-time assets, not dynamically loaded fonts. Node.js and lv_font_conv (installed via mise run deps:npm) are required during generation. The graphics lifetime regression previously seen after #506 subsequently passed in Core #515’s mise run test:workspace validation. This does not establish hardware-target correctness or runtime memory use.

  • Add additional input devices at runtime.
  • Extend window/theme APIs and higher-level widget wrappers.
  • Expand bindings/codegen coverage for broader LVGL surface.
  • Significant portions remain close to LVGL C-style pointer semantics.
  • Global lock simplifies safety but can become a bottleneck under heavy UI call contention.
  • LVGL and callback registration remain process-global; explicit manager ownership does not establish support for multiple independent LVGL runtimes.
  • Event kinds track LVGL 9.6 numeric codes. Guest event transport preserves numeric codes, including close requests, instead of treating unknown values as valid Rust enum variants.
  • Stable module contract: Graphics provides a global service surface for display/input lifecycle, window management, and event delivery to executables through typed APIs.
  • Current implementation details: An owned manager coordinates LVGL state, protects critical sections with its mutex, and stores runtime collections in RwLock-protected metadata; tick/FFI callbacks retain registered static references.
  • Compatibility note: Consumers should rely on API behavior and documented error semantics, not on internal lock composition, pointer wrapping strategy, or exact LVGL wiring details.