6.6 KiB
+++ title = "Friendship ended with dwm, now River is my best friend" date = 2025-10-21 description = "Moving from X11 with dwm to River on Wayland" +++
After years with dwm on X11, I started hitting the limits of Xorg — especially screen tearing, smoothness, and missing Wayland-only features. I decided it was time to switch.
Weapon of choice - River
I wanted an experience as close as possible to my existing X11/dwm/dwmblocks setup. Before i jumped into river, I looked at dwl, sway, and Qtile.
dwl sounded like the best choice for me since it aims to replicate dwm on Wayland. But development is slow, many protocols are still missing, and adding them takes time.
Sway wasn't bad, but it's closer to i3 than dwm, so not ideal for me.
Qtile looked good, but I didn't like configuring it in Python.
Then my friend Emil told me about his river setup. He was happy with it, so I gave it a shot.
What I really like about river is that I can configure it on the fly. The whole configuration is a single Bash script that sends commands to river live - no recompiling or restarting required.
As a layout manager I chose Filtile as it's very similar to dwm behavior.
Bar dwmblocks => waybar
As an alternative to dwmblock, I found Waybar to be the best fit for me.
It has plenty of modules for displaying almost anything on my bar, including my IP address, speaker/headphones state and battery status.
Besides using built-in modules, it's also possible to create custom ones using scripts. I've made my own module for displaying speaker/headphones volume with possibility to mute or change the default audio output on mouse click.
~/.config/waybar/config.jsonc
...
"custom/audio": {
"exec": "~/.local/bin/waybar_audio.sh",
"return-type": "json",
"format": "{icon} {text}",
"format-icons": {
"speaker": ["", "", " "],
"speaker-muted": "",
"headphones": "",
"headphones-muted": ""
},
"interval": 2,
"on-click": "pamixer --toggle-mute",
"on-click-right": "~/.local/bin/toggle_audio",
"on-scroll-up": "pamixer --increase 5",
"on-scroll-down": "pamixer --decrease 5"
},
...
Where waybar_audio.sh and toggle_audio are my custom Bash scripts both available in my dotfiles repository.
Application starter dmenu => Wmenu
I found Wmenu to be the most similar app launcher to dmenu and I don't like changes, so I picked it. I was able to customize it to my liking - vertical list of apps. The only thing that didn't work and I missed that feature so much was fuzzy search. Wmenu doesn't support it and there was no patch available. So I've decided to create my own version of wmenu with fuzzy search support with help of chatgpt codex :D. As much as I hate AI, Codex was actually able to modify that C code in just single prompt. My modified version of Wmenu is available in this github repository.
passmenu integration to wmenu
For my password manager GUI I like to use passmenu - an dmenu script that helps me pick my password anywhere I am. But there was a problem. Passmenu was written only for dmenu. That's where chatgpt helped me again and together we created this rewrite of passmenu for Wmenu. Passmenu is an excellent example of where I use the fuzzy search feature the most.
Terminal alacritty => foot
Since Alacritty sucks at displaying images inside the terminal, I moved to foot as an alternative. It supports displaying images natively.
There was an issue with spawning new terminal inside the current working directory. I was used to Alacritty's <CTRL+Shift+Enter> shortcut. I fixed that with the following addition to my .bashrc:
osc7_cwd() {
local strlen=${#PWD}
local encoded=""
local pos c o
for (( pos=0; pos<strlen; pos++ )); do
c=${PWD:$pos:1}
case "$c" in
[-/:_.!\'\(\)~[:alnum:]] ) o="${c}" ;;
* ) printf -v o '%%%02X' "'${c}" ;;
esac
encoded+="${o}"
done
printf '\e]7;file://%s%s\e\\' "${HOSTNAME}" "${encoded}"
}
PROMPT_COMMAND=${PROMPT_COMMAND:+${PROMPT_COMMAND%;}; }osc7_cwd
And this keybind in foot.ini:
[key-bindings]
spawn-terminal=Control+Shift+Return
Screenshare
Once I had my setup ready, one of the first issues after using it for some time was screen sharing. I dug into the issue and found a solution. I had to add this to my River init file:
# Fix screen share
export XDG_CURRENT_DESKTOP=river
export XDG_SESSION_TYPE=wayland
## Start portals
dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
xdg-desktop-portal-wlr &
xdg-desktop-portal &
Screenshots
On my X.org setup I was using screenshots with shotgun and slop based on my friend's tutorial: Taking screenshots with shotgun and slop. Fortunately he already wrote a guide for wayland alternative: Taking screenshots on Wayland with Grim and Slurp.
But I had issues with the combination of grim and slurp. While pasting an image from clipboard to telegram, it pasted raw PNG data instead of a file. This was the fix:
riverctl map normal None Print spawn 'file="/home/$USER/Pictures/Screenshots/$(date +%F_%T).png"; grim -g "$(slurp -d)" "$file" && wl-copy --type text/uri-list "file://$file"'
Other alternatives
This is a list of other software that I used on X.org and their alternatives on Wayland:
xclip=>wl-copyranger=>yazislock=>waylock
Final thoughts
After a few weeks with River, I don’t miss Xorg much. The setup feels lighter, screenshots and screen sharing work fine now, and I can still keep my DWM-like workflow.
There's just one thing I didn't get working and that is the indication of how many windows are open in monocle layout. Dwm used [X] where X was the number of open windows on that tag.
River feels more responsive overall, and I haven’t run into any crashes so far.
