diff options
Diffstat (limited to 'src/twopane.bash')
-rwxr-xr-x | src/twopane.bash | 295 |
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 | |||
6 | set -o pipefail | 6 | set -o pipefail |
7 | shopt -s lastpipe | 7 | shopt -s lastpipe |
8 | 8 | ||
9 | . read_chars.bash | ||
10 | |||
9 | BOT_SIZE=8 | 11 | BOT_SIZE=8 |
10 | BOT_TITLE=input | 12 | BOT_TITLE=input |
11 | 13 | ||
@@ -59,6 +61,10 @@ msgminwait 0 | |||
59 | # More is needed. Xterm? | 61 | # More is needed. Xterm? |
60 | nonblock on | 62 | nonblock on |
61 | 63 | ||
64 | # Enable mouse focus | ||
65 | defmousetrack on | ||
66 | mousetrack on | ||
67 | |||
62 | caption string '%t' | 68 | caption string '%t' |
63 | layout new | 69 | layout new |
64 | split | 70 | split |
@@ -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 | ||
105 | restart_screen_pane() | 111 | restart_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. | 117 | connect_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 | |||
150 | disconnect_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 | |||
119 | connect() | 166 | connect() |
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 | ||
143 | socat_coproc() | 172 | disconnect() |
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 | ||
157 | sendc() | 178 | sendc() |
@@ -166,17 +187,6 @@ send() | |||
166 | printf '%s\n' "$*" >&${SOCAT[1]} | 187 | printf '%s\n' "$*" >&${SOCAT[1]} |
167 | } | 188 | } |
168 | 189 | ||
169 | disconnect() | ||
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 | |||
180 | restart() | 190 | restart() |
181 | { | 191 | { |
182 | start "$@" | 192 | start "$@" |
@@ -187,78 +197,11 @@ start() | |||
187 | foreground "$@" | 197 | foreground "$@" |
188 | } | 198 | } |
189 | 199 | ||
190 | foreground_loop() | 200 | check_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 | |||
212 | forwarding() | ||
213 | { | ||
214 | [ "$FORWARD_PID" ] || return | ||
215 | FORWARD_JOBSPEC=$(jobs -sl | pid_to_jobspec "$FORWARD_PID") | ||
216 | [ "$FORWARD_JOBSPEC" ] | ||
217 | } | ||
218 | |||
219 | forward() | ||
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 | |||
257 | cfmakeraw() | ||
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 | ||
264 | pid_to_jobspec() | 207 | pid_to_jobspec() |
@@ -274,59 +217,90 @@ pid_to_jobspec() | |||
274 | false | 217 | false |
275 | } | 218 | } |
276 | 219 | ||
277 | resume_forward() | 220 | tty_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 | ||
287 | tty_forward() | 227 | SOCAT() |
288 | { | 228 | { |
289 | read-tty | 229 | (exec -a bottom-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) |
230 | x kill -USR1 $$ | ||
231 | } | ||
232 | |||
233 | echosend() | ||
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 | |||
249 | quiet_bg() | ||
250 | { | ||
251 | local STDERR | ||
252 | { | ||
253 | eval "$(printf '%q ' "$@") & 2>&\$STDERR {STDERR}>&-" | ||
254 | } {STDERR}>&2 2>/dev/null | ||
290 | } | 255 | } |
291 | 256 | ||
292 | background() | 257 | background() |
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 | ||
315 | foreground() | 274 | foreground() |
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 | ||
283 | foreground_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 | |||
330 | twopane() | 304 | twopane() |
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 | ||
392 | resize() | ||
393 | { | ||
394 | screen -X resize "$@" | ||
395 | } | ||
396 | |||
418 | our_bashrc_main() | 397 | our_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 | ||