Просмотр исходного кода

ANSI terminal emulation for windows

It is implemented by intercepting and interpreting the output
escape sequence by calling win32 console apis.

In addition the input from win32 console is translated to linux keycodes

Signed-off-by: Sachin Joshi <sachin_jayant_joshi@hotmail.com>
Sachin Joshi 10 лет назад
Родитель
Сommit
d8c3090dd9

+ 2 - 9
api/client/cli.go

@@ -137,19 +137,12 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, a
 	if tlsConfig != nil {
 		scheme = "https"
 	}
-
 	if in != nil {
-		if file, ok := in.(*os.File); ok {
-			inFd = file.Fd()
-			isTerminalIn = term.IsTerminal(inFd)
-		}
+		inFd, isTerminalIn = term.GetHandleInfo(in)
 	}
 
 	if out != nil {
-		if file, ok := out.(*os.File); ok {
-			outFd = file.Fd()
-			isTerminalOut = term.IsTerminal(outFd)
-		}
+		outFd, isTerminalOut = term.GetHandleInfo(out)
 	}
 
 	if err == nil {

+ 7 - 2
docker/docker.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/docker/autogen/dockerversion"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/utils"
 )
 
@@ -47,6 +48,10 @@ func main() {
 		initLogging(log.InfoLevel)
 	}
 
+	// Set terminal emulation based on platform as required.
+	stdout, stderr, stdin := term.StdStreams()
+	log.SetOutput(stderr)
+
 	// -D, --debug, -l/--log-level=debug processing
 	// When/if -D is removed this block can be deleted
 	if *flDebug {
@@ -124,9 +129,9 @@ func main() {
 	}
 
 	if *flTls || *flTlsVerify {
-		cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
+		cli = client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
 	} else {
-		cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], nil)
+		cli = client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], nil)
 	}
 
 	if err := cli.Cmd(flag.Args()...); err != nil {

+ 1043 - 25
pkg/term/console_windows.go

@@ -3,6 +3,13 @@
 package term
 
 import (
+	"bytes"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
 	"syscall"
 	"unsafe"
 )
@@ -20,21 +27,255 @@ const (
 	// If parameter is a screen buffer handle, additional values
 	ENABLE_PROCESSED_OUTPUT   = 0x0001
 	ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
+
+	//http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes
+	FOREGROUND_BLUE       = 1
+	FOREGROUND_GREEN      = 2
+	FOREGROUND_RED        = 4
+	FOREGROUND_INTENSITY  = 8
+	FOREGROUND_MASK_SET   = 0x000F
+	FOREGROUND_MASK_UNSET = 0xFFF0
+
+	BACKGROUND_BLUE       = 16
+	BACKGROUND_GREEN      = 32
+	BACKGROUND_RED        = 64
+	BACKGROUND_INTENSITY  = 128
+	BACKGROUND_MASK_SET   = 0x00F0
+	BACKGROUND_MASK_UNSET = 0xFF0F
+
+	COMMON_LVB_REVERSE_VIDEO = 0x4000
+	COMMON_LVB_UNDERSCORE    = 0x8000
+
+	// http://man7.org/linux/man-pages/man4/console_codes.4.html
+	// ECMA-48 Set Graphics Rendition
+	ANSI_ATTR_RESET     = 0
+	ANSI_ATTR_BOLD      = 1
+	ANSI_ATTR_DIM       = 2
+	ANSI_ATTR_UNDERLINE = 4
+	ANSI_ATTR_BLINK     = 5
+	ANSI_ATTR_REVERSE   = 7
+	ANSI_ATTR_INVISIBLE = 8
+
+	ANSI_ATTR_UNDERLINE_OFF = 24
+	ANSI_ATTR_BLINK_OFF     = 25
+	ANSI_ATTR_REVERSE_OFF   = 27
+	ANSI_ATTR_INVISIBLE_OFF = 8
+
+	ANSI_FOREGROUND_BLACK   = 30
+	ANSI_FOREGROUND_RED     = 31
+	ANSI_FOREGROUND_GREEN   = 32
+	ANSI_FOREGROUND_YELLOW  = 33
+	ANSI_FOREGROUND_BLUE    = 34
+	ANSI_FOREGROUND_MAGENTA = 35
+	ANSI_FOREGROUND_CYAN    = 36
+	ANSI_FOREGROUND_WHITE   = 37
+	ANSI_FOREGROUND_DEFAULT = 39
+
+	ANSI_BACKGROUND_BLACK   = 40
+	ANSI_BACKGROUND_RED     = 41
+	ANSI_BACKGROUND_GREEN   = 42
+	ANSI_BACKGROUND_YELLOW  = 43
+	ANSI_BACKGROUND_BLUE    = 44
+	ANSI_BACKGROUND_MAGENTA = 45
+	ANSI_BACKGROUND_CYAN    = 46
+	ANSI_BACKGROUND_WHITE   = 47
+	ANSI_BACKGROUND_DEFAULT = 49
+
+	ANSI_MAX_CMD_LENGTH = 256
+
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx
+	STD_INPUT_HANDLE  = -10
+	STD_OUTPUT_HANDLE = -11
+	STD_ERROR_HANDLE  = -12
+
+	MAX_INPUT_BUFFER = 1024
+	DEFAULT_WIDTH    = 80
+	DEFAULT_HEIGHT   = 24
+)
+
+// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
+const (
+	VK_PRIOR    = 0x21 // PAGE UP key
+	VK_NEXT     = 0x22 // PAGE DOWN key
+	VK_END      = 0x23 // END key
+	VK_HOME     = 0x24 // HOME key
+	VK_LEFT     = 0x25 // LEFT ARROW key
+	VK_UP       = 0x26 // UP ARROW key
+	VK_RIGHT    = 0x27 //RIGHT ARROW key
+	VK_DOWN     = 0x28 //DOWN ARROW key
+	VK_SELECT   = 0x29 //SELECT key
+	VK_PRINT    = 0x2A //PRINT key
+	VK_EXECUTE  = 0x2B //EXECUTE key
+	VK_SNAPSHOT = 0x2C //PRINT SCREEN key
+	VK_INSERT   = 0x2D //INS key
+	VK_DELETE   = 0x2E //DEL key
+	VK_HELP     = 0x2F //HELP key
+	VK_F1       = 0x70 //F1 key
+	VK_F2       = 0x71 //F2 key
+	VK_F3       = 0x72 //F3 key
+	VK_F4       = 0x73 //F4 key
+	VK_F5       = 0x74 //F5 key
+	VK_F6       = 0x75 //F6 key
+	VK_F7       = 0x76 //F7 key
+	VK_F8       = 0x77 //F8 key
+	VK_F9       = 0x78 //F9 key
+	VK_F10      = 0x79 //F10 key
+	VK_F11      = 0x7A //F11 key
+	VK_F12      = 0x7B //F12 key
 )
 
 var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
 
 var (
-	setConsoleModeProc             = kernel32DLL.NewProc("SetConsoleMode")
-	getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
+	setConsoleModeProc                = kernel32DLL.NewProc("SetConsoleMode")
+	getConsoleScreenBufferInfoProc    = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
+	setConsoleCursorPositionProc      = kernel32DLL.NewProc("SetConsoleCursorPosition")
+	setConsoleTextAttributeProc       = kernel32DLL.NewProc("SetConsoleTextAttribute")
+	fillConsoleOutputCharacterProc    = kernel32DLL.NewProc("FillConsoleOutputCharacterW")
+	writeConsoleOutputProc            = kernel32DLL.NewProc("WriteConsoleOutputW")
+	readConsoleInputProc              = kernel32DLL.NewProc("ReadConsoleInputW")
+	getNumberOfConsoleInputEventsProc = kernel32DLL.NewProc("GetNumberOfConsoleInputEvents")
+	getConsoleCursorInfoProc          = kernel32DLL.NewProc("GetConsoleCursorInfo")
+	setConsoleCursorInfoProc          = kernel32DLL.NewProc("SetConsoleCursorInfo")
+	setConsoleWindowInfoProc          = kernel32DLL.NewProc("SetConsoleWindowInfo")
+	setConsoleScreenBufferSizeProc    = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
+)
+
+// types for calling various windows API
+// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
+type (
+	SHORT      int16
+	SMALL_RECT struct {
+		Left   SHORT
+		Top    SHORT
+		Right  SHORT
+		Bottom SHORT
+	}
+
+	COORD struct {
+		X SHORT
+		Y SHORT
+	}
+
+	BOOL  int32
+	WORD  uint16
+	WCHAR uint16
+	DWORD uint32
+
+	CONSOLE_SCREEN_BUFFER_INFO struct {
+		Size              COORD
+		CursorPosition    COORD
+		Attributes        WORD
+		Window            SMALL_RECT
+		MaximumWindowSize COORD
+	}
+
+	CONSOLE_CURSOR_INFO struct {
+		Size    DWORD
+		Visible BOOL
+	}
+
+	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684166(v=vs.85).aspx
+	KEY_EVENT_RECORD struct {
+		KeyDown         BOOL
+		RepeatCount     WORD
+		VirtualKeyCode  WORD
+		VirtualScanCode WORD
+		UnicodeChar     WCHAR
+		ControlKeyState DWORD
+	}
+
+	INPUT_RECORD struct {
+		EventType WORD
+		KeyEvent  KEY_EVENT_RECORD
+	}
+
+	CHAR_INFO struct {
+		UnicodeChar WCHAR
+		Attributes  WORD
+	}
 )
 
+// Implements the TerminalEmulator interface
+type WindowsTerminal struct {
+	outMutex            sync.Mutex
+	inMutex             sync.Mutex
+	inputBuffer         chan byte
+	screenBufferInfo    *CONSOLE_SCREEN_BUFFER_INFO
+	inputEscapeSequence []byte
+}
+
+func StdStreams() (stdOut io.Writer, stdErr io.Writer, stdIn io.ReadCloser) {
+	handler := &WindowsTerminal{
+		inputBuffer:         make(chan byte, MAX_INPUT_BUFFER),
+		inputEscapeSequence: []byte(KEY_ESC_CSI),
+	}
+
+	// Save current screen buffer info
+	handle, _ := syscall.GetStdHandle(STD_OUTPUT_HANDLE)
+	screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))
+	if err == nil {
+		handler.screenBufferInfo = screenBufferInfo
+	}
+
+	// Set the window size
+	SetWindowSize(uintptr(handle), DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HEIGHT)
+	if IsTerminal(os.Stdout.Fd()) {
+		stdOut = &terminalWriter{
+			wrappedWriter: os.Stdout,
+			emulator:      handler,
+			command:       make([]byte, 0, ANSI_MAX_CMD_LENGTH),
+		}
+	} else {
+		stdOut = os.Stdout
+
+	}
+	if IsTerminal(os.Stderr.Fd()) {
+		stdErr = &terminalWriter{
+			wrappedWriter: os.Stderr,
+			emulator:      handler,
+			command:       make([]byte, 0, ANSI_MAX_CMD_LENGTH),
+		}
+	} else {
+		stdErr = os.Stderr
+
+	}
+	if IsTerminal(os.Stdin.Fd()) {
+		stdIn = &terminalReader{
+			wrappedReader: os.Stdin,
+			emulator:      handler,
+			command:       make([]byte, 0, ANSI_MAX_CMD_LENGTH),
+		}
+	} else {
+		stdIn = os.Stdin
+	}
+
+	return
+}
+
+// GetHandleInfo returns file descriptor and bool indicating whether the file is a terminal
+func GetHandleInfo(in interface{}) (uintptr, bool) {
+	var inFd uintptr
+	var isTerminalIn bool
+	if tr, ok := in.(*terminalReader); ok {
+		if file, ok := tr.wrappedReader.(*os.File); ok {
+			inFd = file.Fd()
+			isTerminalIn = IsTerminal(inFd)
+		}
+	}
+	return inFd, isTerminalIn
+}
+
+// GetConsoleMode gets the console mode for given file descriptor
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
 func GetConsoleMode(fileDesc uintptr) (uint32, error) {
 	var mode uint32
 	err := syscall.GetConsoleMode(syscall.Handle(fileDesc), &mode)
 	return mode, err
 }
 
+// SetConsoleMode sets the console mode for given file descriptor
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
 func SetConsoleMode(fileDesc uintptr, mode uint32) error {
 	r, _, err := setConsoleModeProc.Call(fileDesc, uintptr(mode), 0)
 	if r == 0 {
@@ -46,34 +287,52 @@ func SetConsoleMode(fileDesc uintptr, mode uint32) error {
 	return nil
 }
 
-// types for calling GetConsoleScreenBufferInfo
-// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
-type (
-	SHORT int16
-
-	SMALL_RECT struct {
-		Left   SHORT
-		Top    SHORT
-		Right  SHORT
-		Bottom SHORT
+// SetCursorVisible sets the cursor visbility
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx
+func SetCursorVisible(fileDesc uintptr, isVisible BOOL) (bool, error) {
+	var cursorInfo CONSOLE_CURSOR_INFO
+	r, _, err := getConsoleCursorInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&cursorInfo)), 0)
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
 	}
-
-	COORD struct {
-		X SHORT
-		Y SHORT
+	cursorInfo.Visible = isVisible
+	r, _, err = setConsoleCursorInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&cursorInfo)), 0)
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
 	}
+	return true, nil
+}
 
-	WORD uint16
-
-	CONSOLE_SCREEN_BUFFER_INFO struct {
-		dwSize              COORD
-		dwCursorPosition    COORD
-		wAttributes         WORD
-		srWindow            SMALL_RECT
-		dwMaximumWindowSize COORD
+// SetWindowSize sets the size of the console window.
+func SetWindowSize(fileDesc uintptr, width, height, max SHORT) (bool, error) {
+	window := SMALL_RECT{Left: 0, Top: 0, Right: width - 1, Bottom: height - 1}
+	coord := COORD{X: width - 1, Y: max}
+	r, _, err := setConsoleWindowInfoProc.Call(uintptr(fileDesc), uintptr(BOOL(1)), uintptr(unsafe.Pointer(&window)))
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
+	}
+	r, _, err = setConsoleScreenBufferSizeProc.Call(uintptr(fileDesc), uintptr(marshal(coord)))
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
 	}
-)
 
+	return true, nil
+}
+
+// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
 func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
 	var info CONSOLE_SCREEN_BUFFER_INFO
 	r, _, err := getConsoleScreenBufferInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&info)), 0)
@@ -85,3 +344,762 @@ func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO,
 	}
 	return &info, nil
 }
