234 lines
9.2 KiB
Bash
Executable file
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
|