Skip to content

Instantly share code, notes, and snippets.

@lbonn
Created April 22, 2020 10:55
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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
@deafmute1
Copy link

deafmute1 commented Apr 22, 2021

Hi, I made a few changes to show the currently focused window with an asterick as well as the workspace of each window, If anyone stumbles across this and is interested.
This makes it a bit more similar to this script: https://github.com/wilecoyote2015/Swytch, but I much prefer your script to that one.

#!/usr/bin/env bash
# Based on https://gist.github.com/lbonn/89d064cde963cfbacabd77e0d3801398 

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")"
focus_win_id=$(jq -r '.. | objects | select(has("app_id")) | select(.focused == true) | .id' <<< "$tree")


switch () {
    local k
    read -r k
    swaymsg "[con_id=${win_ids[$k]}] focus"
}

for k in $(seq 0 $((${#win_ids[@]} - 1))); do
    workspace=$(jq -r --argjson ID "${win_ids[$k]}" '.. | objects | select(has("nodes")) | select(.nodes[].id == $ID ) | .name' <<< "$tree")

    if [[ "${win_ids[$k]}" == "$focus_win_id" ]]; then 
      focus="*"
    else 
      focus=" "
    fi   
    
    echo -e "[${workspace}] ${focus} <span weight=\"bold\">${win_types[$k]}</span> - ${win_names[$k]}"
done | rofi -dmenu -markup-rows -i -p window -format i | switch

@wakatara
Copy link

wakatara commented Nov 4, 2021

@deafmute1
I like your additions. Is nice (though I modified it separately since I use awesome-font icons for the window switcher.

Is there a way to also make this display the icons of the actual application windows involved in rofi? I like this approach better than swayr for its simplicity, just trying to make it look prettier in rofu now... =]

@bellecp
Copy link

bellecp commented May 27, 2022

Very nice. I made a few modifications in order to

  • show icons with rofi -show-icons
  • perform only one call to jq to remove latency.

The window id is encoded inside the strings passed to rofi hidden inside an html comment, for instace <!-- 264 -->, and retrieved later on using sed.

#!/usr/bin/env bash
# Based on https://gist.github.com/lbonn/89d064cde963cfbacabd77e0d3801398 
#
# In order to have meaningful window names for terminals, use

# inside .vimrc:
# set title

# inside .bashrc:
# export PROMPT_COMMAND='echo -en "\033]0; $("pwd")  \a"'

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
    | "[\($ws.name)] \($asterisk) <span weight=\"bold\">\(.app_id)</span>  - \(.name) <!-- \(.id) -->\u0000icon\u001f\(.app_id)"' \
| sed 's/&/&amp;/g' \
| rofi -dmenu -show-icons -markup-rows)

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

@anarcat
Copy link

anarcat commented Nov 24, 2022

... and i rewrote this so it works with fuzzel, which involved (unfortunately) removing the fancy pango markup, but it should work with anything that supports dmenu now (as long as it also supports rofi's icon system):

https://gitlab.com/anarcat/scripts/-/blob/main/sway-window-menu

@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.

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