+
+// setConsoleTextAttribute sets the attributes of characters written to the
+// console screen buffer by the WriteFile or WriteConsole function,
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx
+func setConsoleTextAttribute(fileDesc uintptr, attribute WORD) (bool, error) {
+	r, _, err := setConsoleTextAttributeProc.Call(uintptr(fileDesc), uintptr(attribute), 0)
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
+	}
+	return true, nil
+}
+
+func writeConsoleOutput(fileDesc uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) (bool, error) {
+	r, _, err := writeConsoleOutputProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&buffer[0])), uintptr(marshal(bufferSize)), uintptr(marshal(bufferCoord)), uintptr(unsafe.Pointer(writeRegion)))
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
+	}
+	return true, nil
+}
+
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682663(v=vs.85).aspx
+func fillConsoleOutputCharacter(fileDesc uintptr, fillChar byte, length uint32, writeCord COORD) (bool, error) {
+	out := int64(0)
+	r, _, err := fillConsoleOutputCharacterProc.Call(uintptr(fileDesc), uintptr(fillChar), uintptr(length), uintptr(marshal(writeCord)), uintptr(unsafe.Pointer(&out)))
+	// If the function succeeds, the return value is nonzero.
+	if r == 0 {
+		if err != nil {
+			return false, err
+		}
+		return false, syscall.EINVAL
+	}
+	return true, nil
+}
+
+// Gets the number of space characters to write for "clearing" the section of terminal
+func getNumberOfChars(fromCoord COORD, toCoord COORD, screenSize COORD) uint32 {
+	// must be valid cursor position
+	if fromCoord.X < 0 || fromCoord.Y < 0 || toCoord.X < 0 || toCoord.Y < 0 {
+		return 0
+	}
+	if fromCoord.X >= screenSize.X || fromCoord.Y >= screenSize.Y || toCoord.X >= screenSize.X || toCoord.Y >= screenSize.Y {
+		return 0
+	}
+	// can't be backwards
+	if fromCoord.Y > toCoord.Y {
+		return 0
+	}
+	// same line
+	if fromCoord.Y == toCoord.Y {
+		return uint32(toCoord.X-fromCoord.X) + 1
+	}
+	// spans more than one line
+	if fromCoord.Y < toCoord.Y {
+		// from start till end of line for first line +  from start of line till end
+		retValue := uint32(screenSize.X-fromCoord.X) + uint32(toCoord.X) + 1
+		// don't count first and last line
+		linesBetween := toCoord.Y - fromCoord.Y - 1
+		if linesBetween > 0 {
+			retValue = retValue + uint32(linesBetween*screenSize.X)
+		}
+		return retValue
+	}
+	return 0
+}
+
+func clearDisplayRect(fileDesc uintptr, fillChar byte, attributes WORD, fromCoord COORD, toCoord COORD, windowSize COORD) (bool, uint32, error) {
+	var writeRegion SMALL_RECT
+	writeRegion.Top = fromCoord.Y
+	writeRegion.Left = fromCoord.X
+	writeRegion.Right = toCoord.X
+	writeRegion.Bottom = toCoord.Y
+
+	// allocate and initialize buffer
+	width := toCoord.X - fromCoord.X + 1
+	height := toCoord.Y - fromCoord.Y + 1
+	size := width * height
+	if size > 0 {
+		buffer := make([]CHAR_INFO, size)
+		for i := 0; i < len(buffer); i++ {
+			buffer[i].UnicodeChar = WCHAR(string(fillChar)[0])
+			buffer[i].Attributes = attributes
+		}
+
+		// Write to buffer
+		r, err := writeConsoleOutput(fileDesc, buffer, windowSize, COORD{X: 0, Y: 0}, &writeRegion)
+		if !r {
+			if err != nil {
+				return false, 0, err
+			}
+			return false, 0, syscall.EINVAL
+		}
+	}
+	return true, uint32(size), nil
+}
+
+func clearDisplayRange(fileDesc uintptr, fillChar byte, attributes WORD, fromCoord COORD, toCoord COORD, windowSize COORD) (bool, uint32, error) {
+	nw := uint32(0)
+	// start and end on same line
+	if fromCoord.Y == toCoord.Y {
+		r, charWritten, err := clearDisplayRect(fileDesc, fillChar, attributes, fromCoord, toCoord, windowSize)
+		if !r {
+			if err != nil {
+				return false, charWritten, err
+			}
+			return false, charWritten, syscall.EINVAL
+		}
+		return true, charWritten, nil
+	}
+	// TODO(azlinux): if full screen, optimize
+
+	// spans more than one line
+	if fromCoord.Y < toCoord.Y {
+		// from start position till end of line for first line
+		r, n, err := clearDisplayRect(fileDesc, fillChar, attributes, fromCoord, COORD{X: windowSize.X - 1, Y: fromCoord.Y}, windowSize)
+		if !r {
+			if err != nil {
+				return false, nw, err
+			}
+			return false, nw, syscall.EINVAL
+		}
+		nw += n
+		// lines between
+		linesBetween := toCoord.Y - fromCoord.Y - 1
+		if linesBetween > 0 {
+			r, n, err = clearDisplayRect(fileDesc, fillChar, attributes, COORD{X: 0, Y: fromCoord.Y + 1}, COORD{X: windowSize.X - 1, Y: toCoord.Y - 1}, windowSize)
+			if !r {
+				if err != nil {
+					return false, nw, err
+				}
+				return false, nw, syscall.EINVAL
+			}
+			nw += n
+		}
+		// lines at end
+		r, n, err = clearDisplayRect(fileDesc, fillChar, attributes, COORD{X: 0, Y: toCoord.Y}, toCoord, windowSize)
+		if !r {
+			if err != nil {
+				return false, nw, err
+			}
+			return false, nw, syscall.EINVAL
+		}
+		nw += n
+	}
+	return true, nw, nil
+}
+
+// setConsoleCursorPosition sets the console cursor position
+// Note The X and Y are zero based
+// If relative is true then the new position is relative to current one
+func setConsoleCursorPosition(fileDesc uintptr, isRelative bool, column int16, line int16) (bool, error) {
+	screenBufferInfo, err := GetConsoleScreenBufferInfo(fileDesc)
+	if err == nil {
+		var position COORD
+		if isRelative {
+			position.X = screenBufferInfo.CursorPosition.X + SHORT(column)
+			position.Y = screenBufferInfo.CursorPosition.Y + SHORT(line)
+		} else {
+			position.X = SHORT(column)
+			position.Y = SHORT(line)
+		}
+
+		//convert
+		bits := marshal(position)
+		r, _, err := setConsoleCursorPositionProc.Call(uintptr(fileDesc), uintptr(bits), 0)
+		if r == 0 {
+			if err != nil {
+				return false, err
+			}
+			return false, syscall.EINVAL
+		}
+		return true, nil
+	}
+	return false, err
+}
+
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683207(v=vs.85).aspx
+func getNumberOfConsoleInputEvents(fileDesc uintptr) (uint16, error) {
+	var n WORD
+	r, _, err := getNumberOfConsoleInputEventsProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&n)))
+	//If the function succeeds, the return value is nonzero
+	if r != 0 {
+		//fmt.Printf("################%d #################\n", n)
+		return uint16(n), nil
+	}
+	return 0, err
+}
+
+//http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx
+func readConsoleInputKey(fileDesc uintptr, inputBuffer []INPUT_RECORD) (int, error) {
+	var nr WORD
+	r, _, err := readConsoleInputProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&inputBuffer[0])), uintptr(WORD(len(inputBuffer))), uintptr(unsafe.Pointer(&nr)))
+	//If the function succeeds, the return value is nonzero.
+	if r != 0 {
+		return int(nr), nil
+	}
+	return int(0), err
+}
+
+func getWindowsTextAttributeForAnsiValue(originalFlag WORD, defaultValue WORD, ansiValue int16) (WORD, error) {
+	flag := WORD(originalFlag)
+	if flag == 0 {
+		flag = defaultValue
+	}
+	switch ansiValue {
+	case ANSI_ATTR_RESET:
+		flag &^= COMMON_LVB_UNDERSCORE
+		flag &^= BACKGROUND_INTENSITY
+		flag = flag | FOREGROUND_INTENSITY
+	case ANSI_ATTR_INVISIBLE:
+		// TODO: how do you reset reverse?
+	case ANSI_ATTR_UNDERLINE:
+		flag = flag | COMMON_LVB_UNDERSCORE
+	case ANSI_ATTR_BLINK:
+		// seems like background intenisty is blink
+		flag = flag | BACKGROUND_INTENSITY
+	case ANSI_ATTR_UNDERLINE_OFF:
+		flag &^= COMMON_LVB_UNDERSCORE
+	case ANSI_ATTR_BLINK_OFF:
+		// seems like background intenisty is blink
+		flag &^= BACKGROUND_INTENSITY
+	case ANSI_ATTR_BOLD:
+		flag = flag | FOREGROUND_INTENSITY
+	case ANSI_ATTR_DIM:
+		flag &^= FOREGROUND_INTENSITY
+	case ANSI_ATTR_REVERSE, ANSI_ATTR_REVERSE_OFF:
+		// swap forground and background bits
+		foreground := flag & FOREGROUND_MASK_SET
+		background := flag & BACKGROUND_MASK_SET
+		flag = (flag & BACKGROUND_MASK_UNSET & FOREGROUND_MASK_UNSET) | (foreground << 4) | (background >> 4)
+
+	// FOREGROUND
+	case ANSI_FOREGROUND_DEFAULT:
+		flag = (flag & FOREGROUND_MASK_UNSET) | (defaultValue & FOREGROUND_MASK_SET)
+	case ANSI_FOREGROUND_BLACK:
+		flag = flag ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
+	case ANSI_FOREGROUND_RED:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED
+	case ANSI_FOREGROUND_GREEN:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_GREEN
+	case ANSI_FOREGROUND_YELLOW:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_GREEN
+	case ANSI_FOREGROUND_BLUE:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_BLUE
+	case ANSI_FOREGROUND_MAGENTA:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_BLUE
+	case ANSI_FOREGROUND_CYAN:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_GREEN | FOREGROUND_BLUE
+	case ANSI_FOREGROUND_WHITE:
+		flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
+
+	// Background
+	case ANSI_BACKGROUND_DEFAULT:
+		// Black with no intensity
+		flag = (flag & BACKGROUND_MASK_UNSET) | (defaultValue & BACKGROUND_MASK_SET)
+	case ANSI_BACKGROUND_BLACK:
+		flag = (flag & BACKGROUND_MASK_UNSET)
+	case ANSI_BACKGROUND_RED:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED
+	case ANSI_BACKGROUND_GREEN:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_GREEN
+	case ANSI_BACKGROUND_YELLOW:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_GREEN
+	case ANSI_BACKGROUND_BLUE:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_BLUE
+	case ANSI_BACKGROUND_MAGENTA:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_BLUE
+	case ANSI_BACKGROUND_CYAN:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_GREEN | BACKGROUND_BLUE
+	case ANSI_BACKGROUND_WHITE:
+		flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
+	default:
+
+	}
+	return flag, nil
+}
+
+// HandleOutputCommand interpretes the Ansi commands and then makes appropriate Win32 calls
+func (term *WindowsTerminal) HandleOutputCommand(command []byte) (n int, err error) {
+	// console settings changes need to happen in atomic way
+	term.outMutex.Lock()
+	defer term.outMutex.Unlock()
+
+	r := false
+	// Parse the command
+	parsedCommand := parseAnsiCommand(command)
+
+	// use appropriate handle
+	handle, _ := syscall.GetStdHandle(STD_OUTPUT_HANDLE)
+
+	switch parsedCommand.Command {
+	case "m":
+		// [Value;...;Valuem
+		// Set Graphics Mode:
+		// Calls the graphics functions specified by the following values.
+		// These specified functions remain active until the next occurrence of this escape sequence.
+		// Graphics mode changes the colors and attributes of text (such as bold and underline) displayed on the screen.
+		screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))
+		if err != nil {
+			return len(command), err
+		}
+		flag := screenBufferInfo.Attributes
+		for _, e := range parsedCommand.Parameters {
+			value, _ := strconv.ParseInt(e, 10, 16) // base 10, 16 bit
+			if value == ANSI_ATTR_RESET {
+				flag = term.screenBufferInfo.Attributes // reset
+			} else {
+				flag, err = getWindowsTextAttributeForAnsiValue(flag, term.screenBufferInfo.Attributes, int16(value))
+				if nil != err {
+					return len(command), err
+				}
+			}
+		}
+		r, err = setConsoleTextAttribute(uintptr(handle), flag)
+		if !r {
+			return len(command), err
+		}
+	case "H", "f":
+		// [line;columnH
+		// [line;columnf
+		// Moves the cursor to the specified position (coordinates).
+		// If you do not specify a position, the cursor moves to the home position at the upper-left corner of the screen (line 0, column 0).
+		line, err := parseInt16OrDefault(parsedCommand.getParam(0), 1)
+		if err != nil {
+			return len(command), err
+		}
+		column, err := parseInt16OrDefault(parsedCommand.getParam(1), 1)
+		if err != nil {
+			return len(command), err
+		}
+		// The numbers are not 0 based, but 1 based
+		r, err = setConsoleCursorPosition(uintptr(handle), false, int16(column-1), int16(line-1))
+		if !r {
+			return len(command), err
+		}
+
+	case "A":
+		// [valueA
+		// Moves the cursor up by the specified number of lines without changing columns.
+		// If the cursor is already on the top line, ignores this sequence.
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1)
+		if err != nil {
+			return len(command), err
+		}
+		r, err = setConsoleCursorPosition(uintptr(handle), true, 0, -1*value)
+		if !r {
+			return len(command), err
+		}
+	case "B":
+		// [valueB
+		// Moves the cursor down by the specified number of lines without changing columns.
+		// If the cursor is already on the bottom line, ignores this sequence.
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1)
+		if err != nil {
+			return len(command), err
+		}
+		r, err = setConsoleCursorPosition(uintptr(handle), true, 0, value)
+		if !r {
+			return len(command), err
+		}
+	case "C":
+		// [valueC
+		// Moves the cursor forward by the specified number of columns without changing lines.
+		// If the cursor is already in the rightmost column, ignores this sequence.
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1)
+		if err != nil {
+			return len(command), err
+		}
+		r, err = setConsoleCursorPosition(uintptr(handle), true, int16(value), 0)
+		if !r {
+			return len(command), err
+		}
+	case "D":
+		// [valueD
+		// Moves the cursor back by the specified number of columns without changing lines.
+		// If the cursor is already in the leftmost column, ignores this sequence.
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1)
+		if err != nil {
+			return len(command), err
+		}
+		r, err = setConsoleCursorPosition(uintptr(handle), true, int16(-1*value), 0)
+		if !r {
+			return len(command), err
+		}
+	case "J":
+		// [J   Erases from the cursor to the end of the screen, including the cursor position.
+		// [1J  Erases from the beginning of the screen to the cursor, including the cursor position.
+		// [2J  Erases the complete display. The cursor does not move.
+		// Clears the screen and moves the cursor to the home position (line 0, column 0).
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 0)
+		if err != nil {
+			return len(command), err
+		}
+		var start COORD
+		var cursor COORD
+		var end COORD
+		screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))
+		if err == nil {
+			switch value {
+			case 0:
+				start = screenBufferInfo.CursorPosition
+				// end of the screen
+				end.X = screenBufferInfo.MaximumWindowSize.X - 1
+				end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
+				// cursor
+				cursor = screenBufferInfo.CursorPosition
+			case 1:
+
+				// start of the screen
+				start.X = 0
+				start.Y = 0
+				// end of the screen
+				end = screenBufferInfo.CursorPosition
+				// cursor
+				cursor = screenBufferInfo.CursorPosition
+			case 2:
+				// start of the screen
+				start.X = 0
+				start.Y = 0
+				// end of the screen
+				end.X = screenBufferInfo.MaximumWindowSize.X - 1
+				end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
+				// cursor
+				cursor.X = 0
+				cursor.Y = 0
+			}
+			r, _, err = clearDisplayRange(uintptr(handle), ' ', term.screenBufferInfo.Attributes, start, end, screenBufferInfo.MaximumWindowSize)
+			if !r {
+				return len(command), err
+			}
+			// remember the the cursor position is 1 based
+			r, err = setConsoleCursorPosition(uintptr(handle), false, int16(cursor.X), int16(cursor.Y))
+			if !r {
+				return len(command), err
+			}
+		}
+	case "K":
+		// [K
+		// Clears all characters from the cursor position to the end of the line (including the character at the cursor position).
+		// [K  Erases from the cursor to the end of the line, including the cursor position.
+		// [1K  Erases from the beginning of the line to the cursor, including the cursor position.
+		// [2K  Erases the complete line.
+		value, err := parseInt16OrDefault(parsedCommand.getParam(0), 0)
+		var start COORD
+		var cursor COORD
+		var end COORD
+		screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))
+		if err == nil {
+			switch value {
+			case 0:
+				// start is where cursor is
+				start = screenBufferInfo.CursorPosition
+				// end of line
+				end.X = screenBufferInfo.MaximumWindowSize.X - 1
+				end.Y = screenBufferInfo.CursorPosition.Y
+				// cursor remains the same
+				cursor = screenBufferInfo.CursorPosition
+
+			case 1:
+				// beginning of line
+				start.X = 0
+				start.Y = screenBufferInfo.CursorPosition.Y
+				// until cursor
+				end = screenBufferInfo.CursorPosition
+				// cursor remains the same
+				cursor = screenBufferInfo.CursorPosition
+			case 2:
+				// start of the line
+				start.X = 0
+				start.Y = screenBufferInfo.MaximumWindowSize.Y - 1
+				// end of the line
+				end.X = screenBufferInfo.MaximumWindowSize.X - 1
+				end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
+				// cursor
+				cursor.X = 0
+				cursor.Y = screenBufferInfo.MaximumWindowSize.Y - 1
+			}
+			r, _, err = clearDisplayRange(uintptr(handle), ' ', term.screenBufferInfo.Attributes, start, end, screenBufferInfo.MaximumWindowSize)
+			if !r {
+				return len(command), err
+			}
+			// remember the the cursor position is 1 based
+			r, err = setConsoleCursorPosition(uintptr(handle), false, int16(cursor.X), int16(cursor.Y))
+			if !r {
+				return len(command), err
+			}
+		}
+
+	case "l":
+		for _, value := range parsedCommand.Parameters {
+			switch value {
+			case "?25", "25":
+				SetCursorVisible(uintptr(handle), BOOL(0))
+			case "?1049", "1049":
+				// TODO (azlinux):  Restore terminal
+			case "?1", "1":
+				// If the DECCKM function is reset, then the arrow keys send ANSI cursor sequences to the host.
+				term.inputEscapeSequence = []byte(KEY_ESC_CSI)
+			default:
+			}
+		}
+	case "h":
+		for _, value := range parsedCommand.Parameters {
+			switch value {
+			case "?25", "25":
+				SetCursorVisible(uintptr(handle), BOOL(1))
+			case "?1049", "1049":
+				// TODO (azlinux): Save terminal
+			case "?1", "1":
+				// If the DECCKM function is set, then the arrow keys send application sequences to the host.
+				// DECCKM (default off): When set, the cursor keys send an ESC O prefix, rather than ESC [.
+				term.inputEscapeSequence = []byte(KEY_ESC_O)
+			default:
+			}
+		}
+
+	case "]":
+	/*
+		TODO (azlinux):
+			Linux Console Private CSI Sequences
+
+		       The following sequences are neither ECMA-48 nor native VT102.  They are
+		       native  to the Linux console driver.  Colors are in SGR parameters: 0 =
+		       black, 1 = red, 2 = green, 3 = brown, 4 = blue, 5 = magenta, 6 =  cyan,
+		       7 = white.
+
+		       ESC [ 1 ; n ]       Set color n as the underline color
+		       ESC [ 2 ; n ]       Set color n as the dim color
+		       ESC [ 8 ]           Make the current color pair the default attributes.
+		       ESC [ 9 ; n ]       Set screen blank timeout to n minutes.
+		       ESC [ 10 ; n ]      Set bell frequency in Hz.
+		       ESC [ 11 ; n ]      Set bell duration in msec.
+		       ESC [ 12 ; n ]      Bring specified console to the front.
+		       ESC [ 13 ]          Unblank the screen.
+		       ESC [ 14 ; n ]      Set the VESA powerdown interval in minutes.
+
+	*/
+	default:
+	}
+	return len(command), nil
+}
+
+// WriteChars writes the bytes to given writer.
+func (term *WindowsTerminal) WriteChars(w io.Writer, p []byte) (n int, err error) {
+	return w.Write(p)
+}
+
+const (
+	CAPSLOCK_ON        = 0x0080 //The CAPS LOCK light is on.
+	ENHANCED_KEY       = 0x0100 //The key is enhanced.
+	LEFT_ALT_PRESSED   = 0x0002 //The left ALT key is pressed.
+	LEFT_CTRL_PRESSED  = 0x0008 //The left CTRL key is pressed.
+	NUMLOCK_ON         = 0x0020 //The NUM LOCK light is on.
+	RIGHT_ALT_PRESSED  = 0x0001 //The right ALT key is pressed.
+	RIGHT_CTRL_PRESSED = 0x0004 //The right CTRL key is pressed.
+	SCROLLLOCK_ON      = 0x0040 //The SCROLL LOCK light is on.
+	SHIFT_PRESSED      = 0x0010 // The SHIFT key is pressed.
+)
+
+const (
+	KEY_CONTROL_PARAM_2 = ";2"
+	KEY_CONTROL_PARAM_3 = ";3"
+	KEY_CONTROL_PARAM_4 = ";4"
+	KEY_CONTROL_PARAM_5 = ";5"
+	KEY_CONTROL_PARAM_6 = ";6"
+	KEY_CONTROL_PARAM_7 = ";7"
+	KEY_CONTROL_PARAM_8 = ";8"
+	KEY_ESC_CSI         = "\x1B["
+	KEY_ESC_N           = "\x1BN"
+	KEY_ESC_O           = "\x1BO"
+)
+
+var keyMapPrefix = map[WORD]string{
+	VK_UP:     "\x1B[%sA",
+	VK_DOWN:   "\x1B[%sB",
+	VK_RIGHT:  "\x1B[%sC",
+	VK_LEFT:   "\x1B[%sD",
+	VK_HOME:   "\x1B[1%s~", // showkey shows ^[[1
+	VK_END:    "\x1B[4%s~", // showkey shows ^[[4
+	VK_INSERT: "\x1B[2%s~",
+	VK_DELETE: "\x1B[3%s~",
+	VK_PRIOR:  "\x1B[5%s~",
+	VK_NEXT:   "\x1B[6%s~",
+	VK_F1:     "",
+	VK_F2:     "",
+	VK_F3:     "\x1B[13%s~",
+	VK_F4:     "\x1B[14%s~",
+	VK_F5:     "\x1B[15%s~",
+	VK_F6:     "\x1B[17%s~",
+	VK_F7:     "\x1B[18%s~",
+	VK_F8:     "\x1B[19%s~",
+	VK_F9:     "\x1B[20%s~",
+	VK_F10:    "\x1B[21%s~",
+	VK_F11:    "\x1B[23%s~",
+	VK_F12:    "\x1B[24%s~",
+}
+
+var arrowKeyMapPrefix = map[WORD]string{
+	VK_UP:    "%s%sA",
+	VK_DOWN:  "%s%sB",
+	VK_RIGHT: "%s%sC",
+	VK_LEFT:  "%s%sD",
+}
+
+func getControlStateParameter(shift, alt, control, meta bool) string {
+	if shift && alt && control {
+		return KEY_CONTROL_PARAM_8
+	}
+	if alt && control {
+		return KEY_CONTROL_PARAM_7
+	}
+	if shift && control {
+		return KEY_CONTROL_PARAM_6
+	}
+	if control {
+		return KEY_CONTROL_PARAM_5
+	}
+	if shift && alt {
+		return KEY_CONTROL_PARAM_4
+	}
+	if alt {
+		return KEY_CONTROL_PARAM_3
+	}
+	if shift {
+		return KEY_CONTROL_PARAM_2
+	}
+	return ""
+}
+
+func getControlKeys(controlState DWORD) (shift, alt, control bool) {
+	shift = 0 != (controlState & SHIFT_PRESSED)
+	alt = 0 != (controlState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+	control = 0 != (controlState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
+	return shift, alt, control
+}
+
+func charSequenceForKeys(key WORD, controlState DWORD, escapeSequence []byte) string {
+	i, ok := arrowKeyMapPrefix[key]
+	if ok {
+		shift, alt, control := getControlKeys(controlState)
+		modifier := getControlStateParameter(shift, alt, control, false)
+		return fmt.Sprintf(i, escapeSequence, modifier)
+	}
+
+	i, ok = keyMapPrefix[key]
+	if ok {
+		shift, alt, control := getControlKeys(controlState)
+		modifier := getControlStateParameter(shift, alt, control, false)
+		return fmt.Sprintf(i, modifier)
+	}
+
+	return ""
+}
+
+// mapKeystokeToTerminalString maps the given input event record to string
+func mapKeystokeToTerminalString(keyEvent *KEY_EVENT_RECORD, escapeSequence []byte) string {
+	_, alt, control := getControlKeys(keyEvent.ControlKeyState)
+	if keyEvent.UnicodeChar == 0 {
+		return charSequenceForKeys(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
+	}
+	if control {
+		// TODO(azlinux): Implement following control sequences
+		// <Ctrl>-D  Signals the end of input from the keyboard; also exits current shell.
+		// <Ctrl>-H  Deletes the first character to the left of the cursor. Also called the ERASE key.
+		// <Ctrl>-Q  Restarts printing after it has been stopped with <Ctrl>-s.
+		// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
+		// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
+		// <Ctrl>-E  Quits current command and creates a core
+
+	}
+	// <Alt>+Key generates ESC N Key
+	if !control && alt {
+		return KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
+	}
+	return string(keyEvent.UnicodeChar)
+}
+
+// getAvailableInputEvents polls the console for availble events
+// The function does not return until at least one input record has been read.
+func getAvailableInputEvents() (inputEvents []INPUT_RECORD, err error) {
+	handle, _ := syscall.GetStdHandle(STD_INPUT_HANDLE)
+	if nil != err {
+		return nil, err
+	}
+	for {
+		// Read number of console events available
+		tempBuffer := make([]INPUT_RECORD, MAX_INPUT_BUFFER)
+		nr, err := readConsoleInputKey(uintptr(handle), tempBuffer)
+		if nr == 0 {
+			return nil, err
+		}
+		if 0 < nr {
+			retValue := make([]INPUT_RECORD, nr)
+			for i := 0; i < nr; i++ {
+				retValue[i] = tempBuffer[i]
+			}
+			return retValue, nil
+		}
+	}
+}
+
+// getTranslatedKeyCodes converts the input events into the string of characters
+// The ansi escape sequence are used to map key strokes to the strings
+func getTranslatedKeyCodes(inputEvents []INPUT_RECORD, escapeSequence []byte) string {
+	var buf bytes.Buffer
+	for i := 0; i < len(inputEvents); i++ {
+		input := inputEvents[i]
+		if input.EventType == KEY_EVENT && input.KeyEvent.KeyDown != 0 {
+			keyString := mapKeystokeToTerminalString(&input.KeyEvent, escapeSequence)
+			buf.WriteString(keyString)
+		}
+	}
+	return buf.String()
+}
+
+// ReadChars reads the characters from the given reader
+func (term *WindowsTerminal) ReadChars(w io.Reader, p []byte) (n int, err error) {
+	n = 0
+	for n < len(p) {
+		select {
+		case b := <-term.inputBuffer:
+			p[n] = b
+			n++
+		default:
+			// Read at least one byte read
+			if n > 0 {
+				return n, nil
+			}
+			inputEvents, _ := getAvailableInputEvents()
+			if inputEvents != nil {
+				if len(inputEvents) == 0 && nil != err {
+					return n, err
+				}
+				if len(inputEvents) != 0 {
+					keyCodes := getTranslatedKeyCodes(inputEvents, term.inputEscapeSequence)
+					for _, b := range []byte(keyCodes) {
+						term.inputBuffer <- b
+					}
+				}
+			}
+		}
+	}
+	return n, nil
+}
+
+// HandleInputSequence interprets the input sequence command
+func (term *WindowsTerminal) HandleInputSequence(command []byte) (n int, err error) {
+	return 0, nil
+}
+
+func marshal(c COORD) uint32 {
+	// works only on intel-endian machines
+	return uint32(uint32(uint16(c.Y))<<16 | uint32(uint16(c.X)))
+}

+ 232 - 0
pkg/term/console_windows_test.go

@@ -0,0 +1,232 @@
+// +build windows
+
+package term
+
+import (
+	"fmt"
+	"testing"
+)
+
+func helpsTestParseInt16OrDefault(t *testing.T, expectedValue int16, shouldFail bool, input string, defaultValue int16, format string, args ...string) {
+	value, err := parseInt16OrDefault(input, defaultValue)
+	if nil != err && !shouldFail {
+		t.Errorf("Unexpected error returned %v", err)
+		t.Errorf(format, args)
+	}
+	if nil == err && shouldFail {
+		t.Errorf("Should have failed as expected\n\tReturned value = %d", value)
+		t.Errorf(format, args)
+	}
+	if expectedValue != value {
+		t.Errorf("The value returned does not macth expected\n\tExpected:%v\n\t:Actual%v", expectedValue, value)
+		t.Errorf(format, args)
+	}
+}
+
+func TestParseInt16OrDefault(t *testing.T) {
+	// empty string
+	helpsTestParseInt16OrDefault(t, 0, false, "", 0, "Empty string returns default")
+	helpsTestParseInt16OrDefault(t, 2, false, "", 2, "Empty string returns default")
+
+	// normal case
+	helpsTestParseInt16OrDefault(t, 0, false, "0", 0, "0 handled correctly")
+	helpsTestParseInt16OrDefault(t, 111, false, "111", 2, "Normal")
+	helpsTestParseInt16OrDefault(t, 111, false, "+111", 2, "+N")
+	helpsTestParseInt16OrDefault(t, -111, false, "-111", 2, "-N")
+	helpsTestParseInt16OrDefault(t, 0, false, "+0", 11, "+0")
+	helpsTestParseInt16OrDefault(t, 0, false, "-0", 12, "-0")
+
+	// ill formed strings
+	helpsTestParseInt16OrDefault(t, 0, true, "abc", 0, "Invalid string")
+	helpsTestParseInt16OrDefault(t, 42, true, "+= 23", 42, "Invalid string")
+	helpsTestParseInt16OrDefault(t, 42, true, "123.45", 42, "float like")
+
+}
+
+func helpsTestGetNumberOfChars(t *testing.T, expected uint32, fromCoord COORD, toCoord COORD, screenSize COORD, format string, args ...interface{}) {
+	actual := getNumberOfChars(fromCoord, toCoord, screenSize)
+	mesg := fmt.Sprintf(format, args)
+	assertTrue(t, expected == actual, fmt.Sprintf("%s Expected=%d, Actual=%d, Parameters = { fromCoord=%+v, toCoord=%+v, screenSize=%+v", mesg, expected, actual, fromCoord, toCoord, screenSize))
+}
+
+func TestGetNumberOfChars(t *testing.T) {
+	// Note: The columns and lines are 0 based
+	// Also that interval is "inclusive" means will have both start and end chars
+	// This test only tests the number opf characters being written
+
+	// all four corners
+	maxWindow := COORD{X: 80, Y: 50}
+	leftTop := COORD{X: 0, Y: 0}
+	rightTop := COORD{X: 79, Y: 0}
+	leftBottom := COORD{X: 0, Y: 49}
+	rightBottom := COORD{X: 79, Y: 49}
+
+	// same position
+	helpsTestGetNumberOfChars(t, 1, COORD{X: 1, Y: 14}, COORD{X: 1, Y: 14}, COORD{X: 80, Y: 50}, "Same position random line")
+
+	// four corners
+	helpsTestGetNumberOfChars(t, 1, leftTop, leftTop, maxWindow, "Same position- leftTop")
+	helpsTestGetNumberOfChars(t, 1, rightTop, rightTop, maxWindow, "Same position- rightTop")
+	helpsTestGetNumberOfChars(t, 1, leftBottom, leftBottom, maxWindow, "Same position- leftBottom")
+	helpsTestGetNumberOfChars(t, 1, rightBottom, rightBottom, maxWindow, "Same position- rightBottom")
+
+	// from this char to next char on same line
+	helpsTestGetNumberOfChars(t, 2, COORD{X: 0, Y: 0}, COORD{X: 1, Y: 0}, maxWindow, "Next position on same line")
+	helpsTestGetNumberOfChars(t, 2, COORD{X: 1, Y: 14}, COORD{X: 2, Y: 14}, maxWindow, "Next position on same line")
+
+	// from this char to next 10 chars on same line
+	helpsTestGetNumberOfChars(t, 11, COORD{X: 0, Y: 0}, COORD{X: 10, Y: 0}, maxWindow, "Next position on same line")
+	helpsTestGetNumberOfChars(t, 11, COORD{X: 1, Y: 14}, COORD{X: 11, Y: 14}, maxWindow, "Next position on same line")
+
+	helpsTestGetNumberOfChars(t, 5, COORD{X: 3, Y: 11}, COORD{X: 7, Y: 11}, maxWindow, "To and from on same line")
+
+	helpsTestGetNumberOfChars(t, 8, COORD{X: 0, Y: 34}, COORD{X: 7, Y: 34}, maxWindow, "Start of line to middle")
+	helpsTestGetNumberOfChars(t, 4, COORD{X: 76, Y: 34}, COORD{X: 79, Y: 34}, maxWindow, "Middle to end of line")
+
+	// multiple lines - 1
+	helpsTestGetNumberOfChars(t, 81, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 1}, maxWindow, "one line below same X")
+	helpsTestGetNumberOfChars(t, 81, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 11}, maxWindow, "one line below same X")
+
+	// multiple lines - 2
+	helpsTestGetNumberOfChars(t, 161, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 2}, maxWindow, "one line below same X")
+	helpsTestGetNumberOfChars(t, 161, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 12}, maxWindow, "one line below same X")
+
+	// multiple lines - 3
+	helpsTestGetNumberOfChars(t, 241, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 3}, maxWindow, "one line below same X")
+	helpsTestGetNumberOfChars(t, 241, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 13}, maxWindow, "one line below same X")
+
+	// full line
+	helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 0}, COORD{X: 79, Y: 0}, maxWindow, "Full line - first")
+	helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 23}, COORD{X: 79, Y: 23}, maxWindow, "Full line - random")
+	helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 49}, COORD{X: 79, Y: 49}, maxWindow, "Full line - last")
+
+	// full screen
+	helpsTestGetNumberOfChars(t, 80*50, leftTop, rightBottom, maxWindow, "full screen")
+
+	helpsTestGetNumberOfChars(t, 80*50-1, COORD{X: 1, Y: 0}, rightBottom, maxWindow, "dropping first char to, end of screen")
+	helpsTestGetNumberOfChars(t, 80*50-2, COORD{X: 2, Y: 0}, rightBottom, maxWindow, "dropping first two char to, end of screen")
+
+	helpsTestGetNumberOfChars(t, 80*50-1, leftTop, COORD{X: 78, Y: 49}, maxWindow, "from start of screen, till last char-1")
+	helpsTestGetNumberOfChars(t, 80*50-2, leftTop, COORD{X: 77, Y: 49}, maxWindow, "from start of screen, till last char-2")
+
+	helpsTestGetNumberOfChars(t, 80*50-5, COORD{X: 4, Y: 0}, COORD{X: 78, Y: 49}, COORD{X: 80, Y: 50}, "from start of screen+4, till last char-1")
+	helpsTestGetNumberOfChars(t, 80*50-6, COORD{X: 4, Y: 0}, COORD{X: 77, Y: 49}, COORD{X: 80, Y: 50}, "from start of screen+4, till last char-2")
+}
+
+var allForeground = []int16{
+	ANSI_FOREGROUND_BLACK,
+	ANSI_FOREGROUND_RED,
+	ANSI_FOREGROUND_GREEN,
+	ANSI_FOREGROUND_YELLOW,
+	ANSI_FOREGROUND_BLUE,
+	ANSI_FOREGROUND_MAGENTA,
+	ANSI_FOREGROUND_CYAN,
+	ANSI_FOREGROUND_WHITE,
+	ANSI_FOREGROUND_DEFAULT,
+}
+var allBackground = []int16{
+	ANSI_BACKGROUND_BLACK,
+	ANSI_BACKGROUND_RED,
+	ANSI_BACKGROUND_GREEN,
+	ANSI_BACKGROUND_YELLOW,
+	ANSI_BACKGROUND_BLUE,
+	ANSI_BACKGROUND_MAGENTA,
+	ANSI_BACKGROUND_CYAN,
+	ANSI_BACKGROUND_WHITE,
+	ANSI_BACKGROUND_DEFAULT,
+}
+
+func maskForeground(flag WORD) WORD {
+	return flag & FOREGROUND_MASK_UNSET
+}
+
+func onlyForeground(flag WORD) WORD {
+	return flag & FOREGROUND_MASK_SET
+}
+
+func maskBackground(flag WORD) WORD {
+	return flag & BACKGROUND_MASK_UNSET
+}
+
+func onlyBackground(flag WORD) WORD {
+	return flag & BACKGROUND_MASK_SET
+}
+
+func helpsTestGetWindowsTextAttributeForAnsiValue(t *testing.T, oldValue WORD /*, expected WORD*/, ansi int16, onlyMask WORD, restMask WORD) WORD {
+	actual, err := getWindowsTextAttributeForAnsiValue(oldValue, FOREGROUND_MASK_SET, ansi)
+	assertTrue(t, nil == err, "Should be no error")
+	// assert that other bits are not affected
+	if 0 != oldValue {
+		assertTrue(t, (actual&restMask) == (oldValue&restMask), "The operation should not have affected other bits actual=%X oldValue=%X ansi=%d", actual, oldValue, ansi)
+	}
+	return actual
+}
+
+func TestBackgroundForAnsiValue(t *testing.T) {
+	// Check that nothing else changes
+	// background changes
+	for _, state1 := range allBackground {
+		for _, state2 := range allBackground {
+			flag := WORD(0)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+		}
+	}
+	// cummulative bcakground changes
+	for _, state1 := range allBackground {
+		flag := WORD(0)
+		for _, state2 := range allBackground {
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+		}
+	}
+	// change background after foreground
+	for _, state1 := range allForeground {
+		for _, state2 := range allBackground {
+			flag := WORD(0)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+		}
+	}
+	// change background after change cumulative
+	for _, state1 := range allForeground {
+		flag := WORD(0)
+		for _, state2 := range allBackground {
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+		}
+	}
+}
+
+func TestForegroundForAnsiValue(t *testing.T) {
+	// Check that nothing else changes
+	for _, state1 := range allForeground {
+		for _, state2 := range allForeground {
+			flag := WORD(0)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+		}
+	}
+
+	for _, state1 := range allForeground {
+		flag := WORD(0)
+		for _, state2 := range allForeground {
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+		}
+	}
+	for _, state1 := range allBackground {
+		for _, state2 := range allForeground {
+			flag := WORD(0)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+		}
+	}
+	for _, state1 := range allBackground {
+		flag := WORD(0)
+		for _, state2 := range allForeground {
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
+			flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
+		}
+	}
+}

