ソースを参照

Merge pull request #15392 from Microsoft/js/vendor_ansiterm

Update vendored go-ansiterm for Windows console
Arnaud Porterie 10 年 前
コミット
27fd64c67f

+ 1 - 1
hack/vendor.sh

@@ -6,7 +6,7 @@ rm -rf vendor/
 source 'hack/.vendor-helpers.sh'
 source 'hack/.vendor-helpers.sh'
 
 
 # the following lines are in sorted order, FYI
 # the following lines are in sorted order, FYI
-clone git github.com/Azure/go-ansiterm 0a9ca7117fc3e5629da85238ede560cb5e749783
+clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
 clone git github.com/Sirupsen/logrus v0.8.2 # logrus is a common dependency among multiple deps
 clone git github.com/Sirupsen/logrus v0.8.2 # logrus is a common dependency among multiple deps
 clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
 clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673
 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673

+ 4 - 0
vendor/src/github.com/Azure/go-ansiterm/constants.go

@@ -77,7 +77,11 @@ const (
 	DEFAULT_HEIGHT   = 24
 	DEFAULT_HEIGHT   = 24
 
 
 	ANSI_BEL              = 0x07
 	ANSI_BEL              = 0x07
+	ANSI_BACKSPACE        = 0x08
+	ANSI_TAB              = 0x09
 	ANSI_LINE_FEED        = 0x0A
 	ANSI_LINE_FEED        = 0x0A
+	ANSI_VERTICAL_TAB     = 0x0B
+	ANSI_FORM_FEED        = 0x0C
 	ANSI_CARRIAGE_RETURN  = 0x0D
 	ANSI_CARRIAGE_RETURN  = 0x0D
 	ANSI_ESCAPE_PRIMARY   = 0x1B
 	ANSI_ESCAPE_PRIMARY   = 0x1B
 	ANSI_ESCAPE_SECONDARY = 0x5B
 	ANSI_ESCAPE_SECONDARY = 0x5B

+ 18 - 0
vendor/src/github.com/Azure/go-ansiterm/event_handler.go

@@ -28,6 +28,9 @@ type AnsiEventHandler interface {
 	// Cursor Horizontal position Absolute
 	// Cursor Horizontal position Absolute
 	CHA(int) error
 	CHA(int) error
 
 
+	// Vertical line Position Absolute
+	VPA(int) error
+
 	// CUrsor Position
 	// CUrsor Position
 	CUP(int, int) error
 	CUP(int, int) error
 
 
@@ -37,6 +40,12 @@ type AnsiEventHandler interface {
 	// Text Cursor Enable Mode
 	// Text Cursor Enable Mode
 	DECTCEM(bool) error
 	DECTCEM(bool) error
 
 
+	// Origin Mode
+	DECOM(bool) error
+
+	// 132 Column Mode
+	DECCOLM(bool) error
+
 	// Erase in Display
 	// Erase in Display
 	ED(int) error
 	ED(int) error
 
 
@@ -49,6 +58,12 @@ type AnsiEventHandler interface {
 	// Delete Line
 	// Delete Line
 	DL(int) error
 	DL(int) error
 
 
+	// Insert Character
+	ICH(int) error
+
+	// Delete Character
+	DCH(int) error
+
 	// Set Graphics Rendition
 	// Set Graphics Rendition
 	SGR([]int) error
 	SGR([]int) error
 
 
@@ -64,6 +79,9 @@ type AnsiEventHandler interface {
 	// Set Top and Bottom Margins
 	// Set Top and Bottom Margins
 	DECSTBM(int, int) error
 	DECSTBM(int, int) error
 
 
+	// Index
+	IND() error
+
 	// Reverse Index
 	// Reverse Index
 	RI() error
 	RI() error
 
 

+ 16 - 4
vendor/src/github.com/Azure/go-ansiterm/parser_action_helpers.go

@@ -65,17 +65,29 @@ func getInts(params []string, minCount int, dflt int) []int {
 	return ints
 	return ints
 }
 }
 
 
+func (ap *AnsiParser) modeDispatch(param string, set bool) error {
+	switch param {
+	case "?3":
+		return ap.eventHandler.DECCOLM(set)
+	case "?6":
+		return ap.eventHandler.DECOM(set)
+	case "?25":
+		return ap.eventHandler.DECTCEM(set)
+	}
+	return nil
+}
+
 func (ap *AnsiParser) hDispatch(params []string) error {
 func (ap *AnsiParser) hDispatch(params []string) error {
-	if len(params) == 1 && params[0] == "?25" {
-		return ap.eventHandler.DECTCEM(true)
+	if len(params) == 1 {
+		return ap.modeDispatch(params[0], true)
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
 func (ap *AnsiParser) lDispatch(params []string) error {
 func (ap *AnsiParser) lDispatch(params []string) error {
-	if len(params) == 1 && params[0] == "?25" {
-		return ap.eventHandler.DECTCEM(false)
+	if len(params) == 1 {
+		return ap.modeDispatch(params[0], false)
 	}
 	}
 
 
 	return nil
 	return nil

+ 15 - 1
vendor/src/github.com/Azure/go-ansiterm/parser_actions.go

@@ -25,7 +25,15 @@ func (ap *AnsiParser) escDispatch() error {
 	logger.Infof("escDispatch: %v(%v)", cmd, intermeds)
 	logger.Infof("escDispatch: %v(%v)", cmd, intermeds)
 
 
 	switch cmd {
 	switch cmd {
-	case "M":
+	case "D": // IND
+		return ap.eventHandler.IND()
+	case "E": // NEL, equivalent to CRLF
+		err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN)
+		if err == nil {
+			err = ap.eventHandler.Execute(ANSI_LINE_FEED)
+		}
+		return err
+	case "M": // RI
 		return ap.eventHandler.RI()
 		return ap.eventHandler.RI()
 	}
 	}
 
 
@@ -39,6 +47,8 @@ func (ap *AnsiParser) csiDispatch() error {
 	logger.Infof("csiDispatch: %v(%v)", cmd, params)
 	logger.Infof("csiDispatch: %v(%v)", cmd, params)
 
 
 	switch cmd {
 	switch cmd {
+	case "@":
+		return ap.eventHandler.ICH(getInt(params, 1))
 	case "A":
 	case "A":
 		return ap.eventHandler.CUU(getInt(params, 1))
 		return ap.eventHandler.CUU(getInt(params, 1))
 	case "B":
 	case "B":
@@ -67,12 +77,16 @@ func (ap *AnsiParser) csiDispatch() error {
 		return ap.eventHandler.IL(getInt(params, 1))
 		return ap.eventHandler.IL(getInt(params, 1))
 	case "M":
 	case "M":
 		return ap.eventHandler.DL(getInt(params, 1))
 		return ap.eventHandler.DL(getInt(params, 1))
+	case "P":
+		return ap.eventHandler.DCH(getInt(params, 1))
 	case "S":
 	case "S":
 		return ap.eventHandler.SU(getInt(params, 1))
 		return ap.eventHandler.SU(getInt(params, 1))
 	case "T":
 	case "T":
 		return ap.eventHandler.SD(getInt(params, 1))
 		return ap.eventHandler.SD(getInt(params, 1))
 	case "c":
 	case "c":
 		return ap.eventHandler.DA(params)
 		return ap.eventHandler.DA(params)
+	case "d":
+		return ap.eventHandler.VPA(getInt(params, 1))
 	case "f":
 	case "f":
 		ints := getInts(params, 2, 1)
 		ints := getInts(params, 2, 1)
 		x, y := ints[0], ints[1]
 		x, y := ints[0], ints[1]

+ 30 - 0
vendor/src/github.com/Azure/go-ansiterm/test_event_handler.go

@@ -65,6 +65,11 @@ func (h *TestAnsiEventHandler) CHA(param int) error {
 	return nil
 	return nil
 }
 }
 
 
+func (h *TestAnsiEventHandler) VPA(param int) error {
+	h.recordCall("VPA", []string{strconv.Itoa(param)})
+	return nil
+}
+
 func (h *TestAnsiEventHandler) CUP(x int, y int) error {
 func (h *TestAnsiEventHandler) CUP(x int, y int) error {
 	xS, yS := strconv.Itoa(x), strconv.Itoa(y)
 	xS, yS := strconv.Itoa(x), strconv.Itoa(y)
 	h.recordCall("CUP", []string{xS, yS})
 	h.recordCall("CUP", []string{xS, yS})
@@ -82,6 +87,16 @@ func (h *TestAnsiEventHandler) DECTCEM(visible bool) error {
 	return nil
 	return nil
 }
 }
 
 
+func (h *TestAnsiEventHandler) DECOM(visible bool) error {
+	h.recordCall("DECOM", []string{strconv.FormatBool(visible)})
+	return nil
+}
+
+func (h *TestAnsiEventHandler) DECCOLM(use132 bool) error {
+	h.recordCall("DECOLM", []string{strconv.FormatBool(use132)})
+	return nil
+}
+
 func (h *TestAnsiEventHandler) ED(param int) error {
 func (h *TestAnsiEventHandler) ED(param int) error {
 	h.recordCall("ED", []string{strconv.Itoa(param)})
 	h.recordCall("ED", []string{strconv.Itoa(param)})
 	return nil
 	return nil
@@ -102,6 +117,16 @@ func (h *TestAnsiEventHandler) DL(param int) error {
 	return nil
 	return nil
 }
 }
 
 
+func (h *TestAnsiEventHandler) ICH(param int) error {
+	h.recordCall("ICH", []string{strconv.Itoa(param)})
+	return nil
+}
+
+func (h *TestAnsiEventHandler) DCH(param int) error {
+	h.recordCall("DCH", []string{strconv.Itoa(param)})
+	return nil
+}
+
 func (h *TestAnsiEventHandler) SGR(params []int) error {
 func (h *TestAnsiEventHandler) SGR(params []int) error {
 	strings := []string{}
 	strings := []string{}
 	for _, v := range params {
 	for _, v := range params {
@@ -138,6 +163,11 @@ func (h *TestAnsiEventHandler) RI() error {
 	return nil
 	return nil
 }
 }
 
 
+func (h *TestAnsiEventHandler) IND() error {
+	h.recordCall("IND", nil)
+	return nil
+}
+
 func (h *TestAnsiEventHandler) Flush() error {
 func (h *TestAnsiEventHandler) Flush() error {
 	return nil
 	return nil
 }
 }

+ 1 - 1
vendor/src/github.com/Azure/go-ansiterm/winterm/ansi.go

@@ -9,7 +9,7 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 
 
-	. "github.com/docker/docker/vendor/src/github.com/Azure/go-ansiterm"
+	. "github.com/Azure/go-ansiterm"
 )
 )
 
 
 // Windows keyboard constants
 // Windows keyboard constants

+ 12 - 5
vendor/src/github.com/Azure/go-ansiterm/winterm/attr_translation.go

@@ -13,7 +13,7 @@ const (
 
 
 // collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
 // collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
 // request represented by the passed ANSI mode.
 // request represented by the passed ANSI mode.
-func collectAnsiIntoWindowsAttributes(windowsMode WORD, baseMode WORD, ansiMode SHORT) WORD {
+func collectAnsiIntoWindowsAttributes(windowsMode WORD, inverted bool, baseMode WORD, ansiMode SHORT) (WORD, bool) {
 	switch ansiMode {
 	switch ansiMode {
 
 
 	// Mode styles
 	// Mode styles
@@ -26,9 +26,11 @@ func collectAnsiIntoWindowsAttributes(windowsMode WORD, baseMode WORD, ansiMode
 	case ANSI_SGR_UNDERLINE:
 	case ANSI_SGR_UNDERLINE:
 		windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
 		windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
 
 
-	case ANSI_SGR_REVERSE, ANSI_SGR_REVERSE_OFF:
-		// Note: Windows does not support a native reverse. Simply swap the foreground / background color / intensity.
-		windowsMode = (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
+	case ANSI_SGR_REVERSE:
+		inverted = true
+
+	case ANSI_SGR_REVERSE_OFF:
+		inverted = false
 
 
 	case ANSI_SGR_UNDERLINE_OFF:
 	case ANSI_SGR_UNDERLINE_OFF:
 		windowsMode &^= COMMON_LVB_UNDERSCORE
 		windowsMode &^= COMMON_LVB_UNDERSCORE
@@ -91,5 +93,10 @@ func collectAnsiIntoWindowsAttributes(windowsMode WORD, baseMode WORD, ansiMode
 		windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
 		windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
 	}
 	}
 
 
-	return windowsMode
+	return windowsMode, inverted
+}
+
+// invertAttributes inverts the foreground and background colors of a Windows attributes value
+func invertAttributes(windowsMode WORD) WORD {
+	return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
 }
 }

+ 36 - 14
vendor/src/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go

@@ -7,11 +7,35 @@ const (
 	Vertical
 	Vertical
 )
 )
 
 
-// setCursorPosition sets the cursor to the specified position, bounded to the buffer size
-func (h *WindowsAnsiEventHandler) setCursorPosition(position COORD, sizeBuffer COORD) error {
-	position.X = ensureInRange(position.X, 0, sizeBuffer.X-1)
-	position.Y = ensureInRange(position.Y, 0, sizeBuffer.Y-1)
-	return SetConsoleCursorPosition(h.fd, position)
+func (h *WindowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
+	if h.originMode {
+		sr := h.effectiveSr(info.Window)
+		return SMALL_RECT{
+			Top:    sr.top,
+			Bottom: sr.bottom,
+			Left:   0,
+			Right:  info.Size.X - 1,
+		}
+	} else {
+		return SMALL_RECT{
+			Top:    info.Window.Top,
+			Bottom: info.Window.Bottom,
+			Left:   0,
+			Right:  info.Size.X - 1,
+		}
+	}
+}
+
+// setCursorPosition sets the cursor to the specified position, bounded to the screen size
+func (h *WindowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error {
+	position.X = ensureInRange(position.X, window.Left, window.Right)
+	position.Y = ensureInRange(position.Y, window.Top, window.Bottom)
+	err := SetConsoleCursorPosition(h.fd, position)
+	if err != nil {
+		return err
+	}
+	logger.Infof("Cursor position set: (%d, %d)", position.X, position.Y)
+	return err
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) moveCursorVertical(param int) error {
 func (h *WindowsAnsiEventHandler) moveCursorVertical(param int) error {
@@ -31,17 +55,15 @@ func (h *WindowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
 	position := info.CursorPosition
 	position := info.CursorPosition
 	switch moveMode {
 	switch moveMode {
 	case Horizontal:
 	case Horizontal:
-		position.X = AddInRange(position.X, SHORT(param), info.Window.Left, info.Window.Right)
+		position.X += SHORT(param)
 	case Vertical:
 	case Vertical:
-		position.Y = AddInRange(position.Y, SHORT(param), info.Window.Top, info.Window.Bottom)
+		position.Y += SHORT(param)
 	}
 	}
 
 
-	if err = h.setCursorPosition(position, info.Size); err != nil {
+	if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	logger.Infof("Cursor position set: (%d, %d)", position.X, position.Y)
-
 	return nil
 	return nil
 }
 }
 
 
@@ -53,9 +75,9 @@ func (h *WindowsAnsiEventHandler) moveCursorLine(param int) error {
 
 
 	position := info.CursorPosition
 	position := info.CursorPosition
 	position.X = 0
 	position.X = 0
-	position.Y = AddInRange(position.Y, SHORT(param), info.Window.Top, info.Window.Bottom)
+	position.Y += SHORT(param)
 
 
-	if err = h.setCursorPosition(position, info.Size); err != nil {
+	if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -69,9 +91,9 @@ func (h *WindowsAnsiEventHandler) moveCursorColumn(param int) error {
 	}
 	}
 
 
 	position := info.CursorPosition
 	position := info.CursorPosition
-	position.X = AddInRange(SHORT(param), -1, info.Window.Left, info.Window.Right)
+	position.X = SHORT(param) - 1
 
 
-	if err = h.setCursorPosition(position, info.Size); err != nil {
+	if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 76 - 48
vendor/src/github.com/Azure/go-ansiterm/winterm/scroll_helper.go

@@ -2,89 +2,117 @@
 
 
 package winterm
 package winterm
 
 
-func (h *WindowsAnsiEventHandler) scrollPageUp() error {
-	return h.scrollPage(1)
+// effectiveSr gets the current effective scroll region in buffer coordinates
+func (h *WindowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
+	top := AddInRange(window.Top, h.sr.top, window.Top, window.Bottom)
+	bottom := AddInRange(window.Top, h.sr.bottom, window.Top, window.Bottom)
+	if top >= bottom {
+		top = window.Top
+		bottom = window.Bottom
+	}
+	return scrollRegion{top: top, bottom: bottom}
+}
+
+func (h *WindowsAnsiEventHandler) scrollUp(param int) error {
+	info, err := GetConsoleScreenBufferInfo(h.fd)
+	if err != nil {
+		return err
+	}
+
+	sr := h.effectiveSr(info.Window)
+	return h.scroll(param, sr, info)
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) scrollPageDown() error {
-	return h.scrollPage(-1)
+func (h *WindowsAnsiEventHandler) scrollDown(param int) error {
+	return h.scrollUp(-param)
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) scrollPage(param int) error {
+func (h *WindowsAnsiEventHandler) deleteLines(param int) error {
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	tmpScrollTop := h.sr.top
-	tmpScrollBottom := h.sr.bottom
+	start := info.CursorPosition.Y
+	sr := h.effectiveSr(info.Window)
+	// Lines cannot be inserted or deleted outside the scrolling region.
+	if start >= sr.top && start <= sr.bottom {
+		sr.top = start
+		return h.scroll(param, sr, info)
+	} else {
+		return nil
+	}
+}
 
 
-	// Set scroll region to whole window
-	h.sr.top = 0
-	h.sr.bottom = int(info.Size.Y - 1)
+func (h *WindowsAnsiEventHandler) insertLines(param int) error {
+	return h.deleteLines(-param)
+}
 
 
-	err = h.scroll(param)
+// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
+func (h *WindowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
+	logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
+	logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
 
 
-	h.sr.top = tmpScrollTop
-	h.sr.bottom = tmpScrollBottom
+	// Copy from and clip to the scroll region (full buffer width)
+	scrollRect := SMALL_RECT{
+		Top:    sr.top,
+		Bottom: sr.bottom,
+		Left:   0,
+		Right:  info.Size.X - 1,
+	}
 
 
-	return err
-}
+	// Origin to which area should be copied
+	destOrigin := COORD{
+		X: 0,
+		Y: sr.top - SHORT(param),
+	}
 
 
-func (h *WindowsAnsiEventHandler) scrollUp(param int) error {
-	return h.scroll(param)
-}
+	char := CHAR_INFO{
+		UnicodeChar: ' ',
+		Attributes:  h.attributes,
+	}
 
 
-func (h *WindowsAnsiEventHandler) scrollDown(param int) error {
-	return h.scroll(-param)
+	if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
+		return err
+	}
+	return nil
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) scroll(param int) error {
-
+func (h *WindowsAnsiEventHandler) deleteCharacters(param int) error {
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	return h.scrollLine(param, info.CursorPosition, info)
+}
 
 
-	logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", h.sr.top, h.sr.bottom)
-	logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
-
-	rect := info.Window
-
-	// Current scroll region in Windows backing buffer coordinates
-	top := rect.Top + SHORT(h.sr.top)
-	bottom := rect.Top + SHORT(h.sr.bottom)
+func (h *WindowsAnsiEventHandler) insertCharacters(param int) error {
+	return h.deleteCharacters(-param)
+}
 
 
-	// Area from backing buffer to be copied
+// scrollLine scrolls a line horizontally starting at the provided position by a number of columns.
+func (h *WindowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error {
+	// Copy from and clip to the scroll region (full buffer width)
 	scrollRect := SMALL_RECT{
 	scrollRect := SMALL_RECT{
-		Top:    top + SHORT(param),
-		Bottom: bottom + SHORT(param),
-		Left:   rect.Left,
-		Right:  rect.Right,
-	}
-
-	// Clipping region should be the original scroll region
-	clipRegion := SMALL_RECT{
-		Top:    top,
-		Bottom: bottom,
-		Left:   rect.Left,
-		Right:  rect.Right,
+		Top:    position.Y,
+		Bottom: position.Y,
+		Left:   position.X,
+		Right:  info.Size.X - 1,
 	}
 	}
 
 
 	// Origin to which area should be copied
 	// Origin to which area should be copied
 	destOrigin := COORD{
 	destOrigin := COORD{
-		X: rect.Left,
-		Y: top,
+		X: position.X - SHORT(columns),
+		Y: position.Y,
 	}
 	}
 
 
 	char := CHAR_INFO{
 	char := CHAR_INFO{
 		UnicodeChar: ' ',
 		UnicodeChar: ' ',
-		Attributes:  0,
+		Attributes:  h.attributes,
 	}
 	}
 
 
-	if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, clipRegion, destOrigin, char); err != nil {
+	if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
 		return err
 		return err
 	}
 	}
-
 	return nil
 	return nil
 }
 }

+ 400 - 105
vendor/src/github.com/Azure/go-ansiterm/winterm/win_event_handler.go

@@ -15,11 +15,19 @@ import (
 var logger *logrus.Logger
 var logger *logrus.Logger
 
 
 type WindowsAnsiEventHandler struct {
 type WindowsAnsiEventHandler struct {
-	fd        uintptr
-	file      *os.File
-	infoReset *CONSOLE_SCREEN_BUFFER_INFO
-	sr        scrollRegion
-	buffer    bytes.Buffer
+	fd             uintptr
+	file           *os.File
+	infoReset      *CONSOLE_SCREEN_BUFFER_INFO
+	sr             scrollRegion
+	buffer         bytes.Buffer
+	attributes     WORD
+	inverted       bool
+	wrapNext       bool
+	drewMarginByte bool
+	originMode     bool
+	marginByte     byte
+	curInfo        *CONSOLE_SCREEN_BUFFER_INFO
+	curPos         COORD
 }
 }
 
 
 func CreateWinEventHandler(fd uintptr, file *os.File) AnsiEventHandler {
 func CreateWinEventHandler(fd uintptr, file *os.File) AnsiEventHandler {
@@ -40,60 +48,220 @@ func CreateWinEventHandler(fd uintptr, file *os.File) AnsiEventHandler {
 		return nil
 		return nil
 	}
 	}
 
 
-	sr := scrollRegion{int(infoReset.Window.Top), int(infoReset.Window.Bottom)}
-
 	return &WindowsAnsiEventHandler{
 	return &WindowsAnsiEventHandler{
-		fd:        fd,
-		file:      file,
-		infoReset: infoReset,
-		sr:        sr,
+		fd:         fd,
+		file:       file,
+		infoReset:  infoReset,
+		attributes: infoReset.Attributes,
 	}
 	}
 }
 }
 
 
 type scrollRegion struct {
 type scrollRegion struct {
-	top    int
-	bottom int
+	top    SHORT
+	bottom SHORT
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) Print(b byte) error {
-	return h.buffer.WriteByte(b)
+// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
+// current cursor position and scroll region settings, in which case it returns
+// true. If no special handling is necessary, then it does nothing and returns
+// false.
+//
+// In the false case, the caller should ensure that a carriage return
+// and line feed are inserted or that the text is otherwise wrapped.
+func (h *WindowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
+	if h.wrapNext {
+		if err := h.Flush(); err != nil {
+			return false, err
+		}
+		h.clearWrap()
+	}
+	pos, info, err := h.getCurrentInfo()
+	if err != nil {
+		return false, err
+	}
+	sr := h.effectiveSr(info.Window)
+	if pos.Y == sr.bottom {
+		// Scrolling is necessary. Let Windows automatically scroll if the scrolling region
+		// is the full window.
+		if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom {
+			if includeCR {
+				pos.X = 0
+				h.updatePos(pos)
+			}
+			return false, nil
+		} else {
+			// A custom scroll region is active. Scroll the window manually to simulate
+			// the LF.
+			if err := h.Flush(); err != nil {
+				return false, err
+			}
+			logger.Info("Simulating LF inside scroll region")
+			if err := h.scrollUp(1); err != nil {
+				return false, err
+			}
+			if includeCR {
+				pos.X = 0
+				if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+					return false, err
+				}
+			}
+			return true, nil
+		}
+	} else if pos.Y < info.Window.Bottom {
+		// Let Windows handle the LF.
+		pos.Y++
+		if includeCR {
+			pos.X = 0
+		}
+		h.updatePos(pos)
+		return false, nil
+	} else {
+		// The cursor is at the bottom of the screen but outside the scroll
+		// region. Skip the LF.
+		logger.Info("Simulating LF outside scroll region")
+		if includeCR {
+			if err := h.Flush(); err != nil {
+				return false, err
+			}
+			pos.X = 0
+			if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+				return false, err
+			}
+		}
+		return true, nil
+	}
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) Execute(b byte) error {
-	if ANSI_LINE_FEED == b {
-		info, err := GetConsoleScreenBufferInfo(h.fd)
+// executeLF executes a LF without a CR.
+func (h *WindowsAnsiEventHandler) executeLF() error {
+	handled, err := h.simulateLF(false)
+	if err != nil {
+		return err
+	}
+	if !handled {
+		// Windows LF will reset the cursor column position. Write the LF
+		// and restore the cursor position.
+		pos, _, err := h.getCurrentInfo()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+		h.buffer.WriteByte(ANSI_LINE_FEED)
+		if pos.X != 0 {
+			if err := h.Flush(); err != nil {
+				return err
+			}
+			logger.Info("Resetting cursor position for LF without CR")
+			if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
 
 
-		if int(info.CursorPosition.Y) == h.sr.bottom {
+func (h *WindowsAnsiEventHandler) Print(b byte) error {
+	if h.wrapNext {
+		h.buffer.WriteByte(h.marginByte)
+		h.clearWrap()
+		if _, err := h.simulateLF(true); err != nil {
+			return err
+		}
+	}
+	pos, info, err := h.getCurrentInfo()
+	if err != nil {
+		return err
+	}
+	if pos.X == info.Size.X-1 {
+		h.wrapNext = true
+		h.marginByte = b
+	} else {
+		pos.X++
+		h.updatePos(pos)
+		h.buffer.WriteByte(b)
+	}
+	return nil
+}
+
+func (h *WindowsAnsiEventHandler) Execute(b byte) error {
+	switch b {
+	case ANSI_TAB:
+		logger.Info("Execute(TAB)")
+		// Move to the next tab stop, but preserve auto-wrap if already set.
+		if !h.wrapNext {
+			pos, info, err := h.getCurrentInfo()
+			if err != nil {
+				return err
+			}
+			pos.X = (pos.X + 8) - pos.X%8
+			if pos.X >= info.Size.X {
+				pos.X = info.Size.X - 1
+			}
 			if err := h.Flush(); err != nil {
 			if err := h.Flush(); err != nil {
 				return err
 				return err
 			}
 			}
+			if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+				return err
+			}
+		}
+		return nil
 
 
-			logger.Infof("Scrolling due to LF at bottom of scroll region")
+	case ANSI_BEL:
+		h.buffer.WriteByte(ANSI_BEL)
+		return nil
 
 
-			// Scroll up one row if we attempt to line feed at the bottom
-			// of the scroll region
-			if err := h.scrollUp(1); err != nil {
+	case ANSI_BACKSPACE:
+		if h.wrapNext {
+			if err := h.Flush(); err != nil {
 				return err
 				return err
 			}
 			}
+			h.clearWrap()
+		}
+		pos, _, err := h.getCurrentInfo()
+		if err != nil {
+			return err
+		}
+		if pos.X > 0 {
+			pos.X--
+			h.updatePos(pos)
+			h.buffer.WriteByte(ANSI_BACKSPACE)
+		}
+		return nil
+
+	case ANSI_VERTICAL_TAB, ANSI_FORM_FEED:
+		// Treat as true LF.
+		return h.executeLF()
+
+	case ANSI_LINE_FEED:
+		// Simulate a CR and LF for now since there is no way in go-ansiterm
+		// to tell if the LF should include CR (and more things break when it's
+		// missing than when it's incorrectly added).
+		handled, err := h.simulateLF(true)
+		if handled || err != nil {
+			return err
+		}
+		return h.buffer.WriteByte(ANSI_LINE_FEED)
 
 
-			// Clear line
-			// if err := h.CUD(1); err != nil {
-			// 	return err
-			// }
-			if err := h.EL(0); err != nil {
+	case ANSI_CARRIAGE_RETURN:
+		if h.wrapNext {
+			if err := h.Flush(); err != nil {
 				return err
 				return err
 			}
 			}
+			h.clearWrap()
 		}
 		}
-	}
+		pos, _, err := h.getCurrentInfo()
+		if err != nil {
+			return err
+		}
+		if pos.X != 0 {
+			pos.X = 0
+			h.updatePos(pos)
+			h.buffer.WriteByte(ANSI_CARRIAGE_RETURN)
+		}
+		return nil
 
 
-	if ANSI_BEL <= b && b <= ANSI_CARRIAGE_RETURN {
-		return h.buffer.WriteByte(b)
+	default:
+		return nil
 	}
 	}
-
-	return nil
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) CUU(param int) error {
 func (h *WindowsAnsiEventHandler) CUU(param int) error {
@@ -101,6 +269,7 @@ func (h *WindowsAnsiEventHandler) CUU(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorVertical(-param)
 	return h.moveCursorVertical(-param)
 }
 }
 
 
@@ -109,6 +278,7 @@ func (h *WindowsAnsiEventHandler) CUD(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorVertical(param)
 	return h.moveCursorVertical(param)
 }
 }
 
 
@@ -117,6 +287,7 @@ func (h *WindowsAnsiEventHandler) CUF(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorHorizontal(param)
 	return h.moveCursorHorizontal(param)
 }
 }
 
 
@@ -125,6 +296,7 @@ func (h *WindowsAnsiEventHandler) CUB(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorHorizontal(-param)
 	return h.moveCursorHorizontal(-param)
 }
 }
 
 
@@ -133,6 +305,7 @@ func (h *WindowsAnsiEventHandler) CNL(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorLine(param)
 	return h.moveCursorLine(param)
 }
 }
 
 
@@ -141,6 +314,7 @@ func (h *WindowsAnsiEventHandler) CPL(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorLine(-param)
 	return h.moveCursorLine(-param)
 }
 }
 
 
@@ -149,34 +323,48 @@ func (h *WindowsAnsiEventHandler) CHA(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 	return h.moveCursorColumn(param)
 	return h.moveCursorColumn(param)
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) CUP(row int, col int) error {
+func (h *WindowsAnsiEventHandler) VPA(param int) error {
 	if err := h.Flush(); err != nil {
 	if err := h.Flush(); err != nil {
 		return err
 		return err
 	}
 	}
-	rowStr, colStr := strconv.Itoa(row), strconv.Itoa(col)
-	logger.Infof("CUP: [%v]", []string{rowStr, colStr})
+	logger.Infof("VPA: [[%d]]", param)
+	h.clearWrap()
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	window := h.getCursorWindow(info)
+	position := info.CursorPosition
+	position.Y = window.Top + SHORT(param) - 1
+	return h.setCursorPosition(position, window)
+}
 
 
-	rect := info.Window
-	rowS := AddInRange(SHORT(row-1), rect.Top, rect.Top, rect.Bottom)
-	colS := AddInRange(SHORT(col-1), rect.Left, rect.Left, rect.Right)
-	position := COORD{colS, rowS}
+func (h *WindowsAnsiEventHandler) CUP(row int, col int) error {
+	if err := h.Flush(); err != nil {
+		return err
+	}
+	logger.Infof("CUP: [[%d %d]]", row, col)
+	h.clearWrap()
+	info, err := GetConsoleScreenBufferInfo(h.fd)
+	if err != nil {
+		return err
+	}
 
 
-	return h.setCursorPosition(position, info.Size)
+	window := h.getCursorWindow(info)
+	position := COORD{window.Left + SHORT(col) - 1, window.Top + SHORT(row) - 1}
+	return h.setCursorPosition(position, window)
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) HVP(row int, col int) error {
 func (h *WindowsAnsiEventHandler) HVP(row int, col int) error {
 	if err := h.Flush(); err != nil {
 	if err := h.Flush(); err != nil {
 		return err
 		return err
 	}
 	}
-	rowS, colS := strconv.Itoa(row), strconv.Itoa(row)
-	logger.Infof("HVP: [%v]", []string{rowS, colS})
+	logger.Infof("HVP: [[%d %d]]", row, col)
+	h.clearWrap()
 	return h.CUP(row, col)
 	return h.CUP(row, col)
 }
 }
 
 
@@ -185,22 +373,70 @@ func (h *WindowsAnsiEventHandler) DECTCEM(visible bool) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
 	logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
-
+	h.clearWrap()
 	return nil
 	return nil
 }
 }
 
 
+func (h *WindowsAnsiEventHandler) DECOM(enable bool) error {
+	if err := h.Flush(); err != nil {
+		return err
+	}
+	logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)})
+	h.clearWrap()
+	h.originMode = enable
+	return h.CUP(1, 1)
+}
+
+func (h *WindowsAnsiEventHandler) DECCOLM(use132 bool) error {
+	if err := h.Flush(); err != nil {
+		return err
+	}
+	logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
+	h.clearWrap()
+	if err := h.ED(2); err != nil {
+		return err
+	}
+	info, err := GetConsoleScreenBufferInfo(h.fd)
+	if err != nil {
+		return err
+	}
+	targetWidth := SHORT(80)
+	if use132 {
+		targetWidth = 132
+	}
+	if info.Size.X < targetWidth {
+		if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
+			logger.Info("set buffer failed:", err)
+			return err
+		}
+	}
+	window := info.Window
+	window.Left = 0
+	window.Right = targetWidth - 1
+	if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
+		logger.Info("set window failed:", err)
+		return err
+	}
+	if info.Size.X > targetWidth {
+		if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
+			logger.Info("set buffer failed:", err)
+			return err
+		}
+	}
+	return SetConsoleCursorPosition(h.fd, COORD{0, 0})
+}
+
 func (h *WindowsAnsiEventHandler) ED(param int) error {
 func (h *WindowsAnsiEventHandler) ED(param int) error {
 	if err := h.Flush(); err != nil {
 	if err := h.Flush(); err != nil {
 		return err
 		return err
 	}
 	}
 	logger.Infof("ED: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("ED: [%v]", []string{strconv.Itoa(param)})
+	h.clearWrap()
 
 
 	// [J  -- Erases from the cursor to the end of the screen, including the cursor position.
 	// [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.
 	// [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.
 	// [2J -- Erases the complete display. The cursor does not move.
-	// [3J -- Erases the complete display and backing buffer, cursor moves to (0,0)
 	// Notes:
 	// Notes:
-	// -- ANSI.SYS always moved the cursor to (0,0) for both [2J and [3J
 	// -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
 	// -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
 
 
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	info, err := GetConsoleScreenBufferInfo(h.fd)
@@ -223,20 +459,25 @@ func (h *WindowsAnsiEventHandler) ED(param int) error {
 	case 2:
 	case 2:
 		start = COORD{0, 0}
 		start = COORD{0, 0}
 		end = COORD{info.Size.X - 1, info.Size.Y - 1}
 		end = COORD{info.Size.X - 1, info.Size.Y - 1}
-
-	case 3:
-		start = COORD{0, 0}
-		end = COORD{info.Size.X - 1, info.Size.Y - 1}
 	}
 	}
 
 
-	err = h.clearRange(info.Attributes, start, end)
+	err = h.clearRange(h.attributes, start, end)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if param == 2 || param == 3 {
-		err = h.setCursorPosition(COORD{0, 0}, info.Size)
-		if err != nil {
+	// If the whole buffer was cleared, move the window to the top while preserving
+	// the window-relative cursor position.
+	if param == 2 {
+		pos := info.CursorPosition
+		window := info.Window
+		pos.Y -= window.Top
+		window.Bottom -= window.Top
+		window.Top = 0
+		if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+			return err
+		}
+		if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -249,6 +490,7 @@ func (h *WindowsAnsiEventHandler) EL(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("EL: [%v]", strconv.Itoa(param))
 	logger.Infof("EL: [%v]", strconv.Itoa(param))
+	h.clearWrap()
 
 
 	// [K  -- Erases from the cursor to the end of the line, including 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.
 	// [1K -- Erases from the beginning of the line to the cursor, including the cursor position.
@@ -276,7 +518,7 @@ func (h *WindowsAnsiEventHandler) EL(param int) error {
 		end = COORD{info.Size.X, info.CursorPosition.Y}
 		end = COORD{info.Size.X, info.CursorPosition.Y}
 	}
 	}
 
 
-	err = h.clearRange(info.Attributes, start, end)
+	err = h.clearRange(h.attributes, start, end)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -289,19 +531,35 @@ func (h *WindowsAnsiEventHandler) IL(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("IL: [%v]", strconv.Itoa(param))
 	logger.Infof("IL: [%v]", strconv.Itoa(param))
-	if err := h.scrollDown(param); err != nil {
+	h.clearWrap()
+	return h.insertLines(param)
+}
+
+func (h *WindowsAnsiEventHandler) DL(param int) error {
+	if err := h.Flush(); err != nil {
 		return err
 		return err
 	}
 	}
+	logger.Infof("DL: [%v]", strconv.Itoa(param))
+	h.clearWrap()
+	return h.deleteLines(param)
+}
 
 
-	return h.EL(2)
+func (h *WindowsAnsiEventHandler) ICH(param int) error {
+	if err := h.Flush(); err != nil {
+		return err
+	}
+	logger.Infof("ICH: [%v]", strconv.Itoa(param))
+	h.clearWrap()
+	return h.insertCharacters(param)
 }
 }
 
 
-func (h *WindowsAnsiEventHandler) DL(param int) error {
+func (h *WindowsAnsiEventHandler) DCH(param int) error {
 	if err := h.Flush(); err != nil {
 	if err := h.Flush(); err != nil {
 		return err
 		return err
 	}
 	}
-	logger.Infof("DL: [%v]", strconv.Itoa(param))
-	return h.scrollUp(param)
+	logger.Infof("DCH: [%v]", strconv.Itoa(param))
+	h.clearWrap()
+	return h.deleteCharacters(param)
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) SGR(params []int) error {
 func (h *WindowsAnsiEventHandler) SGR(params []int) error {
@@ -310,33 +568,32 @@ func (h *WindowsAnsiEventHandler) SGR(params []int) error {
 	}
 	}
 	strings := []string{}
 	strings := []string{}
 	for _, v := range params {
 	for _, v := range params {
-		logger.Infof("SGR: [%v]", strings)
 		strings = append(strings, strconv.Itoa(v))
 		strings = append(strings, strconv.Itoa(v))
 	}
 	}
 
 
 	logger.Infof("SGR: [%v]", strings)
 	logger.Infof("SGR: [%v]", strings)
 
 
-	info, err := GetConsoleScreenBufferInfo(h.fd)
-	if err != nil {
-		return err
-	}
-
-	attributes := info.Attributes
 	if len(params) <= 0 {
 	if len(params) <= 0 {
-		attributes = h.infoReset.Attributes
+		h.attributes = h.infoReset.Attributes
+		h.inverted = false
 	} else {
 	} else {
 		for _, attr := range params {
 		for _, attr := range params {
 
 
 			if attr == ANSI_SGR_RESET {
 			if attr == ANSI_SGR_RESET {
-				attributes = h.infoReset.Attributes
+				h.attributes = h.infoReset.Attributes
+				h.inverted = false
 				continue
 				continue
 			}
 			}
 
 
-			attributes = collectAnsiIntoWindowsAttributes(attributes, h.infoReset.Attributes, SHORT(attr))
+			h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, SHORT(attr))
 		}
 		}
 	}
 	}
 
 
-	err = SetConsoleTextAttribute(h.fd, attributes)
+	attributes := h.attributes
+	if h.inverted {
+		attributes = invertAttributes(attributes)
+	}
+	err := SetConsoleTextAttribute(h.fd, attributes)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -349,7 +606,8 @@ func (h *WindowsAnsiEventHandler) SU(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("SU: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("SU: [%v]", []string{strconv.Itoa(param)})
-	return h.scrollPageUp()
+	h.clearWrap()
+	return h.scrollUp(param)
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) SD(param int) error {
 func (h *WindowsAnsiEventHandler) SD(param int) error {
@@ -357,44 +615,30 @@ func (h *WindowsAnsiEventHandler) SD(param int) error {
 		return err
 		return err
 	}
 	}
 	logger.Infof("SD: [%v]", []string{strconv.Itoa(param)})
 	logger.Infof("SD: [%v]", []string{strconv.Itoa(param)})
-	return h.scrollPageDown()
+	h.clearWrap()
+	return h.scrollDown(param)
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) DA(params []string) error {
 func (h *WindowsAnsiEventHandler) DA(params []string) error {
 	logger.Infof("DA: [%v]", params)
 	logger.Infof("DA: [%v]", params)
-
-	// See the site below for details of the device attributes command
-	// http://vt100.net/docs/vt220-rm/chapter4.html
-
-	// First character of first parameter string is '>'
-	if params[0][0] == '>' {
-		// Secondary device attribute request:
-		// Respond with:
-		// "I am a VT220 version 1.0, no options.
-		//                    CSI     >     1     ;     1     0     ;     0     c    CR    LF
-		h.buffer.Write([]byte{CSI_ENTRY, 0x3E, 0x31, 0x3B, 0x31, 0x30, 0x3B, 0x30, 0x63, 0x0D, 0x0A})
-
-	} else {
-		// Primary device attribute request:
-		// Respond with:
-		// "I am a service class 2 terminal (62) with 132 columns (1),
-		// printer port (2), selective erase (6), DRCS (7), UDK (8),
-		// and I support 7-bit national replacement character sets (9)."
-		//                    CSI     ?     6     2     ;     1     ;     2     ;     6     ;     7     ;     8     ;     9     c    CR    LF
-		h.buffer.Write([]byte{CSI_ENTRY, 0x3F, 0x36, 0x32, 0x3B, 0x31, 0x3B, 0x32, 0x3B, 0x36, 0x3B, 0x37, 0x3B, 0x38, 0x3B, 0x39, 0x63, 0x0D, 0x0A})
-	}
-
+	// DA cannot be implemented because it must send data on the VT100 input stream,
+	// which is not available to go-ansiterm.
 	return nil
 	return nil
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
 func (h *WindowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
+	if err := h.Flush(); err != nil {
+		return err
+	}
 	logger.Infof("DECSTBM: [%d, %d]", top, bottom)
 	logger.Infof("DECSTBM: [%d, %d]", top, bottom)
 
 
 	// Windows is 0 indexed, Linux is 1 indexed
 	// Windows is 0 indexed, Linux is 1 indexed
-	h.sr.top = top - 1
-	h.sr.bottom = bottom - 1
+	h.sr.top = SHORT(top - 1)
+	h.sr.bottom = SHORT(bottom - 1)
 
 
-	return nil
+	// This command also moves the cursor to the origin.
+	h.clearWrap()
+	return h.CUP(1, 1)
 }
 }
 
 
 func (h *WindowsAnsiEventHandler) RI() error {
 func (h *WindowsAnsiEventHandler) RI() error {
@@ -402,29 +646,80 @@ func (h *WindowsAnsiEventHandler) RI() error {
 		return err
 		return err
 	}
 	}
 	logger.Info("RI: []")
 	logger.Info("RI: []")
+	h.clearWrap()
 
 
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	info, err := GetConsoleScreenBufferInfo(h.fd)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if info.Window.Top == info.CursorPosition.Y {
-		if err := h.scrollPageDown(); err != nil {
-			return err
-		}
-
-		return h.EL(2)
+	sr := h.effectiveSr(info.Window)
+	if info.CursorPosition.Y == sr.top {
+		return h.scrollDown(1)
 	} else {
 	} else {
-		return h.CUU(1)
+		return h.moveCursorVertical(-1)
 	}
 	}
 }
 }
 
 
+func (h *WindowsAnsiEventHandler) IND() error {
+	logger.Info("IND: []")
+	return h.executeLF()
+}
+
 func (h *WindowsAnsiEventHandler) Flush() error {
 func (h *WindowsAnsiEventHandler) Flush() error {
+	h.curInfo = nil
 	if h.buffer.Len() > 0 {
 	if h.buffer.Len() > 0 {
 		logger.Infof("Flush: [%s]", h.buffer.Bytes())
 		logger.Infof("Flush: [%s]", h.buffer.Bytes())
 		if _, err := h.buffer.WriteTo(h.file); err != nil {
 		if _, err := h.buffer.WriteTo(h.file); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
+
+	if h.wrapNext && !h.drewMarginByte {
+		logger.Infof("Flush: drawing margin byte '%c'", h.marginByte)
+
+		info, err := GetConsoleScreenBufferInfo(h.fd)
+		if err != nil {
+			return err
+		}
+
+		charInfo := []CHAR_INFO{{UnicodeChar: WCHAR(h.marginByte), Attributes: info.Attributes}}
+		size := COORD{1, 1}
+		position := COORD{0, 0}
+		region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
+		if err := WriteConsoleOutput(h.fd, charInfo, size, position, &region); err != nil {
+			return err
+		}
+		h.drewMarginByte = true
+	}
 	return nil
 	return nil
 }
 }
+
+// cacheConsoleInfo ensures that the current console screen information has been queried
+// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
+func (h *WindowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
+	if h.curInfo == nil {
+		info, err := GetConsoleScreenBufferInfo(h.fd)
+		if err != nil {
+			return COORD{}, nil, err
+		}
+		h.curInfo = info
+		h.curPos = info.CursorPosition
+	}
+	return h.curPos, h.curInfo, nil
+}
+
+func (h *WindowsAnsiEventHandler) updatePos(pos COORD) {
+	if h.curInfo == nil {
+		panic("failed to call getCurrentInfo before calling updatePos")
+	}
+	h.curPos = pos
+}
+
+// clearWrap clears the state where the cursor is in the margin
+// waiting for the next character before wrapping the line. This must
+// be done before most operations that act on the cursor.
+func (h *WindowsAnsiEventHandler) clearWrap() {
+	h.wrapNext = false
+	h.drewMarginByte = false
+}