summaryrefslogtreecommitdiff
path: root/src/twopane.bash
diff options
context:
space:
mode:
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