+ 15 - 0
pkg/term/term.go

@@ -4,6 +4,7 @@ package term
 
 import (
 	"errors"
+	"io"
 	"os"
 	"os/signal"
 	"syscall"
@@ -25,6 +26,20 @@ type Winsize struct {
 	y      uint16
 }
 
+func StdStreams() (stdOut io.Writer, stdErr io.Writer, stdIn io.ReadCloser) {
+	return os.Stdout, os.Stderr, os.Stdin
+}
+
+func GetHandleInfo(in interface{}) (uintptr, bool) {
+	var inFd uintptr
+	var isTerminalIn bool
+	if file, ok := in.(*os.File); ok {
+		inFd = file.Fd()
+		isTerminalIn = IsTerminal(inFd)
+	}
+	return inFd, isTerminalIn
+}
+
 func GetWinsize(fd uintptr) (*Winsize, error) {
 	ws := &Winsize{}
 	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))

+ 216 - 0
pkg/term/term_emulator.go

@@ -0,0 +1,216 @@
+package term
+
+import (
+	"io"
+	"strconv"
+	"strings"
+)
+
+// http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
+const (
+	ANSI_ESCAPE_PRIMARY   = 0x1B
+	ANSI_ESCAPE_SECONDARY = 0x5B
+	ANSI_COMMAND_FIRST    = 0x40
+	ANSI_COMMAND_LAST     = 0x7E
+	ANSI_PARAMETER_SEP    = ";"
+	ANSI_CMD_G0           = '('
+	ANSI_CMD_G1           = ')'
+	ANSI_CMD_G2           = '*'
+	ANSI_CMD_G3           = '+'
+	ANSI_CMD_DECPNM       = '>'
+	ANSI_CMD_DECPAM       = '='
+	ANSI_CMD_OSC          = ']'
+	ANSI_CMD_STR_TERM     = '\\'
+	ANSI_BEL              = 0x07
+	KEY_EVENT             = 1
+)
+
+// Interface that implements terminal handling
+type terminalEmulator interface {
+	HandleOutputCommand(command []byte) (n int, err error)
+	HandleInputSequence(command []byte) (n int, err error)
+	WriteChars(w io.Writer, p []byte) (n int, err error)
+	ReadChars(w io.Reader, p []byte) (n int, err error)
+}
+
+type terminalWriter struct {
+	wrappedWriter io.Writer
+	emulator      terminalEmulator
+	command       []byte
+	inSequence    bool
+}
+
+type terminalReader struct {
+	wrappedReader io.ReadCloser
+	emulator      terminalEmulator
+	command       []byte
+	inSequence    bool
+}
+
+// http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
+func isAnsiCommandChar(b byte) bool {
+	switch {
+	case ANSI_COMMAND_FIRST <= b && b <= ANSI_COMMAND_LAST && b != ANSI_ESCAPE_SECONDARY:
+		return true
+	case b == ANSI_CMD_G1 || b == ANSI_CMD_OSC || b == ANSI_CMD_DECPAM || b == ANSI_CMD_DECPNM:
+		// non-CSI escape sequence terminator
+		return true
+	case b == ANSI_CMD_STR_TERM || b == ANSI_BEL:
+		// String escape sequence terminator
+		return true
+	}
+	return false
+}
+
+func isCharacterSelectionCmdChar(b byte) bool {
+	return (b == ANSI_CMD_G0 || b == ANSI_CMD_G1 || b == ANSI_CMD_G2 || b == ANSI_CMD_G3)
+}
+
+func isXtermOscSequence(command []byte, current byte) bool {
+	return (len(command) >= 2 && command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_CMD_OSC && current != ANSI_BEL)
+}
+
+// Write writes len(p) bytes from p to the underlying data stream.
+// http://golang.org/pkg/io/#Writer
+func (tw *terminalWriter) Write(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	if tw.emulator == nil {
+		return tw.wrappedWriter.Write(p)
+	}
+	// Emulate terminal by extracting commands and executing them
+	totalWritten := 0
+	start := 0 // indicates start of the next chunk
+	end := len(p)
+	for current := 0; current < end; current++ {
+		if tw.inSequence {
+			// inside escape sequence
+			tw.command = append(tw.command, p[current])
+			if isAnsiCommandChar(p[current]) {
+				if !isXtermOscSequence(tw.command, p[current]) {
+					// found the last command character.
+					// Now we have a complete command.
+					nchar, err := tw.emulator.HandleOutputCommand(tw.command)
+					totalWritten += nchar
+					if err != nil {
+						return totalWritten, err
+					}
+
+					// clear the command
+					// don't include current character again
+					tw.command = tw.command[:0]
+					start = current + 1
+					tw.inSequence = false
+				}
+			}
+		} else {
+			if p[current] == ANSI_ESCAPE_PRIMARY {
+				// entering escape sequnce
+				tw.inSequence = true
+				// indicates end of "normal sequence", write whatever you have so far
+				if len(p[start:current]) > 0 {
+					nw, err := tw.emulator.WriteChars(tw.wrappedWriter, p[start:current])
+					totalWritten += nw
+					if err != nil {
+						return totalWritten, err
+					}
+				}
+				// include the current character as part of the next sequence
+				tw.command = append(tw.command, p[current])
+			}
+		}
+	}
+	// note that so far, start of the escape sequence triggers writing out of bytes to console.
+	// For the part _after_ the end of last escape sequence, it is not written out yet. So write it out
+	if !tw.inSequence {
+		// assumption is that we can't be inside sequence and therefore command should be empty
+		if len(p[start:]) > 0 {
+			nw, err := tw.emulator.WriteChars(tw.wrappedWriter, p[start:])
+			totalWritten += nw
+			if err != nil {
+				return totalWritten, err
+			}
+		}
+	}
+	return totalWritten, nil
+
+}
+
+// Read reads up to len(p) bytes into p.
+// http://golang.org/pkg/io/#Reader
+func (tr *terminalReader) Read(p []byte) (n int, err error) {
+	//Implementations of Read are discouraged from returning a zero byte count
+	// with a nil error, except when len(p) == 0.
+	if len(p) == 0 {
+		return 0, nil
+	}
+	if nil == tr.emulator {
+		return tr.readFromWrappedReader(p)
+	}
+	return tr.emulator.ReadChars(tr.wrappedReader, p)
+}
+
+// Close the underlying stream
+func (tr *terminalReader) Close() (err error) {
+	return tr.wrappedReader.Close()
+}
+
+func (tr *terminalReader) readFromWrappedReader(p []byte) (n int, err error) {
+	return tr.wrappedReader.Read(p)
+}
+
+type ansiCommand struct {
+	CommandBytes []byte
+	Command      string
+	Parameters   []string
+	IsSpecial    bool
+}
+
+func parseAnsiCommand(command []byte) *ansiCommand {
+	if isCharacterSelectionCmdChar(command[1]) {
+		// Is Character Set Selection commands
+		return &ansiCommand{
+			CommandBytes: command,
+			Command:      string(command),
+			IsSpecial:    true,
+		}
+	}
+	// last char is command character
+	lastCharIndex := len(command) - 1
+
+	retValue := &ansiCommand{
+		CommandBytes: command,
+		Command:      string(command[lastCharIndex]),
+		IsSpecial:    false,
+	}
+	// more than a single escape
+	if lastCharIndex != 0 {
+		start := 1
+		// skip if double char escape sequence
+		if command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_ESCAPE_SECONDARY {
+			start++
+		}
+		// convert this to GetNextParam method
+		retValue.Parameters = strings.Split(string(command[start:lastCharIndex]), ANSI_PARAMETER_SEP)
+	}
+	return retValue
+}
+
+func (c *ansiCommand) getParam(index int) string {
+	if len(c.Parameters) > index {
+		return c.Parameters[index]
+	}
+	return ""
+}
+
+func parseInt16OrDefault(s string, defaultValue int16) (n int16, err error) {
+	if s == "" {
+		return defaultValue, nil
+	}
+	parsedValue, err := strconv.ParseInt(s, 10, 16)
+	if nil != err {
+		return defaultValue, err
+	}
+	return int16(parsedValue), nil
+}

