summaryrefslogtreecommitdiff
path: root/src/twopane.bash
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2024-08-20 21:18:44 -0400
committerAndrew Cady <d@jerkface.net>2024-08-20 21:18:44 -0400
commit62440f41b97fbe85787bb015c9f52bf7e329b97c (patch)
tree5cbc3fcd0f7c4062d8def07b50bccfd37d316f0c /src/twopane.bash
parentbd3d815e9c3757414b50225ba2c427ab8ff298e7 (diff)
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
Diffstat (limited to 'src/twopane.bash')
-rwxr-xr-xsrc/twopane.bash295
1 files changed, 138 insertions, 157 deletions
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
6set -o pipefail 6set -o pipefail
7shopt -s lastpipe 7shopt -s lastpipe
8 8
9. read_chars.bash
10
9BOT_SIZE=8 11BOT_SIZE=8
10BOT_TITLE=input 12BOT_TITLE=input
11 13
@@ -59,6 +61,10 @@ msgminwait 0
59# More is needed. Xterm? 61# More is needed. Xterm?
60nonblock on 62nonblock on
61 63
64# Enable mouse focus
65defmousetrack on
66mousetrack on
67
62caption string '%t' 68caption string '%t'
63layout new 69layout new
64split 70split
@@ -99,7 +105,7 @@ start_screen_pane()
99 TOP_TITLE="Command: ${*@Q}" 105 TOP_TITLE="Command: ${*@Q}"
100 screen -X focus top 106 screen -X focus top
101 screen -X screen -ln -t "$TOP_TITLE" 1 "$@" 107 screen -X screen -ln -t "$TOP_TITLE" 1 "$@"
102 screen -p "$pane" -X exec .!. sh -c 'exec socat UNIX-LISTEN:"$TWOPANE"/socket STDIN,cfmakeraw!!STDOUT' 108 screen -p "$pane" -X exec .!. bash -c 'exec -a top-tty-forward socat UNIX-LISTEN:"$TWOPANE"/socket STDIN,cfmakeraw!!STDOUT'
103} 109}
104 110
105restart_screen_pane() 111restart_screen_pane()
@@ -108,50 +114,65 @@ restart_screen_pane()
108 start_screen_pane "$@" 114 start_screen_pane "$@"
109} 115}
110 116
111# Start SOCAT if necessary. 117connect_coproc()
112# Connect the running SOCAT to file descriptors. 118{
113# Optionally assign the file descriptors to the specified variables. 119 declare -n coproc="$1"
114# Optionally assign the socat PID to the specified variable. 120 shift
115# 121 declare -g -n std1="${!coproc}_STDOUT"
116# The copied file descriptors (unlike the original coprocess file 122 declare -g -n std0="${!coproc}_STDIN"
117# descriptors, in ${SOCAT[0]} and ${SOCAT[1]}) can be passed to external 123 declare -g -n opid="${!coproc}_PID"
118# processes (e.g.: other socat(1) instances) and used in subshells. 124
125 if [ $# = 0 ]
126 then
127 set -- "${!coproc}"
128 fi
129 if ! [ "$opid" ]
130 then
131 local STDERR
132 {
133 coproc "${!coproc}" \
134 {
135 "$@"
136 } 2>&$STDERR {STDERR}>&-
137 } {STDERR}>&2 2>/dev/null
138 i "${!opid}=$opid"
139 x disown
140 fi
141 # The copied file descriptors (unlike the original coprocess
142 # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be
143 # passed to external processes (e.g.: socat(1) instances) and
144 # used in subshells.
145 {
146 exec {std0}<&0 {std1}>&1
147 } <&${coproc[0]} >&${coproc[1]}
148}
149
150disconnect_coproc()
151{
152 if [ $# = 1 ]
153 then
154 declare -n coproc="$1"
155 declare -g -n std1="${!coproc}_STDOUT"
156 declare -g -n std0="${!coproc}_STDIN"
157 declare -g -n pid="${!coproc}_PID"
158 [ "$std0" -a "$std1" ] || return
159 exec {std0}<&- {std1}>&-
160 unset std0 std1
161 x kill "$pid"
162 x wait -f "$pid"
163 fi
164}
165
119connect() 166connect()
120{ 167{
121 case $# in 168 connect_coproc SOCAT
122 3 ) 169 i "SOCAT_PID=$SOCAT_PID"
123 declare -n pid="$1" std0="$2" std1="$3"
124 shift 3
125 ;;
126 2 )
127 local pid
128 declare -n std0="$1" std1="$2"
129 shift 2
130 ;;
131 0 )
132 local pid std0 std1
133 ;;
134 * )
135 return 1
136 ;;
137 esac
138 [ "${SOCAT[0]}" ] || socat_coproc
139 pid=${SOCAT_PID}
140 exec {std0}<&${SOCAT[0]} {std1}>&${SOCAT[1]}
141} 170}
142 171
143socat_coproc() 172disconnect()
144{ 173{
145 set -m 174 i "SOCAT_STDIN=$SOCAT_STDIN SOCAT_STDOUT=$SOCAT_STDOUT SOCAT_PID=$SOCAT_PID"
146 local STDERR 175 x disconnect_coproc SOCAT
147 exec {STDERR}>&2
148 {
149 coproc SOCAT {
150 socat - UNIX-CONNECT:"$TWOPANE"/socket,forever
151 kill -USR1 $$
152 } 2>&$STDERR {STDERR}>&-
153 } 2>/dev/null
154 disown
155} 176}
156 177
157sendc() 178sendc()
@@ -166,17 +187,6 @@ send()
166 printf '%s\n' "$*" >&${SOCAT[1]} 187 printf '%s\n' "$*" >&${SOCAT[1]}
167} 188}
168 189
169disconnect()
170{
171 if [ $# = 2 ]
172 then
173 declare -i -n std0="$1" std1="$2"
174 exec {std0}<&- {std1}>&-
175 unset std0 std1
176 fi
177 wait -f "$SOCAT_PID" 2>/dev/null
178}
179
180restart() 190restart()
181{ 191{
182 start "$@" 192 start "$@"
@@ -187,78 +197,11 @@ start()
187 foreground "$@" 197 foreground "$@"
188} 198}
189 199
190foreground_loop() 200check_tty_reader()
191{
192 while true
193 do
194 start_screen_pane "$@"
195 connect stdin stdout
196 forward
197 disconnect stdin stdout
198
199 case "$TOP_EXIT" in
200 restart )
201 continue ;;
202 quit )
203 exit ;;
204 prompt | * )
205 focus bottom
206 break
207 ;;
208 esac
209 done
210}
211
212forwarding()
213{
214 [ "$FORWARD_PID" ] || return
215 FORWARD_JOBSPEC=$(jobs -sl | pid_to_jobspec "$FORWARD_PID")
216 [ "$FORWARD_JOBSPEC" ]
217}
218
219forward()
220{
221 declare -g FORWARD_PID
222 if ! check_screen_pane
223 then
224 echo "$0: Warning: Nothing to forward. Starting anew." >&2
225 background "$@"
226 elif forwarding
227 then
228 resume_forward
229 return
230 fi
231
232 focus top
233 old_stty=$(stty -g)
234 # Lowercase $stdin/$stdout are the SOCAT coprocess connected to
235 # the other pane's terminal. Uppercase $STDIN/$STDOUT are the
236 # real stdin/stdout of this function, connected to the lower
237 # pane's terminal. Socat here merges inputs from both sources.
238 exec {STDIN}<&0 {STDOUT}>&1 {STDERR}>&2
239
240 # The input is put out raw back over the socket. The input is
241 # copied to stdout after being filtered (to display control
242 # characters with carrot-encoding like '^[' etc).
243 exec {TEE}> >(tee >(output_filter >&$STDOUT) >&$stdout)
244 stty=cfmakeraw,opost=1,onlcr=1
245 {
246 socat FD:$STDIN,$stty!!STDOUT - <&$stdin >&$TEE 2>&$STDERR &
247 } 2>/dev/null
248 FORWARD_PID=$!
249 printf '%s\n' "#!/bin/bash" "kill -TSTP $!" "screen -X focus bottom" > "$TWOPANE"/unforward
250 chmod +x "$TWOPANE"/unforward
251 fg >/dev/null
252 stty "$old_stty"
253 focus bottom
254 echo
255}
256
257cfmakeraw()
258{ 201{
259 cmd=(stty -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl 202 [ "$TTY_READER_PID" ] || return
260 -ixon -opost -echo -echonl -icanon -isig -iexten -parenb cs8) 203 TTY_READER_JOBSPEC=$(jobs -sl | pid_to_jobspec "$TTY_READER_PID")
261 "${cmd[@]}" "$@" 204 [ "$TTY_READER_JOBSPEC" ]
262} 205}
263 206
264pid_to_jobspec() 207pid_to_jobspec()
@@ -274,59 +217,90 @@ pid_to_jobspec()
274 false 217 false
275} 218}
276 219
277resume_forward() 220tty_forward()
278{ 221{
279 old_stty=$(stty -g) 222 declare -i -n pid="$1"
280 cfmakeraw opost onlcr 223 read-tty
281 focus top 224 x kill $pid 2>/dev/null
282 fg "$FORWARD_JOBSPEC" >/dev/null
283 stty "$old_stty"
284 focus bottom
285} 225}
286 226
287tty_forward() 227SOCAT()
288{ 228{
289 read-tty 229 (exec -a bottom-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever)
230 x kill -USR1 $$
231}
232
233echosend()
234{
235 declare -n fd="$1"
236 declare -n pid="${!fd}_PID"
237 if ! [ "$fd" ]
238 then
239 connect_coproc SOCAT
240 i "SOCAT_PID=$SOCAT_PID"
241 exec {fd}> >(
242 (exec -a echosend socat - fd:${SOCAT_STDIN?e}!!-) |
243 tee >(output_filter) >&${SOCAT_STDOUT?e}
244 )
245 pid=$!
246 fi
247}
248
249quiet_bg()
250{
251 local STDERR
252 {
253 eval "$(printf '%q ' "$@") & 2>&\$STDERR {STDERR}>&-"
254 } {STDERR}>&2 2>/dev/null
290} 255}
291 256
292background() 257background()
293{ 258{
294 old_stty=$(stty -g)
295 start_screen_pane "$@" 259 start_screen_pane "$@"
296 focus bottom 260 focus bottom
297 connect stdin stdout 261 echosend ECHOSEND
298 #stty tostop
299 exec {BOTTOM_PANE}> >(trap 'echo SIG >&2' SIGTTOU SIGTTIN SIGTSTP; output_filter)
300 BOTTOM_PANE_PID=$!
301 exec {BOTH_PANES}> >(trap 'echo TTOU >&2' SIGTTOU; ptee 1 $BOTTOM_PANE >&$stdout)
302 exec {NET_MERGE}> >(exec {STDIN}<&0; exec -a merge_reader socat FD:$STDIN!!STDOUT - <&$stdin >&$BOTH_PANES)
303 local STDERR
304 exec {STDERR}>&2
305 { 262 {
306 exec 2>&$STDERR {STDERR}>&- 263 tty_forward ECHOSEND_PID &
307 tty_forward & 264 } >&$ECHOSEND
308 } >&$NET_MERGE 2>/dev/null
309 TTY_READER_PID=$! 265 TTY_READER_PID=$!
310 printf '%s\n' "#!/bin/bash" "kill -TSTP $TTY_READER_PID" "screen -X focus bottom" > "$TWOPANE"/unforward 266 printf '%s\n' \
267 "#!/bin/bash" \
268 "kill -TSTP $TTY_READER_PID" \
269 "screen -X focus bottom" \
270 > "$TWOPANE"/unforward
311 chmod +x "$TWOPANE"/unforward 271 chmod +x "$TWOPANE"/unforward
312 #"$TWOPANE"/unforward
313} 272}
314 273
315foreground() 274foreground()
316{ 275{
317 if ! jobs -p %tty_forward >/dev/null 2>&1 276 check_tty_reader || background
318 then
319 background
320 else
321 old_stty=$(stty -g)
322 fi
323 cfmakeraw opost onlcr
324 focus top 277 focus top
325 fg %tty_forward 278 fg %tty_forward >/dev/null
326 stty "$old_stty" 279 #disconnect
327 focus bottom 280 focus bottom
328} 281}
329 282
283foreground_loop()
284{
285 while true
286 do
287 foreground
288 x kill %tty_forward 2>/dev/null
289 case "$TOP_EXIT" in
290 restart )
291 x kill -INT $TTY_READER_PID $ECHOSEND_PID
292 start_screen_pane "$@"
293 continue ;;
294 quit )
295 exit ;;
296 prompt | * )
297 x kill -INT $TTY_READER_PID $ECHOSEND_PID
298 break
299 ;;
300 esac
301 done
302}
303
330twopane() 304twopane()
331{ 305{
332 start "$@" 306 start "$@"
@@ -378,7 +352,7 @@ colorize()
378 then 352 then
379 printf -v REPLY "^$(chr c + 64)" 353 printf -v REPLY "^$(chr c + 64)"
380 fi 354 fi
381 printf $'\e[1m%s\e[m\n' "$REPLY" 355 printf $'\e[106m%s\e[m\n' "$REPLY"
382 done 356 done
383} 357}
384 358
@@ -411,15 +385,22 @@ soft_cursor()
411 while printf "$FMT" "$REPLY" "$color" 385 while printf "$FMT" "$REPLY" "$color"
412 do 386 do
413 read -r || break 387 read -r || break
414 let '++color <= 107' || color=101 388 let '++color <= 105' || color=101
415 done 389 done
416} 390}
417 391
392resize()
393{
394 screen -X resize "$@"
395}
396
418our_bashrc_main() 397our_bashrc_main()
419{ 398{
399 set -m
420 set -f 400 set -f
421 set -o pipefail 401 set -o pipefail
422 trap "screen -X quit" EXIT 402 #trap 'focus bottom; resize 90%; read -p "Exit> "; quit' EXIT
403 #trap 'echo USR1 >&2; focus bottom; x kill -INT %tty_forward $TTY_READER_PID $ECHOSEND_PID' USR1
423 export PS1="$BOT_TITLE\\\$ " 404 export PS1="$BOT_TITLE\\\$ "
424} 405}
425 406