The React UI is sluggish on the kiosk and the reasons are browser-shaped: a compositor deciding what gets its own layer, a requestAnimationFrame loop repainting the viewport, and a JPEG decode per album card per cold start. The last three commits chased that through the service worker and the cover pipeline. This is the other direction - the same backend, the same layout, the same German copy, without a browser. Browse and play only. The typing game, the room-lights page, parent mode and the IR-remote assignment stay web-only, and nothing here touches python-backend. Four things carry the performance claim, in rough order of how much they should matter: Covers are downscaled once, ever. The backend serves one size - 640px on the long edge, no ?size= - and a grid card is 180. src/covers.rs fetches each cover once on a worker thread, resizes it, and writes a JPEG thumbnail to ~/.cache/musicmouse-slint/covers/. Two tiers and only two, which is what makes the cache worth keeping: a per-widget pixel size would give each album a dozen near-identical files and a fresh decode for each. The cache is dropped when the websocket announces a rescan, because that is the one moment the backend rewrites the art behind an unchanged cover URL - the trap sw.js had to learn about the hard way. The grid is virtualized. Rust hands the UI the album list pre-chunked into rows and the view puts those in a ListView, which instantiates only what is on screen. A flat list of 660 cards gives it no rows to skip, hence the chunking. Same purpose as content-visibility: auto on .grid > .card. The progress bar animates between the 2 Hz pushes rather than running a clock, so interpolation costs a property evaluation per frame on the render side and there is no equivalent of usePlaybackClock. It is suppressed for the frame a track changes on, so a new track jumps instead of sliding across two unrelated positions. Nothing on screen animates by itself. The ambient canvas is not ported, for the reason lib/lowPower.ts already gives: a loop repainting the viewport is a floor you cannot get under while it runs at all. The device must not use FemtoVG. On Mesa V3D it draws every runtime-loaded Image as solid black (slint-ui/slint#11785, open), which here means every album cover; Skia and the software renderer are unaffected. So `kiosk` is linuxkms + Skia and FemtoVG stays the default only for desktop development. Both profiles are verified to build; the kiosk binary links libinput/libgbm/libdrm and no X11 or Wayland at all. lib/search.ts, lib/keyboard.ts and lib/format.ts were already pure functions with their own tests, so they port across as pure Rust with theirs: 43 tests, no window required. The .slint files are layout only - nothing in them formats a number or picks a word. Verified against the real 660-album library rather than a fixture. tools/headless-shots.sh renders the UI inside a nested headless compositor and grabs a frame per screen, which is how that was checked on a machine whose session was locked; a Slint window needs a real compositor, and under bare Xvfb nothing maps at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.1 KiB
Nix
65 lines
2.1 KiB
Nix
# Dev shell for the Slint front-end.
|
|
#
|
|
# NixOS has no rustup-installed toolchain here, and Slint's renderers link against
|
|
# system GL/X11/Wayland libraries that a plain `cargo build` cannot find on its own -
|
|
# hence LD_LIBRARY_PATH below rather than just a list of buildInputs.
|
|
{ pkgs ? import <nixpkgs> { } }:
|
|
|
|
let
|
|
# Everything the winit backend dlopen()s at runtime. Missing one of these shows up
|
|
# as a panic inside winit, not as a link error, so they are easy to misdiagnose.
|
|
runtimeLibs = with pkgs; [
|
|
libGL
|
|
libxkbcommon
|
|
wayland
|
|
xorg.libX11
|
|
xorg.libXcursor
|
|
xorg.libXi
|
|
xorg.libXrandr
|
|
fontconfig
|
|
freetype
|
|
];
|
|
in
|
|
pkgs.mkShell {
|
|
nativeBuildInputs = with pkgs; [
|
|
cargo
|
|
rustc
|
|
rustfmt
|
|
clippy
|
|
pkg-config
|
|
# Only needed when building with --features skia: rust-skia compiles its own
|
|
# bindings shim and wants a C++ toolchain plus python for the GN build.
|
|
clang
|
|
python3
|
|
];
|
|
|
|
# Nunito is the face the design is drawn in. The web front-end self-hosts it as
|
|
# woff2, which Slint cannot read, so the dev shell points Slint at the TTF instead
|
|
# (`SLINT_DEFAULT_FONT` takes one file and makes it the primary font). Without it
|
|
# everything still renders, in whatever sans fontconfig hands back.
|
|
SLINT_DEFAULT_FONT =
|
|
"${pkgs.nunito}/share/fonts/truetype/Nunito/Nunito[wght].ttf";
|
|
|
|
buildInputs = runtimeLibs ++ (with pkgs; [
|
|
fontconfig
|
|
# libinput/libdrm/libseat are the linuxkms (kiosk) backend's dependencies. They
|
|
# cost nothing to have present on a desktop build.
|
|
libinput
|
|
libdrm
|
|
seatd
|
|
udev
|
|
]);
|
|
|
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
|
|
|
|
# rust-skia downloads a prebuilt libskia rather than building it, when it can.
|
|
SKIA_BINARIES_URL_TEMPLATE = null;
|
|
|
|
shellHook = ''
|
|
echo "musicmouse slint-frontend — cargo $(cargo --version | cut -d' ' -f2)"
|
|
echo " cargo run # windowed, femtovg"
|
|
echo " cargo run --features skia # windowed, skia (what the Pi uses)"
|
|
echo " cargo build --features kiosk # linuxkms + skia, for the device"
|
|
'';
|
|
}
|