Skip to content
shim9610Public

About

A touchscreen shell for embedded devices, built on egui: status bar, shade, desktop, on-screen keyboard, gestures and access gates in one frame loop

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

fairing: a touchscreen shell for embedded devices, built on egui

CI License: MIT Rust 1.95+ egui 0.37 unsafe forbidden in UI crates

fairing

A touchscreen shell for embedded devices, built on egui.

Fairing provides the UI around your device application: status and navigation bars, a desktop, screen stacks, quick settings, notifications, an on-screen keyboard and device settings. Your screens and hardware backends run in the same process and egui frame loop. Touch sizes resolve from physical panel dimensions; screens, chrome and widgets share theme and motion tokens.

Development toward 0.1.0; the crates are not published yet. This documentation describes the current checkout. Start with the integrator guide, or read the migration guide when updating an existing app.

Animated: on the desktop an icon zooms open into its screen, a second screen pushes over it and pops off, and home closes the screen back into its icon Animated: on a bench console the notifications card and then the controls card are pulled open from the top edge, each floating on a frosted copy of the page
An icon zooms open, a screen pushes over it and pops off, home closes it (demo) A split shade: each half pulled open as a card on frosted glass (console)
The shade drawn down as a curtain, with quick-settings tiles above a notification list A bench console: a rail down the left joined to the status bar, a progress ring and action tiles
The same shade as a curtain: tiles, notifications, a footer (demo) A bench console built from layout and widgets calls only (console)
The on-screen keyboard's two-set Hangul layout composing a syllable in a text field The built-in Wi-Fi settings screen with a switch and a network list
Two-set Hangul with composition on the on-screen keyboard (demo) One of the built-in settings screens (demo)

What is included

Area Capabilities
Shell Desktop grid and dock, screen stacks, lifecycle events, transitions, two panes and recent-screen cards
Chrome Status/nav bars, pull-down quick settings and notifications, toasts and heads-up banners
Input Touch gestures, edge handles, custom gesture regions, numeric/QWERTY/two-set Hangul keyboard
Device integration Clock, power, Wi-Fi, Bluetooth, display, audio, network and device-information traits, null backends and optional mocks
Access Legacy level gates, credential prompts and lock screens; opt-in issuer-controlled sessions and typed protected UI
Layout and widgets Screen containers, cards, grids, rails and tabs; 23 touch widgets, physical units, vector icons and theme tokens
Host integration Bounded command admission, application-owned command/update transports, caught-unwind quarantine and host-controlled shell replacement

fairing-widgets can be used on its own in an egui application. Fairing re-exports it and egui, so a shell application needs only the fairing dependency.

Quick start

For a Linux desktop, enable runner-x11; a Wayland device can use runner. Pin a reviewed Git revision when integrating a device build.

[dependencies]
fairing = { git = "https://github.com/shim9610/fairing", features = ["runner-x11"] }

This minimal app uses the ordinary Shell API. Its default configuration does not require a login; typed session enforcement is a separate choice below.

use fairing::{egui, icon, screen, Cx, Services, Shell, ShellConfig};

fn main() -> fairing::Result<()> {
    fairing::runner::run_shell(fairing::runner::Options::default(), |ctx| {
        let mut shell = Shell::builder(ShellConfig::default())
            .services(Services::builder().build())
            .physical_mm(154.0, 86.0)
            .build(ctx)?;
        fairing::settings::add_all(&mut shell, &fairing::settings::SettingsConfig::default());
        shell.add(
            screen("dashboard", |ui: &mut egui::Ui, cx: &mut Cx| {
                ui.heading("Dashboard");
                if ui.button("Settings").clicked() {
                    cx.open("settings.home");
                }
            })
            .title("Dashboard")
            .icon(icon::GAUGE)
            .desktop(),
        );
        Ok(shell)
    })
}

The standalone hello example also shows custom status/nav items. Run it with:

cargo run -p fairing --example hello --features runner-x11

Set physical_mm to the panel's visible dimensions for calibrated touch targets. Load your product fonts with ShellBuilder::fonts or font; egui's fallback faces do not contain Hangul. Without the runner, connect logic/logic_with and frame/frame_with in your own host, then drain poll_events after either update. Getting started covers fonts, configuration, features and hosting.

Sessions and protected UI

Choose the integration boundary explicitly:

API Application registration Authority
ShellBuilder::build Ordinary declarations and trusted custom hooks Legacy level gates by default; token mode can be enabled separately
ShellBuilder::build_protected Typed Protected declarations, add_screen and add_elements; no mutable legacy-shell conversion Requires token mode and checks current issuer grants before protected callbacks

Your authenticator verifies credentials and owns a non-cloneable SessionController. Fairing holds its read-only SessionToken. The issuer expands roles into independent grants, updates them or terminally revokes the session. Use separate controller/token pairs for device and web login state. PinTable is a plaintext reference authenticator for the legacy flow; use your own Authenticator for production credential verification.

Define your own permission enum and map its values to token grant identifiers:

use fairing::{action, egui, screen, Gate, ProtectedShell, Shell, ShellConfig};
use fairing::access::{Permission, Protected};

#[derive(Clone, Copy)]
enum PermissionId { ViewResults, ExportResults }

impl Permission for PermissionId {
    fn gate(self) -> Gate {
        Gate::borrowed(match self {
            Self::ViewResults => "results.view",
            Self::ExportResults => "results.export",
        })
    }
}

fn build(ctx: &egui::Context) -> fairing::Result<ProtectedShell> {
    let mut config = ShellConfig::default();
    config.access.session_tokens = true;
    let mut shell = Shell::builder(config).build_protected(ctx)?;
    shell.add(Protected::new(
        PermissionId::ViewResults,
        screen("results", |ui, _cx| { ui.heading("Results"); })
            .icon(fairing::icon::GAUGE)
            .desktop(),
    ));
    shell.add(Protected::new(
        PermissionId::ExportResults,
        action("export", |cx| { cx.shell.toast("Export requested"); }),
    ));
    Ok(shell)
}

