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 Refactoringsource/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 SafetydownloadFile() 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 DeduplicationCopyConfigs 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 FixbindTransmissionToWireGuard() 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 Toolopenriot --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 NewConvertPngToWebP(): 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.”

← Back to README