Skip to content

Repository files navigation

flipctl-slint

Slint implementation of the Flipper One UI: the shared component library plus a demo binary that drives the 256x144 SPI panel, its buttons, and a browser view.

This is Phase 0 and Phase 1 of the flipctl UI plan. The library is intended to be shared by flipperos-installer, flipper-boot-menu and flipctl; the first two of those consume it today, the installer still draws its own components.

Licensing

Our source is MIT. The distributed binary is GPL-3.0-only, because it links Slint and we elect the GPL-3.0-only arm of its tri-license; sources stay publicly buildable either way. THIRD-PARTY-LICENSES.md collects the license text of every crate the panel binary links and is generated by scripts/gen-third-party-licenses.sh from Cargo.lock, so it cannot drift.

The three pixel fonts in third_party/flipctl-fonts carry their own terms, and one is not MIT: HaxrCorp 4090 is an adaptation of a CC BY-SA 3.0 font, so it and the glyph table generated from it stay ShareAlike. REUSE.toml records that per file, and reuse lint checks it.

The same applies to an app under apps/ that draws with the framework: it links flipctl-app, and so Slint, so its binary is GPL-3.0-only too while its source stays MIT. Each app has a THIRD-PARTY-LICENSES.md of its own, generated by the same script, which travels inside its bundle.

So, in the terms the rest of the org states this in:

  • GPL-3.0-only: the whole binary, flipctl's and any framework app's, because it statically links Slint
  • MIT: our source, and the Busy9px font
  • CC-BY-SA-3.0: the HaxrCorp 4090 font, and the glyph table generated from it
  • Unlicense: the Born2bSportyV2 font

Layout

Path What
crates/flipper-ui/tokens.toml Every colour, offset, radius, timing and count. The single source of truth.
crates/flipper-ui/build.rs Generates the Rust and Slint themes from it, and compiles the components.
crates/flipper-ui/ui/ root.slint (the one window), idle.slint, list.slint, statusbar.slint, frame.slint.
crates/flipper-ui/src/app.rs An app's manifest, what it needs installed, and how it is launched.
crates/flipper-ui/src/bundle.rs The ~/Apps walk, and the AppImages in it: read without being run, remembered by stamp.
crates/flipper-ui/src/script.rs A script as an app: the manifest in its head, and the runtime it is run through.
crates/flipper-ui/src/ipc.rs The socket a desktop hands a bundle to, behind flipctl open.
crates/flipper-ui/src/status.rs Live battery, temperatures, power, hostname, profile and addresses, from sysfs and procfs.
crates/flipper-ui/src/kms.rs DRM/KMS panel sink.
crates/flipper-ui/src/evdev.rs Raw evdev button reader, no libinput.
crates/flipper-ui/src/remote/ Browser view: frame stream, input endpoint, comparison page.
crates/flipper-ui/src/tui/ The same view models in a character grid, for a serial console. Cursive over crossterm, behind the tui feature.
tools/appimage/ The bundler: an app under apps/ to an aarch64 AppImage, on the host. ci/bundle.Dockerfile is its container.
tools/ Converters for the prototype's packed fonts and 6-bit greyscale sprites.
crates/flipper-tokens/ The theme generators, as a library both flipctl's build script and an app's can call.
crates/flipctl-app/ The framework a hosted app draws itself with: the window, the keys, the status global, and the widget library.
docs/apps.md How to write an app.
docs/inventory.md The design system as measured, and where fake-flipctl2's own docs have drifted from its code.
docs/dependencies.md What the binary links, and why, with measurements.

Build

The host toolchain usually has no aarch64 std, so this is built on the device or in the dev container, never on the local host.

export CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16
cargo build --release -p flipctl --features device,slint,remote

LTO off and 16 codegen units are for build speed while iterating; drop both for a size-measured build.

Run

What runs on the machine is /usr/bin/flipctl under flipctl.service, the same one the image boots. A deploy installs over that path and restarts the unit; the build tree is where the build happens and nothing more. From the host:

./build_deploy.sh --panel      # the panel and its buttons
./build_deploy.sh              # browser only, leaving the panel alone
./build_deploy.sh --cross      # build here for aarch64 in docker, not on the device
./build_deploy.sh --apps       # also push target/appimage/*.AppImage to ~/Apps

The build happens on the device by default, because that is where the toolchain for it is: about 25s once its target directory is warm. --cross builds here instead, against ci/cross.Dockerfile, and pushes the binary. Nothing in flipctl links a C library except libc, so a cross gcc is the whole of what a host needs, and a cold build measures 3m against 20m on the device. Warm, the device wins; cold, this does.

or on the device, in the tree the deploy copied over:

./run_me.sh

A deploy brings the binary and the browser view's assets. Apps are AppImages in ~/Apps on the device, built by tools/appimage/build.sh and pushed with --apps (docs/apps.md); a deploy without it leaves them alone. It also installs what a bundle needs from the machine until the image ships it: fuse3, and the udev rule, group and modules listed in systemd/README.md.

The mode, the port and the peer live in a deploy drop-in, /etc/systemd/system/flipctl.service.d/50-deploy.conf, rewritten by every deploy. Delete it and the machine runs exactly what the image ships: --panel on 8899 with the installed assets.

systemd-run --unit=flipctl is not an option, and the error is worth knowing: the image installs /etc/systemd/system/flipctl.service, and a transient unit cannot take a name that already has a fragment file on disk, stopped or not (Unit flipctl.service was already loaded or has a fragment file).

A one-off out of the build tree is still the way to measure, since it takes flags the unit does not carry, and it needs the panel's owner stopped first:

sudo systemctl stop cog-seat1.service flipctl.service
sudo ./target/release/flipctl --panel

Then open http://<device>:8899/: our screen, an overlay that counts differing pixels against a peer if one is set, and a control pad laid out like the hardware. --frames N exits after N commits and reports timings; --bench measures the ceiling.

8899 is the port the fake-flipctl2 prototype's own server used to hold, and cog still loads http://localhost:8899, so flipctl now answers the URL that kiosk opens. There is no default peer for the same reason: comparing against 8899 would be comparing against ourselves. Point --peer at a prototype wherever one is actually running:

PEER=127.0.0.1:8900 ./build_deploy.sh   # a prototype moved off 8899

The two cannot share a machine on one port, and the prototype is what has to move, since its server binds 8899 and its browser asks for it.

Tests

cargo test -p flipper-ui --features slint,remote

FLIPPER_UI_BLESS=1 regenerates the goldens. Most goldens come from our own output, so they only prove self-consistency; tests/reference/soft_button_bar.png is derived from the design export instead, and is the one fixture that can catch the implementation being confidently wrong.

About

FlipCTL app

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages