A Launchpad-style application launcher and file search overlay for Hyprland, Sway, and any Wayland compositor with layer-shell support. Written in Rust.
Ported from nwg-piotr/nwg-drawer (Go) with enhancements: compositor-neutral IPC via the Compositor trait, shared pin state with nwg-dock, and graceful fallback on unsupported compositors (Niri, river, Openbox).
- Full-screen overlay — dark transparent Launchpad-style launcher
- Keyboard navigation — arrow keys between icons, Enter to launch, type to search
- Category filtering — filter bar with per-category buttons
- Description line — status bar shows app description on hover/focus
- Power bar — lock/exit/reboot/sleep/poweroff with
--pb-autoauto-detection - Configurable opacity —
--opacity 0-100for background transparency - Subsequence search — type to filter apps by name, description, or command
- File search — columnar results with system theme icons, sorted alphabetically
- Math evaluation — type expressions like
2+2and get results with clipboard copy (viaexmex) - Command execution — prefix with
:to run arbitrary commands - Pin sync — shared pin file with
nwg-dock; changes reflect instantly on both via inotify - Compositor-neutral — runs on Hyprland, Sway, and any layer-shell-capable compositor. Graceful feature degradation on the null backend (Niri, river, Openbox, etc.)
- Go flag compatibility — accepts original Go nwg-drawer flag names (
--pbexit,--nocats, etc.)
- Rust 1.97 or later (pinned in
rust-toolchain.toml; rustup picks it up automatically) - GTK4 and gtk4-layer-shell system libraries
- A Wayland compositor with
wlr-layer-shellsupport (Hyprland, Sway, Niri, river, etc.)
# Arch Linux
sudo pacman -S gtk4 gtk4-layer-shell
# Ubuntu/Debian
sudo apt install libgtk-4-dev libgtk4-layer-shell-dev
# Fedora
sudo dnf install gtk4-devel gtk4-layer-shell-develcargo install nwg-drawerInstalls only the binary; the drawer-installed data assets (drawer.css, category icons) are not copied. The drawer falls back to its embedded defaults when the filesystem assets are missing, so cargo install alone gets you a working drawer; make install adds user-customizable CSS and icons at the system location.
The Makefile install path drops the binary and the bundled CSS + category icons. Three invocations depending on where the binary should land:
Default — system-wide (needs sudo):
sudo make installWrites:
nwg-drawer→/usr/local/bin/nwg-drawer- Data files (drawer.css,
img/) →/usr/local/share/nwg-drawer/
No-sudo, dev workflow (useful when working from a clone):
make install PREFIX=$HOME/.local BINDIR=$HOME/.cargo/binDistro-parity (matches Go upstream's /usr/bin exactly):
sudo make install PREFIX=/usr# Basic with auto-detected power bar
nwg-drawer --pb-auto
# Fully configured
nwg-drawer --opacity 88 --pb-auto --columns 8
# Resident mode (stays in memory, toggle with signals)
nwg-drawer -r --pb-autoThe drawer auto-detects Hyprland (via HYPRLAND_INSTANCE_SIGNATURE) or Sway (via SWAYSOCK) at startup. Most users don't need --wm at all.
When you do want to override:
| Value | Effect |
|---|---|
| omitted (default) | Auto-detect from env vars; falls back to a null backend on Niri/river/Openbox/etc. |
--wm hyprland |
Force the Hyprland IPC backend regardless of environment. |
--wm sway |
Force the Sway IPC backend regardless of environment. |
--wm uwsm |
Launch wrapper for uwsm (Universal Wayland Session Manager) sessions. Compositor detection still falls through to the env vars — this flag controls how launched apps are spawned, not which backend serves drawer IPC. |
--wm uwsm is for users running under a uwsm-managed Wayland session, which currently requires systemd. If you're not on systemd, just don't pass --wm uwsm and the drawer works normally — app launches go directly through the compositor's exec mechanism (Hyprland's dispatch exec, Sway's exec) or via sh -c for the null backend. No uwsm binary is ever invoked.
The drawer has no which uwsm probe and no PATH check — the only place it would shell out to uwsm is when the user explicitly opts in with --wm uwsm. Even there, if the binary turns out to be missing the launcher logs a warning and falls back to direct launch (sh -c), so a misconfigured launcher still launches apps.
The drawer is typically launched by the dock's launcher button. Configure the dock with -c "nwg-drawer --pb-auto" to wire it up:
# ~/.config/hypr/autostart.conf (Hyprland example)
exec-once = nwg-dock -d -i 48 --mb 10 --hide-timeout 400 --launch-animation -c "nwg-drawer --opacity 88 --pb-auto"Pins work bidirectionally — pin an app from the drawer (right-click → Pin) and it shows up in the dock instantly, and vice versa.
Hyprland 0.55+ Lua configurations don't read autostart.conf or
bindings.conf. On Omarchy 4.0 the equivalents live in
~/.config/hypr/autostart.lua and ~/.config/hypr/bindings.lua,
using the o.* helpers that Omarchy's stock hyprland.lua already
loads — nothing extra to require:
-- ~/.config/hypr/autostart.lua — resident mode (optional, faster opens)
o.launch_on_start([[env GDK_WAYLAND_DISABLE=zwp_linux_dmabuf_v1 nwg-drawer -r --opacity 88 --pb-auto]])
-- ~/.config/hypr/bindings.lua — open the drawer (toggles the resident
-- instance if one is running, spawns on-demand otherwise)
o.bind("SUPER + D", "App drawer", "env GDK_WAYLAND_DISABLE=zwp_linux_dmabuf_v1 nwg-drawer --opacity 88 --pb-auto")On plain Lua setups without Omarchy, Hyprland loads only
~/.config/hypr/hyprland.lua (pull other files in with require).
Register the command on the start hook there — this is exactly what
Omarchy's o.launch_on_start wraps:
hl.on("hyprland.start", function()
hl.exec_cmd([[env GDK_WAYLAND_DISABLE=zwp_linux_dmabuf_v1 nwg-drawer -r --opacity 88 --pb-auto]])
end)Migrating to Omarchy 4.0: the Quattro migration generates the new
.lua config files but does not carry custom exec-once or bind
lines across from the .conf files — after the upgrade a resident
drawer (and your drawer keybinding) silently stops working until you
re-add them in the .lua files as above. App launches from the drawer
are fully functional on Lua sessions as of nwg-common 0.7, which
retries dispatches in the session's Lua syntax automatically.
GTK ≤ 4.22 has a bug in its Wayland dmabuf-feedback handler
(gdk/wayland/gdkdmabuf-wayland.c munmaps the wrong pointer when the
compositor re-sends zwp_linux_dmabuf_feedback_v1). Hyprland 0.56
started re-sending that feedback every time an output is
disabled/re-enabled, so any GTK4 client — the drawer included — can
segfault after a DPMS off/on cycle. Resident mode is the exposed case:
the process sits through every screen blank. Whether a given cycle
crashes depends on heap-allocation alignment, so it strikes
intermittently.
Until a fixed GTK ships, launch the drawer with the dmabuf protocol disabled (as in the Lua snippet above, or the classic equivalent):
# ~/.config/hypr/autostart.conf
exec-once = env GDK_WAYLAND_DISABLE=zwp_linux_dmabuf_v1 nwg-drawer -r --opacity 88 --pb-autoThe drawer doesn't use dmabuf texture import or graphics offload, so the practical effect of the switch is dodging the crash. GTK does fall back to shared-memory buffers — a different rendering path — but for a simple 2D surface like the drawer there is no noticeable cost.
# Toggle visibility
pkill -f -35 nwg-drawer # SIGRTMIN+1
# Show
pkill -f -36 nwg-drawer # SIGRTMIN+2
# Hide
pkill -f -37 nwg-drawer # SIGRTMIN+3The drawer loads CSS from ~/.config/nwg-drawer/drawer.css. Changes are picked up instantly via live file-change detection — no restart or signal needed. Hot-reload follows the full @import graph, so tinty and similar theme managers work out of the box.
Override the path with -s /path/to/custom.css.
Config carried over from Go
nwg-drawer(or a pre-v0.4.0 install)? An existing~/.config/nwg-drawer/drawer.cssis never touched — it keeps working as your override. The old Go-era default omitted the;after each block's last declaration, which GTK4's parser flags withExpected ';' at end of blockwarnings on every (re)load. Add the missing semicolons, or delete the file to have the current default re-seeded on next launch.
Use the tinted-nwg-dock templates with the drawer hook:
[[items]]
name = "base16-nwg-drawer"
path = "https://github.com/tinted-theming/tinted-nwg-dock"
themes-dir = "themes"
hook = "cp '%f' ~/.config/nwg-drawer/drawer.css"
supported-systems = ["base16"]Applying a theme reloads the drawer live.
Pin state lives at ~/.cache/mac-dock-pinned, shared with nwg-dock. Pin/unpin from either side; the other picks up the change via inotify within milliseconds.
PRs welcome. main is protected — open from a feature branch. Run make lint (fmt + clippy + test + deny + audit) locally before requesting review.
User-visible PRs add a CHANGELOG bullet under ## [x.y.z] — Unreleased in CHANGELOG.md, following Keep a Changelog.
- Multi-compositor — Go version is Sway/Hyprland via separate branches; Rust port auto-detects and degrades gracefully on unsupported compositors.
- Shared pin file — Go drawer uses
~/.cache/nwg-pin-cache; Rust port shares~/.cache/mac-dock-pinnedwith the dock. - Math evaluation — Go uses the
exprlibrary for arbitrary expression evaluation; Rust port uses theexmexcrate (safe parser, noeval/shell) with a small custom operator factory that addspi/π,%modulo, and base-10log. Scoped to pure arithmetic (no variables) so random search queries don't accidentally evaluate. - CLI flag naming — multi-word flags standardized to kebab-case (
--nocats→--no-cats,--pbsize→--pb-size). Multi-char Go short forms not available; use long forms. - Launcher auto-detection — unrelated but worth noting: if the configured launcher command is missing, the launcher button in the dock hides automatically; the drawer itself has no such dependency.
Ported from nwg-piotr/nwg-drawer (MIT).
MIT. See LICENSE.