#!/bin/bash declare -A icons=( ) jq_get_windows=' # descend to workspace or scratchpad .nodes[0].nodes[0] # save workspace name as .w | {"w": .name} + ( if (.nodes|length) > 0 then # workspace [recurse(.nodes[])] else # scratchpad [] end + .floating_nodes | .[] # select nodes with no children (windows) | select(.nodes==[]) )' jq_windows_to_tsv=' [ (.id | tostring), # remove markup and index from workspace name, replace scratch with "[S]" (.w | gsub("<[^>]*>|:$"; "") | sub("__i3_scratch"; "[S]")), # get app name (or window class if xwayland) (.app_id // .window_properties.class), (.name), (.pid) ] | @tsv' get_hardcoded_icon() { icon="${icons[$1]}" echo "$icon" } get_desktop() { app="$1" p="/usr/share/applications" # fast and easy cases first: for prefix in "" org.kde. org.gnome. org.freedesktop.; do d="$p/$prefix$app.desktop" [[ -r "$d" ]] && { echo "$d" [[ "$verbose" ]] && echo "found '$d'" >&2 return } done # maybe lowercase for prefix in "" org.kde. org.gnome. org.freedesktop.; do d="$p/$prefix${app,,}.desktop" [[ -r "$d" ]] && { echo "$d" [[ "$verbose" ]] && echo "found '$d'" >&2 return } done # this is fairly reliable but slow: # look for a .desktop file with Exec=$app eg # gnome-disks (but exclude gnome-disk-image-writer.desktop) # gnome-font-viewer GREP='egrep -r' type rg &>/dev/null && GREP=rg d=$( $GREP -il "^exec=$app( %u)*[[:space:]]*$" $p | head -n 1) [[ -r "$d" ]] && { echo "$d" [[ "$verbose" ]] && echo "found '$d'" >&2 return } # desperation - weird apps like com.github.wwmm.pulseeffects.desktop!! # shellcheck disable=SC2012 d=$( ls "$p/"*".$app.desktop" 2>/dev/null | head -n 1 ) [[ -r "$d" ]] && { echo "$d" [[ "$verbose" ]] && echo "found '$d'" >&2 return } } get_icon() { app="$1" icon=$( get_hardcoded_icon "$app_name" ) [[ "$icon" && -r "$icon" ]] && { echo "$icon" [[ "$verbose" ]] && echo "using hardcoded icon '$icon'" >&2 return } # let's go poke in the .desktop files: icon_name="" dt=$( get_desktop "$app" ) # sometimes we get the 'class' rather than the exe - so try this: [[ "$dt" ]] || { app=$( tr '\0' '\n' < "/proc/$pid/cmdline" | head -n 1 ) dt=$( get_desktop "$( basename "$app" )" ) } [[ "$dt" ]] && { icon_name=$( awk -v IGNORECASE="set" -F"=" '/^icon/ {print $2}' "$dt" ) [[ -r "$icon_name" ]] && { icon="$icon_name" echo "$icon" [[ "$verbose" ]] && echo "using .desktop icon '$icon'" >&2 return } [[ "$icon_name" ]] && { icon_locations=( icons/hicolor/scalable/apps icons/hicolor/48x48/apps icons/gnome/48x48/apps icons/gnome/48x48/devices pixmaps ) for d in "${icon_locations[@]}"; do for s in .svg .png .xpm ""; do icon=/usr/share/$d/$icon_name$s [[ -r $icon ]] && { echo "$icon" [[ "$verbose" ]] && echo "using .desktop icon '$icon'" >&2 return } done done icon=$(find /usr/share/icons | grep "/${icon_name}-symbolic.svg" | head -n 1) [[ -r $icon ]] && { echo "$icon" [[ "$verbose" ]] && echo "using .desktop icon '$icon'" >&2 return } } } # desperation street: icon=$( find /usr/share/icons /usr/share/pixmaps | grep -E -i "/$app.(png|svg)" | head -n 1 ) [[ "$icon" && -r "$icon" ]] && { echo "$icon" [[ "$verbose" ]] && echo "using found icon '$icon'" >&2 return } # failed: icon="/usr/share/gtk-doc/html/gtk2/image-missing.png" [[ "$verbose" ]] && echo "using missing icon '$icon'" >&2 echo "$icon" } id_fmt='%s' ws_fmt='%s' app_fmt='%-15.15s' title_fmt='%s' applist_array=() applist="" swaymsg -t get_tree | # get list of windows as tab-separated columns jq -r "$jq_get_windows | $jq_windows_to_tsv" | column -s $'\t' -o $'\t' -t | while IFS=$'\t' read -r win_id ws_name app_name win_title pid; do shopt -s extglob app_name="${app_name%%*( )}" icon=$( get_icon "$app_name" "$pid" ) [[ "$verbose" ]] && printf "[%s]=%s\n" "$app_name" "$icon" >&2 # shellcheck disable=SC2059 applist_array_item=$(jq -n --arg icon "${icon}" --arg name "${app_name##*.}" '{icon: $icon,name: $name}') # echo "${applist_array_item}" applist_array+=("${applist_array_item}") # echo "${applist_array[@]}" applist=$(printf '%s\n' "${applist_array[@]}" | jq -s .) # echo ${applist} # jq -n --arg icon "${icon}" --arg name "${app_name##*.}" '{icon: $icon,name: $name}' # printf "img:$icon:text:${id_fmt}\t${app_fmt}\t${ws_fmt}\t${title_fmt}\n" \ # "$win_id" \ # "${app_name##*.}" \ # "$ws_name" \ # "$win_title" done | echo "${applist}" #echo "${applist_array[@]}" #printf '%s\n' "${applist_array[@]}" #echo "${applist_array[@]}" #applist=$(printf '%s\n' "${applist_array[@]}" | jq -s .) #echo ${applist} #sort -k3 -k2 #wofi --cache-file=/dev/null --insensitive --allow-images --show dmenu --prompt='Focus a window'