- Overview
- What Works
- Architecture
- Quick Start
- Configuration
- Home Manager
- Platform Support
- Development
android-kvm turns a USB-connected Android device into an edge-adjacent software KVM target. Push the host pointer through a configured screen edge, and the host starts forwarding keyboard and mouse input to Android through scrcpy's UHID control channel. Move back through the opposite Android edge to release capture and return to the host.
The project intentionally mirrors the lan-mouse interaction model, but keeps Android transport USB-first:
- Host capture comes from lan-mouse's
input-capture/input-eventcrates. - Android injection uses scrcpy's server and control socket directly.
- Audio can be kept alive through a separate audio-only scrcpy process.
- Nix integration exposes both a package and a Home Manager module.
Because the capture layer uses GPL-3.0-or-later lan-mouse crates, this project is licensed as GPL-3.0-or-later.
android-kvm is past the initial scaffold stage. It currently provides:
- A Rust CLI with
daemon,check, andprint-configsubcommands. - TOML configuration from
${XDG_CONFIG_HOME:-~/.config}/android-kvm/config.toml. - lan-mouse-style edge activation with an outward swipe threshold.
- Relative pointer motion forwarding into Android.
- Mouse buttons, scroll, and common keyboard key forwarding through scrcpy UHID.
- Virtual Android pointer bounds tracking so moving back across the Android edge releases capture.
- Direct scrcpy server startup through
adb shell app_process. - Optional audio-only scrcpy routing that does not compete with the UHID control socket.
- A Nix package, dev shell, formatter, and Home Manager module exported from the flake.
flowchart LR
host[Host input capture] --> edge[Edge state machine]
edge --> focused{Android focused?}
focused -->|yes| uhid[scrcpy UHID control socket]
focused -->|no| hostOnly[Host owns input]
uhid --> android[Android device]
adb[adb] --> server[scrcpy server]
server --> uhid
audio[optional audio-only scrcpy] --> android
Runtime ownership is split deliberately:
android-kvmlaunches the scrcpy server and owns the control socket for UHID keyboard/mouse events.- If audio is enabled, a second scrcpy process is started with
--no-controlso audio can route to the host without taking over input. - The edge runtime tracks whether the host or Android is focused, applies activation/release thresholds, and keeps an Android-sized virtual pointer for return-edge detection.
Enter the development shell:
nix developRun directly from GitHub:
nix run github:DivitMittal/android-kvmValidate that configured dependencies are available:
cargo run -- checkPrint the resolved configuration:
cargo run -- print-configPreview the adb / scrcpy commands without starting capture:
cargo run -- daemon --dry-runRun the KVM:
cargo run -- daemonOverride the configured Android placement for one run:
cargo run -- --android-edge left daemonSet android-edge to where the Android device sits relative to the host display: left, right, top, or bottom.
For example, with android-edge = "right":
- Move to the host's right edge.
- Keep swiping outward by at least
activation-pixels. - Android receives mouse and keyboard input.
- Move left to Android's left edge to release capture back to the host.
Resting at the host edge is not enough to activate Android focus; the outward swipe threshold is intentional to avoid accidental capture.
By default, android-kvm reads ${XDG_CONFIG_HOME:-~/.config}/android-kvm/config.toml. See the Configuration wiki page for the full TOML example and option reference.
Import the flake module and configure programs.android-kvm:
{
inputs,
pkgs,
...
}: {
imports = [
inputs.android-kvm.homeManagerModules.android-kvm
];
programs.android-kvm = {
enable = true;
package = inputs.android-kvm.packages.${pkgs.stdenv.hostPlatform.system}.default;
settings = {
android-edge = "right";
activation-pixels = 24;
release-pixels = 4;
poll-interval-ms = 16;
pointer-scale = 1.0;
audio-always-on = true;
keep-awake-while-connected = true;
power-on-on-connect = false;
adb-binary = "adb";
scrcpy = {
audio-enabled = true;
audio-buffer-ms = 200;
};
};
};
}The module writes settings to xdg.configFile."android-kvm/config.toml" and installs the selected package into home.packages.
android-kvm asks lan-mouse's input-capture crate to pick the first available backend for the current OS instead of hard-coding one capture backend in this project.
| Host OS | Runtime support | Packaging support |
|---|---|---|
| Linux / Wayland | Uses available lan-mouse capture backends such as layer-shell and the input-capture portal. | Nix package/dev shell include Linux X11 libraries needed by lan-mouse's optional X11 backend. |
| Linux / X11 | Uses lan-mouse's X11 capture backend when available. | Nix package/dev shell include libX11 and libXtst. |
| macOS | Uses lan-mouse's macOS capture backend; grant Accessibility/Input Monitoring permissions if capture cannot start. | Exposed through the flake for x86_64-darwin and aarch64-darwin. |
| Windows | Uses lan-mouse's Windows capture backend when built with Cargo on Windows. | Nix does not package Windows targets; use the Rust/Cargo workflow on Windows. |
Every host still needs adb, scrcpy, USB access to the Android device, and any OS-specific input-capture permissions.
Common commands:
nix develop
cargo test
cargo run -- check
cargo run -- daemon --dry-run
nix fmt
nix flake checkThe flake uses the same default Linux/Darwin system set as lan-mouse: x86_64-linux, aarch64-linux, x86_64-darwin, and aarch64-darwin.
