#!/bin/bash set -e set -f set -o pipefail shopt -s lastpipe PROGNAME=${0##*/} BOT_SIZE=8 BOT_TITLE=input BOT_CMD=start TOP_CMD='PS1="$TOP_TITLE\$ " bash --noprofile --norc -i' TOP_TITLE=output TWOPANE=$(mktemp -d) export TWOPANE TOP_CMD BOT_CMD TOP_TITLE BOT_TITLE BOT_SIZE trap 'rm -r "$TWOPANE"' EXIT STY=twopane.${TWOPANE##*/} save_file() { cat > "$TWOPANE"/"${1:?$PROGNAME: Error: filename cannot be empty string}" } save_screenrc() { save_file screenrc"${1:+.$1}" } save_screenrc <<'.' unbindall escape \0\0 caption string '%t' layout new split focus bottom resize $BOT_SIZE screen -ln -t "$BOT_TITLE" 0 bash --noprofile --rcfile "$TWOPANE"/bashrc -i source "$TWOPANE"/screenrc.startpane . save_screenrc 'startpane' <<'.' focus top screen -ln -t "$TOP_TITLE" 1 sh -c "$TOP_CMD" exec :!. socat -u UNIX-RECV:"$TWOPANE"/socket STDOUT title 'output' focus bottom layout save 0 . save_screenrc 'restart' <<. layout new split focus bottom resize $BOT_SIZE select 0 title 'input' source "$TWOPANE"/screenrc.startpane . TWOPANE_FUNCTION_EXPORTS='restart sendp sendc send start' restart() { screen -X source "$TWOPANE"/screenrc.restart } sendp() { socat -u STDIN UNIX-SENDTO:"$TWOPANE"/socket } sendc() { [ "${COPROC[1]}" ] || coproc sendp printf '%s' "$*" >&${COPROC[1]} } send() { [ "${COPROC[1]}" ] || coproc sendp printf '%s' "$*"$'\n' >&${COPROC[1]} } start() { [ "${COPROC[1]}" ] || coproc sendp while read -r -N1 &${COPROC[1]} } save_file bashrc <<. set -f set -o pipefail shopt -s lastpipe trap 'COPROC=()' SIGPIPE trap "screen -X quit" EXIT $(declare -f $TWOPANE_FUNCTION_EXPORTS | sed 's/ COPROC / /') export PS1="$BOT_TITLE\$ " \$BOT_CMD . main() { screen -c "$TWOPANE"/screenrc -m -S "$STY" -ln } main "$@"