swaywm
Sway↗ is a tiling window manager and Wayland compositor, inspired by i3, and written in C. This page includes literate configuration to all the components used in my sway setup. All configuration files are under version control using git .
Motivation
After using dwm for sometime, installed sway in Linux Mint for the first time to try Wayland. I moved to Arch Linux mainly to try the updated sway version. Since end'24, using swayWM in Alpine Linux . Articles like In praise of Alpine Linux↗ and In praise of Alpine and apk↗ by people associated with Sway and the need for lightweight OS for pi made me try Sway on Alpine Linux .
Tools
My Sway session using this configuration file is auto launched on tty1 from .profile using the startup script. The setup doesn’t use a session manager like elogind. It however uses eudev as device manager, seatd for seat management, D-Bus and OpenRC User services. SwayWM has been configured using the following tools:
- Terminal - foot
- Notification daemon - mako
- Locking tool - swaylockd
- Feed generator for swaybar - i3blocks with custom scripts
- For displaying time in swaybar - slclock, a vibe coded C utility
- Launcher menu - tofi
- Logout menu - tofi-exit - tofi based custom script
- power management tool - powerctl↗
- Seat management - seatd
To achieve consistent nord theme:
- Gtk theme - Nordic↗
- Icon theme - Nordzy↗
- Firefox - Uses custom userChrome.css↗ based on keyfox↗ . This requires toolkit.legacyUserProfileCustomizations.stylesheets set to true in Firefox’s about:config.
- Theme switching - toggle_theme script to toggle dark/light mode
Launcher script
The Sway launcher script below tangles to ~/.local/bin/SwayWM↗ . This is symlinked to /usr/local/bin/SwayWM.
Environment setup
Wayland compositors require XDG_RUNTIME_DIR to be set before they can be launched. Since i use seatd as seat manager, this needs to be set first. Earlier i was using pam-rundir package.
export XDG_RUNTIME_DIR=$(mkrundir)
The following wayland specific variables were taken from the seatd author’s site↗ .
export XDG_SESSION_TYPE=wayland
export XDG_SESSION_DESKTOP=sway
export XDG_CURRENT_DESKTOP=sway
Application specific variables
The following setting needs to be enabled to run DRM content as documented in Alpine Linux wiki.
The below settings were earlier copied during initial configuration. Disabling them did not cause any errors.
The following settings were added to handle various errors on enabling debug. The setting related to Adwaita is required for Nordic theme to be applied cleanly.
export WLR_DRM_NO_ATOMIC=1
export WLR_NO_HARDWARE_CURSORS=1
export ADW_DISABLE_PORTAL=1
Graphics configuration
I use Intel integrated graphics. So including the necessary driver information.
export LIBVA_DRIVER_NAME=iHD
export MESA_LOADER_DRIVER_OVERRIDE=iris
export __GLX_VENDOR_LIBRARY_NAME=mesa
Window manager execution
Enable debug for sway, if needed. Add “-d” flag to the sway command along with a file to capture the debug log.
exec dbus-run-session sway "$@"
sway-notify
I have a one-line wrapper script for notify-send that logs when notification fails. If notification fails for any reason, logging happens without affecting the script flow.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
notify-send "${@}" || logger -t sway-notify -p user.err "Notification failed to send."
sway-volume-notify
To control multi-media volume i use the following script which tangles to ~/.config/sway/scripts/sway-volume-notify. Previously the below script used notify-send approach based on RichieHH’s dotfiles↗ in combination with official sway volume control guidelines.↗ I only changed symbols from 🔊 to others like 🔇, 🔈 and 🔊 where U+1F507, U+1F508 and U+1F50A respectively.
When i tested fnott , decided to use wob to dispaly the volume instead of notify-send.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
# ALSA/amixer version (replaces wpctl, for plain-ALSA setups without
# PipeWire/WirePlumber).
WOBSOCK="${XDG_RUNTIME_DIR}/wob.sock"
MIXER="Master" # change if `amixer scontrols` shows a different name on your card
get_volume(){
info=$(amixer get "$MIXER")
volumep=$(echo "$info" | grep -o '[0-9]*%' | head -1 | tr -d '%')
if echo "$info" | grep -q '\[off\]'; then
muted=1
else
muted=0
fi
}
case "$1" in
"0")
# Toggle mute status
amixer set "$MIXER" toggle > /dev/null
;;
"+")
# Unmute if needed and increase volume by 5%
amixer set "$MIXER" unmute > /dev/null
amixer set "$MIXER" 5%+ > /dev/null
;;
"-")
# Decrease volume by 5%
amixer set "$MIXER" 5%- > /dev/null
;;
esac
get_volume
if [ "$muted" -eq 1 ]; then
echo "$volumep muted" > "$WOBSOCK"
else
echo "$volumep" > "$WOBSOCK"
fi
pkill -RTMIN+1 i3blocks &
swaylockd
swaylockd↗ is a dumb launcher to spawn swaylock and ensure it’s running no matter what. Otherwise, this uses swaylock configuration.
This section tangles to the swaylock configuration file ~/.config/swaylock/config↗ . The colour scheme nord is based on this issue↗ .
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
color=2e3440
image=/usr/share/backgrounds/nordic/nord_lake.png
# Sets the color of backspace highlight segments
bs-hl-color=b48eadff
# Sets the color of backspace highlight segments when Caps Lock is active
caps-lock-bs-hl-color=d08770ff
# Sets the color of the key press highlight segments when Caps Lock is active
caps-lock-key-hl-color=ebcb8bff
# Sets the indicator radius (default: 50)
indicator-radius=100
# Sets the indicator thickness (default: 10)
indicator-thickness=10
# Sets the color of the inside of the indicator
inside-color=2e3440ff
# Sets the color of the inside of the indicator when cleared
inside-clear-color=81a1c1ff
# Sets the color of the inside of the indicator when verifying
inside-ver-color=5e81acff
# Sets the color of the inside of the indicator when invalid
inside-wrong-color=bf616aff
# Sets the color of key press highlight segments
key-hl-color=a3be8cff
# Sets the background color of the box containing the layout text
layout-bg-color=2e3440ff
# Use the ring color for the line between the inside and ring
line-uses-ring
# Sets the color of the outside of the indicator when typing or idle
ring-color=3b4252ff
# Sets the color of the outside of the indicator when cleared
ring-clear-color=88c0d0ff
# Sets the color of the outside of the indicator when verifying
ring-ver-color=81a1c1ff
# Sets the color of the outside of the indicator when invalid
ring-wrong-color=d08770ff
# Sets the color of the lines that separate highlight segments
separator-color=3b4252ff
# Sets the color of the text
text-color=eceff4ff
# Sets the color of the text when cleared
text-clear-color=3b4252ff
# Sets the color of the text when verifying
text-ver-color=3b4252ff
# Sets the color of the text when invalid
text-wrong-color=3b4252ff
tofi
I went with tofi mainly due to it’s lightweight nature. Eventhough there has not been much of a release since 2+ years as of mid'26, the software is feature complete. Most changes were based on official website↗ . Initial customisation based on a reddit post↗ . Later modified the colors to reflect Nord color palette based on Nordic rofi theme↗ . Further references were made to official Nord Color palette↗ .
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
### Fonts
font = "/usr/share/fonts/hack/Hack-Bold.ttf"
font-size = 15
font-features = ""
font-variations = ""
hint-font = false
### Text theming
text-color = #81a1c1
background-color = #2E3440
# Prompt text theme
prompt-background = #3b4252
prompt-background-padding = 8, 12
prompt-background-corner-radius = 20
# Placeholder text theme
placeholder-color = #FFFFFFA8
placeholder-background-corner-radius = 0
# Input text theme
input-color = #b48ead
# Default result text theme
default-result-color = #5e81ac
# Selection text
selection-color = #8fbcbb
selection-match-color = #b48ead
### Text layout
prompt-text = >_
prompt-padding = 22
placeholder-text = " "
num-results = 5
result-spacing = 30
horizontal = true
min-input-width = 300
### Window theming
width = 66%
height = 70
outline-width = 0
border-width = 4
border-color = #5e81ac
corner-radius = 30
padding-bottom = 8
padding-right = 17
padding-top = 13
padding-left = 17
clip-to-padding = false
scale = true
### Window positioning
output = ""
anchor = center
exclusive-zone = -1
margin-top = 0
margin-bottom = 0
margin-left = 0
margin-right = 0
### Behaviour
history = true
fuzzy-match = false
auto-accept-single = false
hide-input = false
late-keyboard-init = false
multi-instance = false
ascii-input = false
terminal = foot
tofi-exit
This tofi-exit script is based on bdwalton@’s config↗ .
#!/bin/sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
set -e
set -u
selection=$(
printf "Suspend🌙\nLock🔒\nExit \nReboot\nPowerOff\n " |
# printf "Suspend\nLock\nExit\nReboot\nPowerOff\n " |
tofi --prompt-text="" \
--hide-input=true \
--hide-cursor=true \
--font "Symbols Nerd Font:size=14"\
--require-match=true\
--auto-accept-single=true
)
case "${selection}" in
*"Lock"*)
swaylockd
;;
*"Exit"*)
swaymsg exit
;;
*"Suspend"*)
powerctl mem
pkill -SIGUSR2 wayeyes
swaylockd
;;
*"Reboot"*)
doas /sbin/reboot
;;
*"PowerOff"*)
doas /sbin/poweroff
;;
esac
foot
I use foot↗ as terminal due to its lightweight nature.
foot configuration
For the toggle_theme script to work we use include section and foot theme files . To use signals to effect theme changes, foot requires [colors-dark] and [colors-light] sections.
# -*- conf -*-
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
term=xterm-256color
font=Hack:size=14
[main]
include=~/.config/foot/current_theme.ini
[colors-dark]
# nord theme
alpha=0.9
cursor = 2e3440 d8dee9
foreground = d8dee9
background = 2e3440
selection-foreground = d8dee9
selection-background = 4c566a
regular0 = 3b4252
regular1 = bf616a
regular2 = a3be8c
regular3 = ebcb8b
regular4 = 81a1c1
regular5 = b48ead
regular6 = 88c0d0
regular7 = e5e9f0
bright0 = 4c566a
bright1 = bf616a
bright2 = a3be8c
bright3 = ebcb8b
bright4 = 81a1c1
bright5 = b48ead
bright6 = 8fbcbb
bright7 = eceff4
dim0 = 373e4d
dim1 = 94545d
dim2 = 809575
dim3 = b29e75
dim4 = 68809a
dim5 = 8c738c
dim6 = 6d96a5
dim7 = aeb3bb
[colors-light]
# Nord Light / Snow Storm Palette
background=eceff4 # nord6 (Ice white background)
foreground=2e3440 # nord0 (Deep night text for great contrast)
selection-background=d8dee9
selection-foreground=2e3440
regular0=3b4252 # nord1 (Dark grey)
regular1=bf616a # nord11 (Aurora Red)
regular2=a3be8c # nord14 (Aurora Green)
regular3=ebcb8b # nord13 (Aurora Yellow)
regular4=5e81ac # nord10 (Deep Frost Blue)
regular5=b48ead # nord15 (Aurora Purple)
regular6=88c0d0 # nord8 (Bright Frost Cyan)
regular7=e5e9f0 # nord5 (Soft Snow White)
bright0=4c566a # nord3 (Medium grey)
bright1=bf616a # nord11 (Aurora Red)
bright2=a3be8c # nord14 (Aurora Green)
bright3=ebcb8b # nord13 (Aurora Yellow)
bright4=81a1c1 # nord9 (Medium Frost Blue)
bright5=b48ead # nord15 (Aurora Purple)
bright6=8fbcbb # nord7 (Calm Frost Cyan)
bright7=d8dee9 # nord4 (Muted Grey-White)
## Miscelaneous UI overrides
urls=81a1c1
[csd]
border-width=1
foot theme files
The toggle script resets the symbolic file link for current_theme.ini to overwrite the existing link to the appropriate theme.
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
[main]
initial-color-theme=light
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
[main]
initial-color-theme=dark
mako
I use mako↗ as notifications daemon for sway. This section tangles to the configuration file ~/.config/mako/config↗ .
For Nordic theme, i used this hex46’s dotfiles↗ . I maintain a single theme for mako, as notifications lasts few seconds by design. Instead of the default notify-send command, sway-notify , a wrapper is used in some of the sway scripts.
The notification from RSI application wayeyes is rendered in full screen, so the user is alerted about monitor getting turned off.
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
sort=-time
default-timeout=3000
layer=overlay
icon-path=/usr/share/icons/Mint-Y:/usr/share/icons/Nordzy:/usr/share/icons/Nordzy-dark
max-icon-size=64
height=300
font=Hack, Symbols Nerd Font 11
background-color=#4c566a
text-color=#d8dee9
progress-color=#88c0d0 over #3b4252
border-color=#88c0d0
border-radius=20
# padding=0,5,20
padding=10,15,10,15
format=<span font="Hack, Symbols Nerd Font 13" rise="5pt" color="#b48ead" weight="bold">%s</span>\n<span font="Hack, Symbols Nerd Font 11" color="#eceff4">%b</span>
text-alignment=center
[grouped]
format=<span font="Hack, Symbols Nerd Font 13" rise="5pt" color="#b48ead" weight="bold">%s</span>\n<span font="Hack, Symbols Nerd Font 11" color="#eceff4">%b</span>
[app-name=wayeyes]
default-timeout=2000
background-color=#000000
text-color=#eceff4
border-color=#4c566a
# text-color=#000000
# border-color=#000000
border-radius=0
width=1532
height=820
format=<span font="Hack, Symbols Nerd Font 13" line_height="42" rise="1pt" color="#b48ead" weight="bold">%s</span>\n<span font="Hack, Symbols Nerd Font 11" color="#eceff4">%b</span>
anchor=center
[urgency=critical]
border-color=#bf616a
default-timeout=0
Since mako’s format= directive is parsed as Pango markup, any text
substituted into “%s” “%b” (e.g. song titles/artists from playerctl)
can contain “&”, “<”, or “>” and break the markup parser. The
following script provides a function “escape_pango_markup” to escape
these characters before they’re passed to notification command, so
notifications with embedded markup (e.g. the album name span in the
media player block) still render correctly.
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
escape_pango_markup() {
sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g'
}
fnott
Around June'26, tried fnott as notification daemon but went back to mako due to feature limitations in fnott.
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
background=2e3440ff
border-color=4c566aff
title-color=d8dee9ff
summary-color=e5e9f0ff
body-color=d8dee9ff
[low]
background=2e3440ff
border-color=4c56eaff
title-color=8fbcbbff
summary-color=8fbcbbff
body-color=88c0d0ff
[critical]
background=2e3440ff
border-color=bf616aff
title-color=bf616aff
summary-color=bf616aff
body-color=d8dee9ff
wob
I installed wob to view volume changes as overlay bar. Previously i used notify-send based approach for viewing information on volume changes.
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
timeout = 1000
max = 100
width = 250
height = 30
border_offset = 2
border_size = 2
bar_padding = 3
anchor = top right
margin = 20
# Nord: dark background, frost blue bar, subtle border
background_color = 2E3440FF
bar_color = 88C0D0FF
border_color = 4C566AFF
overflow_background_color = 2E3440FF
overflow_bar_color = BF616AFF
overflow_border_color = 4C566AFF
[style.muted]
background_color = 2E3440FF
bar_color = 4C566AFF
border_color = BF616AFF
The below script is used to start wob in sway config file . The “$WOBSOCK” is used by scripts like sway-volume-notify to display volume information.
#!/usr/bin/env sh
WOBSOCK="${XDG_RUNTIME_DIR}/wob.sock"
rm -f "$WOBSOCK"
mkfifo "$WOBSOCK"
tail -f "$WOBSOCK" | wob &
i3blocks
I use i3blocks↗ as a feed generator to generate status line content for my swaybar.
The i3blocks configuration file is mostly based on the sample file with tweaks based on trill’s dotfiles↗ . The $SCRIPT_DIR variable used in i3block configuration file is set in status_command in sway configuration file .
In the calendar section, a small utility slclock↗ written in C language generates the time displayed on the bar. Since slclock is run in persistent mode, an enivornment variable CLICK_SCRIPT is used to give the script location which runs on clicking the time on swaybar. The script location needs to be given in full, as “$HOME”, “~/” are not supported.
# Global properties
align=center
separator=false
align=center
separator_block_width=15
markup=pango
command=$SCRIPT_DIR/$BLOCK_NAME
# [active]
# command=swaymsg -t subscribe -m '["window"]' | jq -r '.container.name'
# interval=once
[calendar]
command=slclock
interval=persist
CLICK_SCRIPT=/home/prabu/.config/sway/scripts/i3blocks/calendar
# The min_width has to adjusted to ensure that time appears in the center of the bar
[spaceblock]
full_text=" "
min_width=640
# min_width=500
align=center
[backup]
interval=once
signal=5
[mediaplayer]
interval=60
signal=2
min_width=20
[wayeyes]
interval=60
signal=3
min_width=20
[volume-pipewire]
interval=once
signal=1
min_width=20
All the scripts used by the i3blocks are maintained in $HOME/.config/sway/scripts/i3blocks/ folder.
backup indicator
btrbk utility is used to snapshot and backup BTRFS subvolumes in Alpine Linux . An indicator is displayed on the swaybar, when backup runs, so that one doesn’t accidentally shutdown/suspend the computer.
#!/bin/sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
INDICATOR_FILE="/tmp/btrbk_running"
# Function to print the icon and status
print_block() {
if [ -f "$INDICATOR_FILE" ]; then
echo ""
echo "btrbk"
echo "#FF0000"
else
echo ""
echo ""
fi
}
handle_button() {
# Exit early if no button was pressed, which is the default behavior
[ -z "$BLOCK_BUTTON" ] && return 0
case "$BLOCK_BUTTON" in
1) notify-send "btrbk" "btrbk backup is in progress" ;;
esac
}
# Handle the mouse button click, and all its output is discarded
handle_button > /dev/null 2>&1
# Print the icon and status to i3blocks
print_block
mediaplayer
I mainly use cmus to play mp3 files and mpv as media player. The below script uses playerctl to control and display media information.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
# Source shared helpers for notification
notify_lib="${HOME}/.config/sway/scripts/mako/notify-lib.sh"
if ! . "$notify_lib" 2>/dev/null; then
logger -t "$(basename "$0")" -p user.err "Failed to source ${notify_lib}"
exit 1
fi
now_playing() {
local current_player title album artist current_song
current_player=$(playerctl metadata --format '{{ playerName }}')
title=$(playerctl metadata --format '{{ title }}' | escape_pango_markup)
album=$(playerctl metadata --format '{{ album }}' | escape_pango_markup)
artist=$(playerctl metadata --format '{{ artist }}' | escape_pango_markup)
current_song="${title} from <span color=\"#88c0d0\" weight=\"bold\">${album}</span> by ${artist}"
makoctl dismiss && sway-notify "$current_player" "$current_song"
}
print_block() {
# Check player status
local player_status
player_status=$(playerctl status 2>/dev/null)
case "$player_status" in
"Paused")
echo ""
echo ""
echo "#FF0000"
;;
"Playing")
echo ""
;;
"Stopped")
echo ""
;;
# Handle the case when playerctl returns nothing (no players or error)
"")
echo " "
;;
# Fallback for other states like 'Idling'
*)
echo " "
;;
esac
}
# 1 Left click: display current song info
# 2 Middle click: Toggle play-pause
# 3 Right click: show scratchpad (effective only if cmus is running there)
# 4 Wheel up: Next song
# 5 Wheel down: Prev song
handle_button() {
# Exit early if no button was pressed, which is the default behavior
[ -z "$BLOCK_BUTTON" ] && return 0
case "$BLOCK_BUTTON" in
1) now_playing && pkill -RTMIN+2 i3blocks ;;
2) playerctl play-pause && pkill -RTMIN+2 i3blocks ;;
3) swaymsg "[app_id=\"play_cmus_in_scratch\"] scratchpad show" ;;
4) playerctl next && now_playing ;;
5) playerctl previous && now_playing ;;
esac
}
# Handle the mouse button click, and all its output is discarded
handle_button > /dev/null 2>&1
# Print the block to status bar
print_block
calendar
On clicking the time displayed on the bar, the current day/date is displayed as a notification message. The same information is also available made available in clipboard by the script.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
sway-notify "$(date +'%A %F')"
date +'%A %F %H:%M:%S' | wl-copy
wayeyes
Wayeyes↗ is a lightweight tool written in C to reduce eye strain for wlroots based Wayland compositors using wlopm↗ utility. The i3blocks script is given below. In addition to the notification from below script, the utility also sends a 2 seconds advance notification. The notification daemon mako shows this notification in full screen notification.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
# Icons are FontAwesome eye-open & eye-slash
STATE_FILE="${XDG_RUNTIME_DIR:-/tmp}/wayeyes.state"
state=$(cat "$STATE_FILE" 2>/dev/null || echo "stopped")
epoch=$(echo "$state" | awk '{print $2}')
time=$(date -d "@$epoch" +%H:%M:%S 2>/dev/null)
case "$BLOCK_BUTTON" in
1) case "$state" in
running*|pre-alert*)
notify-send "wayeyes" "Next break at $time" ;;
paused*)
notify-send "wayeyes" "Paused" ;;
blanked*)
notify-send "wayeyes" "On break" ;;
*)
notify-send "wayeyes" "Not running" ;;
esac ;;
2) pkill -SIGUSR1 wayeyes ;;
esac
case "$state" in
pre-alert*)
if [ ! -f /tmp/wayeyes.alerted ]; then
notify-send "wayeyes" "Next break in a minute"
touch /tmp/wayeyes.alerted
fi
echo ""
echo ""
echo "#FFA500"
;;
running*)
rm -f /tmp/wayeyes.alerted
echo ""
echo ""
echo ""
;;
paused*)
echo ""
echo ""
echo "#FF0000"
;;
blanked*)
rm -f /tmp/wayeyes.alerted
echo ""
echo ""
echo ""
;;
*)
rm -f /tmp/wayeyes.alerted
echo ""
echo ""
echo "#FF0000"
;;
esac
Volume
Pipewire is used for controlling the audio stack, though alsa is used for 5.1 audio as explained in HTPC page.
#!/usr/bin/env sh
# This file is maintained as a literate file in
# 20240317205837-swaywm.org.
print_block() {
local volume
volume=$(wpctl get-volume @DEFAULT_AUDIO_SINK@)
case "$volume" in
*MUTED*)
echo ""
echo ""
echo "#FF0000"
;;
*)
echo ""
;;
esac
}
# Mouse click
# 1 Left click: display current volume
# 2 Middle click: Toggle mute
# 3 Right click: Select sink - Not implemented yet
# 4 Wheel up: increase volume
# 5 Wheel down: decrease volume
handle_button() {
# Exit early if no button was pressed, which is the default behavior
[ -z "$BLOCK_BUTTON" ] && return 0
local notify_cmd
notify_cmd="$HOME/.config/sway/scripts/sway-volume-notify"
case "$BLOCK_BUTTON" in
1) "$notify_cmd" ;;
2) "$notify_cmd" "0" ;;
4) "$notify_cmd" "+" ;;
5) "$notify_cmd" "-" ;;
esac
}
# Handle the mouse button click, and all its output is discarded
handle_button > /dev/null 2>&1
# Print the block to status bar
print_block
Usage notes
Some of these were used in the past.
Bluetooth applet
To run the bluetooth applets:
exec blueman-applet exec nm-applet --indicator
© Prabu Anand K 2020-2026