|
|
|
#!/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> or 'current' or
|
|
|
|
one of 'auto_show'/'auto_hide'/'toggle_auto_hide'
|
|
|
|
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 session (supported terminal required)
|
|
|
|
-n num num or extra text; only needed if for the purpose of sing
|
|
|
|
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).
|
|
|
|
Caution: if there is a tmux/tmuxinator 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)
|
|
|
|
--wm set the window manager name to mimic another window manager
|
|
|
|
(for use with -a)
|
|
|
|
--class manually specify the classname of the 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)
|
|
|
|
--help print help
|
|
|
|
|
|
|
|
See man page for more details.
|
|
|
|
"
|
|
|
|
if [[ $1 == illegal_opt ]]; then
|
|
|
|
exit 1
|
|
|
|
else
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# * 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
|
|
|
|
subtract_when_same=true
|
|
|
|
is_floating=
|
|
|
|
program_flags=
|
|
|
|
clearwid=false
|
|
|
|
cancel_auto_show=true
|
|
|
|
auto_detect_wm=false
|
|
|
|
monitor_aware=false
|
|
|
|
wm=
|
|
|
|
user_set_wm=false
|
|
|
|
class=
|
|
|
|
while getopts :h:w:x:y:s:n:c:C:l:L:p:P:u:U:d:f:-:am 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) program_flags=$OPTARG;;
|
|
|
|
a) auto_detect_wm=true;;
|
|
|
|
m) monitor_aware=true;;
|
|
|
|
-)
|
|
|
|
if [[ $OPTARG =~ ^(auto-detect-wm|monitor-aware|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) program_flags=$OPTARG;;
|
|
|
|
auto-detect-wm) auto_detect_wm=true;;
|
|
|
|
monitor-aware) monitor_aware=true;;
|
|
|
|
wm) wm=$OPTARG
|
|
|
|
user_set_wm=true;;
|
|
|
|
class) class=$OPTARG;;
|
|
|
|
clear) clearwid=true;;
|
|
|
|
no-cancel) cancel_auto_show=false;;
|
|
|
|
help) print_help;;
|
|
|
|
*) print_help illegal_opt;;
|
|
|
|
esac;;
|
|
|
|
*) print_help illegal_opt;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
shift "$((OPTIND-1))"
|
|
|
|
program=$1
|
|
|
|
|
|
|
|
if [[ $# -ne 1 ]]; then
|
|
|
|
echo >&2 "Exactly 1 positional argument is required." \
|
|
|
|
"For help use -h or --help or see the manpage." | \
|
|
|
|
tee -a "$MUTDROP_PATH"/log
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
# validate options that require number values
|
|
|
|
if [[ ! $height$width$xoff$yoff =~ ^[0-9%-]*$ ]]; then
|
|
|
|
echo >&2 "The -h, -w, -x, and -y values must be numbers (or percentages)." | \
|
|
|
|
tee -a "$MUTDROP_PATH"/log
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
if [[ -n $dec_fix ]] && [[ ! $dec_fix =~ ^-?[0-9]+x-?[0-9]+$ ]]; then
|
|
|
|
echo >&2 "The decoration fix value must have form 'num'x'num'." \
|
|
|
|
"The numbers can be negative or zero." | tee -a "$MUTDROP_PATH"/log
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
# non-user-settable global vars
|
|
|
|
wid=
|
|
|
|
# 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)
|
|
|
|
focused_window_exists=true
|
|
|
|
|
|
|
|
# * Multiple Monitor Automatic Re-Sizing
|
|
|
|
percent_of_total() { # percent total
|
|
|
|
awk "BEGIN {printf(\"%.0f\", 0.01*${1%\%}*$2)}"
|
|
|
|
}
|
|
|
|
|
|
|
|
# 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
|
|
|
|
}
|
|
|
|
|
|
|
|
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 wm current_monitor
|
|
|
|
wm=$(get_window_manager)
|
|
|
|
if [[ $wm == bspwm ]]; then
|
|
|
|
current_monitor=$(bspc query -T -m | grep -Po '^{"name":.*?",' | \
|
|
|
|
awk -F ":" '{gsub("[\",]", ""); print $2}')
|
|
|
|
elif [[ $wm == i3 ]]; then
|
|
|
|
# I'd rather not make jq a dependency
|
|
|
|
current_monitor=$(i3-msg -t get_workspaces | sed 's/{"num"/\n/g' | \
|
|
|
|
awk -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 --query | \
|
|
|
|
awk "/$current_monitor/ {gsub(\"primary \",\"\"); print \$3}")
|
|
|
|
x_begin=$(echo "$monitor_geo" | awk -F '+' '{print $2}')
|
|
|
|
y_begin=$(echo "$monitor_geo" | awk -F '+' '{print $3}')
|
|
|
|
x_width=$(echo "$monitor_geo" | awk -F 'x' '{print $1}')
|
|
|
|
y_height=$(echo "$monitor_geo" | awk -F 'x|+' '{print $2}')
|
|
|
|
else
|
|
|
|
local wid wininfo window_x window_y monitors_info x_end y_end
|
|
|
|
# determine current monitor in generic way
|
|
|
|
wid=$(xdotool getactivewindow)
|
|
|
|
if [[ -z $wid ]]; then
|
|
|
|
# will try again after remapping or creating the dropdown
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
wininfo=$(xwininfo -id "$wid")
|
|
|
|
window_x=$(echo "$wininfo" | awk '/Absolute.*X/ {print $4}')
|
|
|
|
window_y=$(echo "$wininfo" | awk '/Absolute.*Y/ {print $4}')
|
|
|
|
monitors_info=$(xrandr --query | awk '/ connected/ {gsub("primary ",""); print}')
|
|
|
|
while read -r monitor; do
|
|
|
|
monitor_geo=$(echo "$monitor" | awk '{print $3}')
|
|
|
|
if [[ $monitor_geo =~ ^[0-9]+x[0-9]+\+[0-9]+\+[0-9]+$ ]]; then
|
|
|
|
x_begin=$(echo "$monitor_geo" | awk -F '+' '{print $2}')
|
|
|
|
y_begin=$(echo "$monitor_geo" | awk -F '+' '{print $3}')
|
|
|
|
x_width=$(echo "$monitor_geo" | awk -F 'x' '{print $1}')
|
|
|
|
y_height=$(echo "$monitor_geo" | awk -F 'x|+' '{print $2}')
|
|
|
|
x_end=$((x_begin+x_width))
|
|
|
|
y_end=$((y_begin+y_height))
|
|
|
|
if [[ $window_x -ge $x_begin ]] && [[ $window_x -lt $x_end ]] && \
|
|
|
|
[[ $window_y -ge $y_begin ]] && [[ $window_y -lt $y_end ]]; then
|
|
|
|
current_monitor=$(echo "$monitor" | awk '{print $1}')
|
|
|
|
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
|
|
|
|
get_window_manager() {
|
|
|
|
# xfwm4 and fvwm at least will give two names (hence piping into head)
|
|
|
|
xprop -notype -id "$(xprop -root -notype | \
|
|
|
|
awk '$1=="_NET_SUPPORTING_WM_CHECK:"{print $5}')" \
|
|
|
|
-f _NET_WM_NAME 8u | awk -F "\"" '/WM_NAME/ {print $2}' | head -n 1
|
|
|
|
}
|
|
|
|
|
|
|
|
set_wm() {
|
|
|
|
if ! $user_set_wm && $auto_detect_wm; then
|
|
|
|
wm=$(get_window_manager)
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
decoration_settings() {
|
|
|
|
if [[ -z $subtract_when_same ]] && $auto_detect_wm; then
|
|
|
|
if [[ $wm =~ ^(Mutter|GNOME Shell|bspwm|i3|GoomwW)$ ]]; then
|
|
|
|
subtract_when_same=false
|
|
|
|
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 =~ ^google-chrome ]]; then
|
|
|
|
class=google-chrome
|
|
|
|
elif [[ $program == st ]]; then
|
|
|
|
class=st-256color
|
|
|
|
elif [[ $program == gnome-terminal ]]; then
|
|
|
|
class=Gnome-terminal
|
|
|
|
elif [[ $program == urxvtc ]]; 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 | awk 'gsub(/{"id"/, "\n{\"id\"")' | \
|
|
|
|
awk '/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() {
|
|
|
|
xprop -id "$1" WM_CLASS | awk '{gsub(/"/, ""); print $4}'
|
|
|
|
}
|
|
|
|
|
|
|
|
get_visibility() {
|
|
|
|
xwininfo -id "$1" 2> /dev/null | awk '/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
|
|
|
|
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 =~ ^(terminix|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 | awk '!/--type/ {print $1}')
|
|
|
|
elif [[ $program == chromium-browser ]]; then
|
|
|
|
pid=$(pgrep -xa chromium-browse | awk '!/--type/ {print $1}')
|
|
|
|
elif [[ $program =~ ^google-chrome ]]; then
|
|
|
|
pid=$(pgrep -xa chrome | awk '!/--type/ {print $1}')
|
|
|
|
fi
|
|
|
|
# need to redirect stdout or function won't return
|
|
|
|
eval "$1 > /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
|
|
|
|
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
|
|
|
|
wids=$(xdotool search --pid "$pid")
|
|
|
|
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
|
|
|
|
sleep 0.01
|
|
|
|
done
|
|
|
|
echo -n "$program_wid"
|
|
|
|
}
|
|
|
|
|
|
|
|
program_start() {
|
|
|
|
local program_command tmux_command wid
|
|
|
|
program_command="$program $program_flags"
|
|
|
|
if [[ -n "$session_name" ]]; then
|
|
|
|
session_name=$(printf "%q" "$session_name")
|
|
|
|
tmux_command="'tmux attach-session -dt $session_name || \
|
|
|
|
tmuxinator start $session_name || \
|
|
|
|
tmux new-session -s $session_name'"
|
|
|
|
if [[ $program =~ ^(urxvt|alacritty|xiatec$) ]]; then
|
|
|
|
program_command="$program_command -e bash -c $tmux_command"
|
|
|
|
elif [[ $program == terminix ]]; then
|
|
|
|
program_command="$program_command -x \"bash -c $tmux_command\""
|
|
|
|
else
|
|
|
|
program_command="$program_command -e \"bash -c $tmux_command\""
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
wid=$(create_win_return_wid "$program_command")
|
|
|
|
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
|
|
|
|
echo -n "$wid"
|
|
|
|
}
|
|
|
|
|
|
|
|
wid_toggle() {
|
|
|
|
# deal with percentages/negatives when no -m
|
|
|
|
if ! $monitor_aware; then
|
|
|
|
local total_geo total_width total_height
|
|
|
|
total_geo=$(xwininfo -root | awk '/geometry/ {print $2}')
|
|
|
|
total_width=$(echo "$total_geo" | awk -F 'x' '{print $1}')
|
|
|
|
total_height=$(echo "$total_geo" | awk -F 'x|+' '{print $2}')
|
|
|
|
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 ]] || [[ -z $(xprop -id "$wid" 2> /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 ]]; then
|
|
|
|
# visibility will be IsUnMapped on most WMs if the dropdown is open
|
|
|
|
# on another desktop
|
|
|
|
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"; then
|
|
|
|
local wininfo x y rel_x rel_y
|
|
|
|
wininfo=$(xwininfo -id "$1")
|
|
|
|
x=$(echo "$wininfo" | awk '/Absolute.*X/ {print $4}')
|
|
|
|
y=$(echo "$wininfo" | awk '/Absolute.*Y/ {print $4}')
|
|
|
|
rel_x=$(echo "$wininfo" | awk '/Relative.*X/ {print $4}')
|
|
|
|
rel_y=$(echo "$wininfo" | awk '/Relative.*Y/ {print $4}')
|
|
|
|
if $subtract_when_same; 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
|
|
|
|
echo -n -e "X=$x\nY=$y"
|
|
|
|
else
|
|
|
|
# window is not floating; don't bother saving geometry
|
|
|
|
echo -n "false"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
retrieve_geometry() {
|
|
|
|
eval "$(< "$MUTDROP_PATH"/auto_hidden/geometry)"
|
|
|
|
local x_fix y_fix X Y
|
|
|
|
if [[ -n $dec_fix ]]; then
|
|
|
|
x_fix=$(echo "$dec_fix" | awk -F "x" '{print $1}')
|
|
|
|
y_fix=$(echo "$dec_fix" | awk -F "x" '{print $2}')
|
|
|
|
X=$((X-x_fix))
|
|
|
|
Y=$((Y-y_fix))
|
|
|
|
fi
|
|
|
|
echo -n "$X $Y"
|
|
|
|
}
|
|
|
|
|
|
|
|
toggle_auto_hide() {
|
|
|
|
local no_hide
|
|
|
|
no_hide=$(< "$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=$(< "$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=$(< "$MUTDROP_PATH"/auto_hidden/no_hide) 2> /dev/null
|
|
|
|
if [[ -z $no_hide ]]; then
|
|
|
|
local was_floating XY
|
|
|
|
wid=$(< "$MUTDROP_PATH"/auto_hidden/wid)
|
|
|
|
class=$(< "$MUTDROP_PATH"/auto_hidden/class)
|
|
|
|
was_floating=$(< "$MUTDROP_PATH"/auto_hidden/geometry)
|
|
|
|
XY=$(retrieve_geometry "$wid")
|
|
|
|
xoff=$(echo "$XY" | awk '{print $1}')
|
|
|
|
yoff=$(echo "$XY" | awk '{print $2}')
|
|
|
|
pre_map "$was_floating"
|
|
|
|
map_and_post_map "$was_floating"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# * 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
|
|
|
|
else
|
|
|
|
wid_toggle
|
|
|
|
fi
|
|
|
|
|
|
|
|
# vim is dumb
|
|
|
|
# vim: set ft=sh noet:
|