#!/bin/ksh -p
#
# ident "@(#)utset.ksh	1.21 04/10/19 SMI"
#
# Copyright 2002,2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

umask 022
PATH="/usr/sbin:/sbin:/usr/bin:/bin"
export PATH

PROGRAM_ID="${0##*/}"

# TRACING

SETTING_REZ=false
WAIT=10

CleanupAndExit() {
  if $SETTING_REZ; then
    print $UTDEVCTL_CMD $PREV_REZ | $UTLIB/utdevctl > /dev/null
    print ""
    print "Aborted, your original setting has been restored."
  fi

  exit $1
}

trap "CleanupAndExit 1" INT QUIT TERM

Usage() {
  cat 1>&2 <<-!
	Usage: $PROGRAM_ID $PROGRAM_OPTS

		 Audio Out
	         -o s=[ash],v=0:31,b=-32:32,m=[on|off],e=[on|off],T=-6:6,B=-6:6

		 Audio In
	         -i s=[ml],g=0:75,l=0:15,r=0:15,v=0:64

		 Display
	         -d r=<WxH@F>

		 Video In
	         -v b=0:255,c=0:63,C=0:127,t=0:255,f=0:3,T=[on|off]

		 List Supported Display Resolutions
	         -l

	!

  CleanupAndExit 1
}

Error() {
  print -u2 "$PROGRAM_ID: error, $1"
  STATUS=1
  return 0
}

Fatal() {
  print -u2 "$PROGRAM_ID: fatal, $1"
  CleanupAndExit 1
}

SetIFS() {
  IFS_SAVED="$IFS"
  IFS="$1"
  return 0
}

RestoreIFS() {
  IFS="$IFS_SAVED"
  return 0
}

Append() {
  CLI_OPT_ARGS="$CLI_OPT_ARGS $1"

  return 0
}

CheckRange() {

  typeset -i val=$1
  typeset -i low=$2
  typeset -i high=$3

  typeset -i retcode=0

  if [[ $val -lt $low ]] ; then
    retcode=1
  elif [[ $val -gt $high ]] ; then
    retcode=1
  fi

  if [[ $retcode -ne 0 ]] ; then
    Error "$4 setting '$1' is out of range ($2 to $3)"
  fi

  return $retcode
}

AppendOutSelect() {
  # TRACE

  case "$1" in
    a) Append "amp_out 0";;
    s) Append "amp_out 1";;
    h) Append "amp_out 2";;
  esac

  return 0
}

AppendOutVolume() {
  # TRACE

  typeset V

  if CheckRange $1 0 31 "Audio Out Volume"; then
    Append "amp_volume $1"
  fi

  return 0
}

TransMutalate() {
  print $(($1 + $(($2 / 2)) ))
  return 0
}

AppendOutBalance() {
  # TRACE

  typeset V

  if CheckRange $1 -32 32 "Audio Out Balance"; then
    V=$(TransMutalate $1 64)
    Append "amp_balance $V"
  fi

  return 0
}

CheckIsOnOrOff() {
  case "$2" in
  [01]|on|off) return 0;;

  *)
    Error "setting '$1' value '$2', must be: 0, off, 1, on"
    return 1
    ;;
  esac
}

TransOnOff() {
  case "$1" in
    0|off) print "0";;
    1|on)  print "1";;
  esac

  return 0
}

AppendOutMute() {
  # TRACE

  typeset V

  if CheckIsOnOrOff "Mute" $1; then
    V=$(TransOnOff $1)
    Append "amp_mute $V"
  fi

  return 0
}

AppendOutEnhance() {
  # TRACE

  typeset V

  if CheckIsOnOrOff "Enhance" $1; then
    V=$(TransOnOff $1)
    Append "amp_enhance $V"
  fi

  return 0
}

AppendOutTreble() {
  # TRACE

  typeset V

  if CheckRange $1 -6 6 "Audio Out Treble"; then
    V=$(TransMutalate $1 12)
    Append "amp_treble $V"
  fi

  return 0
}

AppendOutBass() {
  # TRACE

  typeset V

  if CheckRange $1 -6 6 "Audio Out Bass"; then
    V=$(TransMutalate $1 12)
    Append "amp_bass $V"
  fi

  return 0
}

SetupAudioOut() {
  # TRACE

  typeset S

  SetIFS ","

  set -- $1
  for S in $*; do
    case "$S" in
    s=[ash])              AppendOutSelect  "${S#*=}";;
    v=[0-9]* | v=-[0-9]*) AppendOutVolume  "${S#*=}";;
    b=[0-9]* | b=-[0-9]*) AppendOutBalance "${S#*=}";;
    m=*)                  AppendOutMute    "${S#*=}";;
    e=*)                  AppendOutEnhance "${S#*=}";;
    T=[0-9]* | T=-[0-9]*) AppendOutTreble  "${S#*=}";;
    B=[0-9]* | B=-[0-9]*) AppendOutBass    "${S#*=}";;
    *)
      Error "unknown Audio Out setting '$S'"
      STATUS=1
      ;;
    esac
  done

  RestoreIFS

  return 0
}

