dotfiles/script/bin/pidswallow
Cedric Bonhomme 350610c45b Scripts
2024-01-16 13:23:58 +01:00

234 lines
9.2 KiB
Bash
Executable file

#!/bin/sh
usage="pidswallow (pid swallow WM/DE independent)
Hides terminal window automatically, so that you don't have to
pidswallow [OPTION ...]
OPTIONS:
-h --help Show this message
-s --swallow <CWID> Hides parent window of the given child window id.
-v --vomit <CWID> Unhides parent window of the given child window id.
-t --toggle <CWID> toggle between swallow and vomit. (default)
-g --glue treat if parent and child window are same. (recommended)
-l --loop listen and hide / unhide window on launch / remove.
-V --verbose Shows useful information.
bugs/issues: https://github.com/liupold/pidswallow.
"
shell=" ${PIDSWALLOW_SHELL:-" ${SHELL##*/} "} "
terminal=" ${PIDSWALLOW_TERMINAL:-" $TERMINAL "} "
blacklist=" ${PIDSWALLOW_BLACKLIST:-" $terminal "} "
glue_blacklist=" $PIDSWALLOW_GLUE_BLACKLIST "
swallow_command="${PIDSWALLOW_SWALLOW_COMMAND:-xdo hide \$pwid}"
vomit_command="${PIDSWALLOW_VOMIT_COMMAND:-xdo show \$pwid}"
preglue_hook="$PIDSWALLOW_PREGLUE_HOOK"
verbose=0
glue=0
vecho() {
[ "$verbose" -eq 1 ] && echo "$@" | paste -s -d ' '
}
vomit() {
unset cwid pwid x y w h # similar to local
cwid=$(printf '0x%x' "$1") # hexadecimal conversion
[ -f "/tmp/swallowed-by-$cwid" ] && read -r pwid < "/tmp/swallowed-by-$cwid"
[ -n "$pwid" ] || return 1
vecho "vomit - $cwid vomiting $pwid using $vomit_command"
echo "pwid=$pwid;cwid=$cwid;" "$vomit_command" | /bin/sh
vecho "vomit - $cwid vomited $pwid"
rm "/tmp/swallowed-by-$cwid"
if xwininfo -id "$cwid" >/dev/null 2>&1; then
xdo activate "$cwid" # if child still exists, re-focus it
else
xdo activate "$pwid"
fi
if [ "$glue" -eq 1 ] && [ "${glue_blacklist#* $cname}" = "$glue_blacklist" ];
then
[ -f "/tmp/swallowed-by-$cwid-structure" ] && {
read -r structure < "/tmp/swallowed-by-$cwid-structure"
structure="${structure#*\(}"; x="${structure%%,*}"
structure="${structure#*,}"; y="${structure%%)*}"
structure="${structure#*width }"; w="${structure%%,*}"
h="${structure#*height }"
xdo resize -w "$w" -h "$h" "$pwid"
xdo move -x "$x" -y "$y" "$pwid"
}
[ -f "/tmp/swallowed-by-$cwid-desk" ] && {
read -r desk < "/tmp/swallowed-by-$cwid-desk"
desk="${desk##* }"
xdotool set_desktop_for_window "$pwid" "$desk"
}
fi
rm -f "/tmp/swallowed-by-$cwid-structure" "/tmp/swallowed-by-$cwid-desk"
}
swallow() {
# return values
# 0 -> Found and swallowed
# 3 -> window lacks a _NET_WM_PID property.
# 4 -> window already swallowed / not possible
# 5 -> swallow_command failed
unset cwid pwid cname pname ppid cpid x y w h # similar to local.
cwid=$(printf '0x%x' "$1") # hexadecimal conversion
[ -f "/tmp/swallowed-by-$cwid" ] && return 4
cpid="$(xdo pid "$cwid")" || return 3
cname="$(ps -p "$cpid" -o comm=)"
[ "${blacklist#* $cname }" != "$blacklist" ] && return 0
process_tree="$(pstree -ATlsp "$cpid" \
| sed -n -e 's|(|:|g' -e 's|)||g' \
-e 's|---|\n|g' -e 's|-+-|\n|g' \
-e 's/\n'"$cname"':'"$cpid"'.*//' -e '1p' | tac)"
vecho "swallow - Scanning process tree of $cname($cpid): $process_tree"
for parent in $process_tree; do
pname="${parent%%:*}" # ancestor name
ppid="${parent##*:}"
if [ "${shell#* $pname }" != "$shell" ] && [ -f "/tmp/term-wid-$ppid" ]; then
vecho "swallow - $pname($ppid) detected as shell"
read -r pwid < "/tmp/term-wid-$ppid"
break
elif [ "${terminal#* $pname}" != "$terminal" ]; then
vecho "swallow - $pname($ppid) detected as terminal (fallback)"
pwid=$(xdo id -p "$ppid" | {
read -r temp; echo "$temp"
} )
break
fi
done
[ -z "$pwid" ] && return 4
if [ "$glue" -eq 1 ]; then
vecho "swallow - Running preglue hook: $preglue_hook"
echo "pwid=$pwid;cwid=$cwid;" "$preglue_hook" | /bin/sh
fi
vecho "swallow - $cwid swallowing $pwid using $swallow_command"
echo "pwid=$pwid;cwid=$cwid;" "$swallow_command" | /bin/sh || return 5
echo "$pwid" > "/tmp/swallowed-by-$cwid"
vecho "swallow - $cname ($cwid) swallowed $pname (or its terminal) ($pwid)"
[ "$glue" -ne 1 ] ||\
[ "${glue_blacklist#* $cname}" != "$glue_blacklist" ] && return 0
pgeo=$(xwininfo -shape -id "$pwid" | tr -d '\n ')
x="${pgeo##*Absoluteupper-leftX:}"; x="${x%%Absoluteupper-leftY:*}"
y="${pgeo##*Absoluteupper-leftY:}"; y="${y%%Relativeupper-leftX:*}"
w="${pgeo##*Width:}"; w="${w%%Height:*}"
h="${pgeo##*Height:}"; h="${h%%Depth:*}"
xdo resize -w "$w" -h "$h" "$cwid"
xdo move -x "$x" -y "$y" "$cwid"
echo "event ($x,$y), width $w, height $h" \
> "/tmp/swallowed-by-$cwid-structure"
xprop -notype -id "$cwid" _NET_WM_DESKTOP > "/tmp/swallowed-by-$cwid-desk"
# subscribe to window
{
xev -id "$cwid" -event structure -event property | while read -r event ; do
[ "${event#*DestroyNotify}" != "$event" ] && pkill -s 0 -f "xev.*$cwid"
[ "${event#* width }" != "$event" ] \
&& echo "$event" > "/tmp/swallowed-by-$cwid-structure"
[ "${event#*_NET_WM_DESKTOP}" != "$event" ] \
&& xprop -notype -id "$cwid" _NET_WM_DESKTOP \
> "/tmp/swallowed-by-$cwid-desk"
done
} &
}
toggle() {
cwid=$(printf '0x%x' "$1") # hexadecimal conversion
if [ -f "/tmp/swallowed-by-$cwid" ]; then
vomit "$cwid"
return "$?"
else
glue_="$glue" && \
glue=0 && swallow "$cwid" && glue="$glue_" # overwrite here.
return "$?"
fi
}
loop() {
prev_event="$(xprop -root _NET_CLIENT_LIST)"
xprop -spy -root _NET_CLIENT_LIST | while read -r event; do
unset node_added node_removed
for wid in $(echo "${event##*# }" | tr -d ','); do
[ "${prev_event#*$wid}" = "$prev_event" ] \
&& node_added="$wid" && break
done
for wid in $(echo "${prev_event##*# }" | tr -d ','); do
[ "${event#*$wid}" = "$event" ] \
&& node_removed="$wid" && break
done
prev_event="$event"
[ -n "$node_added" ] && swallow "$node_added"
[ -n "$node_removed" ] && {
xprop -id "$node_removed" >/dev/null 2>&1 || vomit "$node_removed"
}
done
return 1
}
[ -p /dev/stdin ] && eval set -- "$* $(cat /dev/stdin)" # basic pipe support
[ "$#" -eq 0 ] && echo "$usage" && exit 1
TEMP=$(getopt -o 'Vghlt:s:v:' --long 'verbose,glue,help,loop,toggle:,swallow:,vomit:' \
-n 'pidswallow' -- "$@")
# precheck
eval set -- "${TEMP}"
while true; do
case "$1" in
'-V'|'--verbose')
verbose=1
vecho "shell: \"$shell\""
vecho "terminal: \"$terminal\""
shift; continue;;
'-g'|'--glue')
glue=1
shift; continue;;
'--')
shift; break;;
*)
shift; continue;;
esac
done
eval set -- "${TEMP}"; unset TEMP
while true; do
case "$1" in
'-V'|'--verbose')
shift; continue;;
'-g'|'--glue')
shift; continue;;
'-h' | '--help' )
echo "$usage" && exit 0;;
'-t'|'--toggle')
toggle "$2" || exit "$?"
shift 2; continue;;
'-s'|'--swallow')
swallow "$2" || exit "$?"
shift 2; continue;;
'-v'|'--vomit')
vomit "$2" || exit "$?"
shift 2; continue;;
'-l'|'--loop')
loop || exit "$?"
shift; continue;;
'--')
shift; break;;
*)
echo "Internal error!" >&2
echo "$usage" && exit 1;;
esac
done
# toggle WID on first non-flag argument
[ -n "$*" ] && toggle "$1" && shift
# error if there's more than one non-flag argument
if [ -n "$*" ]; then
echo "Unrecognized trailing option '$*'" >&2; exit 1
fi