Omasmash

Beautiful, theme-aware keyboard smash with a real lock screen underneath.
Hand the keyboard to a toddler and they can't reach your agents — and neither can you, on the tenth "fix it, and don't make any mistakes."
Every keypress paints a big letter in your active theme's colours, every click splashes, every drag trails — and the machine underneath stays untouchable.
Status: pre-alpha. The session-lock premise test passes (
docs/premise-test.md), but the passphrase and corner-hold unlock paths have not been tested with real input yet. Read RECOVERY before you run it — all of it.
Why this isn't a web page
The existing smash toys are browser based, and a browser tab cannot hold the input. Escape, or any of a dozen key combos, drops fullscreen — and now the keyboard is pointed at your email. Even BabySmash, the 2008 Windows original, could only block input on a best-effort basis from inside a normal app.
Omasmash is built on ext-session-lock-v1, the Wayland session-lock protocol —
the same mechanism behind Omarchy's real lock screen. Input exclusivity is
enforced by the compositor, not by a focus grab. While it is up, nothing else
on the system receives keyboard or pointer input. No Escape, no Alt-Tab, no
alt-clicking the window away.
So: Omasmash is a lock screen that plays instead of asking for a password.
What it looks like

It opens by flying the camera through a corridor of letters, hits, and drops you into the play surface.
It follows your theme