AppendInputSelect() {
  # TRACE

  case "$1" in
    m) Append "input_select 0";;
    l) Append "input_select 1";;
  esac

  return 0
}

AppendGain() {
  # TRACE

  typeset V

  if CheckRange $1 0 75 "Mic Gain"; then
    Append "mic_gain $1"
  fi

  return 0
}

AppendLineInLeft() {
  # TRACE

  typeset V

  if CheckRange $1 0 15 "Line In Gain L"; then
    Append "linein_l_gain $1"
  fi

  return 0
}

AppendLineInRight() {
  # TRACE

  typeset V

  if CheckRange $1 0 15 "Line In Gain R"; then
    Append "linein_r_gain $1"
  fi

  return 0
}

AppendMonitorVolume() {
  # TRACE

  typeset V

  if CheckRange $1 0 64 "Monitor Volume"; then
    Append "sidetone_attn $1"
  fi

  return 0
}

SetupAudioIn() {
  # TRACE

  typeset S

  SetIFS ","

  set -- $1
  for S in $*; do
    case "$S" in
    s=[ml])               AppendInputSelect   "${S#*=}";;
    g=[0-9]* | g=-[0-9]*) AppendGain          "${S#*=}";;
    l=[0-9]* | l=-[0-9]*) AppendLineInLeft    "${S#*=}";;
    r=[0-9]* | r=-[0-9]*) AppendLineInRight   "${S#*=}";;
    v=[0-9]* | v=-[0-9]*) AppendMonitorVolume "${S#*=}";;
    *)
      Error "unknown Audio In setting '$S'"
      STATUS=1
      ;;
    esac
  done

  RestoreIFS

  return 0
}

Transformulate() {
  awk '
    END { printf("%d\n", 256 * x); }
  ' x=$1 </dev/null

  return 0
}

LookupRez() {
  typeset LU_STATUS=0
  typeset R=""
  typeset I=0

  case "$1" in
    s) R="standard";;
    m) R="monitor";;
    *) R="$1";;
  esac

  while (( $I <= $REZ_MAX )); do
    if [[ ${REZ[$I]} == "$R" ]]; then
      print $I
      break
    fi
    I=$(($I + 1))
  done

  if (( $I > $REZ_MAX )); then
    Error "unknown Display Resolution/Refresh setting '$1'"
    LU_STATUS=1
  fi

  return $LU_STATUS
}

AppendDisplayRez() {
  # TRACE

  typeset V

  if V=$(LookupRez $1); then
    Append "disp_rez $V"
  fi

  return 0
}

SetupDisplay() {
  # TRACE

  typeset S

  SetIFS ","

  set -- $1
  for S in $*; do
    case "$S" in
    r=[0-9]*|r=[sma])
      AppendDisplayRez "${S#*=}"
      ;;

    *)
      Error "unknown Display setting '$S'"
      STATUS=1
      ;;
    esac
  done

  RestoreIFS

  return 0
}

AppendVideoBrightness() {
  # TRACE

  if CheckRange $1 0 255 "Video Brightness"; then
    Append "vidin_bright $1"
  fi

  return 0
}

AppendVideoContrast() {
  # TRACE

  if CheckRange $1 0 63 "Video Contrast"; then
    Append "vidin_contrast $1"
  fi

  return 0
}

AppendVideoColor() {
  # TRACE

  if CheckRange $1 0 127 "Video Color"; then
    Append "vidin_color $1"
  fi

  return 0
}

AppendVideoTint() {
  # TRACE

  if CheckRange $1 0 255 "Video Tint"; then
    Append "vidin_tint $1"
  fi

  return 0
}

AppendVideoFilter() {
  # TRACE

  if CheckRange $1 0 3 "Video Filter"; then
    Append "vidin_filter $1"
  fi

  return 0
}

AppendVideoColorTrap() {
  # TRACE

  typeset V

  if CheckIsOnOrOff "Color Trap" $1; then
    V=$(TransOnOff $1)
    Append "vidin_trap $V"
  fi

  return 0
}

