Skip to content

Instantly share code, notes, and snippets.

@lbonn
Created April 22, 2020 10:55
Show Gist options
  • Save lbonn/89d064cde963cfbacabd77e0d3801398 to your computer and use it in GitHub Desktop.
Save lbonn/89d064cde963cfbacabd77e0d3801398 to your computer and use it in GitHub Desktop.
Window switcher for sway ('rofi -show window' clone)
#!/usr/bin/env bash
set -euo pipefail
tree=$(swaymsg -t get_tree)
readarray -t win_ids <<< "$(jq -r '.. | objects | select(has("app_id")) | .id' <<< "$tree")"
readarray -t win_names <<< "$(jq -r '.. | objects | select(has("app_id")) | .name' <<< "$tree")"
readarray -t win_types <<< "$(jq -r '.. | objects | select(has("app_id")) | .app_id // .window_properties.class' <<< "$tree")"
switch () {
local k
read -r k
swaymsg "[con_id=${win_ids[$k]}] focus"
}
for k in $(seq 0 $((${#win_ids[@]} - 1))); do
echo -e "<span weight=\"bold\">${win_types[$k]}</span> - ${win_names[$k]}"
done | rofi -dmenu -markup-rows -i -p window -format i | switch
@gaardhus
Copy link

gaardhus commented Feb 21, 2024

Sometimes app_ids are not enough in my experience, and sometimes app_ids don't translate to icons.

I updated @bellecp's script to accommodate this:

row=$(swaymsg -t get_tree | jq -r '
    ..
    | objects
    | select(.type == "workspace") as $ws
    | ..
    | objects
    | select(has("app_id"))
    | (if .focused == true then "*" else " " end) as $asterisk
    | (
        if .window_properties.class == "Spotify" then "spotify-client" 
        elif .window_properties.class == "LM Studio" then "lm-studio" 
        else .app_id // .window_properties.class // .name 
      end ) as $icon_name
    | "[\($ws.name)] \($asterisk) <span weight=\"bold\">\(.app_id // .window_properties.class)</span>  - \(.name) <!-- \(.id) -->\u0000icon\u001f\($icon_name)"
    ' |
	sed 's/&/&amp;/g' |
	rofi -dmenu -show-icons -markup-rows -p "window")

if [ ! -z "$row" ]; then
	winid=$(echo "$row" | sed 's/.*<!-- \([0-9]*\) -->.*/\1/')
	swaymsg "[con_id=$winid] focus"
fi

There probably is a better way at detecting icons (rofi -show window) does a somewhat better job. I guess you might be able to map the app names to icons thorugh their .desktop files.

@breno-ca
Copy link

breno-ca commented Jan 2, 2025

Great work, everyone! I've tested the solutions above. Since I use fuzzel, I came up with this approach to address the issue with icons and XWayland apps that don't have an app_id.

# Parse the Sway tree to generate a formatted list of windows for fuzzel.
selected_window=$( swaymsg -t get_tree | jq -r '
	..
	# Get workspaces and customize workspace names
	| objects | select(.type == "workspace") as $workspace
	| ( if $workspace.name == "__i3_scratch" 
		then "-" else $workspace.name end 
	  ) as $ws_name 

        | ..
	# Get opened apps and set "focused" decoration
        | objects | select(has("app_id"))
	| (if .focused == true then "🟢" else "" end) as $selected

	# Exclude apps from list
	| select(.app_id == null or (.app_id | test("pavucontrol") | not))
	
	# Manually set icons to fix cases where they are not displayed
	| ( "vivaldi-pjibgclleladliembfgfagdaldikeohf-Default" ) as $spotify
	| ( if .name != null and (.name | test("Spotify")) then $spotify
		elif .app_id == "Firefox" then "firefox" 
		else .app_id // .window_properties.class // .name end
	  ) as $icon
	
	# Fix for displaying app_id when it is null
	| ( if .app_id == null and (.name | test("Spotify")) then "Spotify" 
		else .name end 
	  ) as $app_id

	# Set dmenu item format (keep the icon separated from the rest)
	# Output ex: 📂 (101) [1] 🟢File Explorer 
	| "(\(.id)) [\($ws_name)] \($selected)\($app_id)" +
	  "\u0000icon\u001f\($icon)"' \
	| fuzzel -d -w 50
)

# Focus the selected window.
if [ "$selected_window" != "" ]; then
	window_id=$(echo "$selected_window" | sed 's/.*(\([[:digit:]]*\)).*/\1/')
	swaymsg [con_id="$window_id"] focus
fi

I've also tried to improve the readability by adding comments to make the code easier to understand. Additionally, I published this version in my repository (sway-util-scripts/window_switcher.sh), referencing this gist and some other links I found online.

Cheers from Brazil!🇧🇷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment