#!/usr/bin/env sh

# This script captures keyboard events on popular POSIX Shells.
#
# Run it by executing ./answer.sh and press the arrow keys
# to see the capture exemple. Press Return to exit.
#
# It has been tested on:
#  - Windows (Git Bash and MSYS+Mintty)
#  - OS X (native terminal, Yosemite)
#  - Linux (Ubuntu dash and bash)
#
# It should work on other POSIX compatible shells like zsh and ksh
# just fine.
#
# It is somewhat reusable, just change or redeclare the answer_keypress
# function with your own bindings.
#
# Capturing code was inspired by http://www.mtxia.com/js/Downloads/Scripts/Korn/Functions/visualSelect/index.shtml

# written by Alexandre Gomes Gaigalas alganet


# Enables word split on zsh
setopt SH_WORD_SPLIT >/dev/null 2>&1 || :

# -e exits on any untreated error
# -u exits on any undeclared variable
# -f disables pathname expansion
set -euf

ANSWER_E=$(printf '\e')
ANSWER_ESC="$(printf '\033')"
ANSWER_RET=$(printf '\r')
ANSWER_LFD=$(printf '\n')
ANSWER_TAB=$(printf '\t')
ANSWER_BKS=$(printf '\b')
ANSWER_DEL=$(printf '\127')

answer_char ()
{
	IFS='' read -n1 -r $1 2>/dev/null
}

answer_key ()
{
	answer_reply=''
	answer_char C1

	# Detects the beginning of an escape sequence
	if [ "_${C1}_" = "_${ANSWER_E}_" ] || [ "_${C1}_" = "_${ANSWER_ESC}_" ]
	then

		C1="${ANSWER_E}"
		answer_char C2

		# Start buffering escapes
		if [ "_${C2}_" = "_[_" ]
		then
			answer_char C3

			case "_${C3}_" in
				_[0-9]_ ) # home, end, pgup, pgdown, etc
					answer_char C4
					answer_reply="${C1}${C2}${C3}${C4}"

					case "_${C4}_" in
						_[0-9]_ ) # function keys, etc
							answer_char C5
							answer_reply="${C1}${C2}${C3}${C4}${C5}"

							;;
						* )
							answer_reply="${C1}${C2}${C3}${C4}"
							;;
					esac
					;;
				* )
					# Arrow keys
					answer_reply="${C1}${C2}${C3}"
					;;
			esac
		else
			# Unspecified escape
			answer_reply="${C1}${C2}"
		fi

	elif [ "_${C1}" = "_${ANSWER_RET}" ]
	then
		answer_reply="${ANSWER_RET}"

	elif [ "_${C1}" = "_${ANSWER_LFD}" ]
	then
		answer_reply="${ANSWER_RET}"

	elif [ "_${C1}" = "_${ANSWER_BKS}" ]
	then
		answer_reply="${ANSWER_BKS}"

	elif [ "_${C1}" = "_${ANSWER_TAB}" ]
	then
		answer_reply="${C1}"

	else
		answer_reply="${C1}"
	fi

	printf %s "${answer_reply}"
}


answer_events ()
{
	# Checks if stty is available
	nostty=0
	stty -g >/dev/null 2>&1 || nostty=1
	
	printf '\033[?25l' # Hide cursor
	printf %s 'Press Arrow Keys: '
	
	trap "answer_end; exit" 2 # Restores terminal settings upon exit
	
	# Store terminal settings if stty is available
	[ $nostty = 1 ] || oldstty="$(stty -g || echo 1)"
	# Changes terminal settings to raw
	[ $nostty = 1 ] || stty raw -echo -isig -ixon -ixoff time 0 2>/dev/null || printfix=1
	while true; do
		[ $nostty = 1 ] && printf '\033[s' # Save cursor position
		keypressed="$( answer_key )"       # Get another key escape
		[ $nostty = 1 ] && printf '\033[u' # Restore cursor position
		case _"$keypressed" in
			_"${ANSWER_RET}" | _"${ANSWER_LFD}" ) # Return Key
				answer_keypress "${ANSWER_RET}" || return
				;;
			_"${ANSWER_BKS}" | _"${ANSWER_DEL}" | _"${ANSWER_E}[3~" ) # Delete Key
				answer_keypress "${ANSWER_BKS}" || return
				;;
			* )
				answer_keypress "$keypressed" || return
				;;
		esac
	done
	printf '\033[?25h' # Show cursor again
}

answer_end ()
{
	[ $nostty = 1 ] || stty "$oldstty"
}

answer_keypress ()
{
#	echo "";printf %s "$1" | od -An -t dC | head -n1;echo "" # Enable this to see ASCII codes
	case _"$1" in
		_"${ANSWER_E}${ANSWER_E}" )
			printf %s "Double ESC"
			;;
		_"${ANSWER_E}[A" ) # Key Up
			printf %s "Up"
			;;
		_"${ANSWER_E}[B" ) # Key Down
			printf %s "Down"
			;;
		_"${ANSWER_E}[C" ) # Key Right
			printf %s "Right"
			;;
		_"${ANSWER_E}[D" ) # Key Left
			printf %s "Left"
			;;
		_"${ANSWER_RET}" ) # Return Key
			[ $nostty = 1 ] && printf '\033[1A'
			printf %s\\n "Return pressed, bye!"
			answer_end
			return 1
			;;
		_"${ANSWER_BKS}" ) # Delete Key
			printf %s "Delete"
			;;
		* )
			#printf %s "$1"  1>&2
			;;
	esac
}

answer_events
