Skip to main content
Lindroids is one Rust binary, lindroids: the CLI and, as lindroids server, a Wayland compositor and PulseAudio-protocol sound server that also serves the page that draws its windows. You can build it, test it and run your build on a phone with Rust alone: no Java, no Android SDK, no NDK. Those are needed only for the few files listed under Files that need other tools, and their outputs are committed. Questions: Discord or an issue.

The code

Read docs/ARCHITECTURE.md first, then docs/DISPLAY.md for the page and the link’s security, or docs/APPS.md for exported apps.

Build and test on a Linux PC

Rust stable from rustup. clang is optional: without it the build leaves out two small preload libraries (the GTK 3 file picker and an x86 alarm() fix) and says so.
Your build runs on the desktop too: target/debug/lindroids run foot starts the server and opens the page in your browser (xdg-open). LINDROIDS_LOG=debug logs more; the server’s log is server.log in its runtime directory (lindroids status shows it). More, including lindroids debug-app for scripted input: docs/TESTING.md.

Test a dev build

scripts/build.sh is for development only: it builds your checkout into a lindroids package that installs over the release, so you can try a change before it is released. Everyone else installs Lindroids with install.sh (README). scripts/use-release.sh goes back to an official release. Both have --help. build.sh finds the package manager of the system it runs in (or takes --format deb|pacman|rpm|apk), checks for everything the build needs and offers to install what is missing with that package manager: Rust (rustup, or Termux’s rust), a C compiler, clang, the Rust target, cargo-ndk and the Android NDK for phone builds on a PC, dpkg-deb, bsdtar, adb. --yes installs it without asking; without a terminal it stops and prints the command. The first build compiles every dependency (two minutes on a fast machine, longer on a phone; about 1 GB with Rust), later ones only what changed. Clone the whole history (no --depth): the version is counted from it. A dev package is the release this commit would be, marked as a dev build: it installs over the releases before it, and the release itself replaces it on a later upgrade. A running server is kept until it is stopped: --install stops an idle one, and one with windows open says so. Run lindroids stop before trying a new build you installed another way, or the old server keeps serving.

In Termux

To try a build without installing it: lindroids stop, then ./target/release/lindroids run foot (the server is started from the binary that asked for it).

In a Linux distro: a container, a proot distro, a desktop

rpm and apk packages are made in a Fedora or Alpine container when podman or docker is there, else natively on an rpm system or on Alpine (packaging/container.sh), so they build in proot distros too. Void (xbps) has no dev packages. To make another system’s package, --format and --target arm64|armhf|amd64|i386.

From a PC, for a phone

With the phone (or any Android device) in developer mode, USB debugging on, and plugged in:
The first run offers what a cross build needs: Rust and the Android target, cargo-ndk, the Android NDK r27c (the 650 MB zip alone, checked, into ~/.local/share/lindroids-dev: no SDK, no Java; an NDK of yours is used if ANDROID_NDK_HOME names it), adb and dpkg-deb from your package manager. On an immutable system (Fedora Silverblue and the like) run it in a toolbox or distrobox. With several devices attached, --device <serial> (from adb devices). Then in Termux (after termux-setup-storage once, so it can read Download), the command it printed:
--target aarch64|arm|x86_64|i686 builds a Termux package without a device. For a proot distro on the phone, --target arm64 --format pacman --device --push (for example): proot-distro shares the Download folder as /sdcard/Download. Packages go to target/dev-build/. Android 7 (API 24) is the oldest supported.

Back to a release

It goes through whatever installed lindroids (apt, pacman, dnf, yum, zypper, apk or a static install), downgrading from a dev build or a newer release as needed. The repositories keep the newest three releases; older ones come from their GitHub release (a .deb checked against its SHA256SUMS, an .rpm). pacman has only those three. Alpine’s repository is not published yet: there the release’s static binary is put in /usr/local/bin instead.

What to try, and what to report

  • lindroids run foot (pkg install foot): a window, typing, the soft keyboard, closing it.
  • lindroids export foot, then the app’s icon with Termux closed.
  • A GTK program (pkg install mousepad), a Flatpak, or an X11 program (lindroids setup x11) if your change touches them.
  • lindroids doctor when something does not start; the server’s log is server.log in the directory lindroids status shows.
In the pull request or on Discord, say what you ran it on: lindroids version, the device and Android version, which Termux (F-Droid, GitHub or Google Play) or which distro, and the browser if a window opened in one.

Files that need other tools

These are built rarely and committed, so nobody else needs the tools:

The docs site

The Markdown in docs/ is also the docs site, published by Mintlify on every push to main: docs.json (at the root) is its navigation, .mintignore keeps everything else out. So a page reads well both on GitHub and on the site:
  • each page starts with frontmatter (title, description) instead of a # heading;
  • placeholders in prose go in backticks (`<name>`): a bare <name> is read as a tag;
  • links to source files are full GitHub URLs; links between pages stay relative.
Preview and check it (Node 20.17 or newer):

Pull requests

  • Branch from main; one change per pull request.
  • CI runs on every pull request: cargo test --workspace, tests/host-integration.sh, shellcheck on the scripts, the window app build, and every package installed by install.sh in containers of a dozen distros. Run the first two yourself before pushing, and tests/install-repos.sh (podman or docker) when you change packaging/ or install.sh.
  • Change the docs with the code: a new command in the README’s usage, a design decision in docs/, with what was tried and what was found.
  • Commit messages say what changed for the user, in a short sentence (see git log).
  • Every merge to main is a release (0.1.<commit count>), published to the package repositories, so main must always work.
  • Contributions are licensed MIT OR Apache-2.0, as the project is.