Skip to content

Releases: aoprisan/fbui

fbui v0.5.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 10:45
60ec5f8

Added

  • fbui-bare timers: Timers<M> — send, send_after, send_every,
    cancelled through a Timer handle — the no_std counterpart of
    fbui::Proxy's timers (fixed-delay repeats; dropping a handle detaches).
    The app gets the handle in the new App::on_start (default: no-op, so
    existing apps are unchanged); board code gets one from Runner::timers.
    Deadlines feed Runner::next_deadline, so Board::wait sleeps exactly
    until the next timer and idle still costs nothing. Timers armed before the
    runner has seen the clock count from its first reading.
  • fbui-bare rotation: Runner::with_rotation / set_rotation (also at
    run time) turn the UI on a sideways- or upside-down-mounted panel through
    the existing copy-out rotation; input stays in panel coordinates and is
    mapped back, and Framebuffer::flush gets panel-space rects.
    fbui_bare::Rotation re-exports fbui_render::Rotation.
  • fbui-bare RGB565 dithering: an Rgb565 framebuffer now gets the
    ordered (4×4 Bayer) dithered copy-out by default, as the Linux runner
    already does, so gradients don't band on 16-bit panels. This changes what
    existing RGB565 boards show (32-bit ones are unaffected);
    Runner::set_dither(false) restores the plain truncation.
  • Banded rendering (NOSTD.md §6.1): Runner::new_banded(app, info, scale, band_rows) paints through a shadow a few rows tall instead of a
    whole-screen one, copying each band to the framebuffer as it is drawn
    (Surface::banded, Surface::paint_banded, Ui::paint_banded,
    Ui::request_full_paint). A 320×240 UI's peak heap drops from 800 KiB to
    161 KiB with static fonts and 16-row bands. Output is byte-identical to a
    whole-screen paint — pinned by fbui-render/tests/banded.rs and
    fbui-widgets/tests/banded.rs — except bilinear-scaled images (±1 level).
    tiny-skia's hairline rasterizer is vendored (fbui-render/src/hairline/,
    BSD-3) so hairlines stay exact across band edges.
  • Bitmap fonts (NOSTD.md §6.2, fbui_render::text::bitmap): text
    with no shaping engine or rasterizer at run time. .fbf files hold glyphs
    pre-rasterized at fixed sizes (4-bit coverage, read in place, validated
    without panics); FontContext::with_bitmap_fonts, add_bitmap_font,
    BitmapFont, BitmapFontWriter, and fbui_bare::App::bitmap_fonts. The
    make_bitmap_font example generates them from any TTF/OTF;
    bundled-bitmap-font ships Inter at 12/16/20/24 px (Latin-1, 75 KB). Every
    widget works on them. The 320×240 counter UI drops to 45 KiB live / 68 KiB
    peak heap, and its firmware from 1.78 MiB to 0.66 MiB of flash with
    outline-text off. No kerning or shaping, fixed sizes, whole-pixel glyph
    positions.
  • outline-text feature (default on in fbui-render, fbui-widgets and
    fbui-bare): cosmic-text and swash are now optional. FontContext::set_scale
    (called by Ui) lets bitmap fonts choose their size in device pixels.
  • Fonts used in place: FontContext::with_static_fonts /
    load_static_font and fbui_bare::App::static_fonts take &'static
    font data without copying it into the heap (~300 KiB for Inter).
    FontContext::with_default_font now uses the bundled font in place too —
    on Linux as well. The doc viewer passes its compiled-in font this way.
  • Runner::run — the main loop on a runner you configured; fbui_bare::run
    is now shorthand for Runner::new(..).run(..).
  • fbui-bare/tests/runner.rs: direct tests of the bare runner — copy-out
    and padded stride, idle, invalidate, key and scaled pointer input,
    rotation (pixels and input for every quarter turn), RGB565 dithering,
    and timers.

Fixed

  • ProgressBar, Slider and TextInput no longer stretch vertically in a
    column with free space. They set flex_grow to fill a row, but in a column
    it grew them in height: a counter screen's progress bar was laid out
    296×139 instead of 296×8. They now cap their height at their control
    height (max_size), keeping the row behaviour. Found while tracing a 41 KB
    first-frame allocation, which was the stretched bar's coverage mask.

