Differences between revisions 2 and 3
Revision 2 as of 2012-02-03 01:43:55
Size: 35789
Editor: dethrophes
Comment: Added a more complex solution that implments a vt100 parser
Revision 3 as of 2012-02-14 22:25:04
Size: 37111
Editor: dethrophes
Comment: Added some clearer cases to show what to expect for function keys.
Deletions are marked like this. Additions are marked like this.
Line 270: Line 270:
          *up) echo "Up Arrow ignoring modifier keys" ;;
          down) echo "Down Arrow No modifier keys" ;;
          S-down) echo "Shift + Down Arrow" ;;
          A-down) echo "Alt + Down Arrow" ;;
          AS-down) echo "Alt + Shift + Down Arrow" ;;
          C-down) echo "Control + Down Arrow" ;;
          CS-down) echo "Control + Shift + Down Arrow" ;;
          CA-down) echo "Control + Alt + Down Arrow" ;;
          CAS-down) echo "Control + Alt + Shift + Down Arrow";;
          MCAS-down) echo "Meta + Control + Alt + Shift + Down Arrow";;
Line 764: Line 774:
         *up) echo "Up Arrow ignoring modifier keys" ;;
          down) echo "Down Arrow No modifier keys" ;;
            S-down) echo "Shift + Down Arrow" ;;
         A-down) echo "Alt + Down Arrow" ;;
          AS-down) echo "Alt + Shift + Down Arrow" ;;
          C-down) echo "Control + Down Arrow" ;;
          CS-down) echo "Control + Shift + Down Arrow" ;;
          CA-down) echo "Control + Alt + Down Arrow" ;;
          CAS-down) echo "Control + Alt + Shift + Down Arrow";;
          MCAS-down) echo "Meta + Control + Alt + Shift + Down Arrow";;

Reading Function Keys in bash

This is sample code to allow more intuitive usage of Function keys in bash. It abstracts the escape sequence messiness for these keys and as such allows for a more portable/intuitive solutions.

