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.
| 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.
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-x11Set 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.
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.
- 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.
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.
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 --strictRust 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.
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.
| 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.
Report vulnerabilities privately through SECURITY.md. MIT; see LICENSE and third-party notices.






