#!/bin/sh
# platform ... devuan dash
# GPL_3+
cat << 'EEE' > /dev/null
/* w_resize.sh ... window resize helper. bourne-shell script.
 * Copyright (C) 2018 Momi-g
 *
 * 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/>.
 */
EEE

#optcheck--------
#	cmd=$(cat << 'END'
#	-h	0 bool
#	END
#	)
#	buf=`ckopt "$cmd"`
#	eval "$buf"



## ---this code is generated by ckopt
#	---optsetting
#-h 0 bool

ckopt_func () { 
if [ "$1" = "-d" ] ; then
	eval "opt_${2#?}"'="$3"'		# opt_?="$3"
	return 0
fi

ckopt_opt="$2"
if [ "$1" = "-e" ] ; then
shift 4
while [ $# -gt 0 ]
do
if [ "$1" != "${1#[#]e}" ] || [ "$1" != "${1#[#]E}" ] ; then
	eval "${1#[#]?}"
	if [ "$?" != "0" ] ; then
		OPTARG="errmsg $ckopt_opt $1"
		return 1
	fi
fi
shift
done
return 0
fi

if [ "$1" = "-c" ] ; then
shift 3
printf '%s\n' "$1" | tr '[:upper:]' '[:lower:]' | awk '$1 ~ /int/ {exit 1}'
	if [ "$?" = "1" ] ; then
		shift
		set -- "dummy" 'printf "%d" "$OPTARG" >/dev/null 2>&1' "$@"
	fi
shift
while [ $# -gt 0 ]
do
	eval "$1"
	if [ "$?" != "0" ] ; then
		OPTARG="errmsg $ckopt_opt $1"
		return 1
	fi
shift
done
return 0
fi

echo "$0: bug. sleep" >/dev/stderr
while :
do
	sleep 1000
done
exit 1
}

# ---dflset

OPTIND=1	# fixed value. posix rule.
OPTERR=0
ckopt_buf=''
ckopt_opt=''

 ckopt_func -d -h 0 bool

#---getopts loop. break if all args read or detect '--'
while :
do
if [ 0 -eq "$#" ] || [ "${OPTARG%% *}" = "errmsg" ] ; then
	break
fi
check_opt='$'"$OPTIND"
eval "check_opt=\"$check_opt\""
if [ "$check_opt" = "--" ] ; then
	shift $OPTIND; break
fi
getopts ":h" ckopt_opt "$@"		# ":a:bc:f:" etc...
if [ "$?" = "1" ] ; then	# detect end/normal args. save general args.
	shift $((OPTIND - 1))
	if [ "$#" -eq "0" ] ; then
		break
	fi
	ckopt_buf="$ckopt_buf "`printf '%s' "$1" | sed -e "{        
	s#'#'\"'\"'#g
	s/^/'/g
	s/$/'/g
	}"`
	shift
	OPTIND=1
	continue
fi
# --- your setting

if [ "$ckopt_opt" = "h" ] ; then
	opt_h="1"
	ckopt_func -c -h 0 bool
	continue
fi
# --- your setting end

# hit err. ckopt chars...
# err detect@silent mode
# $?=1 ... detect optend or '--'
# ':' ... detect option, but dont have subargs (OPTARG="factor").
# '?' ... detect unsupported option char (OPTARG="factor") or args end (OPTARG="blank").
# OPTARG ... "" is optend. "a/b/c..." is invalid option 
# OPTIND ... if err, OPTIND indicates next (new) arg pos. 

OPTARG="errmsg -$OPTARG invalid option / misses subargs"
done

# --- post process. set not optional args & clean & #eE last cmd.

for ii in 1	# dummyjump logic
do
	OPTIND=1
	# skip
	test "${OPTARG%% *}" != "errmsg" || break
	ckopt_buf="set -- $ckopt_buf"' "$@"'	# set -- 'cmd' .. "$@"
	eval "$ckopt_buf"

	# run #E cmd.  cmd + test $? ... xn
 ckopt_func -e -h 0 bool
done

if [ "${OPTARG%% *}" = "errmsg" ] ; then
	printf "$0: opterr. %s\n" "$OPTARG" >/dev/stderr ; while : ; do sleep 1000 ; done ; exit 1
	test 1 = 0
fi
## ---generate by ckopt end


if [ "$?" = "1" ] ; then
	echo "$0: args err. see -h. sleep." >/dev/stderr
	while : ; do sleep 1000 ; done
	exit 1
fi


#win_resize 30ms

if [ "$opt_h" = "1" ] || [ "$#" = "0" ] ; then
cat << 'EEE'
HowTo (w_resize.sh. window resize helper. bourne-shell script)
opt: -h
------
eg.) ~$ w_resize.sh s,0x1234,w200,h100,p1	#>> width:height:pinning
  p1 @----------+ p3       p1 @------+   p3
     |          |    ->       |      |   100     
     |          |             +------+
  p7 +----------+ p9       p7     200    p9
 if you want to keep width/height, use 'ww/hh'  >>  s,0x1234,ww,h100,p1
 
eg.) ~$ w_resize.sh l,0x1234	#>> load/reset window size.
eg.) ~$ w_resize.sh v,0x1234	#>> output verbose data
>>> wmctrl -ir 119552849 -e 0,0,31,734,461
  ...run this command if you want to reload present win pos.
eg.) ~$ w_resize.sh m,0x1234,xmax,y20,p3  #>> move as p3 matches (max,20)

save+set: s,wid,width,height,pinpos
load/out: l,wid  /  v,wid
move    : m,wid,x,y,pinpos  (x,y allows 'xx/yy'(no change) 'xmax/min')
pinpos  : p1,p3,p7,p9,p0(auto). not support p2,p4,p5,p6,p8
wid     : 0x1111 or 1234 style.
this pg makes EHWM original property for data save (add/remove WR_BFINFO)
EEE
exit 0
fi


# w_resize s,0x1234,100,200,p1	#resize, x,y
# w_resize l,0x1234	#extend
# 0xでも123でもxwininfoが対応してくれる

# wmctrlでそのまま使えるwh情報を作る。フレーム配慮。
# 絶対位置はフレームの内側でwmが外っ皮にフレームを加える分がrelative.引く。
func_wininfo() {
(
	wid=$1
	l=0;r=0;u=0;d=0
	
	buf=`xwininfo -id $wid -all | tr ',' ' ' | awk '
$0 ~ /Absolute upper-left X/ {print "x=" $NF}
$0 ~ /Absolute upper-left Y/ {print "y=" $NF}
$1 ~ /Width/ {print "w=" $NF}
$1 ~ /Height/ {print "h=" $NF}
$0 ~ /Frame extents/ { print "l=" $(NF-3) ";r=" $(NF-2) ";u=" $(NF-1) ";d=" $(NF) }'`
	eval "$buf"	
	# x y w h  udlr

	# x,y指定はずれるけどwhはずれない。nxはwmctrlで復帰可能な値。
	nx=$((x-r))
	ny=$((y-u))
	echo "$nx $ny $w $h $l $r $u $d"
)
}

# @40ms程度。xpropとかwmctrlが5-6回で各6ms程度.高速化はこの辺を最小化すれば。。。
func_setwin() {
(
	wid="$1"
	shift
	for ii
	do
		buf=${ii#?}
		buf=${ii%%$buf}	# wc -c 1 ... get first char
		if [ "$buf" = "w" ] ; then
			rw=${ii#?}	#req width
		elif [ "$buf" = "h" ] ; then
			rh=${ii#?}
		elif [ "$buf" = "p" ] ; then
			pos=${ii#?}
		fi
	done

	# 現在のwh情報をロード時に使えるように加工保存
	buf=`func_wininfo $wid`
	set -- $buf
	nx=$1
	ny=$2
	w=$3
	h=$4
	# フレームデータ
	lf=$5
	rf=$6
	uf=$7
	df=$8
	
	# fullsize
	xx=$x
	yy=$y
	ww=$((w+lf+rf))
	hh=$((h+uf+df))
	
	# ポジションck 0とかが確定して帰ってくる。
	pos=`func_posck $pos $xx $yy $ww $hh`

	# save present position
	xprop -id $wid -format WR_BFINFO 8s -set WR_BFINFO "$w""_""$h""_""$pos"
	# or xprop -id $wid -f WR_BFINFO 8s "$*"
	# フレーム幅とかは標準でアレなので、とりあえずそのまま保存。wmctrlにそのまま打ち込めば復活する。
	# SAVED_WINMAP(STRING) = "734_461_9"	... wmctrl -ir xxx -e 0,x,y,734,461
	
	# win resize
	# 大抵フレーム込みの値で要求するだろうけどXwinはフレーム無しで処理するので補正する。
	# https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472552416
	# _NET_FRAME_EXTENTS, left, right, top, bottom, CARDINAL[4]/32

	# サイズ変更無し
	if [ "$rw" = "w" ] ; then
		rw=$((w+$lf+$rf))		#xinfoのwはフレームを含んでない
	fi
	
	if [ "$rh" = "h" ] ; then
		rh=$((h+$uf+$df))
	fi

	nw=$((rw-$lf-$rf))	#wmctrl指定ではフレーム無視>>要求よりフレーム分デブる。引いとく。
	nh=$((rh-$uf-$df))
	
	# min check
	# windowには最小サイズ指定があったりなかったり。
	# 実行結果から再取得してもいいか。>> posで事前に正確なサイズが必要なので計算する。
	# min size 32xで直接とるのは果てしなく面倒なのでgrepとsedで。
	# echo flm@@$flame
	# https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472552416
	buf=`xprop -id $wid | grep "program specified minimum size" | tr -c '[0123456789]' ' '`
	#	program specified minimum size: 482 by 73
	if [ "$buf" != "" ] ; then
		set -- $buf
	
		if [ $nw -lt $1 ] ; then
			nw="$1"
		fi
		if [ $nh -lt $2 ] ; then
			nh="$2"
		fi
	fi

	# pinning shift
	if [ "$pos" = "3" ] || [ "$pos" = "9" ] ; then
		nx=$((nx+w-nw))
	fi
	if  [ "$pos" = "7" ] || [ "$pos" = "9" ] ; then
		ny=$((ny+h-nh))
	fi

	# wmctrl -r unkokkoun -e 'g,x,y,w,h' g=0,x,y,w,hはそのまんま。
	# wmctrl -r unkokkoun -e '0,0,0,400,200'で左上に400x200に指定。
	# wmctrl -ir $wid -e 0,$nx,$ny,$nw,$nh
	
	# ズレるのはwmctrlがEHWMに投げっぱなしのせいか。メッセージのみだから。
	wmctrl -ir $wid -e 0,$nx,$ny,$nw,$nh
	wmctrl -m >/dev/null 2>&1	#dummycode sleep 0.0001 or 'wait wmctrl effective' 
	wmctrl -ir $wid -e 0,$nx,$ny,$nw,$nh
	# echo `func_wininfo $wid` >/dev/stderr
	
	# wmctrl -ir $wid -e 0,$nx,$ny,$nw,$nh と比較すれば一応チェック可能だけど、結局レースコンディション。
	# wmctrl -m でwm情報が帰ってくるからそれで時間稼ぎ。sleepの代わり。
	
	# 499 446 734 461
	# 751 735 482 155
	# 751 735 482 172
	# 
	# 499 446 734 461
	# 751 735 482 172
	# 751 735 482 172
	# 155だったり172だったりタイミングでズレる。ダミーを挟んでもう一回やれば治る。
	# とりあえず大丈夫そう。
)
}

# win_propからとりだしてwmctrlするだけ。
func_loadwin() {
(
	wid="$1"
	shift
	
	buf=`xprop -id $wid WR_BFINFO | awk '$1 == "WR_BFINFO(STRING)" {print $0}' `
	if [ "$buf" = "" ] ; then
		return
	fi

	buf=`echo ${buf##*=} | tr -d '"' | tr '_' ' '`
	set -- $buf
	bw=$1
	bh=$2
	pos=$3
	
	buf=`func_wininfo $wid`
	set -- $buf
	x=$1
	y=$2
	w=$3
	h=$4
	
	# posによってx,yが変化するので計算
	dw=$((bw-w))
	dh=$((bh-h))
	
	if [ "$pos" = "3" ] || [ "$pos" = "9" ] ; then
		x=$((x-dw))
	fi
	if  [ "$pos" = "7" ] || [ "$pos" = "9" ] ; then
		y=$((y-dh))
	fi
	wmctrl -ir $wid -e 0,$x,$y,$bw,$bh
	wmctrl -m >/dev/null 2>&1
	wmctrl -ir $wid -e 0,$x,$y,$bw,$bh	# 見きれている時は盛り上がってくるので二回。
	xprop -id $wid -remove WR_BFINFO 2>/dev/null
)
}

func_outwin() {
(
	wid="$1"
	shift

	buf=`func_wininfo $wid`
	set -- $buf
	x=$1
	y=$2
	w=$3
	h=$4
	printf 'wmctrl -ir %s -e %s\n' "$wid" "0,$x,$y,$w,$h"
)
}

func_movewin() {
(
	wid="$1"
	shift
	
	for ii
	do
		buf=${ii#?}
		buf=${ii%%$buf}	# wc -c 1 ... get first char
		if [ "$buf" = "x" ] ; then
			rx=${ii#?}	#req width
		elif [ "$buf" = "y" ] ; then
			ry=${ii#?}
		elif [ "$buf" = "p" ] ; then
			pos=${ii#?}
		fi
	done


	buf=`func_wininfo $wid`
	set -- $buf
	x=$1
	y=$2
	w=$3	#wmctrl用。実幅より小さい。
	h=$4

	lf=$5
	rf=$6
	uf=$7
	df=$8
	
	# fullsize
	ww=$((w+lf+rf))
	hh=$((h+uf+df))
	dx=0
	dy=0
	
	# 分類がちょっと複雑。一旦omitしたけど、やっぱりあった方が便利
	if [ "$pos" = "0" ] ; then
		
		if [ "$rx" = "max" ] && [ "$ry" = "max" ] ; then
			pos=9
		elif [ "$ry" = "max" ] ; then
			pos=7
		elif [ "$rx" = "max" ] ; then
			pos=3
		else
			pos=1
		fi
	fi

	buf=`echo $pos | tr -d '1379'`
	if [ "$buf" != "" ] ; then
		echo "$0: err. move pos need 1/3/7/9. exit" >/dev/stderr
		exit 1
	fi
	
	# posからx,yを直接演算可能なように補正する
	# pinning shift
	if [ "$pos" = "3" ] || [ "$pos" = "9" ] ; then
		dx=$((-ww))
	fi
	if  [ "$pos" = "7" ] || [ "$pos" = "9" ] ; then
		dy=$((-hh))
	fi

	set -- `xprop -root 32x '\t$0 $1' _NET_DESKTOP_GEOMETRY | awk '{printf("%d %d",$2,$3)}'`
	if [ "$#" != "2" ] ; then	#とれない
		echo "$0: err. failed to get display size(1024x768 etc). exit" >/dev/stderr
		exit 1
	fi

	disp_x="$1"
	disp_y="$2"
	
	

	# 位置特殊指定
	if [ "$rx" = "max" ] ; then
		rx="$disp_x"
	elif [ "$rx" = "min" ] ; then
		rx="0"
	elif [ "$rx" = "x" ] ; then
		rx="$x"
	fi
		
	if [ "$ry" = "max" ] ; then
		ry="$disp_y"
	elif [ "$ry" = "min" ] ; then
		ry="0"
	elif [ "$ry" = "y" ] ; then
		ry="$y"
	fi

	nx=$((rx+dx))	#wmctrl指定ではフレーム無視>>要求よりフレーム分デブる。引いとく。
	ny=$((ry+dy))

	wmctrl -ir $wid -e 0,$nx,$ny,-1,-1
	wmctrl -m >/dev/null 2>&1
	wmctrl -ir $wid -e 0,$nx,$ny,-1,-1	# 見きれている時は盛り上がってくるので二回。
)
}

# func_posck 1 100 200 50 60
# winがどの象限に属するか。
func_posck() {
(
	pos="$1"
	# wmctrl系のデータ
	nx=$2
	ny=$3
	w=$4
	h=$5
	# ポジションauto
	if [ "$pos" = "0" ] ; then
		# get disp width...1280 960 etc. 32x \t ... format select
		# http://xjman.dsl.gr.jp/X11R6/X11/CH14.html
		# https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#Setting_and_Reading_the_WM_NORMAL_HINTS_Property
		
		set -- `xprop -root 32x '\t$0 $1' _NET_DESKTOP_GEOMETRY | awk '{printf("%d %d",$2,$3)}'`
		if [ "$#" != "2" ] ; then	#とれない
			echo "$0: err. failed to get display size(1024x768 etc). exit" >/dev/stderr
			exit 1
		else
		
		buf_x=$((nx+w/2))
		buf_y=$((ny+h/2))
		disp_x=$((${1}/2))
		disp_y=$((${2}/2))
	
		if [ $buf_x -le $disp_x ] && [ $buf_y -le $disp_y ] ; then
			pos=1
		elif [ $buf_x -gt $disp_x ] && [ $buf_y -le $disp_y ] ; then
			pos=3
		elif [ $buf_x -le $disp_x ] && [ $buf_y -gt $disp_y ] ; then
			pos=7
		elif [ $buf_x -gt $disp_x ] && [ $buf_y -gt $disp_y ] ; then
			pos=9
		fi
		
		fi
	fi
	buf=`echo $pos | tr -d '1379'`
	if [ "$buf" != "" ] ; then
		echo "$0: err. select pos 1,3,7,9" >/dev/stderr
		exit 1
	fi
	echo $pos
)
}
	


# 複数入力可 
#--- main
for ii
do
	buf=`echo "$ii" | tr "," " " | tr '[:upper:]' '[:lower:]'`
	set -- $buf
	wid=`printf "%d" $2`
	
	# err ck
	buf=`wmctrl -l | awk '{printf("%d\n",$1)}' | grep $wid`
	if [ "$buf" = "" ] ; then
		echo "$0: err. wid($2) not found" >/dev/stderr
		continue
	fi


	# savemode
	if [ "$1" = "s" ] ; then
		if [ "$#" != "5" ] ; then
			echo "$0: err. s format($@) is invalid" >/dev/stderr
			continue
		fi	
		shift 2
		func_setwin "$wid" "$@"  
	fi
	
	# load mode
	if [ "$1" = "l" ] ; then
		if [ "$#" != "2" ] ; then
			echo "$0: err. l format($@) is invalid" >/dev/stderr
			continue
		fi
		shift 2
		func_loadwin "$wid" "$@" 
	fi
	
	# infomode
	if [ "$1" = "v" ] ; then
		if [ "$#" != "2" ] ; then
			echo "$0: err. v format($@) is invalid" >/dev/stderr
			continue
		fi
	#	shift 2
 		func_outwin "$wid" "$@"
	fi
	

	# movemode
	if [ "$1" = "m" ] ; then
		if [ "$#" != "5" ] ; then
			echo "$0: err. m format($@) is invalid" >/dev/stderr
			continue
		fi
		shift 2
 		func_movewin "$wid" "$@" 
	fi
done
