Zen and the Art of Yak Shaving: Switching From i3 to Sway on Ubuntu 20.04
When I upgraded from Ubuntu 18.04 to 20.04 LTS, I finally took the plunge and switched from the i3 window manager on X11 to Sway, an almost drop-in Wayland replacement. Here's my experience of the transition.
Here be Dragons
Tiling window managers are not for the faint of heart. On top of that, Wayland and Sway are newer technologies than X11 and i3, and in my experience less likely to just work™. But for those of you who:
- Already have a keyboard-driven workflow, or want to switch to one,
- Want to be able to customize your desktop environment and workflow endlessly (in both senses of the word), and
- Aren't afraid of building (most) packages from source
… this article might make the switch from i3 to Sway slightly quicker and more pleasant!
Installing Sway
Sway is at last in the Ubuntu repos, and installing it is as easy as:
sudo apt install sway
The version in the repositories worked alright, but it had two bugs that had been fixed in the master branch: it failed to refocus the keyboard on the previous window after interacting with a popup, and it crashed when wtype was run (due to an issue with virtual keyboard grouping). After procrastinating as long as I could, I built Sway 1.5 rc2 from source, which resolved both issues.
Waybar
I spent a while hacking away at a custom statusline script, with underwhelming results. Eventually I abandoned it and installed Waybar, a more full-featured Sway bar written in C++:
sudo apt install waybar
I didn’t manage to find the FontAwesome package that it depends on, but that was just as well, because I didn’t want the icons anyway. I just edited my Waybar config, and removed all the icons that didn’t display properly.
Other than that, I left the Waybar config mostly default, only removing the stuff I didn't need, and adding a more usable time and date widget. I also removed the garish background colours on the different widgets. The end result, while far from gorgeous, is functional and non-offensive:
Making Waybar Space-Efficient
In my i3 config, I had mod+n
bound to toggle i3bar visibility:
bindsym $mod+n bar mode toggle
To replicate that with Sway and Waybar:
bindsym $mod+n exec killall -SIGUSR1 waybar
Launcher Woes
My entire workflow revolves around my app launcher, which I use to launch apps (duh), find files, calculate things, control my system, and occasionally run commands. I used Gnome Do, which is blazingly fast, and supports fuzzy search. I summon Do with ctrl+spacebar, and then have access to pretty much everything I have used even remotely recently with 2-5 keypresses.
Sadly, Gnome Do hasn't been updated or maintained since 2009. It still worked on Ubuntu 18.04 on X11, but to my great disappointment it is not in the Ubuntu 20.04 repositories. Also, it is an X app.
Albert
In the past, I have also tried Synapse, Albert, Kupfer, Rofi, and dmenu. Albert does not support Wayland, and I have had trouble getting it to even work previously, but it’s also one of the more powerful and actively maintained, and is the closest thing to a drop-in replacement for Gnome Do. I read about people running it on Wayland, even though it doesn’t officially support it, so I decided to give it a shot.
Getting to work turned out to be more arduous than expected, and involved a lot of time head-scratching about why it was having segmentation faults. I eventually got it working by installing the wayland-crash-fix
branch of johanhelsing's fork in lieu of the official version. Here is the script that I cobbled together to successfully install it:
#!/bin/bash
pwd=$PWD
sudo apt update
sudo apt install --no-install-recommends -y cmake g++ libmuparser-dev libqt5charts5-dev libqalculate-dev libqt5svg5-dev libqt5x11extras5-dev python3-dev qtbase5-dev qtdeclarative5-dev unzip virtualbox wget libqt5sql5-sqlite
# git clone https://github.com/albertlauncher/albert.git
git clone -b wayland-crash-fix https://github.com/johanhelsing/albert/ wayland-crash-fix
# cd $pwd/albert
cd $pwd/wayland-crash-fix
git clone https://github.com/albertlauncher/plugins.git
# cd $pwd/albert/plugins/python
cd $pwd/wayland-crash-fix/plugins/python
git clone https://github.com/pybind/pybind11.git
mkdir $pwd/albert-build
cd $pwd/albert-build
# cmake ../albert -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
cmake ../wayland-crash-fix -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
make
sudo make install
However, once installed, you're still not quite done. To get it not to crash (segfault) when summoned, it you have to set the following environment variables somewhere where Albert can find them: QT_QPA_PLATFORM=xcb
and DISPLAY=:0
. To start Albert on boot, I have the following in my Sway config:
exec sh -c "export QT_QPA_PLATFORM=\"xcb\" && export DISPLAY=:0 && /usr/bin/albert"
And to set the hotkey:
bindsym ctrl+space exec '/usr/bin/albert toggle'
Caveat: while Albert now works on Wayland, can find my files, and is fast to summon, it still isn't as good as Gnome Do. It is quite slow to start up, which means I have to launch the first few apps I use with dmenu, while waiting for it; it is strangely slow to open some files (LibreOffice files); and when indexing a lot of files, it is a real memory hog: when idle, it can sit on up to ~680MB of RAM. This sucks, but since it took so long to get it working at all, and I don't have any usable alternative, I'm willing to live with it for now. If I start doing something that's going to hog a lot of RAM, I quit Albert, and drop back to dmenu.
Kupfer
After writing the above section about Albert, I Googled Kupfer to make sure I had the spelling right. I couldn't remember what exactly I had disliked about it when I tried it years ago, but I noticed that it was in the Ubuntu repositories. I thought it was a KDE app (it isn’t), so I assumed it would crash on Wayland like most things do, but installed it anyway just in case it worked:
sudo apt install kupfer
To my surprise, it launched without incident. No environment variables, no coddling, no building from branches of forks of the source code. It just ran on Wayland. To my even greater surprise, it found this file without having to manually add folders to its index, and then add exclusions so that it wouldn't slow down my computer indexing too many files. However, Kupfer doesn’t seem quite a snappy as Albert, and its quirks are different: after using it for a few days, I switched back to my baling-twine-and-elastic build of Albert.
Note: Though it is in the Ubuntu 20.04 repositories, the last Kupfer release was over three years ago, so it may not be a long term solution — but then again, I cheerfully used Gnome Do for ten years after its last release in 2009.
More fun with Segmentation Faults
One of my favourite i3 shortcuts was this hideous line:
bindsym control+semicolon exec date '+%Y-%m-%d' | tr -d "\n" | xsel -i -b && xdotool sleep 0.5 key "ctrl+v"
This was a hacky way to put the current date on the clipboard and paste it. (I tried less hacky ways and they didn't work; but at the time I wrote this snippet I barely knew what a pipe was…) Porting this tiny snippet over to Wayland took more time than anything else in my config.
ydotool
My first attempt, after some initial fiddling with wl-copy
and wl-paste
from wl-clipboard
— notice the ominous first letter of xsel
— involved ydotool, the Wayland equivalent of xdotool.
To install ydotool I had to build it from source, which was fairly straightforward using the provided instructions. Its dependencies? Less so. I installed libevdevPlus from a .deb, and did the same for libuInputPlus. However, the latest version of ydotool (which I was hoping wouldn't segfault) required newer versions of these dependencies, which had to be built from source. I didn’t find any instructions, so I used the commands listed in the GitLab Continuous Integration YAML files. I also had to install the following:
sudo apt install libboost-program-options-dev
However, when I tried to use ydotool for my snippet, I discovered that a) the daemon segfaults mysteriously, and b) it needs to be run as root. So the only way to get it to work (adding it to sudoers, or modifying the permissions of /dev/uinput
) would have kind of nullified the main (only?) benefit of Wayland: security.
In the end, none of it got me closer to my goal, but it did mean that I had a working and up-to-date version of ydotool, in case I ever need it. (The daemon is another story, and a very unpleasant one, too!)
wtype
After making a fool of myself on the #sway IRC channel, and getting help from the helpful danschick and emersion, I ended up with the following snippet:
bindsym control+semicolon exec sh -c "wtype `date '+%Y-%m-%d'`"
As mentioned above, there is a bug in the version of Sway that's available in the Ubuntu repos — which has now been fixed in master — which causes wtype to crash Wayland windows as soon as it is run. But if you build Sway from the master branch, this snippet works, and is the most correct, secure, Wayland equivalent of the monstrosity above.
To install wtype, which must be built from source, like so many other Wayland things:
git clone https://github.com/atx/wtype
cd wtype-master
sudo apt install meson
meson build
ninja -C build
sudo ninja -C build install
There is, unfortunately, one remaining caveat: it only works on Wayland windows. If you hit the keybinding when an Xwayland window is focused, it will produce gibberish. This is the result not of a bug in wtype, but a limitation of X. The silver lining is that if incentivises getting other apps (like Firefox) to run natively on Wayland, rather than running them on Xwayland. (Firefox works pretty well, if you set the necessary environment variables: MOZ_ENABLE_WAYLAND=1
and GDK_BACKEND=wayland
.)
Screenshots
If you're used to hitting the print screen button and having something happen, think again. Wayland / Sway doesn't have that built in. However, it's easy to set up. I installed the necessary tools with:
sudo apt install slurp grim notify-send jq wl-clipboard
Then, grab the grimshot
script from https://github.com/swaywm/sway/blob/master/contrib/grimshot and put it in ~/.config/sway/grimshot
. Then you can put a line in your sway config like this:
bindsym Print exec sh -c "env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR SWAYSOCK=$SWAYSOCK bash ~/.config/sway/grimshot copy active"
This puts a screenshot of the active window on the clipboard. (If you want obnoxiously cheerful audio feedback, add this to the end of the command: && aplay /usr/share/sounds/sound-icons/trumpet-12.wav
)
I thought of binding other more sophisticated screenshot stuff to other combinations of Print Screen on control or shift, but for now I just have that one binding, and in the event I want to grab an area of the screen (and it’s not a browser window) I run grimshot from the command line. I also didn't want to be to silently and automatically saving screenshots on a single keypress, since I never get around to sorting screenshots that I save. A bit of friction can be a good thing!
Swaylock
You will probably want to install Swaylock and Swayidle with:
sudo apt install swaylock swayidle
I have mine setup like so in my Sway config:
exec swayidle -w \
timeout 300 'swaylock -f -c 000000' \
timeout 600 'swaymsg "output * dpms off"' \
resume 'swaymsg "output * dpms on"' \
before-sleep 'swaylock -f -c 000000'
(As far as I recall I just uncommented the example snippet.)
Swaylock is … delightfully minimal, but works as expected — and doesn't force you to focus a field before typing, which is a major plus.
Input configuration
If you're like me, natural scrolling is a must. I enabled that with this snippet:
input "2:14:SynPS/2_Synaptics_TouchPad" {
dwt enabled
tap enabled
natural_scroll enabled
middle_emulation enabled
}
This snippet is in the default Sway config. I just uncommented it and changed 2:14
to what I got when I ran swaymsg -t get_inputs
(in my case, 2:7
).
$SWAYSOCK
I had issues with the $SWAYSOCK environment variable not being set / available. I have the following line in my .bashrc
, which seems to have fixed the problem:
export SWAYSOCK=/run/user/$(id -u)/sway-ipc.$(id -u).$(pgrep -x sway).sock
Install Redshift on Wayland
My favourite screen colour temperature adjustment tool doesn’t work out of the box on Wayland. But there's a fork which does. To install it, I ran:
sudo apt install autopoint autoconf intltool libtool
git clone -b wayland https://github.com/minus7/redshift.git
cd redshift
./bootstrap
./configure
make
sudo make install
And then added the following to my Sway config:
exec redshift-gtk
It turned out that this didn't actually work. It would start, but wouldn't adjust the colour temperature successfully. Now, I have this line:
exec redshift -m wayland
I still haven't gotten the applet (redshift-gtk) to work on Wayland, but that's not a huge deal.
Wayland Clipboard Support in Vim
If you’re used to copying to the system clipboard from Vim, you will notice that "+y
doesn't work under Wayland. I put the following snippet in my .vimrc
to use wl-copy
to replicate this functionality:
" Wayland Clipboard Support
xnoremap "+y y:call system("wl-copy", @")<cr>
nnoremap "+p :let @"=substitute(system("wl-paste --no-newline"), '<C-v><C-m>', '', 'g')<cr>p
nnoremap "*p :let @"=substitute(system("wl-paste --no-newline --primary"), '<C-v><C-m>', '', 'g')<cr>p
Note: wl-clipboard
must be installed for this snippet to work.
Making it possible to run graphical applications with sudo
If you’re used to running sudo gedit
or sudo nautilus
on Ubuntu, you will discover that these fail on Wayland. Here's the solution I ended up using:
Create a file called wsudo.sh (or some such) wherever you store your little utility scripts. Paste the following into it, courtesy of this Reddit thread:
#!/bin/bash
#small script to enable root access to x-windows system
xhost +SI:localuser:root
sudo $1
#disable root access after application terminates
xhost -SI:localuser:root
#print access status to allow verification that root access was removed
xhost
Make it executable with chmod +x /path/to/wsudo.sh
.
Then, make an alias for it in your .bashrc
like so: alias wsudo='bash /path/to/wsudo.sh'
And reload your bashrc with source ~/.bashrc
Now, you can run graphical apps with wsudo
, the way you used to using regular sudo.
More Fun Keybindings
Here are a few other snippets from my config:
# Make Alt+F4 work like usual
bindsym Mod1+F4 kill
# Maps over to Windows nicely, which means that I don't look incompetent when I try to use a PC :)
# Split has been mapped to $mod+s, since I never really used stacked layout.
bindsym $mod+e exec nautilus --new-window
# Reload the Sway configuration file with the same keybinding I had in i3
bindsym $mod+Shift+r reload
# Make Lenovo ThinkPad T430 audio buttons work
bindsym XF86AudioRaiseVolume exec "amixer -q sset Master,0 1+ unmute"
bindsym XF86AudioLowerVolume exec "amixer -q sset Master,0 1- unmute"
bindsym XF86AudioMute exec "amixer -q sset Master,0 toggle && amixer -q sset Speaker,0 toggle"
bindsym XF86AudioMicMute exec "amixer set Capture toggle"
# 1. Set right alt as the compose key, so I can type em-dashes, ellipses, and special characters
# 2. Disable caps lock so it can be used for window switching
input * {
xkb_options compose:ralt,caps:none
}
# Alt tab script
# from: https://gist.github.com/SidharthArya/f4d80c246793eb61be0ae928c9184406
# remember to chmod +x ~/.config/sway/alttab
# Keycode 66 is caps lock. Caps lock is disabled up above, so we have to use the keycode instead of the name
bindcode 66 exec swaymsg [con_id=$(swaymsg -t get_tree | ~/.config/sway/alttab t)] focus
# Keep normal Alt+Tab behaviour since I haven't retrained my fingers to use caps lock exclusively.
bindsym Mod1+tab exec swaymsg [con_id=$(swaymsg -t get_tree | ~/.config/sway/alttab t)] focus
bindsym Mod1+shift+tab exec swaymsg [con_id=$(swaymsg -t get_tree | ~/.config/sway/alttab f)] focus
Conclusion
When I switched, I knew absolutely nothing about Wayland other than the most obvious hand-wavey stuff — despite being a long-time Linux user. I have now been using Sway for three months, and have no plans to switch back.
Even now, I know vanishingly little about Wayland internals and other Sway things that haven’t directly caused an error that I had to debug. This guide, therefore, is more like a series of tree blazes made by a half-delerious explorer than the precise work of a cartographer. While I wouldn’t necessarily recommend the blind-adventurer approach that I took, it has its charms.