Skip to content

Instantly share code, notes, and snippets.

@bjoern-r
Created July 16, 2026 07:45
Show Gist options
  • Select an option

  • Save bjoern-r/3512a1c53936e82ce73f36b20db99965 to your computer and use it in GitHub Desktop.

Select an option

Save bjoern-r/3512a1c53936e82ce73f36b20db99965 to your computer and use it in GitHub Desktop.
script to fetch the current screen layout from sway and convert to kanshi profile
#!/usr/bin/env bash
#
# sway-to-kanshi.sh
#
# Fetches the current monitor arrangement from the running sway instance
# (via `swaymsg -t get_outputs`) and prints it in kanshi config format.
#
# kanshi profile syntax reference:
# https://github.com/emersion/kanshi (man 5 kanshi)
#
# profile [<name>] {
# output <criteria> [enable|disable] [mode <width>x<height>[@<rate>Hz]] \
# [position <x>,<y>] [scale <factor>] [transform <transform>] \
# [adaptive_sync on|off]
# ...
# }
#
# Usage:
# ./sway-to-kanshi.sh # print profile to stdout
# ./sway-to-kanshi.sh -n docked # name the profile "docked"
# ./sway-to-kanshi.sh -o ~/.config/kanshi/config # write/append to a file
# ./sway-to-kanshi.sh --by-name # match outputs by name instead of make/model/serial
set -euo pipefail
PROFILE_NAME=""
OUTFILE=""
MATCH="identifier" # identifier = "make model serial"; name = output name (e.g. DP-1)
usage() {
cat >&2 <<EOF
Usage: ${0##*/} [-n NAME] [-o FILE] [--by-name]
-n, --name NAME Set the kanshi profile name (default: unnamed).
-o, --output FILE Append the generated profile to FILE (default: stdout).
--by-name Match outputs by connector name (DP-1, HDMI-A-1, ...)
instead of the more portable "make model serial".
-h, --help Show this help.
EOF
exit "${1:-0}"
}
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--name) PROFILE_NAME="$2"; shift 2 ;;
-o|--output) OUTFILE="$2"; shift 2 ;;
--by-name) MATCH="name"; shift ;;
-h|--help) usage 0 ;;
*) echo "Unknown argument: $1" >&2; usage 1 ;;
esac
done
command -v swaymsg >/dev/null || { echo "swaymsg not found" >&2; exit 1; }
command -v jq >/dev/null || { echo "jq not found" >&2; exit 1; }
if [[ -z "${SWAYSOCK:-}" && -z "${WAYLAND_DISPLAY:-}" ]]; then
echo "Warning: no SWAYSOCK/WAYLAND_DISPLAY set; is sway running in this session?" >&2
fi
outputs_json="$(swaymsg -t get_outputs)"
# Build the kanshi profile. jq emits one 'output ...' line per connected output.
profile_body="$(
jq -r --arg match "$MATCH" '
# kanshi transform names differ slightly from sway; sway uses the same set,
# so pass through. sway "normal"/"90"/"180"/"270"/"flipped*" are valid in kanshi.
def crit:
if $match == "name" then .name
else ((.make // "Unknown") + " " + (.model // "Unknown") + " " + (.serial // "Unknown"))
end;
.[] |
if (.active | not) then
"\toutput \"" + crit + "\" disable"
else
"\toutput \"" + crit + "\""
+ " enable"
+ " mode " + (.current_mode.width|tostring) + "x" + (.current_mode.height|tostring)
+ "@" + ((.current_mode.refresh/1000)|(.*1000|round/1000)|tostring) + "Hz"
+ " position " + (.rect.x|tostring) + "," + (.rect.y|tostring)
+ " scale " + (.scale|tostring)
+ (if (.transform // "normal") != "normal" then " transform " + .transform else "" end)
+ (if (.adaptive_sync_status // "disabled") == "enabled" then " adaptive_sync on" else "" end)
end
' <<<"$outputs_json"
)"
if [[ -n "$PROFILE_NAME" ]]; then
profile="profile $PROFILE_NAME {"$'\n'"$profile_body"$'\n'"}"
else
profile="profile {"$'\n'"$profile_body"$'\n'"}"
fi
if [[ -n "$OUTFILE" ]]; then
mkdir -p "$(dirname "$OUTFILE")"
{ [[ -s "$OUTFILE" ]] && printf '\n'; printf '%s\n' "$profile"; } >>"$OUTFILE"
echo "Wrote profile to $OUTFILE" >&2
else
printf '%s\n' "$profile"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment