I use Wayland now

I’ve switched to Wayland (with labwc as my compositor) from X11 because I got fed up with the way Xorg would more-or-less repeatably flip out when I did insane things that noone can expect from a graphical environment in the year 2025 such as:

One of the biggest upsides of switching to Wayland is that now screen sharing in Slack doesn’t cause Slack to use 120% CPU, but instead leaves it hovering at a more reasonable ~50%. At least according to my meeting partners earlier today, my audio quality is also noticably better (probably because Slack doesn’t have to copy absurdly large frame buffers back and forth anymore and can spend some time processing audio).

This is extra funny because “Screen sharing on Wayland sucks” is kind of a meme. The only downside I’ve noticed in this general area is that I now have to click multiple times to select the screen I want to share: the first click seems to be what’s required for Slack to gain the permission to show the preview of the shared screen, the second then makes it actually share the screen. I can very much live with that though.

This post is a bit of a rundown of my setup because I did have to tweak a few things to make it work nicely on Debian 13.

As with my other posts, it’s mostly a reminder for myself so that I don’t get confused if I have to repeat this setup somewhere else.

Login

I use greetd as my login manager, with the GTK greeter in a cage session. The setup is relatively straightforward.

This is my /etc/greetd/config.toml:

1[general]
2service = "greetd-spawn"
3
4[terminal]
5vt = 7
6
7[default_session]
8command = "cage -s -- sh -c 'wlr-randr --output eDP-1 --scale 2; gtkgreet'"
9user = "_greetd"

There’s some things in there that are not in Debian’s default configuration:

Labwc session

In /etc/greetd/environments, I have labwc-session as a line on its own to tell gtkgreet that that’s a valid session that it can use. The session is defined as an executable in /usr/local/bin/labwc-session:

 1#!/bin/bash
 2
 3. "${HOME}/.profile"
 4
 5PLAN9=${HOME}/plan9port
 6PATH=${PATH}:${PLAN9}/bin:${HOME}/go/bin
 7systemctl --user import-environment PLAN9 PATH
 8
 9eval `ssh-agent -s`
10systemctl --user import-environment SSH_AUTH_SOCK SSH_AGENT_PID
11
12exec labwc

This sets up environment variables so that my systemd user session can use my SSH keys (for fetching emails and creating backups), and so that it can use Plan9 commands in services.

Setting the session type

To make screen locking work nicely (including session idle hints), PAM needs to be told to explicitly set the XDG_SESSION_TYPE before the session is launched. To that end, set general.service as in my configuration and add the following files:

/etc/greetd/greetd-spawn.pam_env.conf

XDG_SESSION_TYPE DEFAULT=wayland OVERRIDE=wayland

/etc/pam.d/greetd-spawn

auth include greetd
auth required pam_env.so conffile=/etc/greetd/greetd-spawn.pam_env.conf
account include greetd
session include greetd

Screen locking

For screen locking, I use swaylock and swayidle:

1swayidle -w \
2	idlehint 300 \
3	timeout 300 'swaylock -f' \
4	lock 'swaylock -f' \
5	before-sleep 'swaylock -f'

This requires some setup so that systemd-logind knows we’re running a graphical session. If you don’t do that, things will work mostly, but idle session hinting through swayidle won’t work. It’ll log something to the effect of “idle hints are only supported for graphical sessions” if that is the case.

Acme (and Plan9 stuff in general)

Upstream Plan9’s devdraw does not support Wayland, so it’ll fall back to running under XWayland. Along with rather ugly scaling (fuzzy fonts), this means that, at least when running under labwc, cursor warping will not work (even though it ostensibly should). There is a fork of plan9port which has a working Wayland backend for devdraw that supports cursor warping, looks very nice and crispy, and it sets the proper “fat” Plan9 mouse cursor.

I’ve simply added that fork to my local plan9port checkout as another remote and added a merge commit on top that refers to both eaburns’ wayland and upstream master branches:

1$ cd ~/plan9port
2$ jj git remote add eaburns git@github.com:eaburns/plan9port.git
3$ jj new wayland@eaburns master

The only change I made to that is this diff:

diff --git a/src/cmd/devdraw/wayland.c b/src/cmd/devdraw/wayland.c
index 0b13cf6590..79600e4880 100644
--- a/src/cmd/devdraw/wayland.c
+++ b/src/cmd/devdraw/wayland.c
@@ -23,8 +23,8 @@

 // alt+click and ctl+click are mapped to mouse buttons
 // to support single button mice.
-#define ALT_BUTTON 1
-#define CTL_BUTTON 2
+#define ALT_BUTTON 2
+#define CTL_BUTTON 1

 struct WaylandBuffer {
        int w;

which flips the function of the Alt and Ctrl buttons so that they match the button order on a macbook (and IMO optically make more sense): Ctrl/Cmd for “middle” clicks, and Alt/Option for “right” clicks. That is a personal preference though.

Status bar

I use waybar as my status bar, but I’ve disabled the wlr/taskbar widget because it doesn’t deal well with overflow: if there’s more than a handful of windows visible, other widgets are pushed to the right.

Until that’s fixed, a bar without a window list works just as well.

Kanshi

To set up outputs, I use kanshi. It does exactly what I need (set up profiles based on connected/disconnected screens, no matter the port they’re connected on) without insane flickering, causing stuttering while fiddling with outputs for minutes, and so forth.

Kanshi from Debian’s APT mirrors can be used, but it’s an older version that does not support per-output global settings. Building Kanshi from source is relatively straightforward though.

My configuration looks like this, YMMV:

profile desktop {
	output "Sharp Corporation 0x14AD *" enable position 1920,0
	output "Fujitsu Siemens Computers GmbH B23T-6 LED YV4E008045" enable position 0,0
	output "DSC DASUNG *" enable position 3840,0
}

profile nomad {
	output "Sharp Corporation 0x14AD *" enable position 0,0
}

output "Sharp Corporation 0x14AD *" scale 2.0 mode 3840x2160 transform normal
output "Fujitsu Siemens Computers GmbH B23T-6 LED YV4E008045"  scale 1.0 mode 1920x1080 transform normal
output "DSC DASUNG *" scale 1.6 mode 3200x1800 transform 90

If you use the version of kanshi from Debian’s repositories, the output sections have to be repeated in each profile instead of applying “general” settings once and then using the profiles to place outputs at the right locations.

Electron Apps

To make Electron apps (such as Slack and Spotify) behave, tell them to use Wayland as their rendering backend by setting

ELECTRON_OZONE_PLATFORM_HINT=wayland

in your ~/.profile.

If you don’t do that, their UI scale (including the mouse cursor) will likely be off if you use outputs that don’t have 1:1 scaling (such as HiDPI screens).

Qt

To make Qt5 and Qt6 automatically use their Wayland backend instead of falling back to XWayland, install the appropriate Qt platform plugins:

1# apt install qt6-wayland qtwayland5

Contact Mastodon