+ 388 - 0
pkg/term/term_emulator_test.go

@@ -0,0 +1,388 @@
+package term
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"testing"
+)
+
+const (
+	WRITE_OPERATION   = iota
+	COMMAND_OPERATION = iota
+)
+
+var languages = []string{
+	"Български",
+	"Català",
+	"Čeština",
+	"Ελληνικά",
+	"Español",
+	"Esperanto",
+	"Euskara",
+	"Français",
+	"Galego",
+	"한국어",
+	"ქართული",
+	"Latviešu",
+	"Lietuvių",
+	"Magyar",
+	"Nederlands",
+	"日本語",
+	"Norsk bokmål",
+	"Norsk nynorsk",
+	"Polski",
+	"Português",
+	"Română",
+	"Русский",
+	"Slovenčina",
+	"Slovenščina",
+	"Српски",
+	"српскохрватски",
+	"Suomi",
+	"Svenska",
+	"ไทย",
+	"Tiếng Việt",
+	"Türkçe",
+	"Українська",
+	"中文",
+}
+
+// Mock terminal handler object
+type mockTerminal struct {
+	OutputCommandSequence []terminalOperation
+}
+
+// Used for recording the callback data
+type terminalOperation struct {
+	Operation int
+	Data      []byte
+	Str       string
+}
+
+func (mt *mockTerminal) record(operation int, data []byte) {
+	op := terminalOperation{
+		Operation: operation,
+		Data:      make([]byte, len(data)),
+	}
+	copy(op.Data, data)
+	op.Str = string(op.Data)
+	mt.OutputCommandSequence = append(mt.OutputCommandSequence, op)
+}
+
+func (mt *mockTerminal) HandleOutputCommand(command []byte) (n int, err error) {
+	mt.record(COMMAND_OPERATION, command)
+	return len(command), nil
+}
+
+func (mt *mockTerminal) HandleInputSequence(command []byte) (n int, err error) {
+	return 0, nil
+}
+
+func (mt *mockTerminal) WriteChars(w io.Writer, p []byte) (n int, err error) {
+	mt.record(WRITE_OPERATION, p)
+	return len(p), nil
+}
+
+func (mt *mockTerminal) ReadChars(w io.Reader, p []byte) (n int, err error) {
+	return len(p), nil
+}
+
+func assertTrue(t *testing.T, cond bool, format string, args ...interface{}) {
+	if !cond {
+		t.Errorf(format, args...)
+	}
+}
+
+// reflect.DeepEqual does not provide detailed information as to what excatly failed.
+func assertBytesEqual(t *testing.T, expected, actual []byte, format string, args ...interface{}) {
+	match := true
+	mismatchIndex := 0
+	if len(expected) == len(actual) {
+		for i := 0; i < len(expected); i++ {
+			if expected[i] != actual[i] {
+				match = false
+				mismatchIndex = i
+				break
+			}
+		}
+	} else {
+		match = false
+		t.Errorf("Lengths don't match Expected=%d Actual=%d", len(expected), len(actual))
+	}
+	if !match {
+		t.Errorf("Mismatch at index %d ", mismatchIndex)
+		t.Errorf("\tActual String   = %s", string(actual))
+		t.Errorf("\tExpected String = %s", string(expected))
+		t.Errorf("\tActual          = %v", actual)
+		t.Errorf("\tExpected        = %v", expected)
+		t.Errorf(format, args)
+	}
+}
+
+// Just to make sure :)
+func TestAssertEqualBytes(t *testing.T) {
+	data := []byte{9, 9, 1, 1, 1, 9, 9}
+	assertBytesEqual(t, data, data, "Self")
+	assertBytesEqual(t, data[1:4], data[1:4], "Self")
+	assertBytesEqual(t, []byte{1, 1}, []byte{1, 1}, "Simple match")
+	assertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 2, 3}, "content mismatch")
+	assertBytesEqual(t, []byte{1, 1, 1}, data[2:5], "slice match")
+}
+
+/*
+func TestAssertEqualBytesNegative(t *testing.T) {
+	AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch")
+	AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch")
+	AssertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 1, 1}, "content mismatch")
+}*/
+
+// Checks that the calls recieved
+func assertHandlerOutput(t *testing.T, mock *mockTerminal, plainText string, commands ...string) {
+	text := make([]byte, 0, 3*len(plainText))
+	cmdIndex := 0
+	for opIndex := 0; opIndex < len(mock.OutputCommandSequence); opIndex++ {
+		op := mock.OutputCommandSequence[opIndex]
+		if op.Operation == WRITE_OPERATION {
+			t.Logf("\nThe data is[%d] == %s", opIndex, string(op.Data))
+			text = append(text[:], op.Data...)
+		} else {
+			assertTrue(t, mock.OutputCommandSequence[opIndex].Operation == COMMAND_OPERATION, "Operation should be command : %s", fmt.Sprintf("%+v", mock))
+			assertBytesEqual(t, StringToBytes(commands[cmdIndex]), mock.OutputCommandSequence[opIndex].Data, "Command data should match")
+			cmdIndex++
+		}
+	}
+	assertBytesEqual(t, StringToBytes(plainText), text, "Command data should match %#v", mock)
+}
+
+func StringToBytes(str string) []byte {
+	bytes := make([]byte, len(str))
+	copy(bytes[:], str)
+	return bytes
+}
+
+func TestParseAnsiCommand(t *testing.T) {
+	// Note: if the parameter does not exist then the empty value is returned
+
+	c := parseAnsiCommand(StringToBytes("\x1Bm"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "" == c.getParam(0), "should return empty string")
+	assertTrue(t, "" == c.getParam(1), "should return empty string")
+
+	// Escape sequence - ESC[
+	c = parseAnsiCommand(StringToBytes("\x1B[m"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "" == c.getParam(0), "should return empty string")
+	assertTrue(t, "" == c.getParam(1), "should return empty string")
+
+	// Escape sequence With empty parameters- ESC[
+	c = parseAnsiCommand(StringToBytes("\x1B[;m"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "" == c.getParam(0), "should return empty string")
+	assertTrue(t, "" == c.getParam(1), "should return empty string")
+	assertTrue(t, "" == c.getParam(2), "should return empty string")
+
+	// Escape sequence With empty muliple parameters- ESC[
+	c = parseAnsiCommand(StringToBytes("\x1B[;;m"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "" == c.getParam(0), "")
+	assertTrue(t, "" == c.getParam(1), "")
+	assertTrue(t, "" == c.getParam(2), "")
+
+	// Escape sequence With muliple parameters- ESC[
+	c = parseAnsiCommand(StringToBytes("\x1B[1;2;3m"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "1" == c.getParam(0), "")
+	assertTrue(t, "2" == c.getParam(1), "")
+	assertTrue(t, "3" == c.getParam(2), "")
+
+	// Escape sequence With muliple parameters- some missing
+	c = parseAnsiCommand(StringToBytes("\x1B[1;;3;;;6m"))
+	assertTrue(t, c.Command == "m", "Command should be m")
+	assertTrue(t, "1" == c.getParam(0), "")
+	assertTrue(t, "" == c.getParam(1), "")
+	assertTrue(t, "3" == c.getParam(2), "")
+	assertTrue(t, "" == c.getParam(3), "")
+	assertTrue(t, "" == c.getParam(4), "")
+	assertTrue(t, "6" == c.getParam(5), "")
+}
+
+func newBufferedMockTerm() (stdOut io.Writer, stdErr io.Writer, stdIn io.ReadCloser, mock *mockTerminal) {
+	var input bytes.Buffer
+	var output bytes.Buffer
+	var err bytes.Buffer
+
+	mock = &mockTerminal{
+		OutputCommandSequence: make([]terminalOperation, 0, 256),
+	}
+
+	stdOut = &terminalWriter{
+		wrappedWriter: &output,
+		emulator:      mock,
+		command:       make([]byte, 0, 256),
+	}
+	stdErr = &terminalWriter{
+		wrappedWriter: &err,
+		emulator:      mock,
+		command:       make([]byte, 0, 256),
+	}
+	stdIn = &terminalReader{
+		wrappedReader: ioutil.NopCloser(&input),
+		emulator:      mock,
+		command:       make([]byte, 0, 256),
+	}
+
+	return
+}
+
+func TestOutputSimple(t *testing.T) {
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	stdOut.Write(StringToBytes("Hello world"))
+	stdOut.Write(StringToBytes("\x1BmHello again"))
+
+	assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
+	assertBytesEqual(t, StringToBytes("\x1Bm"), mock.OutputCommandSequence[1].Data, "Command data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match")
+}
+
+func TestOutputSplitCommand(t *testing.T) {
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	stdOut.Write(StringToBytes("Hello world\x1B[1;2;3"))
+	stdOut.Write(StringToBytes("mHello again"))
+
+	assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
+	assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match")
+}
+
+func TestOutputMultipleCommands(t *testing.T) {
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	stdOut.Write(StringToBytes("Hello world"))
+	stdOut.Write(StringToBytes("\x1B[1;2;3m"))
+	stdOut.Write(StringToBytes("\x1B[J"))
+	stdOut.Write(StringToBytes("Hello again"))
+
+	assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
+	assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[2].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
+	assertBytesEqual(t, StringToBytes("\x1B[J"), mock.OutputCommandSequence[2].Data, "Command data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[3].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[3].Data, "Write data should match")
+}
+
+// Splits the given data in two chunks , makes two writes and checks the split data is parsed correctly
+// checks output write/command is passed to handler correctly
+func helpsTestOutputSplitChunksAtIndex(t *testing.T, i int, data []byte) {
+	t.Logf("\ni=%d", i)
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	t.Logf("\nWriting chunk[0] == %s", string(data[:i]))
+	t.Logf("\nWriting chunk[1] == %s", string(data[i:]))
+	stdOut.Write(data[:i])
+	stdOut.Write(data[i:])
+
+	assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, data[i:], mock.OutputCommandSequence[1].Data, "Write data should match")
+}
+
+// Splits the given data in three chunks , makes three writes and checks the split data is parsed correctly
+// checks output write/command is passed to handler correctly
+func helpsTestOutputSplitThreeChunksAtIndex(t *testing.T, data []byte, i int, j int) {
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	t.Logf("\nWriting chunk[0] == %s", string(data[:i]))
+	t.Logf("\nWriting chunk[1] == %s", string(data[i:j]))
+	t.Logf("\nWriting chunk[2] == %s", string(data[j:]))
+	stdOut.Write(data[:i])
+	stdOut.Write(data[i:j])
+	stdOut.Write(data[j:])
+
+	assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, data[i:j], mock.OutputCommandSequence[1].Data, "Write data should match")
+
+	assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
+	assertBytesEqual(t, data[j:], mock.OutputCommandSequence[2].Data, "Write data should match")
+}
+
+// Splits the output into two parts and tests all such possible pairs
+func helpsTestOutputSplitChunks(t *testing.T, data []byte) {
+	for i := 1; i < len(data)-1; i++ {
+		helpsTestOutputSplitChunksAtIndex(t, i, data)
+	}
+}
+
+// Splits the output in three parts and tests all such possible triples
+func helpsTestOutputSplitThreeChunks(t *testing.T, data []byte) {
+	for i := 1; i < len(data)-2; i++ {
+		for j := i + 1; j < len(data)-1; j++ {
+			helpsTestOutputSplitThreeChunksAtIndex(t, data, i, j)
+		}
+	}
+}
+
+func helpsTestOutputSplitCommandsAtIndex(t *testing.T, data []byte, i int, plainText string, commands ...string) {
+	t.Logf("\ni=%d", i)
+	stdOut, _, _, mock := newBufferedMockTerm()
+
+	stdOut.Write(data[:i])
+	stdOut.Write(data[i:])
+	assertHandlerOutput(t, mock, plainText, commands...)
+}
+
+func helpsTestOutputSplitCommands(t *testing.T, data []byte, plainText string, commands ...string) {
+	for i := 1; i < len(data)-1; i++ {
+		helpsTestOutputSplitCommandsAtIndex(t, data, i, plainText, commands...)
+	}
+}
+
+func injectCommandAt(data string, i int, command string) string {
+	retValue := make([]byte, len(data)+len(command)+4)
+	retValue = append(retValue, data[:i]...)
+	retValue = append(retValue, data[i:]...)
+	return string(retValue)
+}
+
+func TestOutputSplitChunks(t *testing.T) {
+	data := StringToBytes("qwertyuiopasdfghjklzxcvbnm")
+	helpsTestOutputSplitChunks(t, data)
+	helpsTestOutputSplitChunks(t, StringToBytes("BBBBB"))
+	helpsTestOutputSplitThreeChunks(t, StringToBytes("ABCDE"))
+}
+
+func TestOutputSplitChunksIncludingCommands(t *testing.T) {
+	helpsTestOutputSplitCommands(t, StringToBytes("Hello world.\x1B[mHello again."), "Hello world.Hello again.", "\x1B[m")
+	helpsTestOutputSplitCommandsAtIndex(t, StringToBytes("Hello world.\x1B[mHello again."), 2, "Hello world.Hello again.", "\x1B[m")
+}
+
+func TestSplitChunkUnicode(t *testing.T) {
+	for _, l := range languages {
+		data := StringToBytes(l)
+		helpsTestOutputSplitChunks(t, data)
+		helpsTestOutputSplitThreeChunks(t, data)
+	}
+}

+ 20 - 5
pkg/term/term_windows.go

@@ -2,10 +2,12 @@
 
 package term
 
+// State holds the console mode for the terminal.
 type State struct {
 	mode uint32
 }
 
+// Winsize is used for window size.
 type Winsize struct {
 	Height uint16
 	Width  uint16
@@ -13,6 +15,7 @@ type Winsize struct {
 	y      uint16
 }
 
+// GetWinsize gets the window size of the given terminal
 func GetWinsize(fd uintptr) (*Winsize, error) {
 	ws := &Winsize{}
 	var info *CONSOLE_SCREEN_BUFFER_INFO
@@ -20,8 +23,9 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
 	if err != nil {
 		return nil, err
 	}
-	ws.Height = uint16(info.srWindow.Right - info.srWindow.Left + 1)
-	ws.Width = uint16(info.srWindow.Bottom - info.srWindow.Top + 1)
+
+	ws.Width = uint16(info.Window.Right - info.Window.Left + 1)
+	ws.Height = uint16(info.Window.Bottom - info.Window.Top + 1)
 
 	ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
 	ws.y = 0
@@ -29,6 +33,8 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
 	return ws, nil
 }
 
+// SetWinsize sets the terminal connected to the given file descriptor to a
+// given size.
 func SetWinsize(fd uintptr, ws *Winsize) error {
 	return nil
 }
@@ -39,12 +45,13 @@ func IsTerminal(fd uintptr) bool {
 	return e == nil
 }
 
-// Restore restores the terminal connected to the given file descriptor to a
+// RestoreTerminal restores the terminal connected to the given file descriptor to a
 // previous state.
 func RestoreTerminal(fd uintptr, state *State) error {
 	return SetConsoleMode(fd, state.mode)
 }
 
+// SaveState saves the state of the given console
 func SaveState(fd uintptr) (*State, error) {
 	mode, e := GetConsoleMode(fd)
 	if e != nil {
@@ -53,6 +60,7 @@ func SaveState(fd uintptr) (*State, error) {
 	return &State{mode}, nil
 }
 
+// DisableEcho disbales the echo for given file descriptor and returns previous state
 // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
 func DisableEcho(fd uintptr, state *State) error {
 	state.mode &^= (ENABLE_ECHO_INPUT)
@@ -60,6 +68,9 @@ func DisableEcho(fd uintptr, state *State) error {
 	return SetConsoleMode(fd, state.mode)
 }
 
+// SetRawTerminal puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
 func SetRawTerminal(fd uintptr) (*State, error) {
 	oldState, err := MakeRaw(fd)
 	if err != nil {
@@ -79,8 +90,12 @@ func MakeRaw(fd uintptr) (*State, error) {
 		return nil, err
 	}
 
-	// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
-	state.mode &^= (ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
+	// All three input modes, along with processed output mode, are designed to work together.
+	// It is best to either enable or disable all of these modes as a group.
+	// When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application.
+	// When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.
+	state.mode = 0
 	err = SetConsoleMode(fd, state.mode)
 	if err != nil {
 		return nil, err