#set -o errexit 
#set -o errtrace 
set -o nounset 


  if [ "${S8C1T:-0}" != "1" ] ; then
    declare -gr SS3=$'\eO'      # Single Shift Select of G3 Character Set ( SS3 is 0x8f): affects next character only
    declare -gr CSI=$'\e['      # Control Sequence Introducer ( CSI is 0x9b)
  else
    declare -gr SS3=$'\x8f'     # Single Shift Select of G3 Character Set ( SS3 is 0x8f): affects next character only
    declare -gr CSI=$'\x9b'     # Control Sequence Introducer ( CSI is 0x9b)
  fi


  function  vt100_DECRST {  
    IFS=';' eval 'echo -n "${CSI}?${*?Missing Pm}l"'
  }
  function  vt100_DECSET {  
    IFS=';' eval 'echo -n "${CSI}?${*?Missing Pm}h"'
  }
  mouse_type=(
        [0]=9     ## X10 mouse reporting, for compatibility with X10's xterm, reports on button press.
        [1]=1000  ## X11 mouse reporting, reports on button press and release.
        [2]=1001  ## highlight reporting, useful for reporting mouse highlights
        [3]=1002  ## button movement reporting, reports movement when a button is presse
        [4]=1003  ## all movement reporting, reports all movements.
        [5]=1004  ## FocusIn/FocusOut can be combined with any of the mouse events since it uses a different protocol. When set, it causes xterm to send CSI I when the terminal gains focus, and CSI O when it loses focus.
        [6]=1005  ## Extended mouse mode enables UTF-8 encoding for C x and C y under all tracking modes, expanding the maximum encodable position from 223 to 2015. For positions less than 95, the resulting output is identical under both modes. Under extended mouse mode, positions greater than 95 generate "extra" bytes which will confuse applications which do not treat their input as a UTF-8 stream. Likewise, C b will be UTF-8 encoded, to reduce confusion with wheel mouse events.
    )

  function ord {
    printf -v "${1?Missing Dest Variable}" "${3:-%d}" "'${2?Missing Char}"
  }
  function ord_eascii {
    LC_CTYPE=C ord "${@}"
  }
  function AdjustMousePos {
    local -i _INDEX
    ord_eascii _INDEX "${2}"
    eval ${1}'=$(( ${_INDEX}-32))'
  }

  ###############################
  ##
  ##    READ KEY CRAP
  ##
  ##
  ###############################
  KeyModifiers=(
             [2]="S"   [3]="A"   [4]="AS"   [5]="C"   [6]="CS"  [7]="CA"    [8]="CAS"
    [9]="M" [10]="MS" [11]="MA" [12]="MAS" [13]="MC" [14]="MCS" [15]="MCA" [16]="MCAS"
    )
  KeybFntKeys=(
    [1]="home" [2]="insert" [3]="delete"  [4]="end"   [5]="pageUp" [6]="pageDown"
    [11]="f1"  [12]="f2"    [13]="f3"     [14]="f4"   [15]="f5"
    [17]="f6"  [18]="f7"    [19]="f8"     [20]="f9"   [21]="f10"
    [23]="f11" [24]="f12"   [25]="f13"    [26]="f14"  [28]="f15"
    [29]="f16" [31]="f17"   [32]="f18"    [33]="f19"  [34]="f20"
    )
  SunKeybFntKeys=(
    [214]="home"  [2]="insert" [3]="delete" [4]="end"   [216]="pageUp" [222]="pageDown"
    [224]="f1"  [225]="f2"    [226]="f3"    [227]="f4"  [228]="f5"
    [229]="f6"  [230]="f7"    [231]="f8"    [232]="f9"  [233]="f10"
    [192]="f11" [193]="f12"   [218]="keypad-five" [220]="keypad-delete"
    )
  KeybFntKeysAlt=(
    # A          B              C               D             E                   F             H         
    [0x41]="up" [0x42]="down" [0x43]="right" [0x44]="left" [0x45]="keypad-five" [0x46]="end" [0x48]="home"     
    # I               O
    [0x49]="InFocus" [0x4f]="OutOfFocus"      
    # P           Q           R           S             Z          
    [0x50]="f1" [0x51]="f2" [0x52]="f3" [0x53]="f4"  [0x5a]="S-HT" 
    )
  C0CtrlChars=(
    [0x00]="Null" [0x01]="SOH" [0x02]="STX" [0x03]="ETX" [0x04]="EOT" [0x05]="ENQ" [0x06]="ACK" 
    [0x07]="BEL"  [0x08]="BS"  [0x09]="HT"  [0x0A]="LF"  [0x0B]="VT"  [0x0C]="FF"  [0x0D]="CR"  
    [0x0E]="SO"   [0x0F]="SI"  [0x10]="DLE" [0x11]="DC1" [0x12]="DC2" [0x13]="DC3" [0x14]="DC4" 
    [0x15]="NAK"  [0x16]="SYN" [0x17]="ETB" [0x18]="CAN" [0x19]="EM"  [0x1A]="SUB" [0x1B]="ESC" 
    [0x1C]="FS"   [0x1D]="GS"  [0x1E]="RS"  [0x1F]="US"  [0x20]="SP"  [0x7F]="DEL" 
  )
  C0CtrlCharsAlt=(
    [0x01]="C-A" [0x02]="C-B" [0x03]="C-C" [0x04]="C-D" [0x05]="C-E" [0x06]="C-F" [0x07]="C-G" 
    [0x08]="C-H" [0x09]="C-I" [0x0a]="C-J" [0x0b]="C-K" [0x0c]="C-L" [0x0d]="C-M" [0x0e]="C-N"  
    [0x0f]="C-O" [0x10]="C-P" [0x11]="C-Q" [0x12]="C-R" [0x13]="C-S" [0x14]="C-T" [0x15]="C-U" 
    [0x16]="C-V" [0x17]="C-W" [0x18]="C-X" [0x19]="C-Y" [0x1a]="C-Z" [0x1b]="C-[" [0x1c]="C-]" 
    [0x1d]="C-}" [0x1e]="C-^" [0x1f]="C-_" [0x20]="C-SP"  [0x7F]="DEL" 
  )
  C1CtrlCharsEsc=(
    [0x40]="PAD"  [0x41]="HOP"  [0x42]="BPH" [0x43]="NBH" 
    [0x44]="IND"  [0x45]="NEL"  [0x46]="SSA" [0x47]="ESA" 
    [0x48]="HTS"  [0x49]="HTJ"  [0x4A]="VTS" [0x4B]="PLD" 
    [0x4C]="PLU"  [0x4D]="RI"   [0x4E]="SS2" [0x4F]="SS3" 
    [0x50]="DCS"  [0x51]="PU1"  [0x52]="PU2" [0x53]="STS" 
    [0x54]="CCH"  [0x55]="MW"   [0x56]="SPA" [0x57]="EPA" 
    [0x58]="SOS"  [0x59]="SGCI" [0x5A]="SCI" [0x5B]="CSI" 
    [0x5C]="ST"   [0x5D]="OSC"  [0x5E]="PM"  [0x5F]="APC" 
  )
  C1CtrlChars=(
    [0x80]="PAD"  [0x81]="HOP"  [0x82]="BPH" [0x83]="NBH" 
    [0x84]="IND"  [0x85]="NEL"  [0x86]="SSA" [0x87]="ESA" 
    [0x88]="HTS"  [0x89]="HTJ"  [0x8A]="VTS" [0x8B]="PLD" 
    [0x8C]="PLU"  [0x8D]="RI"   [0x8E]="SS2" [0x8F]="SS3" 
    [0x90]="DCS"  [0x91]="PU1"  [0x92]="PU2" [0x93]="STS" 
    [0x94]="CCH"  [0x95]="MW"   [0x96]="SPA" [0x97]="EPA" 
    [0x98]="SOS"  [0x99]="SGCI" [0x9A]="SCI" [0x9B]="CSI" 
    [0x9C]="ST"   [0x9D]="OSC"  [0x9E]="PM"  [0x9F]="APC" 
  )    
  C1CtrlCharsAlt=(
    [0x01]="CA-A" [0x02]="CA-B" [0x03]="CA-C" [0x04]="CA-D"  [0x05]="CA-E" [0x06]="CA-F" [0x07]="CA-G" 
    [0x08]="CA-H" [0x09]="CA-I" [0x0a]="CA-J" [0x0b]="CA-K"  [0x0c]="CA-L" [0x0d]="CA-M" [0x0e]="CA-N"  
    [0x0f]="CA-O" [0x10]="CA-P" [0x11]="CA-Q" [0x12]="CA-R"  [0x13]="CA-S" [0x14]="CA-T" [0x15]="CA-U" 
    [0x16]="CA-V" [0x17]="CA-W" [0x18]="CA-X" [0x19]="CA-Y"  [0x1a]="CA-Z" [0x1b]="CA-[" [0x1c]="CA-]" 
    [0x1d]="CA-}" [0x1e]="CA-^" [0x1f]="CA-_" [0x20]="CA-SP" [0x7F]="A-DEL" 
  )
  MouseButtons=(
    [0x00]="MB1-P" [0x01]="MB2-P" [0x02]="MB3-P" [0x03]="MB-R"
    [0x20]="MB1-M" [0x21]="MB2-M" [0x22]="MB3-M" [0x23]="MB-M"
    [0x40]="MB4-P" [0x41]="MB5-P" 
  )
  MouseMetaButtons=(
    [0x04]="S-"    [0x08]="A-"    [0x0c]="AS-" 
    [0x10]="C-"    [0x14]="CS-"   [0x1c]="CAS-"
  )
  function GetMouseButton {
    local MouseBtn
    AdjustMousePos MouseBtn "${2}"
    MouseBtn="${MouseMetaButtons[$(( ${MouseBtn} & 0x1C))]-}${MouseButtons[$(( ${MouseBtn} & 0xe3))]}"
    eval ${1}='"${MouseBtn}"'
  }
  mouse_on="$(vt100_DECSET ${mouse_type[1]})"
  mouse_off="$(vt100_DECRST "${mouse_type[1]}" )"


  function ReadKey {
    unset UInput[@]
    local escapeSequence 
    local REPLY 

    echo -n "${mouse_on}"
    if IFS='' read  -srN1 ${1:-} escapeSequence; then
      case "${escapeSequence}" in
        [^[:cntrl:]]) 
          UInput[0]="${escapeSequence}" 
          ;;
        $'\e')
          while IFS='' read -srN1 -t0.0001 ; do
            escapeSequence+="${REPLY}"
          done
          case "${escapeSequence}" in
              $'\e'[^[:cntrl:]]) echo -n "A-${escapeSequence:1}" ;;
              ${CSI}t) 
                UInput[0]="MouseTrack"
                AdjustMousePos UInput[1] "${escapeSequence:3:1}"
                AdjustMousePos UInput[2] "${escapeSequence:4:1}"
                ;;
              ${CSI}T) 
                UInput[0]="MouseTrack"
                AdjustMousePos UInput[1] "${escapeSequence:3:1}"
                AdjustMousePos UInput[2] "${escapeSequence:4:1}"
                AdjustMousePos UInput[3] "${escapeSequence:5:1}"
                AdjustMousePos UInput[4] "${escapeSequence:6:1}"
                AdjustMousePos UInput[5] "${escapeSequence:7:1}"
                AdjustMousePos UInput[6] "${escapeSequence:8:1}"
                ;;
              ${CSI}M*)  
                GetMouseButton UInput[0] "${escapeSequence:3:1}"
                if [ -n "${UInput[0]}" ]; then  
                  AdjustMousePos UInput[1] "${escapeSequence:4:1}"
                  AdjustMousePos UInput[2] "${escapeSequence:5:1}"
                else
                  UInput[0]=$(printf 'Mouse-\\x%02x %q'  "'${escapeSequence:3:1}" "${escapeSequence:4}")
                fi
                ;;
              ${CSI}[0-9]*[ABCDEFHIOZPQRSz~])
                local CSI_Params=( ${escapeSequence//[!0-9]/ } )
                local CSI_Func="${escapeSequence:${#escapeSequence}-1}"
                case "${CSI_Func}" in
                  z) # Sun Function Keys
                    UInput[0]="${SunKeybFntKeys[${CSI_Params[0]}]-}"
                    if [ -n "${UInput[0]}" ]; then
                      [ ${#CSI_Params[@]} -le 1 ] ||  UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
                    else
                      UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
                    fi
                    ;;
                  '~') # Function Keys
                    UInput[0]="${KeybFntKeys[${CSI_Params[0]}]-}"
                    if [ -n "${UInput[0]}" ]; then
                      [ ${#CSI_Params[@]} -le 1 ] ||  UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
                    else
                      UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
                    fi
                    ;;
                  A|B|C|D|E|F|H|I|O|Z|P|Q|R|S)
                    ord_eascii CSI_Func "${CSI_Func}"
                    UInput[0]="${KeybFntKeysAlt[${CSI_Func}]}"
                    if [ -n "${UInput[0]}" ]; then
                      [ ${#CSI_Params[@]} -le 1 ] ||  UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
                    else
                      UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
                    fi
                    ;;
                  *)
                    UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
                    ;;
                esac
                ;;
              ${SS3}*[ABCDEFHPQRSIO~])
                local SS3_Params=( ${escapeSequence//[!0-9]/ } )
                local SS3_Func="${escapeSequence:${#escapeSequence}-1}"
                case "${SS3_Func}" in
                  A|B|C|D|E|F|H|P|Q|R|S|~)
                    ord_eascii SS3_Func "${SS3_Func}"
                    UInput[0]="${KeybFntKeysAlt[${SS3_Func}]-}"
                    if [ -n "${UInput[0]}" ]; then
                      [ ${#SS3_Params[@]} -lt 1 ] ||  UInput[0]="${KeyModifiers[${SS3_Params[0]}]}-${UInput[0]}"
                    else
                      UInput[0]="SS3 ${SS3_Params[*]-} ${SS3_Func}"
                    fi
                    ;;
                  *)
                    UInput[0]="SS3 ${SS3_Params[*]-} ${SS3_Func}"
                    ;;
                esac
                ;;
              $'\e'[[:cntrl:]])
                ord_eascii UInput[0] "${escapeSequence:1:1}"
                UInput[0]="${C1CtrlCharsAlt[${UInput[0]}]:-}"
                [ -n "${UInput[0]:-}" ] ||  UInput[0]="$(printf "%q" "${escapeSequence}")"
                ;;
              $'\e') UInput[0]="ESC" ;;
              *)
                UInput[0]="$(printf "%q" "${escapeSequence}")"
                ;;
          esac
          ;;
        *)
          ord_eascii UInput[0] "${escapeSequence}"
          UInput[0]="${C0CtrlChars[${UInput[0]}]:-}"
          [ -n "${UInput[0]:-}" ] ||  UInput[0]="$(printf '%q' "'${escapeSequence}")"
          ;;
      esac
    fi
    echo -n "${mouse_off}"
  }

  • The ReadKey function can then be called in this manor.

  •   function HandleKey {
        local -a UInput
        while true; do
          if ReadKey ; then
            case "${UInput[0]:-}" in
              CR|NULL|LF|q) 
                echo "\"${UInput[*]-}\""
                break
                ;;
              *up)       echo "Up Arrow ignoring modifier keys"  ;;
              down)      echo "Down Arrow No modifier keys"      ;;
              S-down)    echo "Shift + Down Arrow"               ;;
              A-down)    echo "Alt + Down Arrow"                 ;;
              AS-down)   echo "Alt + Shift + Down Arrow"         ;;
              C-down)    echo "Control + Down Arrow"             ;;
              CS-down)   echo "Control + Shift + Down Arrow"      ;;
              CA-down)   echo "Control + Alt + Down Arrow"        ;;
              CAS-down)  echo "Control + Alt + Shift + Down Arrow";;
              MCAS-down) echo "Meta + Control + Alt + Shift + Down Arrow";;
              *)
                echo "\"${UInput[*]-}\""
                ;;
            esac
          fi
        done
      }
    HandleKey

