A visual Alt+Tab window switcher for the Hyprland Wayland compositor.
The recommended implementation is an Omarchy shell plugin (QML,
hosted by the omarchy-shell Quickshell process). Two legacy standalone implementations —
a Rust GTK4 binary and a Python prototype — are kept in the repo.
omarchy-plugin/ Omarchy shell plugin (QML) — recommended
rust/ Legacy standalone GTK4 binary
python/ Legacy prototype
- 🪟 Visual switcher on the layer-shell
Overlaylayer with exclusive keyboard focus - 🗂️ Grouped by workspace (active first), sorted by recency (
focusHistoryID), pinned windows and thespecial:qconsoleworkspace excluded - 🖼️ Window previews — a still frame of each window (toplevel screencopy), with its app icon
as a corner badge and its title underneath, so several windows of the same app are easy to tell
apart. Icons are resolved from
.desktopfiles (id variants,StartupWMClass, Omarchy terminal app class, launcher URL and whole-word title match for Chrome PWAs) and stand in when a window can't be captured. Agent windows (omarchy agent) show the agent they run, or the default agent, when an icon of that name exists, else the default terminal's icon - 🔴 Notification badges — a red dot on windows whose app sent a notification since you last looked at it (optional module, see Configuration)
- 🎨 Theme-aware — follows the active Omarchy theme automatically
- ⌨️ Tab/Right → next, Shift+Tab/Left → previous, Return → activate, Escape → cancel, P → toggle previews, Q → close window, releasing ALT/SUPER → activate; hover selects, click activates. ALT/SUPER+Shift+Tab opens on the last window of the list
- 🫀 Stays open while ALT or SUPER is physically held (verified via
hl.is_key_downwhen no keyboard event reaches the overlay)
omarchy plugin add https://github.com/c4software/hyprland-alttab --enableFor local development, symlink a checkout instead and enable it:
ln -sfn "$PWD/hyprland-alttab" ~/.config/omarchy/plugins/vbrosseau.alttab
omarchy plugin enable vbrosseau.alttabThen bind it — in ~/.config/hypr/bindings.lua (or customisation.lua):
hl.unbind("ALT + TAB")
o.bind("ALT + TAB", nil, hl.dsp.global("omarchy-alttab:next"), { repeating = true })
hl.unbind("SUPER + TAB")
o.bind("SUPER + TAB", nil, hl.dsp.global("omarchy-alttab:next"), { repeating = true })
hl.unbind("ALT + SHIFT + TAB")
o.bind("ALT + SHIFT + TAB", nil, hl.dsp.global("omarchy-alttab:prev"), { repeating = true })
hl.unbind("SUPER + SHIFT + TAB")
o.bind("SUPER + SHIFT + TAB", nil, hl.dsp.global("omarchy-alttab:prev"), { repeating = true })
hl.layer_rule({ match = { namespace = "omarchy-alttab" }, no_anim = true })Finally omarchy restart shell, then hyprctl reload and check hyprctl configerrors.
The shortcut must appear in hyprctl globalshortcuts. Manual trigger for testing:
hyprctl dispatch 'hl.dsp.global("omarchy-alttab:next")'Window previews are on by default. Press P while the overlay is open to switch between previews
and app icons; the choice is saved to ~/.config/omarchy/alttab.json. You can also edit that file
directly (applied live, no shell restart needed):
{ "previews": false }Set "previews": true or delete the file to turn them back on.
Why a separate file? Omarchy's settings UI (the
schemadeclared in a plugin manifest) is only available to bar widgets;serviceplugins like this one get no settings from the shell.
Optional features live in omarchy-plugin/modules/ and can be turned off
one by one in the same file; a disabled module is not loaded at all:
{ "modules": { "notificationBadges": false } }notificationBadges(on by default): a red dot on windows whose app sent a notification since you last looked at it, read from omarchy-shell's notifications (on-screen and history). Chromium web apps (WhatsApp, Outlook…) are matched through the site Chromium puts in the notification; a notification from a regular browser tab gets no badge.
See omarchy-plugin/README.md for details.
omarchy plugin remove vbrosseau.alttab --yesThen remove the two binds and the layer rule from your Hyprland config and hyprctl reload.
The plugin never modifies your configuration files itself.
The QML plugin runs as a service inside the long-lived omarchy-shell process:
| Legacy (Rust/Python) | Plugin (QML) |
|---|---|
| Daemon + Unix socket + PID files | The resident shell process |
| One-shot GTK switcher process | LazyLoader → PanelWindow in-process |
--focus-address helper process |
Hyprland.dispatch() after close |
| Spawn guard against double-launch | An open boolean |
| Parses theme files itself | Shell Color singleton (live theme) |
| Needs an autostart entry | Loaded by the shell every session |
Both are standalone programs that predate the plugin, driven by a daemon and their own Unix sockets. They are not installed, invoked or required by the plugin.
alttab Starts the daemon if needed, sends "tab" (bind this)
alttab --daemon Starts the daemon in the background
alttab --show Shows the GTK4 switcher (one-shot)
alttab --kill Stops the daemon
alttab --focus-address ADDR Focus a window by Hyprland address
Build instructions, architecture and runtime details: rust/README.md.

