Sway Wayland Session Configuration

28 January 2026 · Updated 28 January 2026

The Macintosh Classic II e-ink mod can run a full Sway Wayland compositor session instead of the basic terminal mode. Sway runs headless with a virtual 800x600 output, and paper-tty sway captures frames via wlr-screencopy-unstable-v1, converts to grayscale, and pushes to the IT8951 e-ink display over SPI.

Architecture

Keyboard → evdev → paper-tty (EVIOCGRAB) → virtual keyboard → Sway (headless)
Sway (headless) → wlr-screencopy → paper-tty → grayscale conversion → SPI → IT8951
  • Sway runs with WLR_BACKENDS=headless (no physical display output)
  • paper-tty sway connects as a Wayland client to capture frames
  • Keyboard input is read from /dev/input/event0 with exclusive grab (EVIOCGRAB) and forwarded to Sway via zwp-virtual-keyboard-v1
  • Modifier keys (Shift, Ctrl, Alt, Super) are tracked and sent as explicit modifiers() events
  • Pixel-level frame diffing avoids unnecessary display refreshes
  • Partial updates are 4-pixel aligned for IT8951 compatibility

Systemd Services

The services run as systemd user services (not system services) to ensure the Wayland socket is created in the correct XDG_RUNTIME_DIR. User lingering is enabled so services start at boot without requiring a login.

sway-eink.service

~/.config/systemd/user/sway-eink.service

[Unit]
Description=Sway Headless for E-ink Display

[Service]
Type=simple
Environment=WLR_BACKENDS=headless
ExecStart=/usr/bin/sway -c /etc/paper-tty/sway-eink.conf
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target

paper-tty-sway.service

~/.config/systemd/user/paper-tty-sway.service

[Unit]
Description=Paper-TTY Sway E-ink Capture
After=sway-eink.service
BindsTo=sway-eink.service

[Service]
Type=simple
ExecStartPre=/bin/sleep 3
ExecStart=/home/kyle/paper-tty-sway.sh
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target

Wrapper script

~/paper-tty-sway.sh — auto-detects the Wayland socket and reads config:

#!/bin/bash
source /etc/paper-tty.conf

for s in wayland-0 wayland-1; do
    if [ -S "/run/user/1000/$s" ]; then
        export WAYLAND_DISPLAY="$s"
        break
    fi
done

exec ~/paper-tty-rs/target/release/paper-tty -v sway \
    --mode "${DISPLAY_MODE}" \
    --frame-interval 500 \
    --margin-left "${MARGIN_LEFT}" \
    --margin-right "${MARGIN_RIGHT}" \
    --margin-top "${MARGIN_TOP}" \
    --margin-bottom "${MARGIN_BOTTOM}"

A wrapper script is used because systemd unit files cannot easily pass shell variables through to bash -c commands (systemd interprets $ in ExecStart lines).

Enabling/Disabling

# Enable user lingering (required for boot-time user services)
sudo loginctl enable-linger kyle

# Switch from terminal mode to Sway mode
sudo systemctl disable paper-tty.service
sudo systemctl disable epaper-dashboard.timer epaper-dashboard.service
systemctl --user enable sway-eink.service paper-tty-sway.service
systemctl --user daemon-reload

# Switch back to terminal mode
systemctl --user disable sway-eink.service paper-tty-sway.service
sudo systemctl enable paper-tty.service

Environment Configuration

/etc/paper-tty.conf — shared between terminal and Sway modes:

DISPLAY_MODE=gl16
MARGIN_LEFT=80
MARGIN_RIGHT=80
MARGIN_TOP=20
MARGIN_BOTTOM=20
THEME=--light
REFRESH=--partial
FONT_SIZE=16
REFRESH_RATE=250
CURSOR=block

Sway Configuration

/etc/paper-tty/sway-eink.conf

# Sway config for e-ink display (headless)
output HEADLESS-1 resolution 800x600

# Minimal chrome for e-ink
default_border pixel 2
gaps inner 0
gaps outer 0

# Mod key = Super (Command on Mac keyboards)
set $mod Mod4

# Workspace switching
bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
bindsym $mod+4 workspace number 4
bindsym $mod+5 workspace number 5
bindsym $mod+6 workspace number 6
bindsym $mod+7 workspace number 7
bindsym $mod+8 workspace number 8
bindsym $mod+9 workspace number 9

