From a3349e4a1748a7249ac3d2cd97ffe60457c2ad1a Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sat, 11 Dec 2021 12:01:02 +0900 Subject: [PATCH] term: let "DECSCUSR" pass through terminal multiplexers --- docs/ChangeLog.md | 1 + note.txt | 36 +++++++++++--- src/util.sh | 124 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index c68eb550..9a11da19 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -80,6 +80,7 @@ - blerc: fix the name of the option `bleopt canvas_winch_action` (reported by Knusper) b1be640 - menu (menu-style:desc): improve descriptions (motivated by Shahabaz-Bagwan) `#D1685` 4de1b45 - menu (menu-style:desc): support multicolumns (motivated by Shahabaz-Bagwan) `#D1686` 231dc39 +- term: let DECSCUSR pass through terminal multiplexers (motivated by cmplstofB) `#D1697` 0000000 ## Changes diff --git a/note.txt b/note.txt index 72ff746a..8c7b7e9c 100644 --- a/note.txt +++ b/note.txt @@ -1624,13 +1624,6 @@ bash_tips * 一つのキーで複数の widget を呼び出す方法? https://github.com/michaelmob/portable-config/commit/49f9566afd8b62d47a090328104ad803962e6d3f - * tmux や screen の中では pass through シーケンスを用いる? - https://github.com/cmplstofB/dotfiles/blob/3e41ac47f47cc7788215409a5c3f26635f02d6a0/_dffiles/.config/blesh/blerc#L193 - - tmux: \ePtmux;%s\e\\ (中に含まれる \e は二重にする) - また tmux; の部分は存在しなくても良い様である (https://gist.github.com/saitoha/4723390) - screen: \eP%s\e\\ - * bind: "" で囲まれていない keyseq \C-l を解釈できない https://github.com/cmplstofB/dotfiles/blob/master/_dffiles/.config/bash/bashrc#L170-L172 @@ -2002,6 +1995,8 @@ bash_tips いているのであればそれで良いのではないか。一応他のシェルに移植する時に一括 で動作を変更するのが楽という可能性はなくはない。 + 2021-12-11 ble/string#match という関数名で追加した。 + 2021-09-20 * sabbrev を accept-line の時にも展開する可能性について。 @@ -5641,6 +5636,33 @@ bash_tips 2021-12-11 + * util (cursor-state): tmux や screen の中では pass through シーケンスを用いる? (motivated by cmplstofB) [#D1697] + https://github.com/cmplstofB/dotfiles/blob/3e41ac47f47cc7788215409a5c3f26635f02d6a0/_dffiles/.config/blesh/blerc#L193 + + tmux: \ePtmux;%s\e\\ (中に含まれる \e は二重にする) + また tmux; の部分は存在しなくても良い様である (https://gist.github.com/saitoha/4723390) + screen: \eP%s\e\\ + + 対応した。 + + * done: 本来は外側の端末の対応状況に応じて _ble_term_Ss を送るべきの気がする。 + + | 然しそうするとユーザー設定または terminfo の _ble_term_Ss とどちらを優 + | 先するべきなのかという問題が生じる。うーん。実質的に _ble_term_Ss は対 + | 応しているとしたら一意なので、有限文字列かそうでないかだけが問題である。 + | だとすれば、ble.sh の側で外側の端末が Ss に対応していると判定したならば、 + | それに対応するシーケンスを送りつけてしまって問題ない気がする。 + + _ble_term_Ss が空で端末multiplexerの外側の端末が対応していると判断できる時、 + 独自の判断で外側の端末の対応する DECSCUSR を pass through で送信して良い。 + + Note: _ble_term_Ss が空でない時には既に外側に pass through で送りつける様に + なっている。但し、_ble_term_Ss に設定された値を尊重する。ユーザーが好みで設 + 定しているかもしれないので。 + + →外側の端末情報の取得も実装した。任意の階層だけ入れ子になった端末マルチ + プレクサでもちゃんとカーソル形状の変更が伝播する様になった。 + * edit: self-insert の振る舞い [#D1696] * 一番最後のキーを挿入するべき。現在は一番最初のキーを挿入している。 diff --git a/src/util.sh b/src/util.sh index 9167764f..69b421ee 100644 --- a/src/util.sh +++ b/src/util.sh @@ -1272,6 +1272,8 @@ function ble/string#quote-word { fi } +function ble/string#match { [[ $1 =~ $2 ]]; } + ## @fn ble/string#create-unicode-progress-bar/.block value ## @var[out] ret function ble/string#create-unicode-progress-bar/.block { @@ -5455,7 +5457,13 @@ function ble/term/cursor-state/.update { local state=$(($1)) [[ $_ble_term_cursor_current == "$state" ]] && return 0 - ble/util/buffer "${_ble_term_Ss//@1/$state}" + local ret=${_ble_term_Ss//@1/$state} + + # Note: 既に pass-through seq が含まれている時はスキップする。 + [[ $ret && $ret != $'\eP'*$'\e\\' ]] && + ble/term/quote-passthrough "$ret" '' all + + ble/util/buffer "$ret" _ble_term_cursor_current=$state } @@ -5505,77 +5513,141 @@ fi #---- DA2 --------------------------------------------------------------------- -_ble_term_TERM= +_ble_term_TERM=() +_ble_term_DA1R=() +_ble_term_DA2R=() + +## @fn ble/term/DA2/initialize-term [depth] +## @var[out] _ble_term_TERM function ble/term/DA2/initialize-term { - local rex='^[0-9]*(;[0-9]*)*$'; [[ $_ble_term_DA2R =~ $rex ]] || return + local depth=$1 + local DA2R=${_ble_term_DA2R[depth]} + local rex='^[0-9]*(;[0-9]*)*$'; [[ $DA2R =~ $rex ]] || return local da2r - ble/string#split da2r ';' "$_ble_term_DA2R" + ble/string#split da2r ';' "$DA2R" da2r=("${da2r[@]/#/10#0}") # 0で始まっていても10進数で解釈 (#D1570 is-array OK) - case $_ble_term_DA2R in + case $DA2R in ('1;0'?????';0') - _ble_term_TERM=foot ;; + _ble_term_TERM[depth]=foot ;; ('1;'*) if ((4000<=da2r[1]&&da2r[1]<4100&&3<=da2r[2])); then - _ble_term_TERM=kitty + _ble_term_TERM[depth]=kitty elif ((2000<=da2r[1]&&da2r[1]<5400&&da2r[2]==0)); then - _ble_term_TERM=vte + _ble_term_TERM[depth]=vte fi ;; ('99;'*) - _ble_term_TERM=contra ;; + _ble_term_TERM[depth]=contra ;; ('65;'*) if ((5300<=da2r[1]&&da2r[2]==1)); then - _ble_term_TERM=vte + _ble_term_TERM[depth]=vte elif ((da2r[1]>=100)); then - _ble_term_TERM=RLogin + _ble_term_TERM[depth]=RLogin fi ;; ('67;'*) local rex='^67;[0-9]{3,};0$' - if [[ $TERM == cygwin && $_ble_term_DA2R =~ $rex ]]; then - _ble_term_TERM=cygwin + if [[ $TERM == cygwin && $DA2R =~ $rex ]]; then + _ble_term_TERM[depth]=cygwin fi ;; ('83;'*) local rex='^83;[0-9]+;0$' - [[ $_ble_term_DA2R =~ $rex ]] && _ble_term_TERM=screen ;; + [[ $DA2R =~ $rex ]] && _ble_term_TERM[depth]=screen ;; ('84;0;0') - _ble_term_TERM=tmux ;; + _ble_term_TERM[depth]=tmux ;; esac - [[ $_ble_term_TERM ]] && return 0 + [[ $_ble_term_TERM[depth] ]] && return 0 # xterm if rex='^xterm(-|$)'; [[ $TERM =~ $rex ]]; then local version=${da2r[1]} - if rex='^1;[0-9]+;0$'; [[ $_ble_term_DA2R =~ $rex ]]; then + if rex='^1;[0-9]+;0$'; [[ $DA2R =~ $rex ]]; then # Note: vte (2000以上), kitty (4000以上) は処理済み true - elif rex='^0;[0-9]+;0$'; [[ $_ble_term_DA2R =~ $rex ]]; then + elif rex='^0;[0-9]+;0$'; [[ $DA2R =~ $rex ]]; then ((95<=version)) - elif rex='^(2|24|1[89]|41|6[145]);[0-9]+;0$'; [[ $_ble_term_DA2R =~ $rex ]]; then + elif rex='^(2|24|1[89]|41|6[145]);[0-9]+;0$'; [[ $DA2R =~ $rex ]]; then ((280<=version)) - elif rex='^32;[0-9]+;0$'; [[ $_ble_term_DA2R =~ $rex ]]; then + elif rex='^32;[0-9]+;0$'; [[ $DA2R =~ $rex ]]; then ((354<=version&&version<2000)) else false - fi && { _ble_term_TERM=xterm:$version; return; } + fi && { _ble_term_TERM[depth]=xterm:$version; return; } fi + _ble_term_TERM[depth]=unknown return 0 } -_ble_term_DA1R= -_ble_term_DA2R= function ble/term/DA1/notify { _ble_term_DA1R=$1; blehook/invoke DA1R; } function ble/term/DA2/notify { # Note #D1485: screen で attach した時に外側の端末の DA2R が混入する # 事がある。2回目以降に受信した内容は ble.sh の内部では使用しない事 # にする。 - if [[ ! $_ble_term_DA2R ]]; then - _ble_term_DA2R=$1 - ble/term/DA2/initialize-term + local depth=${#_ble_term_DA2R[@]} + if ((depth==0)) || ble/string#match "${_ble_term_TERM[depth-1]}" '^(screen|tmux)$'; then + _ble_term_DA2R[depth]=$1 + ble/term/DA2/initialize-term "$depth" + case ${_ble_term_TERM[depth]} in + (screen|tmux) + # 外側の端末にも DA2 要求を出す。[ Note: 最初の DA2 要求は + # ble/decode/attach (decode.sh) から送信されている。 ] + local ret + ble/term/quote-passthrough $'\e[>c' $((depth+1)) + ble/util/buffer "$ret" ;; + (contra) + : "${_ble_term_Ss:=$'\e[@1 q'}" ;; + esac + + # 外側の端末情報は以降では処理しない + ((depth)) && return 0 fi + blehook/invoke DA2R } +## @fn ble/term/quote-passthrough seq [level] [opts] +## 指定したシーケンスを、端末マルチプレクサを通過する様に加工します。 +## +## @param[in] seq +## 送信するシーケンスを指定します。 +## +## @param[in,opt] level +## シーケンスを届ける階層。0 が一番内側の Bash が動作している端末マルチプレ +## クサ。省略した場合は一番外側の端末にシーケンスを届ける。 +## +## @param[in,opt] opts +## コロン区切りの設定。 +## +## all +## 指定した階層以下の全ての端末・端末マルチプレクサに同じシーケンスを送信 +## する。[ Note: terminal multiplexer 自体が処理して外側に作用するかもし +## れないので、先に pass-through で外側に送った後に terminal multiplexer +## 自体にも送る。 ] +## +## @var[out] ret +## 加工されたシーケンスを格納します。 +## +function ble/term/quote-passthrough { + local seq=$1 level=${2:-$((${#_ble_term_DA2R[@]}-1))} opts=$3 + local all=; [[ :$opts: == *:all:* ]] && all=1 + ret=$seq + [[ $seq ]] || return 0 + local i + for ((i=level;--i>=0;)); do + if [[ ${_ble_term_TERM[i]} == tmux ]]; then + # Note: tmux では pass-through seq の中に含まれる \e は \e\e の様に + # escape する。 + ret=$'\ePtmux;'${ret//$'\e'/$'\e\e'}$'\e\\'${all:+$seq} + else + # Note: screen は、最初に現れる \e\\ で pass-through sequence が終わって + # しまうので単純に pass-through sequence を入れ子にはできない。なので、例 + # えば "\ePXXX\e\\YYY" を pass-through する時には、\e と \\ の間で + # [\ePXXX\e][\\YYY] の様に分割して、それぞれ pass-through する。 + ret=$'\eP'${ret//$'\e\\'/$'\e\e\\\eP\\'}$'\e\\'${all:+$seq} + fi + done +} + _ble_term_DECSTBM= function ble/term/test-DECSTBM.hook { (($1==2)) && _ble_term_DECSTBM=$'\e[%s;%sr'