Changed

  • Breaking for default-features = false users of fbui-render /
    fbui-widgets / fbui-bare:
    outline text is now the outline-text
    feature. A dependent that turns default features off must add it to keep
    TTF/OTF text (FontContext::with_fonts and friends); without it only
    bitmap fonts are available. Default builds are unchanged.
  • Painter no longer clones the clip mask (a full surface-sized buffer) for
    every primitive drawn under a clip; peak heap while painting a clipped
    region drops by one mask (75 KiB at 320×240). Output is unchanged.
  • fbui-render's license expression is now
    (MIT OR Apache-2.0) AND BSD-3-Clause AND OFL-1.1: the crate's own code
    is unchanged in license, but it ships the vendored tiny-skia hairline
    rasterizer (BSD-3-Clause) and the Inter font files (OFL-1.1), whose
    license texts are included in the package.

fbui v0.4.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 07:01
08d42b0

Changed

  • Breaking for default-features = false users: fbui-render and
    fbui-widgets gain a default std feature (and fbui-widgets a
    default all-widgets). A dependent that already turned default
    features off must now list std (plus all-widgets, and harness as
    before) to keep the API it had. Default builds are unchanged.

Added

  • fbui with no operating system — the no_std track (NOSTD.md).
    • fbui-render and fbui-widgets build #![no_std] + alloc with
      default-features = false. A new default std feature keeps the
      hosted build exactly as it was; without it the image-crate codecs, PNG
      export, file paths, Ui::request_screenshot, the flow harness and
      profile are unavailable. fbui_render::math::F32Ext supplies the f32
      math API from libm (inherent methods still win under std).
    • Minimal widget set: fbui-widgets without the new all-widgets
      feature (on by default) compiles only Label, Button, Container,
      Stack, ScrollView, List, ImageView and ProgressBar.
    • fbui-bare (new crate): the bare-metal runner. A board supplies a
      Framebuffer (info/pixels/flush) and a Board (poll_input,
      now_ms, wait); fbui_bare::run is the main loop, keeping the idle
      rule (no damage, no animation → wait(None)). Runner steps it for
      tests. A footprint test gates a 320×240 UI under 2 MiB of heap
      (measured: 875 KiB peak).
    • fbui-doc (new crate, always no_std): PNG/JPEG decoding to
      tiny-skia pixmaps and a PDF subset renderer — xref tables/streams,
      object streams, a reconstructing fallback; Flate/LZW/ASCII85/ASCIIHex/
      RunLength/DCT; paths, clipping, shadings, tiling patterns, images with
      soft masks; embedded TrueType/OpenType/CFF/Type 1/Type 3 fonts, Type 0
      with Identity-H, and a fallback face for non-embedded fonts. Fixtures
      from reportlab, fpdf2 and pikepdf, and a no-panic mutation test.
    • fbui_render::Image::from_pixmap wraps a premultiplied tiny-skia
      pixmap without a copy.
    • apps/doc-viewer: a no_std PDF/PNG/JPEG viewer app (library,
      paging, zoom, pan) on fbui-bare, built from the minimal widgets plus a
      custom PageView; apps/doc-viewer-rpi3 boots it on a Raspberry Pi
      3 with no OS (MMU, mailbox framebuffer, UART keys, wfi idle), verified
      in QEMU raspi3b by scripts/qemu_drive.py.
    • CI gains a nostd job: thumbv7em-none-eabihf builds, host no_std
      tests, the Pi 3 image, and a scripted QEMU session.

Fixed

  • FontContext::layout no longer panics when the font database is empty
    (it lays out nothing) — a no_std target that loaded no font shows no
    text instead of crashing.

fbui v0.3.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 05:17
ba8b42e

