diff --git a/terminal/cursor.go b/terminal/cursor.go index 88e25094..75117e08 100644 --- a/terminal/cursor.go +++ b/terminal/cursor.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package terminal @@ -78,8 +79,8 @@ func (c *Cursor) Hide() error { return err } -// Move moves the cursor to a specific x,y location. -func (c *Cursor) Move(x int, y int) error { +// move moves the cursor to a specific x,y location. +func (c *Cursor) move(x int, y int) error { _, err := fmt.Fprintf(c.Out, "\x1b[%d;%df", x, y) return err } @@ -194,7 +195,7 @@ func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) { defer c.Restore() // move the cursor to the very bottom of the terminal - c.Move(999, 999) + c.move(999, 999) // ask for the current location bottom, err := c.Location(buf) diff --git a/terminal/cursor_windows.go b/terminal/cursor_windows.go index e24440e7..c2645919 100644 --- a/terminal/cursor_windows.go +++ b/terminal/cursor_windows.go @@ -16,31 +16,37 @@ type Cursor struct { Out FileWriter } -func (c *Cursor) Up(n int) { - c.cursorMove(0, n) +func (c *Cursor) Up(n int) error { + return c.cursorMove(0, n) } -func (c *Cursor) Down(n int) { - c.cursorMove(0, -1*n) +func (c *Cursor) Down(n int) error { + return c.cursorMove(0, -1*n) } -func (c *Cursor) Forward(n int) { - c.cursorMove(n, 0) +func (c *Cursor) Forward(n int) error { + return c.cursorMove(n, 0) } -func (c *Cursor) Back(n int) { - c.cursorMove(-1*n, 0) +func (c *Cursor) Back(n int) error { + return c.cursorMove(-1*n, 0) } // save the cursor location -func (c *Cursor) Save() { - cursorLoc, _ = c.Location(nil) +func (c *Cursor) Save() error { + loc, err := c.Location(nil) + if err != nil { + return err + } + cursorLoc = *loc + return nil } -func (c *Cursor) Restore() { +func (c *Cursor) Restore() error { handle := syscall.Handle(c.Out.Fd()) // restore it to the original position - procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursorLoc)))) + _, _, err := procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursorLoc)))) + return normalizeError(err) } func (cur Coord) CursorIsAtLineEnd(size *Coord) bool { @@ -51,40 +57,49 @@ func (cur Coord) CursorIsAtLineBegin() bool { return cur.X == 0 } -func (c *Cursor) cursorMove(x int, y int) { +func (c *Cursor) cursorMove(x int, y int) error { handle := syscall.Handle(c.Out.Fd()) var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return err + } var cursor Coord cursor.X = csbi.cursorPosition.X + Short(x) cursor.Y = csbi.cursorPosition.Y + Short(y) - procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) + _, _, err := procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) + return normalizeError(err) } -func (c *Cursor) NextLine(n int) { - c.Up(n) - c.HorizontalAbsolute(0) +func (c *Cursor) NextLine(n int) error { + if err := c.Up(n); err != nil { + return err + } + return c.HorizontalAbsolute(0) } -func (c *Cursor) PreviousLine(n int) { - c.Down(n) - c.HorizontalAbsolute(0) +func (c *Cursor) PreviousLine(n int) error { + if err := c.Down(n); err != nil { + return err + } + return c.HorizontalAbsolute(0) } // for comparability purposes between windows // in windows we don't have to print out a new line -func (c *Cursor) MoveNextLine(cur Coord, terminalSize *Coord) { - c.NextLine(1) +func (c *Cursor) MoveNextLine(cur *Coord, terminalSize *Coord) error { + return c.NextLine(1) } -func (c *Cursor) HorizontalAbsolute(x int) { +func (c *Cursor) HorizontalAbsolute(x int) error { handle := syscall.Handle(c.Out.Fd()) var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return err + } var cursor Coord cursor.X = Short(x) @@ -94,43 +109,54 @@ func (c *Cursor) HorizontalAbsolute(x int) { cursor.X = csbi.size.X } - procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) + _, _, err := procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) + return normalizeError(err) } -func (c *Cursor) Show() { +func (c *Cursor) Show() error { handle := syscall.Handle(c.Out.Fd()) var cci consoleCursorInfo - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + if _, _, err := procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))); normalizeError(err) != nil { + return err + } cci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + _, _, err := procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + return normalizeError(err) } -func (c *Cursor) Hide() { +func (c *Cursor) Hide() error { handle := syscall.Handle(c.Out.Fd()) var cci consoleCursorInfo - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + if _, _, err := procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))); normalizeError(err) != nil { + return err + } cci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + _, _, err := procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + return normalizeError(err) } -func (c *Cursor) Location(buf *bytes.Buffer) (Coord, error) { +func (c *Cursor) Location(buf *bytes.Buffer) (*Coord, error) { handle := syscall.Handle(c.Out.Fd()) var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return nil, err + } - return csbi.cursorPosition, nil + return &csbi.cursorPosition, nil } func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) { handle := syscall.Handle(c.Out.Fd()) var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return nil, err + } // windows' coordinate system begins at (0, 0) csbi.size.X-- csbi.size.Y-- diff --git a/terminal/display_windows.go b/terminal/display_windows.go index 0adc1ded..fc9db9f7 100644 --- a/terminal/display_windows.go +++ b/terminal/display_windows.go @@ -5,11 +5,13 @@ import ( "unsafe" ) -func EraseLine(out FileWriter, mode EraseLineMode) { +func EraseLine(out FileWriter, mode EraseLineMode) error { handle := syscall.Handle(out.Fd()) var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return err + } var w uint32 var x Short @@ -23,5 +25,7 @@ func EraseLine(out FileWriter, mode EraseLineMode) { cursor.X = 0 x = csbi.size.X } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(x), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w))) + + _, _, err := procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(x), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w))) + return normalizeError(err) } diff --git a/terminal/output_windows.go b/terminal/output_windows.go index 8c25ebaf..eaf5c434 100644 --- a/terminal/output_windows.go +++ b/terminal/output_windows.go @@ -12,18 +12,6 @@ import ( "github.com/mattn/go-isatty" ) -var ( - cursorFunctions = map[rune]func(c *Cursor) func(int){ - 'A': func(c *Cursor) func(int) { return c.Up }, - 'B': func(c *Cursor) func(int) { return c.Down }, - 'C': func(c *Cursor) func(int) { return c.Forward }, - 'D': func(c *Cursor) func(int) { return c.Back }, - 'E': func(c *Cursor) func(int) { return c.NextLine }, - 'F': func(c *Cursor) func(int) { return c.PreviousLine }, - 'G': func(c *Cursor) func(int) { return c.HorizontalAbsolute }, - } -) - const ( foregroundBlue = 0x1 foregroundGreen = 0x2 @@ -98,9 +86,14 @@ func (w *Writer) handleEscape(r *bytes.Reader) (n int, err error) { buf := make([]byte, 0, 10) buf = append(buf, "\x1b"...) + var ch rune + var size int // Check '[' continues after \x1b - ch, size, err := r.ReadRune() + ch, size, err = r.ReadRune() if err != nil { + if err == io.EOF { + err = nil + } fmt.Fprint(w.out, string(buf)) return } @@ -116,6 +109,9 @@ func (w *Writer) handleEscape(r *bytes.Reader) (n int, err error) { for { ch, size, err = r.ReadRune() if err != nil { + if err == io.EOF { + err = nil + } fmt.Fprint(w.out, string(buf)) return } @@ -127,47 +123,62 @@ func (w *Writer) handleEscape(r *bytes.Reader) (n int, err error) { argBuf = append(argBuf, string(ch)...) } - w.applyEscapeCode(buf, string(argBuf), code) + err = w.applyEscapeCode(buf, string(argBuf), code) return } -func (w *Writer) applyEscapeCode(buf []byte, arg string, code rune) { +func (w *Writer) applyEscapeCode(buf []byte, arg string, code rune) error { c := &Cursor{Out: w.out} switch arg + string(code) { case "?25h": - c.Show() - return + return c.Show() case "?25l": - c.Hide() - return + return c.Hide() } - if f, ok := cursorFunctions[code]; ok { + if code >= 'A' && code <= 'G' { if n, err := strconv.Atoi(arg); err == nil { - f(c)(n) - return + switch code { + case 'A': + return c.Up(n) + case 'B': + return c.Down(n) + case 'C': + return c.Forward(n) + case 'D': + return c.Back(n) + case 'E': + return c.NextLine(n) + case 'F': + return c.PreviousLine(n) + case 'G': + return c.HorizontalAbsolute(n) + } } } switch code { case 'm': - w.applySelectGraphicRendition(arg) + return w.applySelectGraphicRendition(arg) default: buf = append(buf, string(code)...) - fmt.Fprint(w.out, string(buf)) + _, err := fmt.Fprint(w.out, string(buf)) + return err } } // Original implementation: https://github.com/mattn/go-colorable -func (w *Writer) applySelectGraphicRendition(arg string) { +func (w *Writer) applySelectGraphicRendition(arg string) error { if arg == "" { - procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.orgAttr)) - return + _, _, err := procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.orgAttr)) + return normalizeError(err) } var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + if _, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))); normalizeError(err) != nil { + return err + } attr := csbi.attributes for _, param := range strings.Split(arg, ";") { @@ -230,5 +241,13 @@ func (w *Writer) applySelectGraphicRendition(arg string) { } } - procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + _, _, err := procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + return normalizeError(err) +} + +func normalizeError(err error) error { + if syserr, ok := err.(syscall.Errno); ok && syserr == 0 { + return nil + } + return err }