Tokyo Night, Gruvbox, Rosé Pine, Everforest — including light ones.
Every other keyboard-smash toy looks like itself. This one looks like your desktop.
Theming
Colours come from your active theme, live. Omasmash reads
~/.local/state/omarchy/current/theme/colors.toml directly rather than going
through the shell's Color singleton, because that singleton only exposes
five roles and a play surface wants the whole crayon box. Your wallpaper is
drawn behind the play surface, dimmed, so it still reads as your desktop.
Switch themes and it follows — the palette is re-read when
current/theme.name changes.
Starting it
Three ways in, all of them the same toggle underneath — it locks if unlocked and unlocks if locked, so one action does both.
From the app menu. Hit SUPER, type omasmash. This requires having run
bin/omasmash-install — omarchy plugin add installs the plugin, not a
desktop entry, so the menu entry does not appear on its own.
From a hotkey. Bind a chord to omasmash-toggle. No keybinding is
installed by this project — binding a chord that locks your session is your
call. See docs/hotkey.md for a suggested bind and how to
pick one a toddler cannot reach with a flat palm.
From a terminal, which is also how you get out of trouble:
omasmash-toggle # if you ran omasmash-install
omarchy-shell omasmash lock # always available
omarchy-shell omasmash unlock
Unlocking
Three ways out, in order of everyday usefulness:
- Type the passphrase —
omarchyby default. Seven characters in sequence is effectively unreachable by random smashing. Nothing is displayed while you type. - Hold the top-right corner for three seconds — there is a small "or hold here" plate there, which fills as you hold. Discoverable for an adult, impossible while flailing. It sits opposite the passphrase hint so that reaching for one cannot trigger the other.
- Type your real password and press Enter. There is no visible field — keystrokes accumulate invisibly and Enter submits them to PAM. This always works, even if you changed the passphrase and forgot it. Safe to use in front of people: nothing you type is ever displayed (see why).
This is a child lock, not a security lock
Say it plainly: the passphrase is a known string. It stops a toddler, and it
stops you from doing damage while you smash. It does not stop a person with
physical access to your machine, and it is not a substitute for
omarchy.lock. Do not walk away from an unattended machine
with only Omasmash up and consider it locked.
Hotkeys are blocked too
ext-session-lock-v1 stops input reaching other clients, but Hyprland
resolves its own keybinds before delivery — so without more work SUPER+Q
and SUPER+SHIFT+E still fire through a lock screen, and a toddler can close
windows or kill your session through it.
Omasmash puts Hyprland into a submap while active, which makes every other bind on the system inert. The submap carries exactly one chord:
SUPER + CTRL + ALT + SHIFT + Escape # restores keybinds, then unlocks
Deliberately awkward, because it must never be reachable by a palm on the keyboard — and because a process that dies holding the submap would otherwise leave you a desktop with no shortcuts at all. The service also clears the submap unconditionally on startup, since nothing else on the system will.
Typed characters are never shown
Press k and you get some other letter, or a dinosaur. The glyph is random,
deliberately — it is what makes the password route above safe to use in a room
with other people in it. Full reasoning, and the one case for turning it off,
in docs/security.md.
Emoji also arrive on their own, at intervals that keep changing. A letter every time is a machine; a letter every time except when a rocket shows up is worth staying for.
RECOVERY
ext-session-lock-v1 gives the compositor the last word: if the locking
client dies while the lock is held, the screen stays locked. That is the
protocol working as designed — it is what stops someone unlocking your machine
by crashing your lock screen — but for a toy it is a genuine risk of locking
yourself out. Layers of defence, cheapest first:
1. The paint watchdog (automatic). The play surface drives a canary from the render loop. If it stops advancing for two consecutive five-second samples, the service concludes the surface has stopped painting and releases the lock itself — verified end to end, about twelve seconds from stall to unlock (test).
It covers rendering stopping while the event loop still runs. It cannot cover a dead process, and it cannot cover a wedged QML event loop — the watchdog runs in the same engine, so an infinite loop stops it too. That is what the routes below are for.
2. Your PAM password, typed on the play surface followed by Enter. Nothing
you type is displayed — see docs/security.md.
3. IPC unlock, from another machine or an SSH session:
~/Work/omarchy-omasmash/bin/omasmash unlock
Works whenever the process is alive and answering, including when the screen is showing nothing useful.
4. hl.clear_crashed_lockscreen. Hyprland can be told to drop a lock
whose client has died, without a TTY:
hyprctl dispatch 'hl.clear_crashed_lockscreen()'
5. The TTY escape. If the process is dead and the compositor is still holding the lock:
Ctrl+Alt+F2 # switch to a text console
<log in>
pkill -f omasmash # or: pkill quickshell
Ctrl+Alt+F1 # switch back
Omarchy's own stranded-lock handling then applies: a freshly started instance
detects a lock it did not take (via omarchy-hyprland-session-locked) and
adopts it, giving you a surface that can accept your PAM password.
Verify you can get to a TTY on your machine before you run the first lock
test. That is the floor under everything else here. On a systemd machine,
Ctrl+Alt+F2 spawns a login on demand only if autovt@.service resolves to a
valid getty@.service and logind's NAutoVTs covers that VT — check both
rather than assuming.
Prerequisite: allow_session_lock_restore
Recovery step 3 only works if Hyprland has
misc:allow_session_lock_restore = true. Omarchy sets this by default
(default/hypr/looknfeel.lua). If you have turned it off, there is no
in-session recovery at all: a new client trying to take over a stranded lock
is killed by the compositor with a fatal Wayland protocol error, and only a
TTY will get you back in. Verify with:
hyprctl getoption misc:allow_session_lock_restore
See docs/premise-test.md for the full reproduction.
Known upstream issues this inherits
- Omarchy #6888 — stranded-lock recovery never completes.
- Omarchy #7478 — lock screen crash-loops roughly every 18s after DPMS-off.
Both are live against the session-lock path Omasmash is built on, and both are part of the premise test's acceptance criteria.
Requirements and what this plugin touches
Everything here ships with Omarchy — there is nothing extra to install.
| Used | For |
|---|---|
hyprctl |
Registering and switching the keybind submap, and reading it back |
omarchy-shell |
The toggle and panic scripts reach the service over shell IPC |
omarchy-hyprland-session-locked |
Detecting a stranded lock left by a dead client |
bash |
The scripts in bin/ |
PAM (/etc/pam.d/omarchy-lock-password) |
The password unlock route, shared with Omarchy's own lock screen |
Privilege boundaries, stated plainly, because nothing downstream checks them for you:
- It takes a real session lock. While active, the compositor gives it exclusive input and will keep the screen locked if this process dies. See RECOVERY before first use.
- It changes your global keybinds. While active it puts Hyprland into a submap, which makes every other keybind on your system inert until it releases. It clears the submap on unlock and again on startup.
- It authenticates against PAM, using Omarchy's existing
omarchy-lock-passwordconfiguration. It does not read, store, or transmit your password; keystrokes go to PAM and are never displayed (seedocs/security.md). - No network access. No telemetry. Nothing written outside the plugin directory.
Development-only extras, not needed to run it: grim, ffmpeg and
imagemagick for bin/omasmash-capture, and a nested Hyprland for
bin/omasmash-nested.
Installing
omarchy plugin add https://github.com/haydenmckay/omarchy-omasmash --enable
omarchy-restart-shell
The restart is not optional: the Omarchy shell runs with its file watcher disabled, so it will not pick up a newly installed or updated plugin until it restarts.
Optionally, to make Omasmash searchable in the SUPER menu and put the CLI on your PATH:
~/.config/omarchy/plugins/io.github.haydenmckay.omasmash/bin/omasmash-install
It installs a desktop entry, an icon, and three symlinks in ~/.local/bin —
and nothing else, and nothing it does not own. Every target is checked
first; if anything is already there that this installer did not create, it
lists them, changes nothing, and exits. Pass --force to replace them
deliberately, or --uninstall to remove only what it recorded creating.
No hotkey is installed — binding a chord that locks your session is your
decision. See docs/hotkey.md.
Or just ask your agent
You are probably running one. Paste this at it:
Install the Omasmash plugin for Omarchy from
https://github.com/haydenmckay/omarchy-omasmash
Then:
1. omarchy plugin add the repo and enable it, then omarchy-restart-shell
(the shell has its file watcher disabled and will not pick it up otherwise)
2. Run bin/omasmash-install from the installed plugin directory so it appears
in the app menu
3. Bind a hotkey to omasmash-toggle. Pick a chord a toddler cannot hit with a
flat palm, and check it against `hyprctl binds` first so you do not
clobber an existing Omarchy binding
4. Before locking anything, read the RECOVERY section of the README back to me
and confirm I can reach a TTY on this machine
Step 4 is not padding. This takes a real session lock, and the compositor will keep the screen locked if the process dies — you want to know your way out before you need it, not after.
Binding a hotkey means editing your Hyprland config, which this project will never do on its own. Asking your agent to do it is your consent; it is not the same as the installer doing it behind you.
Removing
omarchy plugin remove io.github.haydenmckay.omasmash
omarchy-restart-shell
If you ran omasmash-install, undo it first — it removes only the files it
recorded creating, so nothing of yours goes with it:
bin/omasmash-install --uninstall
Development
Runs as a standalone Quickshell instance, not an installed plugin — a crash here cannot take your bar down with it, and restarts take about a second instead of bouncing the whole shell.
bin/omasmash preview # windowed visual preview -- no lock, no compositor
bin/omasmash full # fullscreen preview -- still no lock, Escape quits
bin/omasmash run # the real service (does NOT lock on its own)
bin/omasmash lock # lock
bin/omasmash unlock # release
bin/omasmash status # JSON: lock state, theme, watchdog, hotkeys, PAM
Use the previews for anything visual — they hot-reload and cannot lock you out. The nested compositor is only needed for changes to the lock lifecycle itself.
Run every lock, crash, and kill -9 test inside the nested compositor,
where a stranded lock strands a window instead of your machine:
bin/omasmash-nested # nested Hyprland with Omasmash inside
bin/omasmash-nested --kill # from the host, if it wedges
Licence
MIT.