# Move container to workspace
bindsym $mod+Shift+1 move container to workspace number 1
bindsym $mod+Shift+2 move container to workspace number 2
bindsym $mod+Shift+3 move container to workspace number 3
bindsym $mod+Shift+4 move container to workspace number 4
bindsym $mod+Shift+5 move container to workspace number 5
bindsym $mod+Shift+6 move container to workspace number 6
bindsym $mod+Shift+7 move container to workspace number 7
bindsym $mod+Shift+8 move container to workspace number 8
bindsym $mod+Shift+9 move container to workspace number 9

# Launch terminal
bindsym $mod+Return exec foot

# Close focused window
bindsym $mod+Shift+q kill

# Focus
bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right

# Move
bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right

# Split
bindsym $mod+b splith
bindsym $mod+v splitv

# Fullscreen
bindsym $mod+f fullscreen

# Layout
bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+e layout toggle split

# Floating
bindsym $mod+Shift+space floating toggle
bindsym $mod+space focus mode_toggle

# Reload config
bindsym $mod+Shift+c reload

# Lock screen
bindsym $mod+Shift+x exec swaylock

# White background for e-ink
output * bg #ffffff solid_color

# Auto-start
exec waybar
exec foot

Keyboard Shortcuts

ShortcutAction
Cmd+ReturnOpen new terminal
Cmd+Shift+QClose focused window
Cmd+1-9Switch to workspace
Cmd+Shift+1-9Move window to workspace
Cmd+H/J/K/LFocus left/down/up/right
Cmd+Shift+H/J/K/LMove window left/down/up/right
Cmd+BSplit horizontal
Cmd+VSplit vertical
Cmd+FToggle fullscreen
Cmd+SStacking layout
Cmd+WTabbed layout
Cmd+EToggle split layout
Cmd+SpaceToggle focus (tiled/floating)
Cmd+Shift+SpaceToggle floating
Cmd+Shift+CReload Sway config
Cmd+Shift+XLock screen

Application Configurations

Foot Terminal

~/.config/foot/foot.ini — light theme for e-ink:

[colors]
background=ffffff
foreground=000000

[cursor]
color=ffffff 000000
blink=no

[main]
font=monospace:size=12

Waybar

~/.config/waybar/config — Mac-style top bar:

{
    "layer": "top",
    "position": "top",
    "height": 22,
    "modules-left": ["sway/window"],
    "modules-center": [],
    "modules-right": ["clock"],
    "sway/window": {
        "format": "{title}",
        "max-length": 50
    },
    "clock": {
        "format": "{:%a %b %d  %I:%M %p}",
        "tooltip": false
    }
}

~/.config/waybar/style.css:

* {
    border: none;
    border-radius: 0;
    font-family: monospace;
    font-size: 12px;
    min-height: 0;
}

window#waybar {
    background-color: #ffffff;
    color: #000000;
    border-bottom: 1px solid #000000;
}

#window {
    font-weight: bold;
    padding: 0 8px;
}

#clock {
    padding: 0 8px;
}

Swaylock

~/.config/swaylock/config — white background with black indicator:

color=ffffff
inside-color=ffffff
inside-clear-color=ffffff
inside-ver-color=ffffff
inside-wrong-color=ffffff
key-hl-color=000000
bs-hl-color=000000
ring-color=000000
ring-clear-color=000000
ring-ver-color=000000
ring-wrong-color=000000
line-color=000000
line-clear-color=000000
line-ver-color=000000
line-wrong-color=000000
text-color=000000
text-clear-color=000000
text-ver-color=000000
text-wrong-color=000000
separator-color=000000
indicator-radius=80
indicator-thickness=8

Display Modes

ModeDescriptionUse Case
duFast monochrome (black/white only)Fast typing, cursor movement
gc16Full 16-level grayscaleHigh quality images, initial render
gl16Fast 16-level grayscaleGood balance for general use
a2Animation mode (fastest)Rapid updates, low quality

The default mode is gl16. Frame 1 always uses gc16 for the initial full render.

Building

# On the Raspberry Pi
cd ~/paper-tty-rs
cargo build --release --features sway

The sway feature flag enables the Wayland dependencies: wayland-client, wayland-protocols, wayland-protocols-wlr, wayland-protocols-misc, and rustix.

Troubleshooting

  • GPIO busy: Another paper-tty process is holding the display. Kill it or stop the systemd service first.
  • No Wayland compositor: Sway isn’t running or WAYLAND_DISPLAY isn’t set. Check sway-eink.service status.
  • No display updates: If using a dark terminal theme, DU mode produces invisible updates. Use GL16 mode and a light theme.
  • Diagonal artifacts on partial updates: The IT8951 requires 4-pixel aligned widths. This is handled automatically.
  • Doubled keypresses: EVIOCGRAB ensures exclusive keyboard access. If doubling occurs, check that no other process reads the evdev device.