#!/bin/ksh
# @(#) bell.ksh 1.0 93/04/03
# 93/03/07 john h. dubois iii (john@armory.com)
# 93/03/13 Warn if visual is attempted on a non-console tty.
# 93/04/02 When given visual, make screens switch to other screens on the
#          same adapter.  When given "visual console", map only screens
#          that can be opened and are not running line discipline 2 (ev).
# 93/04/03 Work around need for /dev/stdin

set -A AdaptNames vga ega cga mono

# Sets global Adapters[] to the types of adapters present
function FindAdapters {
    typeset Name
    typeset -i i=0 j=0
    while [ i -lt ${#AdaptNames[*]} ]; do
	( < /dev/${AdaptNames[i]} ) > /dev/null 2>&1 &&
	Adapters[i]=${AdaptNames[i]}
	let i+=1
    done
    [ ${#Adapters[*]} -gt 2 ] &&
    echo "Invalid result: more than two adapters present?!"
}

# Usage: FindConsTTYTypes ttyname ...
# Mono[1..m] and Color[1..c] are set to the tty numbers of the mono and
# color ttys.  IsMono[ttynum] is set to 1 for mono or 0 for color for
# each tty number.
function FindConsTTYTypes {
    typeset -Z2 ttynum
    typeset -i mi=1 ci=1 l w c

    for tty; do
	tty=${tty#/dev/}
	ttynum=${tty##*([!0-9])}
	vidi -d font8x8 < "/dev/$tty" 2>/dev/null | tr | wc | read l w c
	# c (number of non-null chars emitted by vidi) will be
	# 0 if the input was a mono adapter
	if [ $c = 0 ]; then
	    Mono[mi]=$ttynum
	    IsMono[ttynum]=1
	    let mi+=1
	else
	    Color[ci]=$ttynum
	    IsMono[ttynum]=0
	    let ci+=1
	fi
	let ttynum+=1
    done
}

# Usage:
# ReplaceBell ttyname sequence
# ttyname is the console tty to change the bell sequence on.
# sequence is the sequence to replace the bell with.
# If sequence is not given, bell mapping is turned off.
function ReplaceBell {

typeset tty=/dev/${1#/dev/}

if [ ! -r $tty ]; then
    echo "Cannot read $tty to get current mapping."
    return
fi

# Generate tmpfile name if we need one
[ ! -r /dev/stdin ] && tmpfile=bell.$$ || tmpfile=

mapchan $tty | awk '
$0 == "output" { 
    InOutput = 1
}

# If replacement has not been done yet and we are in the output section
# and (the old bell mapping or the end of the section) has been found...
!Done && InOutput && ($1 ~ "^(0x7|dead|compose|control)$") {
    if (replacement != "")
	map = map "7 " replacement "\n"
    Done = 1
    if ($1 == "0x7")	# Replacing bell sequence, so do not print old one
	next
}

# Channel previously had no mapping
$0 == "null" {
    if (replacement != "")
	map = map "input\noutput\n7 " replacement "\n"
    Done = 1
    exit 0
}

{
    map = map $0 "\n"
}

END {
    if (!Done) {
	# Old map had only input & output sections & no old map for bell
	if (InOutput) {
	    if (replacement != "")
		map = map "7 " replacement "\n"
	}
	else {
	    print "Error interpreting map." | "cat 1>&2"
	    exit 1
	}
    }
    if (tmpfile == "") {
	procname = "mapchan -f /dev/stdin " tty
	print map | procname
    }
    else {
	print map > tmpfile
	system("mapchan -f " tmpfile " " tty "; rm " tmpfile)
    }
}
' replacement="$2" tty="$tty" tmpfile=$tmpfile
}

name=${0##*/}

typeset -i verbose=0
if [ "$1" = -v ]; then
    verbose=1
    shift
fi

cmd=$1

case "$2" in
console)
    # Map all console ttys that are readable and are not using the event driver
    for tty in /dev/tty[01][0-9]; do
	( < $tty ) 2>/dev/null && [[ "`stty < $tty`" != *"line = 2"* ]] &&
	ttys="$ttys $tty"
    done
    ;;
"")
    ttys=$(tty)
    ;;
*)
    shift
    ttys=$*
    ;;
esac

case "$cmd" in
on)
    for tty in $ttys; do
	ReplaceBell $tty
    done
    ;;
off)
    for tty in $ttys; do
	ReplaceBell $tty 1
    done
    ;;
visual)
    typeset -Z2 SwitchTo ScreenNum

    FindAdapters
    [ ${#Adapters[*]} -ne 1 ] && FindConsTTYTypes $ttys
    Last[0]=${Color[${#Color[*]}]}
    Last[1]=${Mono[${#Mono[*]}]}
    First[0]=${Color[1]}
    First[1]=${Mono[1]}
    for tty in $ttys; do
	tty=${tty#/dev/}
	ttynum=${tty##*([!0-9])}
	if [[ "$ttynum" != [01][0-9] ]]; then
	    echo "$tty: Not a console tty."
	    continue
	fi
	if [ ttynum -eq ${Last[${IsMono[ttynum]}]} ]; then
	    SwitchTo=${First[${IsMono[ttynum]}]}
	else
	    SwitchTo=${Last[${IsMono[ttynum]}]}
	fi
	[ verbose -eq 1 ] && echo "$tty switches to tty$SwitchTo and back."
	ScreenNum=$((ttynum-1))
	SwitchTo=$((SwitchTo-1))
	Sequence="033 '[' '${SwitchTo%?}' '${SwitchTo#?}' 'z'"
	Sequence="$Sequence 033 '[' '${ScreenNum%?}' '${ScreenNum#?}' 'z'"
	ReplaceBell $tty "$Sequence"
    done
    ;;
*)
    echo \
"Usage: $name [-v] [on|off|visual] [console|ttyname ...]
$name modifies the bell mapping on the named tty, or on the current tty if
no ttyname is given.  tty names may be given with or without a leading
/dev/.  If console is given, mapping is attempted on all console ttys
(tty01..tty12).  Use /dev/console to specifically map it.  Mapping cannot
be done on a tty that is using the event driver.  Any non-bell mapping
currently in effect on the tty is not changed.
Options:
off	turns off the bell by mapping the bell character (^G) to ^A,
	which does not display anything on most terminals.
on	turns the bell back on by removing any mapping.
visual	replaces the console bell with a visual indication by mapping
	the bell character to a sequence which switches to another
	tty on the same adapter and back.  If -v is given, the switching
	actions that are set up are reported.  visual should only be used
	on console ttys.  
Note: If \"$name visual console\" is going to be done (mapping bell on all
console ttys to visual bell), the kernel parameter NEMAP should be
increased, since this will result in 12 distinct maps and the default for
NEMAP is 10."
    ;;
esac