SetupVideo() {
  # TRACE

  typeset S

  SetIFS ","

  set -- $1
  for S in $*; do
    case "$S" in
    b=[0-9]* | b=-[0-9]*) AppendVideoBrightness  "${S#*=}";;
    c=[0-9]* | c=-[0-9]*) AppendVideoContrast    "${S#*=}";;
    C=[0-9]* | C=-[0-9]*) AppendVideoColor       "${S#*=}";;
    t=[0-9]* | t=-[0-9]*) AppendVideoTint        "${S#*=}";;
    f=[0-9]* | f=-[0-9]*) AppendVideoFilter      "${S#*=}";;
    T=*)                  AppendVideoColorTrap   "${S#*=}";;
    *)
      Error "unknown Video setting '$S'"
      STATUS=1
      ;;
    esac
  done

  RestoreIFS

  return 0
}

DebugPrint() {
  case "$UTSET_DEBUG" in
    1) print -u2 "DebugPrint($1)";;
  esac
}

UnTransMutalate() {
  expr $1 - $(expr $2 / 2)
}

UnTransOnOff() {
  case "$1" in
    0) print "off";;
    1) print "on";;
  esac

  return 0
}

GetVersion() {
  print "SunRay${1#?*:?*:Corona}"
  return 0
}

PrintStatus() {
  typeset LINE N S
  typeset SN   # Setting Name
  typeset RO   # Read-Only state var

  print status |
  $UTLIB/utdevctl |
  while read LINE; do # {
    set -- $LINE

    case "$1" in
    ok)
      break   # we are done
      ;;

    %*)
      SN="${1#%}"
      RO=true
      ;;

    *)
      SN="$1"
      RO=false
      ;;
    esac

    case "$SN" in # {
    model)
      print -n "Version: $(GetVersion "$2")"
      RO=true
      ;;

    mic_gain)
      print -n "Audio Input Mic Gain: $2"
      ;;

    linein_[lr]_gain)
      case "$1" in
	*_l_*) S="L";;
	*_r_*) S="R";;
      esac
      print -n "Audio Input Line In Gain $S: $2"
      ;;

    input_select)
      case "$2" in
        0) S="Microphone";;
        1) S="Line In";;
      esac
      print -n "Audio Input Select: $S"
      ;;

    sidetone_attn)
      print -n "Audio Input Monitor Volume: $2"
      ;;

    amp_volume)
      print -n "Audio Output Volume: $2"
      ;;

    amp_balance)
      N=$(UnTransMutalate $2 64)
      print -n "Audio Output Balance: $N"
      ;;

    amp_treble)
      N=$(UnTransMutalate $2 12)
      print -n "Audio Output Treble: $N"
      ;;

    amp_bass)
      N=$(UnTransMutalate $2 12)
      print -n "Audio Output Bass: $N"
      ;;

    amp_out)
      case "$2" in
        0) S="Auto";;
        1) S="Speaker";;
        2) S="Headphone";;
        3) S="Speaker and Headphone";;
      esac
      print -n "Audio Output Select: $S"
      ;;

    amp_mute)
      S="$(UnTransOnOff $2)"
      print -n "Audio Output Mute: $S"
      ;;

    amp_enhance)
      S="$(UnTransOnOff $2)"
      print -n "Audio Output Stereo Enhance: $S"
      ;;

    mouse_thresh)
      print -n "Mouse Threshold: $2"
      ;;

    mouse_velocity)
      # Note the explicit decimal point in the denominator.  This forces
      # awk to do FP arithmetic rather than integer arithmetic.
      awk '
	END {
	  printf("Mouse Acceleration: %3.1f", n / 256.0);
        }
      ' n=$2 </dev/null
      ;;

    disp_rez)
      N="$2"
      print -n "Display Resolution / Refresh Rate: ${REZ[$N]}"
      ;;

    disp_blank)
      print -n "Display Blanking: $S"
      ;;

    vidin_bright)
      print -n "Video Brightness: $2"
      ;;

    vidin_contrast)
      print -n "Video Contrast: $2"
      ;;

    vidin_color)
      print -n "Video Color: $2"
      ;;

    vidin_tint)
      print -n "Video Tint: $2"
      ;;

    vidin_filter)
      print -n "Video Filter: $2"
      ;;

    vidin_trap)
      S="$(UnTransOnOff $2)"
      print -n "Video Color Trap: $S"
      ;;

    vidin_select|vidin_colorkill)
      continue
      ;;
    esac # }

    if $RO; then
      print " (read only)"
    else
      print ""
    fi
  done # }

  return 0
}

GetCurrentRez() {
  print status | $UTLIB/utdevctl | sed -n '/^disp_rez /s///p'
}

REZ_MAX=0

CreateREZ() {

  # The "natural" way to do this is to pipe the output of 'utresdef'
  # into a 'while read FOO ..." loop.  That's how it was originally
  # done on Solaris.  Unfortunately on Linux the 'while' loop gets
  # executed in a subshell so the REZ[] array is lost when the loop
  # (and subshell) terminates.  Instead we use 'awk' to construct a
  # string containing the desired series of shell variable assignments
  # (of the form "REZ[n]=timing_n ; REZ[x]=timing_x ; ... ; REZ_MAX=z")
  # and then we 'eval' that string.

  eval ` /etc/opt/SUNWut/basedir/sbin/utresdef -x | \
	awk '{printf "REZ[%d]=%s ; ",$1,$2 ; if ($1>MAX) {MAX=$1}} \
		END {printf "REZ_MAX=%d", MAX}' `
  return 0
}

