You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdrop-xtoolwait/tdrop

891 lines
26 KiB
Bash

#!/usr/bin/env bash
MUTDROP_PATH=/tmp/tdrop_"$USER"
# shellcheck disable=SC2174
mkdir -p "$MUTDROP_PATH" -m 700
print_help() {
echo "
usage: tdrop [options] <program> [program options ...]
or 'current'
or one of 'auto_show'/'auto_hide'/'toggle_auto_hide'
or 'hide_all'
options:
-h height specify a height for a newly created term (default: 45%)
-w width specify a width for a newly created term (default: 100%)
-x pos specify x offset for a newly created term (default: 0)
-y pos specify y offset for a newly created term (default: 1, see man)
-s name name for tmux/tmuxinator/tmuxifier session (supported
terminal required)
-n num num or extra text; only needed if for the purpose of using
multiple dropdowns of same program
-c cmd provide a pre-create command
-C cmd provide a post-create command
-l cmd provide a command to float the window before it is mapped
-L cmd provide a command to float the window after it is mapped
-p cmd provide a pre-map command
-P cmd provide a post-map command
-u cmd provide a pre-unmap command
-U cmd provide a post-unmap command
-d XxY give decoration/border size to accurately restore window
position; only applicable with auto_show
-S cmd can be used to fix saved geometry with auto_hide; see manpage
-i cmd provide a command to detect whether the current window is a
floating window; on applicable with auto_hide
-f flags specify flags/options to be used when creating the term or
window (e.g. -f '--title mytitle'; default: none).
NOTE: This flag is deprecated. Specify flags after the program name
instead. This flag may be removed in the future.
Caution: if there is a tmux session specified (with -s), the option
to execute a program (usually -e for terminal programs) is
implicitly added by tdrop
-a automatically detect window manager and set relevant options
(e.g. this makes specifying -l/-L, -d, and -i uneccessary
for supported WMs) (default: false)
-m for use with multiple monitors and only with dropdowns
(i.e. not for auto_show or auto_hide); convert percentages used
for width or height to values relative to the size of the
current monitor and force resizing of the dropdown when
the monitor changes (default: false)
-t use mouse pointer location for detecting which monitor is the current
one
--wm set the window manager name to mimic another window manager
(for use with -a)
--class name manually specify the class of the window (can be obtained with xprop)
--name name set a new name for the dropdown window
--clear clear saved window id; useful after accidentally make a
window a dropdown (e.g. '$ tdrop --clear current')
--no-cancel don't cancel auto-showing (default is to prevent this when
manually toggling a window after it is auto-hidden)
--timeout set the timeout (in seconds) that tdrop will wait for a window
to appear before giving up in case the program fails to start
(default: 10)
--help print help
See man page for more details.
"
}
error() {
echo >&2 "$@" | tee -a "$MUTDROP_PATH"/log
exit 1
}
# * Default Options and Option Parsing
# xdotool can take percentages; cannot take decimal percentages though
width="100%"
height="45%"
xoff=0
yoff=2
session_name=
num=
pre_create=
post_create=
pre_float=
post_float=
pre_map=
post_map=
pre_unmap=
post_unmap=
dec_fix=
# NOTE:
# pekwm, xfwm4, sawfish, openbox need subtract_when_same to be true
# for awesome, fluxbox, blackbox, mutter, fvwm, and metacity, the value
# does not matter
# set in decoration_settings
subtract_when_same=
is_floating=
program_flags=()
clearwid=false
cancel_auto_show=true
auto_detect_wm=false
monitor_aware=false
pointer_monitor_detection=false
wm=
user_set_wm=false
class=
name=
timeout=10
while getopts :h:w:x:y:s:n:c:C:l:L:p:P:u:U:d:S:i:f:-:amt opt
do
case $opt in
h) height=$OPTARG;;
w) width=$OPTARG;;
x) xoff=$OPTARG;;
y) yoff=$OPTARG;;
s) session_name=$OPTARG;;
n) num=$OPTARG;;
c) pre_create=$OPTARG;;
C) post_create=$OPTARG;;
l) pre_float=$OPTARG;;
L) post_float=$OPTARG;;
p) pre_map=$OPTARG;;
P) post_map=$OPTARG;;
u) pre_unmap=$OPTARG;;
U) post_unmap=$OPTARG;;
d) dec_fix=$OPTARG;;
S) subtract_when_same=false;;
i) is_floating=$OPTARG;;
f) eval "program_flags=($OPTARG)";;
a) auto_detect_wm=true;;
m) monitor_aware=true;;
t) pointer_monitor_detection=true;;
-)
if [[ $OPTARG =~ ^(auto-detect-wm|monitor-aware|pointer-monitor-detection|clear|no-cancel|help)$ ]] || \
[[ $OPTARG == *=* ]]; then
OPTION=${OPTARG%%=*}
OPTARG=${OPTARG#*=}
else
OPTION=$OPTARG
# shellcheck disable=SC2124
OPTARG=${@:$OPTIND:1}
((OPTIND++))
fi
case $OPTION in
height) height=$OPTARG;;
width) width=$OPTARG;;
x-offset) xoff=$OPTARG;;
y-offset) yoff=$OPTARG;;
session) session_name=$OPTARG;;
number) num=$OPTARG;;
pre-create-hook) pre_create=$OPTARG;;
post-create-hook) post_create=$OPTARG;;
pre-map-float-command) pre_float=$OPTARG;;
post-map-float-command) post_float=$OPTARG;;
pre-map-hook) pre_map=$OPTARG;;
post-map-hook) post_map=$OPTARG;;
pre-unmap-hook) pre_unmap=$OPTARG;;
post-unmap-hook) post_unmap=$OPTARG;;
decoration-fix) dec_fix=$OPTARG;;
no-subtract-when-same) subtract_when_same=false;;
is-floating) is_floating=$OPTARG;;
program-flags) eval "program_flags=($OPTARG)";;
auto-detect-wm) auto_detect_wm=true;;
monitor-aware) monitor_aware=true;;
pointer-monitor-detection) pointer_monitor_detection=true;;
wm) wm=$OPTARG
user_set_wm=true;;
class) class=$OPTARG;;
name) name=$OPTARG;;
clear) clearwid=true;;
no-cancel) cancel_auto_show=false;;
timeout) timeout=$OPTARG;;
help) print_help;;
*) error "Unknown option --$OPTION." \
"Use --help to see available flags.";;
esac;;
*) error "Unknown option -$OPTARG." \
"Use --help to see available flags.";;
esac
done
shift "$((OPTIND-1))"
program=$1
if [[ ${#program_flags[@]} -eq 0 ]]; then
program_flags=("${@:2}")
fi
if [[ -z $program ]]; then
error "Program to run is required as a positional argument." \
"For help use -h or --help or see the manpage."
fi
# check that the program is in PATH
if [[ ! $program =~ ^(current|auto_hide|auto_show|toggle_auto_hide|hide_all)$ ]] && \
! type "$program" &> /dev/null; then
error "The program should be in PATH."
fi
# validate options that require number values
if [[ ! $height$width$xoff$yoff =~ ^[0-9%-]*$ ]]; then
error "The -h, -w, -x, and -y values must be numbers (or percentages)."
fi
if [[ -n $dec_fix ]] && [[ ! $dec_fix =~ ^-?[0-9]+x-?[0-9]+$ ]]; then
error "The decoration fix value must have form 'num'x'num'." \
"The numbers can be negative or zero."
fi
# non-user-settable global vars
wid=
# * Multiple Monitor Automatic Re-Sizing
percent_of_total() { # percent total
# gawk "BEGIN {printf(\"%.0f\", 0.01*${1%\%}*$2)}"
echo $((${1%\%} * ${2} / 100))
}
# acts on globals
convert_geometry_to_pixels() {
total_width=$1
total_height=$2
local minus_width minus_height minus_xoff minus_yoff
if [[ $width =~ %$ ]]; then
width=$(percent_of_total "$width" "$total_width")
elif [[ $width =~ ^- ]]; then
minus_width=${width#-}
width=$((total_width-minus_width))
fi
if [[ $height =~ %$ ]]; then
height=$(percent_of_total "$height" "$total_height")
elif [[ $height =~ ^- ]]; then
minus_height=${height#-}
height=$((total_height-minus_height))
fi
if [[ $xoff =~ %$ ]]; then
xoff=$(percent_of_total "$xoff" "$total_width")
elif [[ $xoff =~ ^- ]]; then
minus_xoff=${xoff#-}
xoff=$((total_width-minus_xoff))
fi
if [[ $yoff =~ %$ ]]; then
yoff=$(percent_of_total "$yoff" "$total_height")
elif [[ $yoff =~ ^- ]]; then
minus_yoff=${yoff#-}
yoff=$((total_height-minus_yoff))
fi
}
# meant to set non-local variables
split_geometry() { # <monitor geometry>
monitor_geo=$1
# x_begin=$(echo "$monitor_geo" | gawk -F '+' '{print $2}')
x_begin=${monitor_geo#*+}
x_begin=${x_begin%+*}
# y_begin=$(echo "$monitor_geo" | gawk -F '+' '{print $3}')
y_begin=${monitor_geo##*+}
# x_width=$(echo "$monitor_geo" | gawk -F 'x' '{print $1}')
x_width=${monitor_geo%x*}
# y_height=$(echo "$monitor_geo" | gawk -F 'x|+' '{print $2}')
y_height=${monitor_geo#*x}
y_height=${y_height%%+*}
}
update_geometry_settings_for_monitor() {
# 1. Correctly interpret width/height percentages when there exist multiple
# monitors so an initially created dropdown is the correct size (xdotool
# would create a dropdown the width of all screens for 100% width)
# 2. Force resize the dropdown to the correct percentage of the current
# monitor IF the monitor has changed since the last time the dropdown
# was used
# it is conceivable that a user may want to use -m but not -a, so
# get the wm from within this function
local current_monitor
if [[ $wm == bspwm ]]; then
current_monitor=$(bspc query --names --monitors --monitor)
elif [[ $wm == i3 ]]; then
# TODO use jq if installed
# I'd rather not make jq a dependency
current_monitor=$(i3-msg -t get_workspaces | sed 's/{"num"/\n/g' | \
gawk -F ',' '/focused":true/ {sub(".*output",""); gsub("[:\"]",""); print $1}')
fi
local monitor_geo x_begin y_begin x_width y_height
if [[ -n $current_monitor ]]; then
monitor_geo=$(xrandr --current | \
gawk "/^$current_monitor/ {gsub(\"primary \",\"\"); print \$3}")
split_geometry "$monitor_geo"
else
local current_x current_y monitors_info x_end y_end
if ! $pointer_monitor_detection; then
# determine current monitor using active window
local wid wininfo
wid=$(xdotool getactivewindow)
if [[ -z $wid ]]; then
# will try again after remapping or creating the dropdown
return 1
fi
wininfo=$(xwininfo -id "$wid")
current_x=$(echo "$wininfo" | gawk '/Absolute.*X/ {print $4}')
current_y=$(echo "$wininfo" | gawk '/Absolute.*Y/ {print $4}')
else
# shellcheck disable=SC2034
local X Y SCREEN WINDOW
# determine current monitor using pointer location
eval "$(xdotool getmouselocation --shell)"
current_x=X
current_y=Y
fi
monitors_info=$(xrandr --current | gawk '/ connected/ {gsub("primary ",""); print}')
while read -r monitor; do
monitor_geo=$(echo "$monitor" | gawk '{print $3}')
if [[ $monitor_geo =~ ^[0-9]+x[0-9]+\+[0-9]+\+[0-9]+$ ]]; then
split_geometry "$monitor_geo"
x_end=$((x_begin+x_width))
y_end=$((y_begin+y_height))
if [[ $current_x -ge $x_begin ]] && [[ $current_x -lt $x_end ]] && \
[[ $current_y -ge $y_begin ]] && [[ $current_y -lt $y_end ]]; then
# current_monitor=$(echo "$monitor" | gawk '{print $1}')
current_monitor=${monitor%% *}
break
fi
fi
done <<< "$monitors_info"
fi
# convert w/h/x/y percentages/negatives to pixels
convert_geometry_to_pixels "$x_width" "$y_height"
# update x and y offsets, so that will appear on correct screen
# (required for some WMs apparently, but not for others)
((xoff+=x_begin))
((yoff+=y_begin))
}
map_and_reset_geometry() {
xdotool windowmap "$wid" windowmove "$wid" "$xoff" "$yoff" \
windowsize "$wid" "$width" "$height" 2> /dev/null
}
# * WM Detection and Hooks
set_wm() {
if ! $user_set_wm && $auto_detect_wm; then
local id
id=$(xprop -root -notype _NET_SUPPORTING_WM_CHECK)
id=${id##* }
# xfwm4 and fvwm at least will give two names (hence piping into head)
wm=$(xprop -notype -id "$id" _NET_WM_NAME | head -n 1)
wm=${wm##* }
wm=${wm//\"/}
fi
}
decoration_settings() {
if [[ -z $subtract_when_same ]]; then
if $auto_detect_wm \
&& [[ $wm =~ ^(Mutter|GNOME Shell|bspwm|i3|GoomwW)$ ]]; then
subtract_when_same=false
else
subtract_when_same=true
fi
fi
if [[ -z $dec_fix ]] && $auto_detect_wm; then
# settings for stacking/floating wms where can't get right position
# easily from xwininfo; take borders into account
if [[ $wm == Blackbox ]]; then
dec_fix="1x22"
elif [[ $wm =~ ^(Mutter|GNOME Shell)$ ]]; then
dec_fix="-10x-8"
elif [[ $wm =~ ^(Mutter \(Muffin\))$ ]]; then
dec_fix="-9x-8"
fi
fi
}
set_class() {
if [[ -z $class ]]; then
if [[ $program =~ ^emacsclient ]]; then
class=emacs
elif [[ $program =~ ^google-chrome ]]; then
class=google-chrome
elif [[ $program == st ]]; then
class=st-256color
elif [[ $program == gnome-terminal ]]; then
class=Gnome-terminal
elif [[ $program =~ ^urxvt.* ]]; then
class=urxvt
elif [[ $program == xiatec ]]; then
class=xiate
elif [[ $program == alacritty ]]; then
class=Alacritty
elif [[ $program == current ]]; then
class=$(cat "$MUTDROP_PATH"/current"$num"_class 2> /dev/null)
else
class=$program
fi
fi
}
is_floating() {
if [[ -n $is_floating ]]; then
eval "$is_floating $1"
elif $auto_detect_wm; then
if [[ $wm == i3 ]]; then
# TODO make sure this returns 1 on failure
i3-msg -t get_tree | gawk 'gsub(/{"id"/, "\n{\"id\"")' | \
gawk '/focused":true.*floating":"user_on/ {print $1}'
elif [[ $wm == bspwm ]]; then
bspc query -T -n | grep '"state":"floating"'
else
return 0
fi
else
return 0
fi
}
pre_float() {
if [[ $wm == bspwm ]]; then
# newest (using "instance" names)
if [[ $class =~ [A-Z] ]]; then
bspc rule -a "$class" -o state=floating
else
bspc rule -a \*:"$class" -o state=floating
fi
fi
}
post_float() {
if [[ $wm == awesome ]]; then
echo 'local awful = require("awful") ; awful.client.floating.set(c, true)' | \
awesome-client
elif [[ $wm == i3 ]]; then
i3-msg "[id=$wid] floating enable" > /dev/null
elif [[ $wm == herbstluftwm ]]; then
herbstclient fullscreen on
fi
}
pre_create() {
if [[ -n $pre_create ]]; then
eval "$pre_create"
fi
}
post_create() {
if [[ -n $post_create ]]; then
eval "$post_create"
fi
}
pre_map() {
float=${1:-true}
if [[ $float != false ]]; then
if [[ -n $pre_float ]]; then
eval "$pre_float"
elif $auto_detect_wm; then
pre_float
fi
fi
if [[ -n $pre_map ]]; then
eval "$pre_map"
fi
}
map_and_post_map() {
# always reset geometry
map_and_reset_geometry
float=${1:-true}
if [[ $float != false ]]; then
if [[ -n $post_float ]]; then
eval "$post_float"
elif $auto_detect_wm; then
post_float
fi
fi
# need to set geometry again if wasn't previously floating
map_and_reset_geometry
if [[ -n $post_map ]]; then
eval "$post_map"
fi
}
pre_unmap() {
if [[ -n $pre_unmap ]]; then
eval "$pre_unmap"
fi
}
post_unmap() {
if [[ -n $post_unmap ]]; then
eval "$post_unmap"
fi
}
unmap() {
hide=$1
pre_unmap
xdotool windowunmap "$wid"
if [[ -z $hide ]]; then
if [[ $wm == herbstluftwm ]]; then
# TODO should only happen if wasn't previously fullscreen
herbstclient fullscreen off
fi
fi
post_unmap
}
# Old notes:
# floating WMs that may move a window after remapping it
# pekwm|Fluxbox|Blackbox|xfwm4|Metacity|FVWM|Sawfish|GoomwW|Mutter|GNOME Shell|Mutter \(Muffin\)|KWin|Metacity \(Marco\)|[Cc]ompiz|bspwm
# floating WMs that may both move and resize a window after remapping it
# Openbox
# * General Helper Functions
get_class_name() {
local class
class=$(xprop -id "$1" WM_CLASS 2> /dev/null)
class=${class##* }
class=${class//\"/}
echo "$class"
}
get_visibility() {
xwininfo -id "$1" 2> /dev/null | gawk '/Map State/ {print $3}'
}
maybe_cancel_auto_show() {
if $cancel_auto_show && \
[[ $1 == $(cat "$MUTDROP_PATH"/auto_hidden/wid 2> /dev/null) ]]; then
# shellcheck disable=SC2188
> "$MUTDROP_PATH"/auto_hidden/wid
fi
}
# * Dropdown Initialization
# TODO ideally this function wouldn't be necessary and some external program
# (something like xtoolwait) could be used to return the wid
create_win_return_wid() {
local blacklist program_command pid visible_wid wids wid program_wid
# blacklist all existing wids of program
# (for programs where one pid shares all wids)
blacklist=$(xdotool search --classname "$program")
# for programs where $! won't always work (e.g. one pid for all windows)
if [[ $program =~ ^(tilix|xfce4-terminal)$ ]]; then
pid=$(pgrep -x "$program")
elif [[ $program == urxvtc ]]; then
blacklist=$(xdotool search --classname urxvtd)
pid=$(pgrep -x urxvtd)
elif [[ $program == xiatec ]]; then
pid=$(pgrep -x xiate)
elif [[ $program == chromium ]]; then
# this may work fine
# pid=$(pgrep -xo chromium)
pid=$(pgrep -xa chromium | gawk '!/--type/ {print $1}')
elif [[ $program == chromium-browser ]]; then
pid=$(pgrep -xa chromium-browse | gawk '!/--type/ {print $1}')
elif [[ $program =~ ^google-chrome ]]; then
pid=$(pgrep -xa chrome | gawk '!/--type/ {print $1}')
elif [[ $program =~ ^emacsclient ]]; then
blacklist=$(xdotool search --classname emacs)
fi
# need to redirect stdout or function won't return
"$@" > /dev/null &
if [[ -z $pid ]]; then
# for normal programs
# also for when one of the programs above hadn't already been started
pid=$!
fi
visible_wid=false
counter=0
while : ; do
if [[ $program == gnome-terminal ]]; then
# only 1 pid; changes at some point after first run
# actual process name for me is gnome-terminal-
pid=$(pgrep gnome-terminal)
fi
if [[ $program == discord ]]; then
wids=$(xdotool search --classname discord)
blacklist=
elif [[ $program == qutebrowser ]]; then
# one pid, but can't use for getting wids with xdotool
wids=$(xdotool search --classname qutebrowser)
elif [[ $program =~ ^emacsclient ]]; then
wids=$(xdotool search --classname emacs)
else
wids=$(xdotool search --pid "$pid")
fi
if [[ -n $wids ]]; then
while read -r wid; do
if [[ ! $blacklist =~ (^|$'\n')$wid($|$'\n') ]] && \
[[ $(get_visibility "$wid") == IsViewable ]]; then
visible_wid=true
program_wid=$wid
fi
done <<< "$wids"
fi
if $visible_wid; then
break
fi
((counter=counter+1))
if [[ $counter -gt $((timeout * 100)) ]]; then
error "Exceeded timeout of $timeout seconds waiting for program."
fi
sleep 0.01
done
# workaround for urxvt tabbed plugin using -embed
if [[ $program =~ urxvt ]] && [[ -n $program_wid ]]; then
maybe_program_wid=$(xprop -id "$program_wid" | \
gawk -F '"' '/-embed/ {print $6}')
if [[ -n $maybe_program_wid ]]; then
program_wid=$maybe_program_wid
fi
fi
echo -n "$program_wid"
}
program_start() {
local program_command tmux_command wid
program_command=("$program")
if [[ $program == alacritty ]]; then
# prevent alacritty from resizing the terminal to 80x24
program_command+=(-d 0 0)
fi
program_command+=("${program_flags[@]}")
if [[ -n $session_name ]]; then
session_name=$(printf "%q" "$session_name")
tmux_command="tmux attach-session -dt $session_name || \
tmuxifier load-session $session_name || \
tmuxinator start $session_name || \
tmux new-session -s $session_name"
# note: st will work with or without the -e flag (like kitty)
# note: regular console works with or without quotes, but trinity's
# konsole only works without quotes
if [[ $program =~ ^(urxvt|alacritty|xiatec|st|lxterminal|qterminal|cool-retro-term|lilyterm|konsole$) ]]; then
program_command+=(-e bash -c "$tmux_command")
elif [[ $program == kitty ]]; then
program_command+=(bash -c "$tmux_command")
else
program_command+=(-e "\"bash -c $tmux_command\"")
fi
fi
wid=$(create_win_return_wid "${program_command[@]}")
if [[ -n $name ]]; then
xdotool set_window --name "$name" "$wid"
fi
echo "$wid" > "$MUTDROP_PATH/$program$num"
echo -n "$wid"
}
current_create() {
# turns active window into a dropdown
local wid
wid=$(xdotool getactivewindow)
echo "$wid" > "$MUTDROP_PATH"/current"$num"
get_class_name "$wid" > "$MUTDROP_PATH"/current"$num"_class
if [[ -n $name ]]; then
xdotool set_window --name "$name" "$wid"
fi
echo -n "$wid"
}
wid_toggle() {
# used for -m option; at first tdrop assumes that there is a focused window
# on the current desktop; if there isn't (and the WM doesn't have some way
# to query the current monitor), this will be set to false, and tdrop will
# have to find out the current monitor info after opening the dropdown
# (currently, using xwininfo to find the position of a window is the only
# WM-independent way I know to find out what the current monitor is)
local focused_window_exists
focused_window_exists=true
# deal with percentages/negatives when no -m
if ! $monitor_aware; then
local total_geo total_width total_height
total_geo=$(xwininfo -root | gawk '/geometry/ {gsub("+*",""); print $2}')
# total_width=$(echo "$total_geo" | gawk -F 'x' '{print $1}')
total_width=${total_geo%x*}
# total_height=$(echo "$total_geo" | gawk -F 'x' '{print $2}')
total_height=${total_geo#*x}
convert_geometry_to_pixels "$total_width" "$total_height"
fi
# get saved window id if already created
local exists visibility
# cat to silence error
wid=$(cat "$MUTDROP_PATH/$program$num" 2> /dev/null)
exists=true
if [[ -n $wid ]]; then
visibility=$(get_visibility "$wid")
# sometimes xwininfo will still report a window as existing hence xprop check
if [[ -z $visibility ]] || ! xprop -id "$wid" &> /dev/null; then
# window no longer exists
exists=false
# shellcheck disable=SC2188
> "$MUTDROP_PATH/$program$num"
fi
else
exists=false
fi
if $exists; then
if [[ $visibility =~ ^(IsUnMapped|IsUnviewable)$ ]]; then
# visibility will be IsUnMapped on most WMs if the dropdown is open
# on another desktop; may also be IsUnviewable
xdotool set_desktop_for_window "$wid" "$(xdotool get_desktop)"
if [[ $(get_visibility "$wid") == IsUnMapped ]]; then
pre_map
else
xdotool windowactivate "$wid"
fi
# update here if possible so this doesn't cause a delay
# between the window being remapped and resized
if $monitor_aware; then
update_geometry_settings_for_monitor || \
focused_window_exists=false
fi
map_and_post_map
# cancel auto-show for a window when manually remapping it
maybe_cancel_auto_show "$wid"
if ! $focused_window_exists; then
# need to use dropdown as active window to get monitor info
update_geometry_settings_for_monitor
# always resize/move; if monitor hasn't changed then it won't be
# necessary, but it won't cause problems either
map_and_reset_geometry
fi
else
unmap
fi
else
# necessary to deal with negative width or height
# if creating on an empty desktop and can't determine the monitor,
# must set temporary values for negative width and/or height
local original_width original_height
if $monitor_aware && ! update_geometry_settings_for_monitor; then
focused_window_exists=false
if [[ $width =~ ^- ]]; then
original_width=$width
width=100%
fi
if [[ $height =~ ^- ]]; then
original_height=$height
height=100%
fi
fi
# make it
pre_create
if [[ $program == current ]]; then
wid=$(current_create)
unmap
else
pre_map
wid=$(program_start)
map_and_post_map
# update window dimensions if necessary
if ! $focused_window_exists; then
width=${original_width:-$width}
height=${original_height:-$height}
update_geometry_settings_for_monitor
map_and_reset_geometry
fi
fi
post_create
fi
}
# * Helper Functions for Auto Hiding/Showing
get_geometry() {
# so that won't float a tiled window later when showing
if is_floating "$1" &> /dev/null; then
local wininfo x y rel_x rel_y width height
wininfo=$(xwininfo -id "$1")
x=$(echo "$wininfo" | gawk '/Absolute.*X/ {print $4}')
y=$(echo "$wininfo" | gawk '/Absolute.*Y/ {print $4}')
rel_x=$(echo "$wininfo" | gawk '/Relative.*X/ {print $4}')
rel_y=$(echo "$wininfo" | gawk '/Relative.*Y/ {print $4}')
if [[ $subtract_when_same != false ]]; then
# behaviour works for most WMs (at least floating ones)
x=$((x-rel_x))
y=$((y-rel_y))
else
# don't subtract when abs and rel values are the same
# necessary for WMs like bspwm and i3
if [[ $x -ne $rel_x ]]; then
x=$((x-rel_x))
fi
if [[ $y -ne $rel_y ]]; then
y=$((y-rel_y))
fi
fi
width=$(xwininfo -id "$(xdotool getactivewindow)" | \
gawk '/Width/ {print $2}')
height=$(xwininfo -id "$(xdotool getactivewindow)" | \
gawk '/Height/ {print $2}')
echo -n -e "xoff=$x\nyoff=$y\nwidth=$width\nheight=$height"
else
# window is not floating; don't bother saving geometry
echo -n "false"
fi
}
# set global xoff, yoff, width, and height based on stored values
restore_geometry() {
local x_fix y_fix
eval "$(< "$MUTDROP_PATH"/auto_hidden/geometry)"
if [[ -n $dec_fix ]]; then
x_fix=$(echo "$dec_fix" | gawk -F "x" '{print $1}')
y_fix=$(echo "$dec_fix" | gawk -F "x" '{print $2}')
xoff=$((xoff-x_fix))
yoff=$((yoff-y_fix))
fi
}
toggle_auto_hide() {
local no_hide
no_hide=$(cat "$MUTDROP_PATH"/auto_hidden/no_hide 2> /dev/null)
mkdir -p "$MUTDROP_PATH"/auto_hidden
if [[ -z $no_hide ]]; then
echo "true" > "$MUTDROP_PATH"/auto_hidden/no_hide
else
# shellcheck disable=SC2188
> "$MUTDROP_PATH"/auto_hidden/no_hide
fi
}
# * Auto Hiding/Showing
auto_hide() {
local no_hide
no_hide=$(cat "$MUTDROP_PATH"/auto_hidden/no_hide 2> /dev/null)
if [[ -z $no_hide ]]; then
wid=$(xdotool getactivewindow)
mkdir -p "$MUTDROP_PATH"/auto_hidden
echo "$wid" > "$MUTDROP_PATH"/auto_hidden/wid
get_class_name "$wid" > "$MUTDROP_PATH"/auto_hidden/class
get_geometry "$wid" > "$MUTDROP_PATH"/auto_hidden/geometry
unmap hide
fi
}
auto_show() {
local no_hide
no_hide=$(cat "$MUTDROP_PATH"/auto_hidden/no_hide 2> /dev/null)
if [[ -z $no_hide ]]; then
local was_floating
wid=$(< "$MUTDROP_PATH"/auto_hidden/wid)
class=$(< "$MUTDROP_PATH"/auto_hidden/class)
was_floating=$(< "$MUTDROP_PATH"/auto_hidden/geometry)
restore_geometry "$wid"
pre_map "$was_floating"
map_and_post_map "$was_floating"
fi
}
# * Hide All
hide_all() {
shopt -s nullglob dotglob
local dropdowns
dropdowns=("$MUTDROP_PATH"/*)
for dropdown in "${dropdowns[@]}"; do
# cat to silence errors
wid=$(cat "$dropdown" 2> /dev/null)
unmap "$wid" 2> /dev/null
done
shopt -u nullglob dotglob
}
# * Main
# ** Setup
set_wm
decoration_settings
set_class
# ** Primary Action
if $clearwid; then
# shellcheck disable=SC2188
> "$MUTDROP_PATH/$program$num"
elif [[ $program == toggle_auto_hide ]]; then
toggle_auto_hide
elif [[ $program == auto_hide ]]; then
auto_hide
elif [[ $program == auto_show ]]; then
auto_show
elif [[ $program == hide_all ]]; then
hide_all
else
wid_toggle
fi
# vim is dumb
# vim: set ft=sh noet: