From 62440f41b97fbe85787bb015c9f52bf7e329b97c Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Tue, 20 Aug 2024 21:18:44 -0400 Subject: many changes basically working the problem is that when the top pane sends SIGUSR1 it does not quit the bottom in the right order and an extra character ends up being read from the terminal in the bottom before a write error signals the end of the connection. Our signal of termination from the top is not reaching the right pid, the pid of the socat inside of the read_char needs to be reached by SIGUSR1, instead of $$ the outermost shell, where killing the job in job control should be enough to break the connection to the tty, it remains probably as long as socat remains running, we need to kill the whole process group of read-tty when killing the main process or when it exits --- src/twopane.bash | 295 ++++++++++++++++++++++++++----------------------------- 1 file changed, 138 insertions(+), 157 deletions(-) (limited to 'src/twopane.bash') diff --git a/src/twopane.bash b/src/twopane.bash index f496701..4c3ec62 100755 --- a/src/twopane.bash +++ b/src/twopane.bash @@ -6,6 +6,8 @@ set -f set -o pipefail shopt -s lastpipe +. read_chars.bash + BOT_SIZE=8 BOT_TITLE=input @@ -59,6 +61,10 @@ msgminwait 0 # More is needed. Xterm? nonblock on +# Enable mouse focus +defmousetrack on +mousetrack on + caption string '%t' layout new split @@ -99,7 +105,7 @@ start_screen_pane() TOP_TITLE="Command: ${*@Q}" screen -X focus top screen -X screen -ln -t "$TOP_TITLE" 1 "$@" - screen -p "$pane" -X exec .!. sh -c 'exec socat UNIX-LISTEN:"$TWOPANE"/socket STDIN,cfmakeraw!!STDOUT' + screen -p "$pane" -X exec .!. bash -c 'exec -a top-tty-forward socat UNIX-LISTEN:"$TWOPANE"/socket STDIN,cfmakeraw!!STDOUT' } restart_screen_pane() @@ -108,50 +114,65 @@ restart_screen_pane() start_screen_pane "$@" } -# Start SOCAT if necessary. -# Connect the running SOCAT to file descriptors. -# Optionally assign the file descriptors to the specified variables. -# Optionally assign the socat PID to the specified variable. -# -# The copied file descriptors (unlike the original coprocess file -# descriptors, in ${SOCAT[0]} and ${SOCAT[1]}) can be passed to external -# processes (e.g.: other socat(1) instances) and used in subshells. +connect_coproc() +{ + declare -n coproc="$1" + shift + declare -g -n std1="${!coproc}_STDOUT" + declare -g -n std0="${!coproc}_STDIN" + declare -g -n opid="${!coproc}_PID" + + if [ $# = 0 ] + then + set -- "${!coproc}" + fi + if ! [ "$opid" ] + then + local STDERR + { + coproc "${!coproc}" \ + { + "$@" + } 2>&$STDERR {STDERR}>&- + } {STDERR}>&2 2>/dev/null + i "${!opid}=$opid" + x disown + fi + # The copied file descriptors (unlike the original coprocess + # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be + # passed to external processes (e.g.: socat(1) instances) and + # used in subshells. + { + exec {std0}<&0 {std1}>&1 + } <&${coproc[0]} >&${coproc[1]} +} + +disconnect_coproc() +{ + if [ $# = 1 ] + then + declare -n coproc="$1" + declare -g -n std1="${!coproc}_STDOUT" + declare -g -n std0="${!coproc}_STDIN" + declare -g -n pid="${!coproc}_PID" + [ "$std0" -a "$std1" ] || return + exec {std0}<&- {std1}>&- + unset std0 std1 + x kill "$pid" + x wait -f "$pid" + fi +} + connect() { - case $# in - 3 ) - declare -n pid="$1" std0="$2" std1="$3" - shift 3 - ;; - 2 ) - local pid - declare -n std0="$1" std1="$2" - shift 2 - ;; - 0 ) - local pid std0 std1 - ;; - * ) - return 1 - ;; - esac - [ "${SOCAT[0]}" ] || socat_coproc - pid=${SOCAT_PID} - exec {std0}<&${SOCAT[0]} {std1}>&${SOCAT[1]} + connect_coproc SOCAT + i "SOCAT_PID=$SOCAT_PID" } -socat_coproc() +disconnect() { - set -m - local STDERR - exec {STDERR}>&2 - { - coproc SOCAT { - socat - UNIX-CONNECT:"$TWOPANE"/socket,forever - kill -USR1 $$ - } 2>&$STDERR {STDERR}>&- - } 2>/dev/null - disown + i "SOCAT_STDIN=$SOCAT_STDIN SOCAT_STDOUT=$SOCAT_STDOUT SOCAT_PID=$SOCAT_PID" + x disconnect_coproc SOCAT } sendc() @@ -166,17 +187,6 @@ send() printf '%s\n' "$*" >&${SOCAT[1]} } -disconnect() -{ - if [ $# = 2 ] - then - declare -i -n std0="$1" std1="$2" - exec {std0}<&- {std1}>&- - unset std0 std1 - fi - wait -f "$SOCAT_PID" 2>/dev/null -} - restart() { start "$@" @@ -187,78 +197,11 @@ start() foreground "$@" } -foreground_loop() -{ - while true - do - start_screen_pane "$@" - connect stdin stdout - forward - disconnect stdin stdout - - case "$TOP_EXIT" in - restart ) - continue ;; - quit ) - exit ;; - prompt | * ) - focus bottom - break - ;; - esac - done -} - -forwarding() -{ - [ "$FORWARD_PID" ] || return - FORWARD_JOBSPEC=$(jobs -sl | pid_to_jobspec "$FORWARD_PID") - [ "$FORWARD_JOBSPEC" ] -} - -forward() -{ - declare -g FORWARD_PID - if ! check_screen_pane - then - echo "$0: Warning: Nothing to forward. Starting anew." >&2 - background "$@" - elif forwarding - then - resume_forward - return - fi - - focus top - old_stty=$(stty -g) - # Lowercase $stdin/$stdout are the SOCAT coprocess connected to - # the other pane's terminal. Uppercase $STDIN/$STDOUT are the - # real stdin/stdout of this function, connected to the lower - # pane's terminal. Socat here merges inputs from both sources. - exec {STDIN}<&0 {STDOUT}>&1 {STDERR}>&2 - - # The input is put out raw back over the socket. The input is - # copied to stdout after being filtered (to display control - # characters with carrot-encoding like '^[' etc). - exec {TEE}> >(tee >(output_filter >&$STDOUT) >&$stdout) - stty=cfmakeraw,opost=1,onlcr=1 - { - socat FD:$STDIN,$stty!!STDOUT - <&$stdin >&$TEE 2>&$STDERR & - } 2>/dev/null - FORWARD_PID=$! - printf '%s\n' "#!/bin/bash" "kill -TSTP $!" "screen -X focus bottom" > "$TWOPANE"/unforward - chmod +x "$TWOPANE"/unforward - fg >/dev/null - stty "$old_stty" - focus bottom - echo -} - -cfmakeraw() +check_tty_reader() { - cmd=(stty -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl - -ixon -opost -echo -echonl -icanon -isig -iexten -parenb cs8) - "${cmd[@]}" "$@" + [ "$TTY_READER_PID" ] || return + TTY_READER_JOBSPEC=$(jobs -sl | pid_to_jobspec "$TTY_READER_PID") + [ "$TTY_READER_JOBSPEC" ] } pid_to_jobspec() @@ -274,59 +217,90 @@ pid_to_jobspec() false } -resume_forward() +tty_forward() { - old_stty=$(stty -g) - cfmakeraw opost onlcr - focus top - fg "$FORWARD_JOBSPEC" >/dev/null - stty "$old_stty" - focus bottom + declare -i -n pid="$1" + read-tty + x kill $pid 2>/dev/null } -tty_forward() +SOCAT() { - read-tty + (exec -a bottom-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) + x kill -USR1 $$ +} + +echosend() +{ + declare -n fd="$1" + declare -n pid="${!fd}_PID" + if ! [ "$fd" ] + then + connect_coproc SOCAT + i "SOCAT_PID=$SOCAT_PID" + exec {fd}> >( + (exec -a echosend socat - fd:${SOCAT_STDIN?e}!!-) | + tee >(output_filter) >&${SOCAT_STDOUT?e} + ) + pid=$! + fi +} + +quiet_bg() +{ + local STDERR + { + eval "$(printf '%q ' "$@") & 2>&\$STDERR {STDERR}>&-" + } {STDERR}>&2 2>/dev/null } background() { - old_stty=$(stty -g) start_screen_pane "$@" focus bottom - connect stdin stdout - #stty tostop - exec {BOTTOM_PANE}> >(trap 'echo SIG >&2' SIGTTOU SIGTTIN SIGTSTP; output_filter) - BOTTOM_PANE_PID=$! - exec {BOTH_PANES}> >(trap 'echo TTOU >&2' SIGTTOU; ptee 1 $BOTTOM_PANE >&$stdout) - exec {NET_MERGE}> >(exec {STDIN}<&0; exec -a merge_reader socat FD:$STDIN!!STDOUT - <&$stdin >&$BOTH_PANES) - local STDERR - exec {STDERR}>&2 + echosend ECHOSEND { - exec 2>&$STDERR {STDERR}>&- - tty_forward & - } >&$NET_MERGE 2>/dev/null + tty_forward ECHOSEND_PID & + } >&$ECHOSEND TTY_READER_PID=$! - printf '%s\n' "#!/bin/bash" "kill -TSTP $TTY_READER_PID" "screen -X focus bottom" > "$TWOPANE"/unforward + printf '%s\n' \ + "#!/bin/bash" \ + "kill -TSTP $TTY_READER_PID" \ + "screen -X focus bottom" \ + > "$TWOPANE"/unforward chmod +x "$TWOPANE"/unforward - #"$TWOPANE"/unforward } foreground() { - if ! jobs -p %tty_forward >/dev/null 2>&1 - then - background - else - old_stty=$(stty -g) - fi - cfmakeraw opost onlcr + check_tty_reader || background focus top - fg %tty_forward - stty "$old_stty" + fg %tty_forward >/dev/null + #disconnect focus bottom } +foreground_loop() +{ + while true + do + foreground + x kill %tty_forward 2>/dev/null + case "$TOP_EXIT" in + restart ) + x kill -INT $TTY_READER_PID $ECHOSEND_PID + start_screen_pane "$@" + continue ;; + quit ) + exit ;; + prompt | * ) + x kill -INT $TTY_READER_PID $ECHOSEND_PID + break + ;; + esac + done +} + twopane() { start "$@" @@ -378,7 +352,7 @@ colorize() then printf -v REPLY "^$(chr c + 64)" fi - printf $'\e[1m%s\e[m\n' "$REPLY" + printf $'\e[106m%s\e[m\n' "$REPLY" done } @@ -411,15 +385,22 @@ soft_cursor() while printf "$FMT" "$REPLY" "$color" do read -r || break - let '++color <= 107' || color=101 + let '++color <= 105' || color=101 done } +resize() +{ + screen -X resize "$@" +} + our_bashrc_main() { + set -m set -f set -o pipefail - trap "screen -X quit" EXIT + #trap 'focus bottom; resize 90%; read -p "Exit> "; quit' EXIT + #trap 'echo USR1 >&2; focus bottom; x kill -INT %tty_forward $TTY_READER_PID $ECHOSEND_PID' USR1 export PS1="$BOT_TITLE\\\$ " } -- cgit v1.2.3