Sway Wayland Session Configuration
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 swayconnects as a Wayland client to capture frames- Keyboard input is read from
/dev/input/event0with exclusive grab (EVIOCGRAB) and forwarded to Sway viazwp-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
| Shortcut | Action |
|---|---|
Cmd+Return | Open new terminal |
Cmd+Shift+Q | Close focused window |
Cmd+1-9 | Switch to workspace |
Cmd+Shift+1-9 | Move window to workspace |
Cmd+H/J/K/L | Focus left/down/up/right |
Cmd+Shift+H/J/K/L | Move window left/down/up/right |
Cmd+B | Split horizontal |
Cmd+V | Split vertical |
Cmd+F | Toggle fullscreen |
Cmd+S | Stacking layout |
Cmd+W | Tabbed layout |
Cmd+E | Toggle split layout |
Cmd+Space | Toggle focus (tiled/floating) |
Cmd+Shift+Space | Toggle floating |
Cmd+Shift+C | Reload Sway config |
Cmd+Shift+X | Lock 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
| Mode | Description | Use Case |
|---|---|---|
du | Fast monochrome (black/white only) | Fast typing, cursor movement |
gc16 | Full 16-level grayscale | High quality images, initial render |
gl16 | Fast 16-level grayscale | Good balance for general use |
a2 | Animation 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_DISPLAYisn’t set. Checksway-eink.servicestatus. - 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.