com.harryaskham.x11droid · Kotlin · C

X11 Droid

Runs X servers for programs from Nix-on-Droid and shows each program as its own Android floating window, or a whole X desktop such as i3 fullscreen. Android touch, keyboard and clipboard reach the X programs, and their sound plays through PulseServer on the same phone.

Its sibling Waylandroid does the same for Wayland desktops; the two share floating windows, the zero-copy GPU bridge and the remote-control listener.

The X apps launcher: a floating X window titled 'Input fixture :1000' over a list of presets, including GIMP with its launch command and environment, and a window managers section with i3.
A floating X window over the launcher.

Launching a program

From a tap to a floating X window Launching an X program. The X apps launcher asks nod-exec, a small command server inside Nix-on-Droid, to start the program with DISPLAY pointing at an X server the app manages and PULSE_SERVER pointing at PulseServer on the same phone. Each floating program gets its own X screen, numbered from 1000; window managers such as i3 and full desktops run on the fullscreen session :0 instead. X11 Droid turns each screen into an Android floating window with a title bar and resize corner, and sends Android touch, keys and the clipboard back into X. X apps launcherxterm · GIMP · custom nod-execin Nix-on-Droid X serverscreen :1000, :1001 … X programDISPLAY · PULSE_SERVER floating windowone per screen touch · keysclipboard NIX-ON-DROID starts frames
One X screen per float. Window managers and desktops share the fullscreen session :0.

The X apps launcher has presets for xterm, xclock, xeyes and GIMP, a custom command, and window managers such as i3, which gets a minimal configuration on first run so it never stalls. Programs start through nod-exec, a small command server inside Nix-on-Droid, with DISPLAY set to an X server the app manages and PULSE_SERVER=127.0.0.1:4713 set to PulseServer.

A floating window can be dragged, resized, minimised to a pill, docked fullscreen and closed, and it comes back after the app's process is killed or the phone restarts. Resizing either scales the program's existing framebuffer or asks the X screen to adopt the new size; the X font DPI stays at 144 either way, so text does not jump.

The clipboard is bridged both ways with X's CLIPBOARD and PRIMARY selections. Android 10 and later only let an app read the clipboard while it is in front, so copying from Android into X happens while one of X11 Droid's screens is open; copying out of X works from the background.

Home-screen widgets, in compact, standard and large-text sizes, show whether the X server and Waylandroid's host are running and start a desktop or open the launcher; a Quick Settings tile toggles the X server.

A grid of home-screen widget variants for X11 Droid and Waylandroid in compact, standard and large-text sizes, showing running and stopped states with Open, Apps, Start and Stop buttons.
The widgets for X11 Droid and Waylandroid, in each size.

Getting frames to the screen

Three ways a frame reaches the screen X11 Droid's three presentation paths, from the most compatible to the fastest. Capture reads the X framebuffer back on the CPU and uploads it to Android: it works everywhere and costs two copies. Docked GPU transfer lets the X server draw into shared framebuffer memory that the bridge copies into Android's buffer ring on the GPU, removing the readback and the upload, though the X server still draws in software. The opt-in GPU X server, compiled on the phone from sources in the app, keeps a GPU program's frames on the GPU and hands them to that program's floating window with no CPU copies. captureCPU readback + upload docked GPU transfershared framebuffer → GPU copy GPU X serverDRI3 · per-window hand-off 2 CPU copies 1 GPU copyX draws in software 0 CPU copiesGPU programs everywhere docked screenAndroid 13+ per-window floatsAndroid 13+, Turnip
Compatibility first, speed where the phone allows. A failed GPU path falls back to capture rather than leaving a blank window.

By default the app captures each X screen's framebuffer and draws it into the float, which works with every X program and every phone, and the app's live performance status reports it as a CPU-readback transport. On Android 13 and later, a docked screen can instead share its framebuffer with the GPU bridge, removing the readback and upload. The opt-in GPU X server goes further for programs that render with the GPU (through Turnip or zink): it is built on the phone from sources carried in the app, runs DRI3, and hands each program's frames to its float through the same zero-copy bridge as Waylandroid. Turning it on never restarts a running server.

Limits