swaywm
Table of Contents
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] 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" ;; suspended*) notify-send "wayeyes" "Suspended" ;; 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*) ;; *) rm -f /tmp/wayeyes.alerted ;; esac case "$state" in pre-alert*) count=$(echo "$state" | awk '{print $3}') if [ ! -f /tmp/wayeyes.alerted ]; then if [ $(( (count + 1) % 4 )) -eq 0 ]; then notify-send -u critical "wayeyes" "Next break in a minute is a Long break!" else notify-send "wayeyes" "Next break in a minute" fi touch /tmp/wayeyes.alerted fi echo "" echo "" echo "#FFA500" ;; running*) echo "" echo "" echo "" ;; suspended*) echo "" echo "" echo "#FF0000" ;; paused*) echo "" echo "" echo "#FFFF00" ;; blanked*) echo "" echo "" echo "" ;; *) echo "" echo "" echo "#FF0000" ;; esac
Volume
I was using Pipewire exclusively for audio. Later used it only for controlling the audio volume as alsa was used for 5.1 audio as explained in HTPC page. As of 2026-09-14, i am using amixer to control audio. Pipewire is not used for audio.
#!/usr/bin/env sh # This file is maintained as a literate file in # 20240317205837-swaywm.org. MIXER="Master" # check sound card name using `amixer scontrols` print_block() { local info info=$(amixer get "$MIXER") case "$info" in *off*) 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