294 lines
9.9 KiB
Plaintext
294 lines
9.9 KiB
Plaintext
|
#!/usr/bin/env bash
|
||
|
# https://gitlab.com/wef/dotfiles/-/blob/master/bin/sway-select-window
|
||
|
# shellcheck disable=SC2034
|
||
|
TIME_STAMP="20230129.084152"
|
||
|
|
||
|
# Copyright (C) 2019-2023 Bob Hepple <bob dot hepple at gmail dot com>
|
||
|
|
||
|
# This program is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or (at
|
||
|
# your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful, but
|
||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
# General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
# CUSTOMISE HERE IF YOU'RE NOT GETTING ALL YOUR ICONS:
|
||
|
|
||
|
# This array contains my most commonly used apps on f34, cached here
|
||
|
# for speed. If they don't work for you, you can either just remove
|
||
|
# these entries and rely on on-the-fly lookup of icons (which may be
|
||
|
# slow). Or, for speedier operation, you can:
|
||
|
|
||
|
# 1. get all your common apps running
|
||
|
# 2. remove the entries in the array
|
||
|
# 3. run the script from the CLI
|
||
|
# 4. copy-paste the list of "[app]=icon" lines into the array
|
||
|
|
||
|
# Also, if all else fails, you can hardcode your app here:
|
||
|
declare -A icons=(
|
||
|
[floating-kitty]=/usr/share/icons/hicolor/256x256/apps/kitty.png
|
||
|
[kitty]=/usr/share/icons/hicolor/256x256/apps/kitty.png
|
||
|
[foot]=/usr/share/icons/hicolor/48x48/apps/foot.png
|
||
|
[emacs]=/usr/share/icons/breeze/apps/48/emacs.svg
|
||
|
[firefox]=/usr/share/icons/hicolor/48x48/apps/firefox.png
|
||
|
[mythfrontend]=/usr/share/pixmaps/mythfrontend.png
|
||
|
[kitty]=/usr/share/icons/hicolor/256x256/apps/kitty.png
|
||
|
[Gdcalc]=/usr/share/pixmaps/HP-16C-48.xpm
|
||
|
[pavucontrol]=/usr/share/icons/gnome/48x48/apps/multimedia-volume-control.png
|
||
|
[pulseeffects]=/usr/share/icons/hicolor/scalable/apps/pulseeffects.svg
|
||
|
[blueman-manager]=/usr/share/icons/hicolor/scalable/apps/blueman.svg
|
||
|
[gnome-system-monitor]=/usr/share/icons/hicolor/scalable/apps/org.gnome.SystemMonitor.svg
|
||
|
[gnome-disks]=/usr/share/icons/hicolor/scalable/apps/org.gnome.DiskUtility.svg
|
||
|
[org.gnome.Weather]=/usr/share/icons/hicolor/scalable/apps/org.gnome.Weather.svg
|
||
|
[xfce4-appfinder]=/usr/share/icons/hicolor/scalable/apps/org.xfce.appfinder.svg
|
||
|
[LibreWolf]=/var/lib/flatpak/exports/share/icons/hicolor/128x128/apps/io.gitlab.librewolf-community.png
|
||
|
[firefox]=/var/lib/flatpak/exports/share/icons/hicolor/128x128/apps/org.mozilla.firefox.png
|
||
|
)
|
||
|
|
||
|
KEYBOARD_DEVICE="/dev/input/event4"
|
||
|
verbose=""
|
||
|
|
||
|
PROG=$( basename "$0" )
|
||
|
case "$1" in
|
||
|
-h|--help)
|
||
|
echo "Usage: $PROG"
|
||
|
echo
|
||
|
echo "show running programs and select one to display (uses wofi)."
|
||
|
|
||
|
echo "If evtest(1) is installed and sudo is available then you can press"
|
||
|
echo "Shift-Enter to move the window here rather than move to"
|
||
|
echo "the windows workspace. You may need to change KEYBOARD_DEVICE above."
|
||
|
|
||
|
exit 0
|
||
|
;;
|
||
|
-v|--verbose)
|
||
|
verbose="set"
|
||
|
shift
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
jq_get_windows='
|
||
|
# descend to workspace or scratchpad
|
||
|
.nodes[].nodes[]
|
||
|
# 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_fs_win_in_ws() {
|
||
|
id="${1:?}"
|
||
|
|
||
|
swaymsg -t get_tree |
|
||
|
jq -e -r --argjson id "$id" "
|
||
|
[ $jq_get_windows ]
|
||
|
| (.[]|select(.id == \$id)) as \$selected_workspace
|
||
|
| .[]
|
||
|
| select( .w == \$selected_workspace.w and .fullscreen_mode == 1 )
|
||
|
| $jq_windows_to_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" && return
|
||
|
done
|
||
|
|
||
|
# maybe lowercase
|
||
|
for prefix in "" org.kde. org.gnome. org.freedesktop.; do
|
||
|
d="$p/$prefix${app,,}.desktop"
|
||
|
[[ -r "$d" ]] && echo "$d" && 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" && 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" && return
|
||
|
}
|
||
|
|
||
|
get_icon() {
|
||
|
icon_locations=(
|
||
|
icons/hicolor/scalable/apps
|
||
|
icons/hicolor/48x48/apps
|
||
|
icons/hicolor/128x128/apps
|
||
|
icons/hicolor/256x256/apps
|
||
|
icons/gnome/48x48/apps
|
||
|
icons/gnome/48x48/devices
|
||
|
pixmaps
|
||
|
)
|
||
|
|
||
|
app="$1"
|
||
|
|
||
|
icon=$( get_hardcoded_icon "$app_name" )
|
||
|
[[ "$icon" && -r "$icon" ]] && echo "$icon" && 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" )
|
||
|
[[ "$icon_name" ]] && {
|
||
|
for d in "${icon_locations[@]}"; do
|
||
|
for s in .svg .png ""; do
|
||
|
icon=/usr/share/$d/$icon_name$s
|
||
|
[[ -r $icon ]] && echo "$icon" && return
|
||
|
done
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# desperation street:
|
||
|
icon=$( find /usr/share/icons /usr/share/pixmaps | grep -E -i "/$app.(png|svg)" | head -n 1 )
|
||
|
[[ "$icon" && -r "$icon" ]] && echo "$icon" && return
|
||
|
|
||
|
# failed:
|
||
|
#echo "/usr/share/icons/Adwaita/32x32/status/image-missing-symbolic.symbolic.png"
|
||
|
echo "$HOME/.local/share/icons/img-missing-sym.png"
|
||
|
}
|
||
|
|
||
|
focus_window() {
|
||
|
id="${1}"
|
||
|
ws_name="${2}"
|
||
|
app_name="${3}"
|
||
|
win_title="${4}"
|
||
|
|
||
|
[[ "$verbose" ]] && printf "focusing window (in workspace %s): [%s] (%s) \`%s\`\n" "$ws_name" "$id" "$app_name" "$win_title" >&2
|
||
|
|
||
|
# look for fullscreen among other windows in selected window's workspace
|
||
|
if fs_win_tsv="$( get_fs_win_in_ws "$id" )" ; then
|
||
|
read -r win_id ws_name app_name win_title <<<"$fs_win_tsv"
|
||
|
if [ "$win_id" != "$id" ] ; then
|
||
|
[[ "$verbose" ]] && printf 'found fullscreen window in target workspace (%s): [%s] (%s) "%s"\n' "$ws_name" "$win_id" "$app_name" "$win_title" >&2
|
||
|
swaymsg "[con_id=$win_id] fullscreen disable"
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
swaymsg "[con_id=$id]" focus
|
||
|
}
|
||
|
|
||
|
id_fmt='<span stretch="ultracondensed" size="xx-small">%s</span>'
|
||
|
ws_fmt='<span size="small">%s</span>'
|
||
|
app_fmt='<span weight="bold">%s</span>'
|
||
|
title_fmt='<span style="italic">%s</span>'
|
||
|
|
||
|
#id_fmt='%s'
|
||
|
#ws_fmt='%s'
|
||
|
#app_fmt='%s'
|
||
|
#title_fmt='%s'
|
||
|
|
||
|
# allow Alt+Tab etc to not be caught by sway:
|
||
|
trap 'swaymsg "mode default"' EXIT
|
||
|
swaymsg "mode passthrough"
|
||
|
|
||
|
swaymsg -t get_tree |
|
||
|
# get list of windows as tab-separated columns
|
||
|
jq -r "$jq_get_windows | $jq_windows_to_tsv" |
|
||
|
# align columns w/ spaces (keep tab as separator)
|
||
|
column -s $'\t' -o $'\t' -t |
|
||
|
# pango format the window list
|
||
|
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
|
||
|
# hide win_id for now
|
||
|
#win_id=""
|
||
|
# simplify ws_name instead of Scratch
|
||
|
#(( ${#ws_name} > 8 )) && ws_name="${ws_name:0:1}"
|
||
|
printf "img:$icon:text:${id_fmt}\t${ws_fmt}\t${app_fmt}\t${title_fmt}\n" \
|
||
|
"$win_id" \
|
||
|
"$ws_name" \
|
||
|
"$app_name" \
|
||
|
"$win_title"
|
||
|
done |
|
||
|
# sort by workspace name and then app_name:
|
||
|
sort -k2 -k3 |
|
||
|
# defeat the initial cache in order to preserve the order:
|
||
|
wofi -m --cache-file=/dev/null --insensitive --allow-images --show dmenu --prompt='Focus a window' | {
|
||
|
FS=$'\t' read -r win_id ws_name app_name win_title
|
||
|
# printf "win_id='$win_id' ws_name='$ws_name' app_name='$app_name' win_title='$win_title'" >&2
|
||
|
# use evtest (if installed) to detect the state of the left shift
|
||
|
# key and if it's 'down' then move the window here rather than move
|
||
|
# to the window's workspace. Should be safe if evtest or sudo are
|
||
|
# not available.
|
||
|
## i've disabled this
|
||
|
type evtest && sudo evtest --query $KEYBOARD_DEVICE EV_KEY KEY_LEFTSHIFT
|
||
|
SHIFT_STATE=$?
|
||
|
if [[ "$win_id" ]]; then
|
||
|
if (( SHIFT_STATE == 10 )); then
|
||
|
# bring the window here
|
||
|
swaymsg "[con_id=$win_id] move window to workspace current, focus"
|
||
|
else
|
||
|
focus_window "$win_id" "$ws_name" "$app_name" "$win_title"
|
||
|
fi
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
exit 0
|
||
|
|
||
|
# this is the example documented in 'man 7 wofi' but it doesn't understand the scratchpad:
|
||
|
swaymsg -t get_tree |
|
||
|
jq -r '.nodes[].nodes[] | if .nodes then [recurse(.nodes[])] else [] end + .floating_nodes | .[] | select(.nodes==[]) | ((.id | tostring) + " " + .name)' |
|
||
|
wofi --show dmenu | {
|
||
|
read -r id name
|
||
|
swaymsg "[con_id=$id]" focus
|
||
|
}
|
||
|
|
||
|
# Local Variables:
|
||
|
# mode: shell-script
|
||
|
# time-stamp-pattern: "4/TIME_STAMP=\"%:y%02m%02d.%02H%02M%02S\""
|
||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||
|
# End:
|