Added

  • crates.io publishing. All five crates are now publishable (publish = false is gone) and carry the metadata crates.io and docs.rs want: a root
    README.md, keywords/categories, LICENSE-MIT/LICENSE-APACHE texts, and
    [package.metadata.docs.rs] feature sets. Sibling crates pin each other
    exactly (=x.y.z, via [workspace.dependencies]), matching the lockstep
    policy. Pushing a v<version> tag runs .github/workflows/release.yml:
    consistency check, tests, cargo package --workspace, an ordered
    cargo publish --workspace (trusted publishing, or a CARGO_REGISTRY_TOKEN
    for the first upload), and a GitHub release from this changelog.
    scripts/release-check.sh guards the pins/changelog/tag, and CI now
    packages and verifies every crate on each push. See RELEASING.md.

  • fbui-ctl and the remote console's text endpoints — the third flow
    executor, and the field-support loop.

    • fbui-ctl (a binary in fbui, --features remote, std::net only):
      tree, json, shot, tap/press/release/move/wheel, type,
      key, trace [--follow], metrics, and run <flow.txt> — which resolves
      a flow's references against the live device via GET /tree and injects
      through POST /input. FBUI_CTL picks the console; FBUI_REMOTE_TOKEN
      authenticates.
    • GET /tree.txt serves Ui::inspect_text(), and GET /trace the
      last 500 trace lines — which the console records whenever it is enabled,
      even without FBUI_TRACE.
    • fbui::remote::parse_tree reads the /tree document back into an
      InspectNode, pinned to the writer by a round-trip test.
    • docs/tooling.md documents the whole track; PHASE-TOOLING.md records
      verified-vs-pending status and every deviation from the design;
      CLAUDE.md gains the no-screen workflow.
    • CI gains a headless job: every committed flow, every example lint-checked,
      and a monkey session on three examples — no sudo, no modprobe.
  • Traces, diagnostics and lints — reading why, and catching what an eye
    would catch.

    • FBUI_TRACE=path (or -) writes one line per notable event on the
      replay/wall clock: start, input (with the named widget under the
      pointer), msg, mutate (ops and damage), frame, timer, proxy,
      expect, lint. That is the causal chain input → message → mutation →
      damage → frame, which makes "the button did nothing" a one-line
      diagnosis: no msg after the input means the callback is missing; a
      msg with no mutate means update matched the wrong arm.
    • App::describe_message (default None) gives the trace the app's own
      vocabulary; a #[derive(Debug)] message type is a one-line impl.
    • Ui::diagnostics() / take_diagnostics() — mutations, damage rects
      and area, layouts, paints, messages, events. A few integer increments per
      operation, always on, so a test can assert cost rather than only pixels.
    • Ui::lint() reports what a tree dump never will: touch-target,
      truncated-text, unreachable-focus, off-surface, overflow,
      empty-scroll, stacked-modals, duplicate-name, undescribed, and
      (from the script resolver) ambiguous-ref. Ui::allow_lint suppresses
      one rule on one widget and Ui::set_touch_target sets the tappable
      minimum (default 24 logical px; a touch-only kiosk should set 44).
      FBUI_LINT=1 runs the pass after every layout and reports each finding
      once; expect no-lints makes it a flow failure.
    • All ten shipped examples are lint-clean. Getting there fixed two real
      bugs the pass found: the showcase's panels row pushed its content off a
      1024x600 screen, and custom_widget's Dot described nothing.