This registers requirements; it does not issue a session. Until the authenticator returns a valid AuthOutcome::Session(token), protected work is denied. Issue matching grants with SessionGrant::base_for(value) after authentication; the session guide covers issuance, asynchronous request correlation, role expansion, expiry and logout.

For permission-dependent content inside a screen, implement PermissionBound and elements::Element, then register an ElementTree with add_elements. Fairing omits denied children before measurement/layout and rechecks authority before paint. Custom Screen + PermissionBound implementations use add_screen. The typed integration example shows nested UI and independent operation permissions.

The runner::run_shell*/run_app* helpers accept both shell modes through a sealed host interface. testing::Harness::from_builder also accepts build_protected. Register built-in settings with settings::add_all_protected, supplying your permission mapping for each screen ID. Run the development integration example with cargo run -p fairing --example protected --features runner-x11; its public DEMO marker demonstrates session wiring and must be replaced by a real verifier. Protected construction rejects untyped global painters/layouts, image loaders and procedural wallpapers; put custom protected UI in typed declarations or elements.

The common mediation contract proves the shared admission kernel and automatically checks compiler-discovered protected entry routes. An admitted callback retains raw egui, app and backend access; credential verification, permission classification and arbitrary callback effects remain integration responsibilities. This is a UI execution boundary within one process.

Customization and backends

  • Configuration: choose placement, visibility, gate assignments, palette and motion in fairing.toml.
  • Declarations: add/replace/remove screens, actions, status/nav items and tiles by identifier. Protected registration carries a permission value.
  • Drawing: supply themes and metrics; ordinary shells also accept global painter/layout hooks. Protected custom drawing uses typed elements.
  • Hardware: implement backend traits or register a custom backend. Backends publish snapshots and wake the UI; the application owns workers and transport.
  • Persistence: consume ShellEvent::SettingChanged, save values in your own storage and restore them at startup. Fairing writes no files itself.

See customization, services and the configuration reference.

Command admission defaults to 1,024 shared slots and 256 commands per update. Use try_send to recover rejected ownership. Optional display work has separate bounds and fallible APIs. Caught callback unwinds quarantine the shell; the host chooses recovery. These mechanisms do not establish whole-renderer recovery from heap exhaustion; the fixed-heap crate is an isolated unpublished diagnostic. Memory and recovery describes the limits and APIs.

Examples

cargo run -p fairing --example demo --features runner-x11 -- --size=1024x600
cargo run -p fairing --example console --features runner-x11 -- --size=1280x800
Example Focus
hello A minimal standalone app
demo Shell features, settings, keyboard and legacy unlock prompts
console Bench instrument, rail layout and split shade
kiosk Café ordering, portrait/counter/compact layouts and live language switching
custom_chrome Custom bar, desktop cells, wallpaper and tiles
motion_lab Interactive motion-token tuning and TOML export
palette_sheet Widget palette comparison
bench Headless frame-time scenarios

The gallery comes from the example tours. Contributing explains screenshot and GIF regeneration.

Verification

fairing::testing::Harness drives actual egui frames without a window or GPU and reports rendered geometry for pointer-driven tests. The repository also checks:

Check Evidence
Strict audit Formatting, Clippy, workspace/guide doctests, API docs, dependency approvals, generated files, aarch64 type-check and blocking-call scan
API and UI regressions Native/generated histories, retained widget state, independent expectations and production mutation controls
Protected execution Source-bound Kani/Verus kernel proofs, compiler-derived routing checks and extension/bypass controls
Additional assurance Miri, Loom concurrency scenarios, sanitizer fuzzing, coverage and dependency inventories
Release archives Extracted-package tests and an external consumer for minimal/default/all features
Code scanning GitHub-managed CodeQL for Rust, Python and Actions
cargo run -p xtask --locked -- audit --strict

Rust assurance, the API contract map and release checks describe commands, evidence and scope. Verification is tied to source/configuration; it is not a certification of arbitrary application code or all possible UI histories.

Platforms and current limits

The target is embedded Linux on x86_64/aarch64 with OpenGL ES 2 through eframe's glow backend. CI runs the full audit and graphical tours on Linux and type-checks aarch64; it does not validate a physical aarch64 device. Selected CPU display and runtime-fault checks also run on Windows. The complete Windows runner and macOS are not validated. The declared minimum Rust version is 1.95; repository development uses 1.99.

Built-in text is English/Korean; supply translations and fonts for your product. The keyboard has numeric, QWERTY and two-set Hangul layouts. There is no accessibility tree or right-to-left layout. See the roadmap for further limits.

Documentation and workspace

Resource Purpose
Integrator guide Installation, screens, chrome, access, backends and deployment
Migration Source and behavior changes from the earlier checkout
Architecture State ownership, frame order and module map
Session contract Issuer/session lifecycle and typed UI integration
Protected mediation Shared kernel and automatic route verification
Application metadata checker Check your workspace's permission declarations

Build the local API reference with cargo doc -p fairing --all-features --no-deps --open. docs.rs will host it after publication. The changelog records changes.

fairing and fairing-widgets are the product crates. xtask owns repository maintenance; verification is an unpublished assurance workspace. fairing-fixed-heap is a diagnostic with no product dependency edge.

Security and licence

Report vulnerabilities privately through SECURITY.md. MIT; see LICENSE and third-party notices.

About

A touchscreen shell for embedded devices, built on egui: status bar, shade, desktop, on-screen keyboard, gestures and access gates in one frame loop

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages