OpenRiot v7.9.1 — The One Where We Fixed the Menu and Invented a Switcher
“Art should comfort the disturbed and disturb the comfortable.” — Banksy, on why your window switcher should look like a gallery, not a spreadsheet
Release Overview
v7.9.1 is the release where we stopped accepting single-column scrolling as a user interface, stopped letting Signal multiply like bacteria, and stopped tolerating a violet so bright it could guide aircraft.
Rofi Visual Overhaul — The app menu (Super+D) is now two columns wide,
580 pixels, with a 2px violet border (#997de1, dimmed 10% from its previous
airport-runway intensity). The number of visible rows is dynamically calculated
as ceil(total_items / 2), which means the grid stays balanced no matter how
many apps you have. Twenty apps? Ten rows, two columns. Four apps? Two rows,
two columns. No more dead space, no more cut-off text, no more scrolling
through a single column like it’s 1995. Submenus (Games, etc.) inherit the
same layout. The border color matches the polybar accent so your launcher
doesn’t look like it wandered in from another desktop.
Window Switcher (Super+Tab) — focus next is dead. Long live
--window-switch. Press Super+Tab and you get a single-column list of
every open window, sorted by workspace, with the focused window first. Each
entry shows an icon (from icons.toml) and a name in the format
AppName - WindowTitle. Terminals display their session name:
Terminal - crush ~/Code/OpenRiot. Firefox shows Firefox - Proton Mail.
Apps without titles show their capitalized class. The base app name column is
fixed-width — padded to the longest name so everything aligns vertically.
The window is 750px wide so titles don’t truncate. Polybar, dunst, and i3bar
are filtered out because you cannot switch to a status bar.
Selection focuses the window instantly via i3-msg [con_id=<id>] focus. No
thumbnail daemon. No maim flash. No 500ms capture sweep. Just a tree walk,
an icon lookup, and a rofi grid. It is the fastest window switcher we have
ever shipped because it does not pretend to be a compositor.
Signal Already-Running Guard — The rofi launcher now checks if gurk is
running before spawning another instance. If it is, you get a notification
saying “Signal — Already Running” instead of a second terminal pretending to be
your first terminal. This matches the Transmission guard that has existed
since v7.4. We should have done it for Signal earlier. We did not. Now we
have.
Polybar Violet Dimmed — The accent color in config/polybar/config.ini
is now #997de1 instead of #aa8bfa. It is 10% darker. Your retinas will not
notice the difference consciously, but your subconscious will stop squinting at
the CPU graph.
Volume Refactoring — source/audio/volume.go no longer wraps sndioctl
calls in sh -c pipelines with 2>/dev/null | cut -d= -f2. It calls
sndioctl directly with -n (numeric) and parses the output in Go. Helper
functions (vol(), micVol(), isMuted(), micMuted()) are extracted and
reused. This is less fragile, slightly faster, and significantly less likely to
break when someone’s .bashrc prints a fortune cookie on login.
File Copy Safety — downloadFile() in source/imaging/download.go and
copyDir() in source/imaging/site.go now check f.Close() errors after
io.Copy. Previously they deferred the close and ignored the return value,
which meant a successful copy followed by a failed close (disk full, network
filesystem hiccup) would leave a corrupt file on disk. Now the temporary file
is deleted if either the copy or the close fails. This is the kind of bug that
only shows up in production, which is why we fixed it in development.
Installer Deduplication — CopyConfigs in source/installer/configs.go
now tracks which directories it has already created with a map[string]bool.
Previously it called os.MkdirAll for every single file in the config tree,
which is harmless but wasteful. Now it creates each directory once. If you have
a thousand config files, you will not notice. If you have ten thousand, you
might.
Transmission Bind Fix — bindTransmissionToWireGuard() in
source/commands/helpers.go removed a no-op strings.Replace that replaced a
string with itself. This did nothing. It is gone now. The remaining logic that
actually inserts bind-address-ipv6 is unchanged and still works.
WebP Conversion Tool — openriot --convert-webp converts all .png files
in backgrounds/ and Locked/ to .webp using cwebp (or ImageMagick
convert as fallback). The entire wallpaper and lock-screen library has been
re-encoded. File sizes dropped significantly — backgrounds average ~350KB down
from ~6.4MB, lock screens average ~310KB down from ~6.1MB. Your disk is
happier. Your i3 startup is faster. The visual quality is identical because
WebP is better at its job than PNG.
🧾 Files Changed
| File | Nature of Change |
|---|---|
source/rofi/rofi.go |
Two-column layout, dynamic lines, 580px width, |
2px #997de1 border, IsProcessRunning() helper, gurk guard |
|
source/window/switch.go |
New — Window switcher: i3 tree walk, icon |
resolution, label formatting, rofi invocation, con_id focus |
|
source/commands/commands.go |
Register --window-switch, --convert-webp |
config/i3/keybindings.conf |
Super+Tab → --window-switch; Super+D/Space |
stay on --rofi (omnibox reverted) |
|
config/polybar/config.ini |
Violet #aa8bfa → #997de1 |
source/audio/volume.go |
Direct sndioctl exec; helper extraction; no |
sh -c wrappers |
|
source/imaging/download.go |
Check io.Copy and f.Close errors; delete |
| temp on failure | |
source/imaging/site.go |
Same close-error checking in copyDir() |
source/imaging/webp.go |
New — ConvertPngToWebP(): cwebp or convert |
source/installer/configs.go |
mkdirOnce deduplication via createdDirs map |
source/commands/helpers.go |
Remove no-op strings.Replace in Transmission bind |
backgrounds/*.webp |
Re-encoded from PNG; ~75% size reduction |
Locked/*.webp |
Re-encoded from PNG; ~72% size reduction |
install/openriot |
Binary rebuilt |
🎵 What We’re Listening To
Same playlist. Same questionable Theremin solo. But now your app launcher is
a grid, your window switcher is a gallery, and your lock screens take up a
quarter of the disk space they used to. This is the kind of efficiency that
makes you wonder why every operating system doesn’t ship a --convert-webp
command by default.
The window switcher is fast because it cheats: it doesn’t capture thumbnails. It uses icons and titles, which are already in memory, and rofi, which is already compiled. The result feels instant because it is instant. Your brain can parse an icon and a window title faster than it can parse a blurry screenshot anyway. This is not a compromise. This is the correct design for X11 in 2026.
🗣️ Final Words
“The best window switcher is the one you don’t notice you’re using.”
— The OpenRiot Crew, after pressing Super+Tab two hundred times in a row
v7.9.1 is the release where we looked at the app menu and said, “This should be two columns.” Then we looked at the window switcher and said, “This should exist.” Then we looked at the violet and said, “This should not blind people.” Then we looked at the volume code and said, “Why are we shelling out to cut?” Then we looked at file copies and said, “What if close fails?” Then we looked at the installer and said, “What if we didn’t MkdirAll a thousand times?” Then we looked at the wallpapers and said, “What if they were smaller?”
None of these are revolutionary. All of them are correct. That is what a patch release is for.
Your launcher is a grid. Your switcher is a list. Your violet is dimmer. Your volume code is cleaner. Your copies are safer. Your installer is smarter. Your wallpapers are smaller. Your Signal only spawns once.
This is maintenance. This is care. This is what it looks like when people who use the software also write it.
— The OpenRiot Crew
“Your desktop should get better while you’re not looking.”