Changed

  • Container::shrink() — the flexbox min-size: 0 idiom, so a growing
    container bounded by its parent wins over an oversized child. Without it a
    windowing child (a List of 50 rows) demands its full content height and
    pushes the page off the screen, which is what the new overflow lint found
    in the showcase example.

  • Flow scripts (fbui-rec 2) and two executors — a UI interaction
    written as steps and expectations, meaning the same thing in a cargo test
    and under the runner.

    fbui-rec 2
    tap #inc
    type "milk"
    key Enter
    wait settle
    expect #count text "1 item"
    
    • fbui_widgets::script holds everything that decides meaning: the
      parser, the reference resolver (#name, #screen/field, Kind "text",
      @x,y), and expectation evaluation. References resolve against the live
      tree at the moment their step runs, so a flow follows the layout; a
      reference that matches nothing — or matches a widget that is not on
      screen — fails the step instead of tapping the void.
    • fbui_widgets::harness runs a flow in-process against a Ui
      (run_text, assert_flow), feeding widget events through the same path
      the behavior tests use and calling the caller's update for every
      message. A failure panics with the failing line, the actual value, and
      the tree.
    • The runner plays v2 flows wherever FBUI_REPLAY takes a recording;
      the header picks the format. Input goes through the real gesture
      recognizer, a flow exits 0 when every expectation held and non-zero when
      one did not, and a failure writes <flow>.fail.txt and .fail.png
      beside the flow. FBUI_REPLAY_STEP (default 50 ms) paces the steps.
      Raw @ms lines may be mixed in and keep their own timestamps.
    • Committed flows for three shipped examples under fbui/flows/, which run
      headless with no display, tty or privileges.
    • Ui::name-based addressing means the examples no longer keep
      Option<WidgetId> fields for their own labels.
    • Recordings are annotated: a live press is written with the named
      widget under it (@120 b l p # tap #inc), so a recorded session can be
      read — and hand-converted into a flow — without replaying it.
  • Widget names, Widget::describe, and the tree as text — the widget
    tree becomes readable without looking at the screen.

    • Widget::describe (fbui-widgets) reports a widget's user-visible
      content and state as ordered key/value pairs — a label's words, a text
      field's content and caret, checked/open/selected, a slider's value
      and range, a scroll offset. Every built-in widget implements it; the
      default is empty, so third-party widgets keep compiling. It runs only
      on Ui::inspect, never on the paint or event path.
    • Names: Ui::name, Ui::add_named, Ui::find, Ui::name_of,
      Ui::names. A name is tree-unique, optional, and dies with its widget;
      find("form/name") scopes a name by its named ancestors.
    • Ui::inspect_text() renders the laid-out tree one widget per line —
      Kind #name [x,y wxh] "text" prop=value focused — which is what an
      author reads instead of a screenshot, and what a review diff shows when
      the screen changes.
    • FBUI_REPLAY_TREE=path writes that dump at the end of a replay,
      beside FBUI_REPLAY_SHOT. An empty replay file is now a valid empty
      recording, so FBUI_REPLAY=/dev/null renders the first screen, writes
      the artifacts and exits.

Changed

  • InspectNode.name is now InspectNode.kind (the widget type), and
    name holds the app-assigned name from Ui::name (Option<String>). The
    node also grows text, props and visible. The remote console's
    GET /tree JSON follows: kind replaces name, and nodes gain optional
    name, text, props and a visible flag. The built-in console UI shows
    all of them. Pre-1.0 rename, in one change, with the only in-repo consumer
    updated.

  • A headless display backend (fbui-platform, feature headless, on by
    default) — FBUI_BACKEND=headless runs the unmodified runner against two
    RAM back buffers that present nowhere: no display device, no tty, no seat,
    no input devices, no privileges. This is what lets CI and an author who
    cannot see the screen drive a real app (FBUI_REPLAY, FBUI_MONKEY,
    FBUI_REPLAY_SHOT, the remote console) and get artifacts back. It is a
    backend rather than a test runner on purpose: the frame clock, gestures,
    timers, Proxy, power policy and record/replay are the real ones, so a
    headless result is evidence about the real app.

    • FBUI_HEADLESS_SIZE=WxH sets the surface (default 1024x600); a
      malformed value is a hard error like the other FBUI_* knobs.
    • Buffer ages follow the DRM double-buffered sequence exactly, so the
      partial redraw path runs headless rather than the age = 0
      repaint-everything path a single buffer would force.
    • Rows are padded to 64 bytes, so stride != width * bpp and any code that
      recomputes the stride breaks loudly in CI instead of quietly on a device.
    • Presents complete synchronously, so the event loop needs no pacing timer
      and an idle headless app still burns ~0% CPU.
    • SIGUSR1 (or display::headless::request_mode) simulates a hotplug /
      mode change, giving on_display_changed an off-device test for the first
      time.
    • NullSeat (fbui-platform) is the shared device-less seat behind the
      terminal and headless backends.
  • Text editing: TextArea, the clipboard, and a fuller TextInput —
    closing the "clipboard-less, single-line" v1 scope from Phase 3.

    • TextArea (fbui-widgets): a multi-line text box with word-wrapped
      paragraphs, a caret that moves by visual line (Up/Down keep the
      goal column across shorter lines, Home/End are per line,
      Ctrl+Home/End document-wide, PageUp/PageDown by viewport), Enter
      inserting a line break, selection across lines (drawn per line with a
      line-end tail), and a vertical scroll that follows the caret with a thumb
      when content overflows — the wheel scrolls it and bubbles ...
Read more

0.1

0.1

Choose a tag to compare

@aoprisan aoprisan released this 12 Jun 18:21
0508c9b

What's Changed

  • Phase 0: kernel-facing spike (DRM/KMS, shadow buffer, VT guard, fbdev) by @aoprisan in #1
  • Phase 1: Implement fbui-platform, the stable foundation layer by @aoprisan in #2

New Contributors

Full Changelog: https://github.com/aoprisan/fbui/commits/0.1