README.md raw
liftoff
A classically Unix-ish application launcher for Wayland.
Most launchers today are built around XDG desktop entries — they show you
.desktop files. liftoff does the older, simpler thing: it runs programs from
your $PATH, optionally with arguments, the way a shell would. You type a
command, it completes on the fly, and pressing space (to add arguments) or enter
(to run) uses the highlighted match. It’s not identical to typing at a shell
prompt, but it’s close.

Behavior
- Completion, live. As you type, liftoff filters the executables on your
$PATHand lists the matches. When the highlighted match continues what you typed, its remaining letters are shown as a dimmed “ghost” after the cursor. $PATHorder, not alphabetical. Matches are listed in search order — everything from the first directory on your$PATH, then the second, and so on — with names from the same directory sorted alphabetically. So the copy a shell would actually run is the one you reach first. A command found in several directories is listed once, under the earliest.- A whole name wins. If what you typed is a command, it heads the list
wherever it lives on your
$PATH:gitgets yougit, notgitkfrom some directory ahead of it. Where the matching ignores case, so does this — typingsteamputsSteamat the top. - Forgiving matching. liftoff tries progressively looser criteria and stops
at the first that finds anything: names that start with what you typed, then
names that contain it, then a case-insensitive contains. So
valvefindscom.valvesoftware.Steam— but an exact prefix is never diluted by looser hits. - The highlighted match is what runs. It defaults to the first match listed; the arrow keys move it.
- Space commits, then you type arguments. Pressing space replaces your typed text with the highlighted command and switches to argument entry. From there, further typing builds an argument string (split with shell-like quoting rules), and spaces are literal.
- Enter runs it. The highlighted command is launched, detached, with whatever arguments you’ve typed, and liftoff exits.
Executables reached through symlinks count too, so flatpak’s exported app ids
(like com.valvesoftware.Steam) turn up alongside everything else — no
.desktop files needed. And because liftoff only ever launches a name it found
on your $PATH, it never tries to run something that doesn’t exist.
Keys
| Key | Action |
|---|---|
| (type) | Filter commands by prefix |
↑ / ↓ | Move the highlight up / down the match list |
Space / Tab | Commit the highlighted command and start typing arguments |
Enter | Run the highlighted command (with any arguments) and exit |
Backspace | Delete a character; from empty arguments, return to editing the command |
Esc | Dismiss without running anything |
Requirements
- A Wayland compositor implementing the
wlr-layer-shellprotocol — the wlroots family (Sway, Hyprland, river, Wayfire, …) and others. liftoff appears as a centered overlay and takes keyboard focus while it is open. - A recent Rust toolchain (edition 2024).
liftoff renders itself in software with cosmic-text for font shaping, so it
needs no GPU and pulls in no GTK/Qt. It uses your installed system fonts.
Install
git clone https://git.sr.ht/~djarb/liftoff
cd liftoff
cargo install --path .
This puts a liftoff binary in ~/.cargo/bin.
Use
liftoff has no options — run it and start typing. In practice you bind it to a key in your compositor. For example, in Sway:
bindsym $mod+space exec liftoff
or in Hyprland (Lua config):
hl.bind("SUPER + SPACE", hl.dsp.exec_cmd("liftoff"))
How it works
liftoff is split into a display-independent core library and a thin UI binary:
executablesscans every directory on$PATHfor executable files (following symlinks), keeping the names sorted and deduplicated so prefix matches are a contiguous binary-searched range and the substring fallbacks are a simple scan. Each name remembers which$PATHdirectory it came from, and the matches a search returns are stably re-sorted by exactness and then that — which, from an alphabetical starting point, yields the typed name first and then search order with an alphabetical tie-break.queryis the editing model behind the input line: a small state machine that moves between completing a command name and typing its arguments, and tracks which candidate is highlighted.launchspawns the resolved command in its own session so it survives liftoff exiting.- The binary draws the overlay with
smithay-client-toolkit(layer shell, input) andcosmic-text, compositing into a shared-memory buffer by hand.
The overlay is put on screen before anything slow happens. Until it is mapped it
does not hold keyboard focus, so any key struck in the meantime goes to the
window underneath — which matters, because both scanning $PATH and loading
fonts can take a noticeable moment on a cold cache. So liftoff commits its
surface first and does that work afterwards, with the $PATH scan on a worker
thread. You can type immediately; a keystroke that needs the command list before
it has arrived (enter, tab, the arrow keys) is held and applied the moment it
does, in the order you typed it.
The core is covered by unit tests (cargo test).
License
liftoff is released into the public domain under the Unlicense. Do whatever you like with it.