A single-window browser drawing straight onto the Linux framebuffer.
Part of the odio project — Full documentation.
A single-window QtWebEngine browser drawing straight onto /dev/fb0, with no
X server or Wayland compositor: one page, full screen, no chrome around it. It
is published as a Debian package to the odio APT repository.
The source in src/ derives from
Framebuffer-browser at commit
4213a69, reduced to the kiosk window and ported to Qt 6. Upstream's full
browser — the Qt 5 simplebrowser example, with tabs, downloads, popups and
dialogs — is not carried here. What remains is main.cpp and mainwindow.cpp,
still LGPL-3; see debian/copyright.
Qt 5.15 carries Chromium 87, from late 2020. It predates :where(),
accent-color, container queries, unprefixed mask-* and Object.hasOwn, and
the failures are quiet: an unknown pseudo-class invalidates the CSS rule that
contains it, and a missing built-in surfaces as a caught exception somewhere in
a library. Qt 6.8, the version Debian trixie ships, carries Chromium 122.
-
/usr/bin/odio-kiosk -
/etc/default/odio-kiosk— the node's Qt environment: the framebuffer every screen defaults to, theQT_QPA_FB_*settings and the Chromium flags. -
/usr/lib/systemd/user/odio-kiosk@.service— a template, one instance per screen.~/.config/odio-kiosk/<instance>.confnames the page that screen shows, and its framebuffer if it is not the first:URL=http://localhost:8018/ui FB=/dev/fb1No instance is enabled on install:
sudo usermod -aG tty,video,input $USER # then log in again
sudo loginctl enable-linger $USER
mkdir -p ~/.config/odio-kiosk
printf 'URL=http://localhost:8018/ui\n' > ~/.config/odio-kiosk/main.conf
systemctl --user enable --now odio-kiosk@main.service/etc/default/odio-kiosk sets the QT_QPA_* variables upstream's fbrowser
launcher would export, and the URL comes from the command line, so
config.json is not needed.
The unit names no server. Which one to wait for, and be restarted with, is deployment configuration, so it goes in a drop-in:
# ~/.config/systemd/user/odio-kiosk@main.service.d/wait-for.conf
[Unit]
After=default.target my-server.service
PartOf=my-server.serviceAfter=default.target belongs there too: without it the target orders itself
after the kiosk, and a server that is itself After=default.target closes a
cycle at boot.
QtWebEngine ignores the system trust store: Chromium 122 carries its own root
list, and Debian gives it no updates. On every start, before the engine loads,
the kiosk marks the certificates in distrust/ as distrusted in
~/.pki/nssdb, the user database Chromium reads, and refuses to start if it
cannot. They are eMudhra's compromised intermediates and the two roots Debian
removed for them (odios#104),
and the roots Chrome has since dropped for cause: Entrust, AffirmTrust,
Chunghwa Telecom and NetLock. A certificate to distrust is a PEM file dropped
there and a rebuild.
amd64(Debian trixie)arm64(Debian trixie / Raspberry Pi OS trixie)
No armhf. Raspberry Pi OS armhf is Raspbian, a separate rebuild of the
archive, and it publishes the qt6-webengine source and its Architecture: all
pieces but no armhf binary — exactly as it did for Qt 5. The Qt 6 move does not
bring armhf back; a 64-bit Pi OS install does.
docker build -t fbui-builder -f docker/builder.Dockerfile docker
docker run --rm -v "$PWD":/workspace \
-e DEBEMAIL=you@example.com -e DEB_BUILD_OPTIONS=noddebs fbui-builder bash -c '
mkdir -p /build/src && cp -a /workspace/. /build/src/ && cd /build/src &&
dch --newversion 0.0.0+local -b --distribution trixie --force-distribution "Local build." &&
dpkg-buildpackage -b -us -uc && cp /build/*.deb /workspace/dist/'Or, against the system Qt 6:
cmake -B build && cmake --build build
./build/odio-kiosk http://localhost:8018/uidebian/changelog holds the version, and the tag has to
name it — a tag that does not match fails the build, so a stale tag cannot
publish something else:
git tag v2.0.0 && git push origin v2.0.0A prerelease (routed to the testing channel of the APT repo) adds a suffix,
which becomes a ~ version so apt upgrades off it:
git tag v2.0.0-rc1 && git push origin v2.0.0-rc1Branch pushes build but publish nothing; they are versioned 0.0.0+ci.<sha>.
Pre-baked images on GHCR carry the Qt toolchain. They are rebuilt
automatically when docker/builder.Dockerfile changes, monthly, or on demand
— and must exist before the first build:
gh workflow run build-images.ymlPlain debhelper, in debian/, native format: the source and its
packaging live in the same repo, so the version carries no Debian revision.
Depends comes from dh_shlibdeps, and lintian gates every build.
The build scripts, unit and Dockerfile are MIT. The browser source
in src/, and the packages built from it, are
LGPL-3.0.