Recently I’ve been embarking on a quest to find the best window manager for Linux. I finally switched to Sway this week, and here’s my experience.
The problem with my current window manager
Two weeks ago, the release of KWin 5.19.0 brought the hope of finally fixing the KDE Wayland experience.
I finally went back to AMD (X5700 XT) and for months now, I’ve been dealing with overlapping windows flickering as soon as the main front window is out of focus. That was apparently fixed with 5.19.
While Firefox and any other application don’t create any more flickering artifacts whenever they’re overlapping – subsurface clipping was one of the selling points of KWin 5.19 – now the mouse started to leave a trail whenever above the desktop background, and window positions started not matching their displayed place.
This was with the fully opensource AMD graphics stack, which allows me to play games natively under Linux already (and mesa-9999).
I decided I had enough, and started checking what other compositors projects (never liked Gnome appearance and styles) were claiming Wayland support with little issues.
Being born just as an equivalent of an X compositor and tiling window manager, Sway seemed to be one of the most promising projects.
In fact, after emerging 4 packages, gui-wm/sway
was installed.
Executing rc-config restart xdm
made it instantly appear in SDDM’s list, but there were still a couple niceties to re-add before switching: KDE is a heavily integrated desktop environment. Migrating to a bare compositor/window manager meant (as far as I knew):
- giving up hotkeys
- giving up media hotkeys
- giving up graphical shortcuts to applications
- giving up the system tray
- giving up magnetic window borders
After a bit of fiddling, and some really good resources, I managed to solve 1) and 2) easily.
After one week, turns out 3) and 4) are not really a requirement, and 5) is sort-of baked in in Sway already.
One afterthought was the missing ALT+F2 combination to start apps: x11-misc/dmenu
solves the problem brilliantly.
Requirements
# cd /etc/portage # cat sets/sway gui-wm/sway media-sound/playerctl x11-misc/dmenu # cat package.accept_keywords/sway_unmasks gui-apps/swaylock gui-wm/sway media-sound/playerctl # cat package.use/sway gui-wm/sway tray ### add "mpris" to global USEs in your make.conf ### enables controlling mpris-enabled players (mpv) via Media Player Remote Interfacing Specification (a DBus interface for remote control)
The tray
USE on sway
enables the desktop notification protocol used by KDE.
Tweaking the default config
Of the default config ( /etc/sway/config
), you may want to comment out things you don’t need.
Some other things you can overwrite:
output * bg <path-to-possibly-non-existent-background>
: comment out: Sway will complain if you didn’t enable thewallpapers
USE (which installs the default wallpaper)bar { ... }
: comment out: we will customize this further later, the default datetime format is US 12h- ensure that the last line
include /etc/sway/config.d/*
is not commented
/etc/sway/config.d/apps
Here we setup the default applications.$menu
is actually the same as the default config, but I still set it up in a config.d file in case the default ever changes.
Being a KDE user for a long time, konsole
is the default choice. I actually still prefer Mod+d -> kons -> Enter
anyway.
set $term konsole set $menu dmenu_path | dmenu | xargs swaymsg exec --
/etc/sway/config.d/input
Here we setup the keyboard and mouse layout, nothing too fancy.
You can add multiple layouts and toggle between them via hotkeys (still haven’t used/learned it).
Note that the “type” selectors allow you to apply the same configuration to all keyboard and pointer inputs that sway identifies. You can further refine the selection if you have a strong case for it and add specific settings for specific input sources, but the config below will just work.
input "type:keyboard" { xkb_layout it,us } input "type:pointer" { left_handed disabled natural_scroll disabled dwt disabled }
/etc/sway/config.d/lockscreen
This is the configuration for screen auto-lock.
I just tweaked the default settings to lower timeouts (180/270s vs 300/600).
exec swayidle -w \ timeout 180 'swaylock -f -c 000000' \ timeout 270 'swaymsg "output * dpms off"' \ resume 'swaymsg "output * dpms on"' \ before-sleep 'swaylock -f -c 000000'
/etc/sway/config.d/media_keys
This binds the media hotkeys, which were a very important point to me.
I really dislike having to always go for the system tray, pulling up the config, selecting the right sink and finally adjusting every single time my wife tries to communicate with me. Also, I don’t have a system tray anymore. You can put pavucontrol
in a separate workspace and leave it maximized, but it gets super-annoying after a couple reboots.
Three notes on this one:
--locked
will allow you to use the hotkeys even when the screen is locked- The
AudioNext
andAudioPrev
buttons were hijacked to switch sinks and move all the streams around – see next section for the scripts @DEFAULT_SINK@
it’s an identifier coming from Pulseaudio: it selects the sink that is currently set as default (note: this does not necessarily correspond to the currently in-use sink)
#bindsym XF86MonBrightnessDown exec brightnessctl set 5%- #bindsym XF86MonBrightnessUp exec brightnessctl set +5% bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% bindsym --locked XF86AudioMicMute exec pactl set-source-mute @DEFAULT_SOURCE@ toggle bindsym --locked XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle bindsym --locked XF86AudioNext exec /etc/sway/move_sinks_monitor.sh bindsym --locked XF86AudioPlay exec playerctl play-pause bindsym --locked XF86AudioPrev exec /etc/sway/move_sinks_headphones.sh bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5%
/etc/sway/config.d/status_bar
This 95% copy-paste from the default config is just to switch away from the annoying American format for dates and times.
bar { position top status_command while date +'%Y.%m.%d %H:%M:%S'; do sleep 1; done colors { statusline #ffffff background #323232 inactive_workspace #32323200 #32323200 #5c5c5c } }
/etc/sway/config.d/wallpaper
Just replacing the default background with my own =)
output * bg /etc/sway/Wallpaper.png fill
Bash scripts to control audio
You may have noticed that the media_keys
config file above references a couple bash scripts.
It took me a bit of experimentation (also thanks to this post), but eventually I managed to map the essential media keys (audio control) and use the previous/next track keys to switch streams between the audio outputs.
/etc/sway/move_sinks.sh
This script:
- Sets the default sink to the provided one (via Pulseaudio sink index)
- Moves all the streams to the new default sink
As I mentioned above, the default sink is not necessarily the currently used/playing one. It is the sink where new streams are automatically added to.
This means that if we were to move all the streams to the new sink without changing the default one, an application starting another stream will place it on the device that we want to “deselect”.
One more important consequence of not moving the default sink is that volume up/down and mute keys would not work. Since we don’t have a reference to which stream we want to change when we press the buttons, we can only change the volume for the @DEFAULT_SINK@ via pactl
.
If we don’t change it here, we will keep changing the volume for another device.
If you are annoyed by the messages in the logs, you can later comment the logger
lines, however I recommend keeping them at least in the beginning, where you may be debugging why things don’t work.
#!/bin/bash logger "sway: $(basename $0): called with $1" pacmd set-default-sink "$1" logger "sway: $(basename $0): set default sink to: $(pacmd dump | grep set-default-sink)" pacmd list-sink-inputs | grep index | while read line; do pacmd move-sink-input $(echo $line | cut -f2 -d' ') "$1" done logger "sway: $(basename $0): done moving sinks!"
/etc/sway/move_sinks_headphones.sh
This script uses some bash-fu to grab the sink index from a small part of the device name.
You can list your available sink names and indexes with pacmd list-sinks | grep -e 'index:' -e 'name:'
, and grab a unique part of the name for each sink. For my Logitech G933 headphones and my HDMI monitor, “G933” and “hdmi” work perfectly.
Note that the sink indexes and names may change, so you most likely want something unique from the name: alsa_output.usb-Logitech_G933_Artemis_Spectrum_Snow-00.iec958-stereo
-> G933
.
In my particular case, when I have a decent multi-channel source, I setup the headphone switching to also allow me to select the software-emulated surround sink: if the default sink is already the headphone one, I switch it to “vsurround” instead.
Each key press allows to toggle between the stereo and vsurround mode.
If you absolutely want to distinguish when one or the other is activated, you can play a very short sound that you can identify. If I’m in doubt, I usually quickly cycle through the monitor first, and then go again for the headphones.
#!/bin/bash sink_identifier="G933" sink_idx=$(pacmd list-sinks | grep -e 'index:' -e 'name:' | grep "${sink_identifier}" -B1 | head -1 | sed 's: ::g' | cut -d':' -f2) if pacmd dump | grep set-default-sink | grep -q "${sink_identifier}"; then sink_identifier="vsurround" sink_idx=$(pacmd list-sinks | grep -e 'index:' -e 'name:' | grep "${sink_identifier}" -B1 | head -1 | sed 's: ::g' | cut -d':' -f2 | tr -d ' ') fi logger "sway: $(basename $0): sink_identifier: $sink_identifier" /etc/sway/move_sinks.sh "$sink_idx" logger "sway: $(basename $0): called move_sinks.sh with $sink_idx"
/etc/sway/move_sinks_monitor.sh
Just like the headphone one, this scripts switches to the monitor sink. This is simpler than the headphones one because of course, the monitor doesn’t really have a 5.1 output =)
#!/bin/bash sink_identifier="hdmi" sink_idx=$(pacmd list-sinks | grep -e 'index:' -e 'name:' | grep "${sink_identifier}" -B1 | head -1 | sed 's: ::g' | cut -d':' -f2) logger "sway: $(basename $0): sink_idx: $sink_idx" /etc/sway/move_sinks.sh "$sink_idx" logger "sway: $(basename $0): called move_sinks with $sink_idx"
Wrap up
Setting everything up was much easier than expected.
Sway is a very minimal window manager, and my requirements are too.
I can still easily use the KDE apps I’m used to if I need, but my daily workflow usually boils down Firefox, Konsole and mpv. Steam games work too (sometimes thanks to XWayland), as they already did with KWin/Wayland and KWin/Xorg.
The desktop feels much more responsive. Window redraws are super-snappy, and so is the switching between workspaces, etc. Honestly, the part I miss less about KWin is the perceived lag, even when all the effects are disabled.
Being able to never leave the keyboard to switch, move and resize windows is an added bonus I didn’t know I’d appreciate this much.
Thanks for the @DEFAULT_SINK@ tip!