# main() {

STATUS=0

SUNWUT_DIR=/etc/opt/SUNWut/basedir

export TEXTDOMAIN="$PROGRAM_ID"
export TEXTDOMAINDIR="$SUNWUT_DIR/lib/locale"

OPTSTR="lo:i:m:d:fv:"
PROGRAM_OPTS="[-l] [-o arg,...] [-i arg,...] [-d arg,...] [-f] [-v arg,...]"

LIST=false
DO_AUDIO_OUT=false; AUDIO_OUT_ARG=""
DO_AUDIO_IN=false;   AUDIO_IN_ARG=""
DO_DISPLAY=false;     DISPLAY_ARG=""
FORCE=false
DO_VIDEO=false;         VIDEO_ARG=""

while getopts $OPTSTR OPT 2>&-; do
  case "$OPT" in
    l) LIST=true;;
    o) DO_AUDIO_OUT=true; AUDIO_OUT_ARG="$OPTARG";;
    i) DO_AUDIO_IN=true;   AUDIO_IN_ARG="$OPTARG";;
    d) DO_DISPLAY=true;     DISPLAY_ARG="$OPTARG";;
    f) FORCE=true;;
    v) DO_VIDEO=true;         VIDEO_ARG="$OPTARG";;
    ?) Usage;;
  esac
done
shift $(($OPTIND - 1))

if (( $# != 0 )); then
  Usage
fi

CreateREZ

if $LIST; then
  (
    for R in ${REZ[*]}; do
      S=""
      case "$R" in
        standard)  R="s"; S="(standard)";;
        monitor)   R="m"; S="(monitor)";;
      esac
      print "$R $S"
    done
  ) |
  (export LC_ALL=C; sort -t 'x' +0 -1n +1 -2n +2 -3n)

  exit 0
fi

CLI_OPT_ARGS=""

if $DO_AUDIO_OUT; then SetupAudioOut "$AUDIO_OUT_ARG"; fi
if $DO_AUDIO_IN;  then SetupAudioIn  "$AUDIO_IN_ARG";  fi
if $DO_DISPLAY;   then SetupDisplay  "$DISPLAY_ARG";   fi
if $DO_VIDEO;     then SetupVideo    "$VIDEO_ARG";     fi

if (( $STATUS == 0 )); then
  if [[ -n "$UTLIB_DEBUG" ]]; then
    UTLIB="$UTLIB_DEBUG"
  else
    UTLIB="$SUNWUT_DIR/lib"
  fi

  case "$DO_AUDIO_OUT $DO_AUDIO_IN $DO_DISPLAY $DO_VIDEO" in
  *true*)
    #
    # a category opt has been spec'd
    #
    UTDEVCTL_CMD=""

    for TOKEN in $CLI_OPT_ARGS; do
      if [[ -z "$UTDEVCTL_CMD" ]]; then
        UTDEVCTL_CMD="$TOKEN"
      else
	if ! $FORCE; then
          case "$UTDEVCTL_CMD" in
	  disp_rez)
	    PREV_REZ="$(GetCurrentRez)"

	    print ""
	    print "Your resolution / refresh rate setting will now be changed."
	    print "If your display does not reappear correctly, please wait"
	    print "$WAIT seconds and your original settings will be restored."

            SETTING_REZ=true
	    ksh -c "sleep $WAIT; kill $$" &
	    BG_PID=$!
	    ;;
	  esac
	fi

        print $UTDEVCTL_CMD $TOKEN | $UTLIB/utdevctl > /dev/null

	if ! $FORCE; then
          case "$UTDEVCTL_CMD" in
	  disp_rez)
	    print ""
	    read "REPLY?Do you want to keep this setting (y,[n])? "

	    case "$REPLY" in
	    y) ;; # keep it

	    *)
              print $UTDEVCTL_CMD $PREV_REZ | $UTLIB/utdevctl > /dev/null
	      print ""
	      print "Your original setting has been restored."
	      ;;
	    esac

	    if ps -p $BG_PID 1>&-; then
	      kill $BG_PID
	    fi
            SETTING_REZ=false
	    ;;
	  esac
	fi

        UTDEVCTL_CMD=""
      fi
    done
    ;;

  *)
    #
    # no category opt, output dtu status
    #
    PrintStatus
    ;;
  esac
fi

CleanupAndExit $STATUS

# }