A more complex solution using a full vt100 parser.

declare -gri VTPARSE_ACTION_CLEAR="1"
declare -gri VTPARSE_ACTION_COLLECT="2"
declare -gri VTPARSE_ACTION_CSI_DISPATCH="3"
declare -gri VTPARSE_ACTION_ESC_DISPATCH="4"
declare -gri VTPARSE_ACTION_ESC_EXECUTE="5"
declare -gri VTPARSE_ACTION_EXECUTE="6"
declare -gri VTPARSE_ACTION_HOOK="7"
declare -gri VTPARSE_ACTION_IGNORE="8"
declare -gri VTPARSE_ACTION_OSC_END="9"
declare -gri VTPARSE_ACTION_OSC_PUT="10"
declare -gri VTPARSE_ACTION_OSC_START="11"
declare -gri VTPARSE_ACTION_PARAM="12"
declare -gri VTPARSE_ACTION_PRINT="13"
declare -gri VTPARSE_ACTION_PUT="14"
declare -gri VTPARSE_ACTION_SS2_DISPATCH="15"
declare -gri VTPARSE_ACTION_SS3_DISPATCH="16"
declare -gri VTPARSE_ACTION_UNHOOK="17"

declare -gra VTPARSE_ACTION_NAMES=(
   [0 ]="<no action>"
   [${VTPARSE_ACTION_CLEAR}]="CLEAR"
   [${VTPARSE_ACTION_COLLECT}]="COLLECT"
   [${VTPARSE_ACTION_CSI_DISPATCH}]="CSI_DISPATCH"
   [${VTPARSE_ACTION_ESC_DISPATCH}]="ESC_DISPATCH"
   [${VTPARSE_ACTION_ESC_EXECUTE}]="ESC_EXECUTE"
   [${VTPARSE_ACTION_EXECUTE}]="EXECUTE"
   [${VTPARSE_ACTION_HOOK}]="HOOK"
   [${VTPARSE_ACTION_IGNORE}]="IGNORE"
   [${VTPARSE_ACTION_OSC_END}]="OSC_END"
   [${VTPARSE_ACTION_OSC_PUT}]="OSC_PUT"
   [${VTPARSE_ACTION_OSC_START}]="OSC_START"
   [${VTPARSE_ACTION_PARAM}]="PARAM"
   [${VTPARSE_ACTION_PRINT}]="PRINT"
   [${VTPARSE_ACTION_PUT}]="PUT"
   [${VTPARSE_ACTION_SS2_DISPATCH}]="SS2_DISPATCH"
   [${VTPARSE_ACTION_SS3_DISPATCH}]="SS3_DISPATCH"
   [${VTPARSE_ACTION_UNHOOK}]="UNHOOK"
)
declare -gri VTPARSE_STATE_ANYWHERE="0"
declare -gri VTPARSE_STATE_CSI_ENTRY="1"
declare -gri VTPARSE_STATE_CSI_IGNORE="2"
declare -gri VTPARSE_STATE_CSI_INTERMEDIATE="3"
declare -gri VTPARSE_STATE_CSI_PARAM="4"
declare -gri VTPARSE_STATE_DCS_ENTRY="5"
declare -gri VTPARSE_STATE_DCS_IGNORE="6"
declare -gri VTPARSE_STATE_DCS_INTERMEDIATE="7"
declare -gri VTPARSE_STATE_DCS_PARAM="8"
declare -gri VTPARSE_STATE_DCS_PASSTHROUGH="9"
declare -gri VTPARSE_STATE_ESCAPE="10"
declare -gri VTPARSE_STATE_ESCAPE_INTERMEDIATE="11"
declare -gri VTPARSE_STATE_GROUND="12"
declare -gri VTPARSE_STATE_OSC_STRING="13"
declare -gri VTPARSE_STATE_SOS_PM_APC_STRING="14"
declare -gri VTPARSE_STATE_SS2_ENTRY="15"
declare -gri VTPARSE_STATE_SS3_ENTRY="16"
declare -gri VTPARSE_STATE_SS3_PARAM="17"

