#!/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 . */ 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