declare -gra VTPARSE_STATE_NAMES=(
   [${VTPARSE_STATE_ANYWHERE}]="ANYWHERE"
   [${VTPARSE_STATE_CSI_ENTRY}]="CSI_ENTRY"
   [${VTPARSE_STATE_CSI_IGNORE}]="CSI_IGNORE"
   [${VTPARSE_STATE_CSI_INTERMEDIATE}]="CSI_INTERMEDIATE"
   [${VTPARSE_STATE_CSI_PARAM}]="CSI_PARAM"
   [${VTPARSE_STATE_DCS_ENTRY}]="DCS_ENTRY"
   [${VTPARSE_STATE_DCS_IGNORE}]="DCS_IGNORE"
   [${VTPARSE_STATE_DCS_INTERMEDIATE}]="DCS_INTERMEDIATE"
   [${VTPARSE_STATE_DCS_PARAM}]="DCS_PARAM"
   [${VTPARSE_STATE_DCS_PASSTHROUGH}]="DCS_PASSTHROUGH"
   [${VTPARSE_STATE_ESCAPE}]="ESCAPE"
   [${VTPARSE_STATE_ESCAPE_INTERMEDIATE}]="ESCAPE_INTERMEDIATE"
   [${VTPARSE_STATE_GROUND}]="GROUND"
   [${VTPARSE_STATE_OSC_STRING}]="OSC_STRING"
   [${VTPARSE_STATE_SOS_PM_APC_STRING}]="SOS_PM_APC_STRING"
   [${VTPARSE_STATE_SS2_ENTRY}]="SS2_ENTRY"
   [${VTPARSE_STATE_SS3_ENTRY}]="SS3_ENTRY"
   [${VTPARSE_STATE_SS3_PARAM}]="SS3_PARAM"
)
declare -gra VTPARSE_STATE_TABLE=(
  [${VTPARSE_STATE_ANYWHERE}]="0000000000000000000000000000000000000000000000006c006c0a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c6c6c6c6c6c6c6c6c6c6c6c6c6c0f6c056c6c6c6c6c6c6c0e6c6c016c0d0e0e"
  [${VTPARSE_STATE_CSI_ENTRY}]="606060606060606060606060606060606060606060606060006000006060606023232323232323232323232323232323c4c4c4c4c4c4c4c4c4c402c4242424243c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c80"
  [${VTPARSE_STATE_CSI_IGNORE}]="606060606060606060606060606060606060606060606060006000006060606080808080808080808080808080808080808080808080808080808080808080800c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c80"
  [${VTPARSE_STATE_CSI_INTERMEDIATE}]="606060606060606060606060606060606060606060606060006000006060606020202020202020202020202020202020020202020202020202020202020202023c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c80"
  [${VTPARSE_STATE_CSI_PARAM}]="606060606060606060606060606060606060606060606060006000006060606023232323232323232323232323232323c0c0c0c0c0c0c0c0c0c002c0020202023c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c80"
  [${VTPARSE_STATE_DCS_ENTRY}]="808080808080808080808080808080808080808080808080008000008080808027272727272727272727272727272727c8c8c8c8c8c8c8c8c8c806c82828282809090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090980"
  [${VTPARSE_STATE_DCS_IGNORE}]="8080808080808080808080808080808080808080808080800080000080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000000000000000000000000000000000000000000000000000000c"
  [${VTPARSE_STATE_DCS_INTERMEDIATE}]="8080808080808080808080808080808080808080808080800080000080808080202020202020202020202020202020200606060606060606060606060606060609090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090980"
  [${VTPARSE_STATE_DCS_PARAM}]="808080808080808080808080808080808080808080808080008000008080808027272727272727272727272727272727c0c0c0c0c0c0c0c0c0c006c00606060609090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090980"
  [${VTPARSE_STATE_DCS_PASSTHROUGH}]="e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e000e00000e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e080000000000000000000000000000000000000000000000000000000000c"
  [${VTPARSE_STATE_ESCAPE}]="50505050505050505050505050505050505050505050505000500000505050502b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c0f0g054c4c4c4c4c4c4c0e4c4c014c0d0e0e4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c5c"
  [${VTPARSE_STATE_ESCAPE_INTERMEDIATE}]="6060606060606060606060606060606060606060606060600060000060606060202020202020202020202020202020204c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c80"
  [${VTPARSE_STATE_GROUND}]="6060606060606060606060606060606060606060606060600060000060606060d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d06060606060606060606060606060606000606060606060606060600060"
  [${VTPARSE_STATE_OSC_STRING}]="8080808080808080808080808080808080808080808080800080000080808080a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000000000000000000000000000000000000000000000000000000c"
  [${VTPARSE_STATE_SOS_PM_APC_STRING}]="8080808080808080808080808080808080808080808080800080000080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000000000000000000000000000000000000000000000000000000c"
  [${VTPARSE_STATE_SS2_ENTRY}]="60606060606060606060606060606060606060606060606000600000606060600000000000000000000000000000000000000000000000000000000000000000fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfc80"
  [${VTPARSE_STATE_SS3_ENTRY}]="606060606060606060606060606060606060606060606060006000006060606000000000000000000000000000000000chchchchchchchchchch000000000000gcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgc80"
  [${VTPARSE_STATE_SS3_PARAM}]="606060606060606060606060606060606060606060606060006000006060606000000000000000000000000000000000c0c0c0c0c0c0c0c0c0c0000000000000gcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgc80"
)

declare -gr VTPARSE_BASE="0123456789abcdefghijklmnopqrstuvwxyz"
declare -gr VTPARSE_ENTRY_ACTIONS="0100010007100b0110"

declare -gr VTPARSE_EXIT_ACTIONS="000000000h00090000"

  function ord {
    printf -v "${1?Missing Dest Variable}" "${3:-%d}" "'${2?Missing Char}"
  }
  function ord_eascii {
    LC_CTYPE=C ord "${@}"
  }
  function substr {
      local idx
      typeset -gi _INDEX
      case "${1}" in
        *${2}*)
            idx="${1%"${2}"*}";
            _INDEX=${#idx}
        ;;
          *)
            _INDEX=0
        ;;
      esac
  }




  declare -gr  MAX_INTERMEDIATE_CHARS=2

  declare -gi  vtparser_state
  declare -g   vtparser_intermediate_chars
  declare -ga  vtparser_params
  declare -gi  vtparser_ignore_flagged
  declare -g   vtparser_cb
  declare -g   vtparser_read


  function vtparse_init {
    vtparser_state="${VTPARSE_STATE_GROUND}"
    vtparser_intermediate_chars=""
    vtparser_params=( )
    vtparser_ignore_flagged=0
    vtparser_cb=${1?Missing Callback Function}
    vtparser_read=${2?Missing Read Function}
  }
  function nop {
    echo -n ""
  }
  function do_action {
    # Some actions we handle internally (like parsing parameters), others
    # we hand to our client for processing 
    case "${1}" in
      ${VTPARSE_ACTION_ESC_EXECUTE}|${VTPARSE_ACTION_PRINT}|${VTPARSE_ACTION_EXECUTE}|${VTPARSE_ACTION_HOOK}|${VTPARSE_ACTION_PUT}|${VTPARSE_ACTION_OSC_START}|${VTPARSE_ACTION_OSC_PUT}|${VTPARSE_ACTION_OSC_END}|${VTPARSE_ACTION_UNHOOK}|${VTPARSE_ACTION_CSI_DISPATCH}|${VTPARSE_ACTION_ESC_DISPATCH}|${VTPARSE_STATE_SS2_ENTRY}|${VTPARSE_STATE_SS3_ENTRY})
        ${vtparser_cb} "${@}" || return $?
        ;;
      ${VTPARSE_ACTION_IGNORE})
        nop
        ;;
      ${VTPARSE_ACTION_COLLECT})
        if [ ${#vtparser_intermediate_chars} -ge ${MAX_INTERMEDIATE_CHARS} ]; then
            vtparser_ignore_flagged=1
        else
            vtparser_intermediate_chars+="${2}";
        fi
        ;;
      ${VTPARSE_ACTION_PARAM})
        # process the param character 
        if [ "${2}" = ";" ]; then
            vtparser_params[${#vtparser_params[@]}]="";
        else
            # the character is a digit
            if [ ${#vtparser_params[@]} -eq 0 ]; then
                vtparser_params[0]=""
            fi
            vtparser_params[${#vtparser_params[@]}-1]+="${2}"
        fi
        ;;
      ${VTPARSE_ACTION_CLEAR})
        vtparser_intermediate_chars=""
        vtparser_params=( )
        vtparser_ignore_flagged=0
        ;;
      *)
        echo "Unknown action ${1}"
        ;;

    esac
    return 0
  }
  function vtparse_conv_base {
    local _INDEX
    substr "${VTPARSE_BASE}" "${2:0:1}"
    eval ${1}'="${_INDEX}"'
  }
  function do_state_change {
    # A state change is an action and/or a new state to transition to.
    local -i action
    local -i new_state
    local -i RValue=0

    vtparse_conv_base action "${1:0:1}"
    vtparse_conv_base new_state "${1:1:1}"
    #printf "action=%s new_state=%s vtparser_state=%s change=%s\n" "${action}"  "${new_state}"  "${vtparser_state}"  "${1}"
    
    if [ "${new_state:=0}" != "0" ]; then
        # Perform up to three actions:
        #   1. the exit action of the old state
        #   2. the action associated with the transition
        #   3. the entry actionk of the new action
        local -i exit_action
        local -i entry_action
        vtparse_conv_base exit_action  "${VTPARSE_EXIT_ACTIONS:${vtparser_state}:1}"
        vtparse_conv_base entry_action "${VTPARSE_ENTRY_ACTIONS:${new_state}:1}"
        [ "${exit_action}" = "0" ]  ||  do_action "${exit_action}" $'\x00' 0
        [ "${action}" = "0" ]       ||  do_action "${action}" "${2}" "${3}" || RValue=$?
        [ "${entry_action}" = "0" ] ||  do_action "${entry_action}" $'\x00' 0
        vtparser_state=${new_state}
        return ${RValue}
    else
        [ "${action}" = "0" ]       ||  do_action "${action}" "${2}" "${3}" || return $?
    fi
  }

  function vtparse {
      local -i _INDEX
      local REPLY
      while ${vtparser_read} 1 ; do
          ord_eascii _INDEX "${REPLY:0:1}"

          # If a transition is defined from the "anywhere" state, always
          # use that.  Otherwise use the transition from the current state. 
          
          _INDEX=${_INDEX}*2  # 2 characters per entry
          local change="${VTPARSE_STATE_TABLE[${VTPARSE_STATE_ANYWHERE}]:${_INDEX}:2}"
          [ "${change:-00}" != "00" ] || change="${VTPARSE_STATE_TABLE[${vtparser_state}]:${_INDEX}:2}"
          _INDEX=${_INDEX}/2

          do_state_change "${change}" "${REPLY}" "${_INDEX}" || break
      done
  }
  function vtparse_read_stdin {
    read -srN${1:-1} -d '' "${@:2}"
  }
  declare -g vtparse_read_buffer
  function vtparse_read_buffer {
    REPLY="${vtparse_read_buffer:0:${1:-1}}"
    vtparse_read_buffer="${vtparse_read_buffer:${1}}"
    [ ${#REPLY} -eq ${1} ] || return $?
  }


  function vtparser_callback_debug {
    local CArg
    printf "Received action %s, char=0x%02x char=%q\n" "${VTPARSE_ACTION_NAMES[${1}]}" "'${2}" "${2}"
    printf "Intermediate chars: '%s'\n" "${vtparser_intermediate_chars}"
    printf "%d Parameters:\n"  ${#vtparser_params[@]}
    for CArg in "${vtparser_params[@]-}"; do
        printf "\t%d\n" "${CArg}"
    done
    printf "\n"
  }
  ###############################
  ##
  ##    READ KEY CRAP
  ##
  ##
  ###############################
  KeyModifiers=(
    [1]=""  [2]="S-"   [3]="A-"   [4]="AS-"   [5]="C-"   [6]="CS-"  [7]="CA-"    [8]="CAS-"
    [9]="M-" [10]="MS-" [11]="MA-" [12]="MAS-" [13]="MC-" [14]="MCS-" [15]="MCA-" [16]="MCAS-"
    )
  KeybFntKeys=(
    [1]="home" [2]="insert" [3]="delete"  [4]="end"   [5]="pageUp" [6]="pageDown"
    [11]="f1"  [12]="f2"    [13]="f3"     [14]="f4"   [15]="f5"
    [17]="f6"  [18]="f7"    [19]="f8"     [20]="f9"   [21]="f10"
    [23]="f11" [24]="f12"   [25]="f13"    [26]="f14"  [28]="f15"
    [29]="f16" [31]="f17"   [32]="f18"    [33]="f19"  [34]="f20"
    )
  SunKeybFntKeys=(
    [214]="home"  [2]="insert" [3]="delete" [4]="end"   [216]="pageUp" [222]="pageDown"
    [224]="f1"  [225]="f2"    [226]="f3"    [227]="f4"  [228]="f5"
    [229]="f6"  [230]="f7"    [231]="f8"    [232]="f9"  [233]="f10"
    [192]="f11" [193]="f12"   [218]="keypad-five" [220]="keypad-delete"
    )
  KeybFntKeysAlt=(
    # A          B              C               D             E                   F             H         
    [0x41]="up" [0x42]="down" [0x43]="right" [0x44]="left" [0x45]="keypad-five" [0x46]="end" [0x48]="home"     
    # I               O
    [0x49]="InFocus" [0x4f]="OutOfFocus"      
    # P           Q           R           S             Z          
    [0x50]="f1" [0x51]="f2" [0x52]="f3" [0x53]="f4"  [0x5a]="S-HT" 
    )
  C0CtrlChars=(
    [0x00]="Null" [0x01]="SOH" [0x02]="STX" [0x03]="ETX" [0x04]="EOT" [0x05]="ENQ" [0x06]="ACK" 
    [0x07]="BEL"  [0x08]="BS"  [0x09]="HT"  [0x0A]="LF"  [0x0B]="VT"  [0x0C]="FF"  [0x0D]="CR"  
    [0x0E]="SO"   [0x0F]="SI"  [0x10]="DLE" [0x11]="DC1" [0x12]="DC2" [0x13]="DC3" [0x14]="DC4" 
    [0x15]="NAK"  [0x16]="SYN" [0x17]="ETB" [0x18]="CAN" [0x19]="EM"  [0x1A]="SUB" [0x1B]="ESC" 
    [0x1C]="FS"   [0x1D]="GS"  [0x1E]="RS"  [0x1F]="US"  [0x20]="SP"  [0x7F]="DEL" 
  )
  C0CtrlCharsAlt=(
    [0x01]="C-A" [0x02]="C-B" [0x03]="C-C" [0x04]="C-D" [0x05]="C-E" [0x06]="C-F" [0x07]="C-G" 
    [0x08]="C-H" [0x09]="C-I" [0x0a]="C-J" [0x0b]="C-K" [0x0c]="C-L" [0x0d]="C-M" [0x0e]="C-N"  
    [0x0f]="C-O" [0x10]="C-P" [0x11]="C-Q" [0x12]="C-R" [0x13]="C-S" [0x14]="C-T" [0x15]="C-U" 
    [0x16]="C-V" [0x17]="C-W" [0x18]="C-X" [0x19]="C-Y" [0x1a]="C-Z" [0x1b]="C-[" [0x1c]="C-]" 
    [0x1d]="C-}" [0x1e]="C-^" [0x1f]="C-_" [0x20]="C-SP"  [0x7F]="DEL" 
  )

  C1CtrlCharsEsc=(
    [0x40]="PAD"  [0x41]="HOP"  [0x42]="BPH" [0x43]="NBH" 
    [0x44]="IND"  [0x45]="NEL"  [0x46]="SSA" [0x47]="ESA" 
    [0x48]="HTS"  [0x49]="HTJ"  [0x4A]="VTS" [0x4B]="PLD" 
    [0x4C]="PLU"  [0x4D]="RI"   [0x4E]="SS2" [0x4F]="SS3" 
    [0x50]="DCS"  [0x51]="PU1"  [0x52]="PU2" [0x53]="STS" 
    [0x54]="CCH"  [0x55]="MW"   [0x56]="SPA" [0x57]="EPA" 
    [0x58]="SOS"  [0x59]="SGCI" [0x5A]="SCI" [0x5B]="CSI" 
    [0x5C]="ST"   [0x5D]="OSC"  [0x5E]="PM"  [0x5F]="APC" 
  )
  C1CtrlChars=(
    [0x80]="PAD"  [0x81]="HOP"  [0x82]="BPH" [0x83]="NBH" 
    [0x84]="IND"  [0x85]="NEL"  [0x86]="SSA" [0x87]="ESA" 
    [0x88]="HTS"  [0x89]="HTJ"  [0x8A]="VTS" [0x8B]="PLD" 
    [0x8C]="PLU"  [0x8D]="RI"   [0x8E]="SS2" [0x8F]="SS3" 
    [0x90]="DCS"  [0x91]="PU1"  [0x92]="PU2" [0x93]="STS" 
    [0x94]="CCH"  [0x95]="MW"   [0x96]="SPA" [0x97]="EPA" 
    [0x98]="SOS"  [0x99]="SGCI" [0x9A]="SCI" [0x9B]="CSI" 
    [0x9C]="ST"   [0x9D]="OSC"  [0x9E]="PM"  [0x9F]="APC" 
  )    
  C1CtrlCharsAlt=(
    [0x01]="CA-A" [0x02]="CA-B" [0x03]="CA-C" [0x04]="CA-D"  [0x05]="CA-E" [0x06]="CA-F" [0x07]="CA-G" 
    [0x08]="CA-H" [0x09]="CA-I" [0x0a]="CA-J" [0x0b]="CA-K"  [0x0c]="CA-L" [0x0d]="CA-M" [0x0e]="CA-N"  
    [0x0f]="CA-O" [0x10]="CA-P" [0x11]="CA-Q" [0x12]="CA-R"  [0x13]="CA-S" [0x14]="CA-T" [0x15]="CA-U" 
    [0x16]="CA-V" [0x17]="CA-W" [0x18]="CA-X" [0x19]="CA-Y"  [0x1a]="CA-Z" [0x1b]="CA-[" [0x1c]="CA-]" 
    [0x1d]="CA-}" [0x1e]="CA-^" [0x1f]="CA-_" [0x20]="CA-SP" [0x7F]="A-DEL" 
  )

  MouseButtons=(
    [0x00]="MB1-P" [0x01]="MB2-P" [0x02]="MB3-P" [0x03]="MB-R"
    [0x20]="MB1-M" [0x21]="MB2-M" [0x22]="MB3-M" [0x23]="MB-M"
    [0x40]="MB4-P" [0x41]="MB5-P" 
  )
  MouseMetaButtons=(
    [0x04]="S-"    [0x08]="A-"    [0x0c]="AS-" 
    [0x10]="C-"    [0x14]="CS-"   [0x1c]="CAS-"
  )
  
  function AdjustMousePos {
    local -i _INDEX
    ord_eascii _INDEX "${2}"
    eval ${1}'=$(( ${_INDEX}-32))'
  }
  function GetMouseButton {
    local MouseBtn
    AdjustMousePos MouseBtn "${2}"
    MouseBtn="${MouseMetaButtons[$(( ${MouseBtn} & 0x1C))]-}${MouseButtons[$(( ${MouseBtn} & 0xe3))]}"
    eval ${1}='"${MouseBtn}"'
  }

  function vtparser_callback_readkey {
    unset UInput[@]
    case "${1}" in 
      ${VTPARSE_ACTION_ESC_EXECUTE})
        case "${2}" in 
          [[:cntrl:]])
            UInput[0]="${C1CtrlCharsAlt[${3}]:-$(printf "%q" $'\e'"${2}")}"
            ;;
          *)
            UInput[0]="${C1CtrlChars[${3}]:-$(printf "%q" $'\e'"${2}")}"
            ;;
        esac
        ;;
      ${VTPARSE_ACTION_EXECUTE})
          UInput[0]="${C0CtrlChars[${3}]:-$(printf "%q" "${2}")}"
        ;;
      ${VTPARSE_ACTION_CSI_DISPATCH})
        case "${2}" in 
          z) # Sun Function Keys
            UInput[0]="${SunKeybFntKeys[${vtparser_params[0]}]:-}"
            if [ -n "${UInput[0]}" ]; then
              UInput[0]="${KeyModifiers[${vtparser_params[1]:-1}]}${UInput[0]}"
              UInput[1]="1" # Repeat Count
            else
              UInput[0]="CSI ${vtparser_params[*]} ${2}"
            fi
            ;;
          '~') # Function Keys
            UInput[0]="${KeybFntKeys[${vtparser_params[0]}]}"
            if [ -n "${UInput[0]}" ]; then
              UInput[0]="${KeyModifiers[${vtparser_params[1]:-1}]}${UInput[0]}"
              UInput[1]="1" # Repeat Count
            else
              UInput[0]="CSI ${vtparser_params[*]} ${2}"
            fi
            ;;
          A|B|C|D|E|F|H|I|O|Z|P|Q|R|S)
            UInput[0]="${KeybFntKeysAlt[${3}]:-}"
            if [ -n "${UInput[0]}" ]; then
              UInput[0]="${KeyModifiers[${vtparser_params[1]:-1}]}${UInput[0]}"
              UInput[1]="${vtparser_params[0]:-1}" # Repeat Count
            else
              UInput[0]="CSI ${vtparser_params[*]} ${2}"
            fi
            ;;
          t) 
            ${vtparser_read} 2
            UInput[0]="MouseTrack"
            AdjustMousePos UInput[1] "${REPLY:0:1}"
            AdjustMousePos UInput[2] "${REPLY:1:1}"
            ;;
          T) 
            ${vtparser_read} 6
            UInput[0]="MouseTrack"
            AdjustMousePos UInput[1] "${REPLY:0:1}"
            AdjustMousePos UInput[2] "${REPLY:1:1}"
            AdjustMousePos UInput[3] "${REPLY:2:1}"
            AdjustMousePos UInput[4] "${REPLY:3:1}"
            AdjustMousePos UInput[5] "${REPLY:4:1}"
            AdjustMousePos UInput[6] "${REPLY:5:1}"
            ;;
          M)  # Mouse 
            ${vtparser_read} 3
            GetMouseButton UInput[0] "${REPLY:0:1}"
            if [ -n "${UInput[0]}" ]; then  
              AdjustMousePos UInput[1] "${REPLY:1:1}"
              AdjustMousePos UInput[2] "${REPLY:2:1}"
            else
              UInput[0]=$(printf 'Mouse-\\x%02x %q'  "'${escapeSequence:0:1}" "${escapeSequence:1}")
            fi
            ;;

          *)
            UInput[0]="CSI ${vtparser_params[*]} ${2}"
            ;;
        esac
        ;;
      ${VTPARSE_ACTION_SS3_DISPATCH})
        case "${2}" in
          A|B|C|D|E|F|H|P|Q|R|S|~)
            UInput[0]+="${KeybFntKeysAlt[${3}]}"
            if [ -n "${UInput[0]}" ]; then
              UInput[0]="${KeyModifiers[${vtparser_params[1]:-1}]}${UInput[0]}"
              UInput[1]="${vtparser_params[0]:-1}" # Repeat Count
            else
              UInput[0]="SS3 ${vtparser_params[*]} ${2}"
            fi
            ;;
          *)
            UInput[0]="SS3 ${vtparser_params[*]} ${2}"
            ;;
        esac
        ;;
      ${VTPARSE_ACTION_ESC_DISPATCH})
        case "${2}" in 
          [][}^_A-Z]) UInput[0]="${C1CtrlChars[${3}]}" ;;
          [^[:cntrl:]]) UInput[0]="CA-${2}" ;;
          *) UInput[0]="${C1CtrlChars[${3}]:-$(printf "%q" $'\e'"${2}")}" ;;
        esac
        ;;
      ${VTPARSE_ACTION_PRINT})
        case "${2}" in 
          [^[:cntrl:]]) UInput[0]="${2}" ;;
          *) UInput[0]="${C0CtrlChars[${3}]:-$(printf "%q" $'\e'"${2}")}" ;;
        esac
        ;;
      *)
        vtparser_callback_debug "${@}" || return $?
        ;;
    esac
    return 1

  }

   function HandleKey {
        vtparse_init vtparser_callback_readkey vtparse_read_stdin
        while true; do
                vtparse 
                case "${UInput[0]}" in
                        LF)
                                echo "${UInput[@]}"
                                break
                                ;;
                        *up)       echo "Up Arrow ignoring modifier keys"  ;;
                        down)      echo "Down Arrow No modifier keys"      ;;
                        S-down)    echo "Shift + Down Arrow"               ;;
                        A-down)    echo "Alt + Down Arrow"                 ;;
                        AS-down)   echo "Alt + Shift + Down Arrow"         ;;
                        C-down)    echo "Control + Down Arrow"             ;;
                        CS-down)   echo "Control + Shift + Down Arrow"      ;;
                        CA-down)   echo "Control + Alt + Down Arrow"        ;;
                        CAS-down)  echo "Control + Alt + Shift + Down Arrow";;
                        MCAS-down) echo "Meta + Control + Alt + Shift + Down Arrow";;
                        *)
                                echo "${UInput[@]}"
                                ;;
                esac
        done

   }
HandleKey

ReadingFunctionKeysInBash (last edited 2021-10-20 16:52:02 by EmanueleTorre)