From 0ebaf4a1c3fec02ca7a8fefc8fcd417687699d4b Mon Sep 17 00:00:00 2001 From: maplain Date: Wed, 12 Jul 2017 15:27:20 -0700 Subject: [PATCH 1/7] Change uilive.Out to type io.Writer it's set to be os.Stdout by default, which is a *os.File, though it's only used as an io.Writer --- writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/writer.go b/writer.go index 2639887..f89f8fe 100644 --- a/writer.go +++ b/writer.go @@ -16,7 +16,7 @@ const ESC = 27 var RefreshInterval = time.Millisecond // Out is the default output writer for the Writer -var Out = os.Stdout +var Out = io.Writer(os.Stdout) // ErrClosedPipe is the error returned when trying to writer is not listening var ErrClosedPipe = errors.New("uilive: read/write on closed pipe") From 921d4ab784bde24c849f0d93b4c2e615fc4a590c Mon Sep 17 00:00:00 2001 From: Lyle Franklin Date: Mon, 3 Dec 2018 17:38:07 -0800 Subject: [PATCH 2/7] Allow Start to be called immediately after Stop - Prior to this change the library would panic if you called Start a second time immediately after calling Stop - This change updates Stop to block until the background Listen method is finished Signed-off-by: Genevieve Lesperance --- writer.go | 6 ++++-- writer_test.go | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/writer.go b/writer.go index 2639887..c58f231 100644 --- a/writer.go +++ b/writer.go @@ -86,7 +86,7 @@ func (w *Writer) Flush() error { func (w *Writer) Start() { if w.ticker == nil { w.ticker = time.NewTicker(w.RefreshInterval) - w.tdone = make(chan bool, 1) + w.tdone = make(chan bool) } go w.Listen() @@ -95,7 +95,8 @@ func (w *Writer) Start() { // Stop stops the listener that updates the terminal func (w *Writer) Stop() { w.Flush() - close(w.tdone) + w.tdone <- true + <-w.tdone } // Listen listens for updates to the writer's buffer and flushes to the out provided. It blocks the runtime. @@ -111,6 +112,7 @@ func (w *Writer) Listen() { w.ticker.Stop() w.ticker = nil w.mtx.Unlock() + close(w.tdone) return } } diff --git a/writer_test.go b/writer_test.go index 5ed200f..dd12617 100644 --- a/writer_test.go +++ b/writer_test.go @@ -22,3 +22,14 @@ func TestWriter(t *testing.T) { t.Fatalf("want %q, got %q", want, b.String()) } } + +func TestStartCalledTwice(t *testing.T) { + w := New() + b := &bytes.Buffer{} + w.Out = b + + w.Start() + w.Stop() + w.Start() + w.Stop() +} From 4512d98b127f3f3a1b7c3cf1104969fdd17b31d9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Thu, 16 May 2019 23:55:21 +0530 Subject: [PATCH 3/7] add support for get terminal size for windows (#22) * add support for get terminal size for windows * return 0, 0 terminal size instead of calling os.Exit * use the termbox library to fetch terminal size [fix] #24 because it is platform agnostic -> works on windows too * include missing windows imports [fix] #24 * Travis-CI: add OSX and linux->win cross-compilation [fix] * Use latest two supported versions of Go instead of tip. Disable CGO. --- .travis.yml | 29 +++++++++++++++--- terminal_size.go | 6 ++-- terminal_size_windows.go | 63 ++++++++++++++++++++++++++++++++++++++++ writer_windows.go | 11 +++---- 4 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 terminal_size_windows.go diff --git a/.travis.yml b/.travis.yml index 04c834a..345d805 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,28 @@ language: go sudo: false + +matrix: + include: + - go: 1.11.x + os: linux + - go: 1.12.x + os: linux + - go: 1.11.x + os: linux + env: CROSS_COMPILE=true + - go: 1.12.x + os: linux + env: CROSS_COMPILE=true + - go: 1.11.x + os: osx + - go: 1.12.x + os: osx + install: - - go get ./... -go: - - 1.4 - - tip + - if [ "$TRAVIS_OS_NAME" = "linux" -a "$CROSS_COMPILE" = "true" ]; then go get github.com/mattn/go-isatty ; fi + - go get -t -v ./... + +script: + - go build + - go test + - if [ "$TRAVIS_OS_NAME" = "linux" -a "$CROSS_COMPILE" = "true" ]; then env GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -v; fi diff --git a/terminal_size.go b/terminal_size.go index 3ae0132..c5f48b5 100644 --- a/terminal_size.go +++ b/terminal_size.go @@ -1,3 +1,5 @@ +// +build !windows + package uilive import ( @@ -22,13 +24,13 @@ func getTermSize() (int, int) { if runtime.GOOS == "openbsd" { out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { - os.Exit(1) + return 0, 0 } } else { out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0) if err != nil { - os.Exit(1) + return 0, 0 } } _, _, _ = syscall.Syscall(syscall.SYS_IOCTL, diff --git a/terminal_size_windows.go b/terminal_size_windows.go new file mode 100644 index 0000000..71b8bec --- /dev/null +++ b/terminal_size_windows.go @@ -0,0 +1,63 @@ +// +build windows + +package uilive + +import ( + "math" + "syscall" + "unsafe" +) + +type consoleFontInfo struct { + font uint32 + fontSize coord +} + +const ( + SmCxMin = 28 + SmCyMin = 29 +) + +var ( + tmpConsoleFontInfo consoleFontInfo + moduleUser32 = syscall.NewLazyDLL("user32.dll") + procGetCurrentConsoleFont = kernel32.NewProc("GetCurrentConsoleFont") + getSystemMetrics = moduleUser32.NewProc("GetSystemMetrics") +) + +func getCurrentConsoleFont(h syscall.Handle, info *consoleFontInfo) (err error) { + r0, _, e1 := syscall.Syscall( + procGetCurrentConsoleFont.Addr(), 3, uintptr(h), 0, uintptr(unsafe.Pointer(info)), + ) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getTermSize() (int, int) { + out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + return 0, 0 + } + + x, _, err := getSystemMetrics.Call(SmCxMin) + y, _, err := getSystemMetrics.Call(SmCyMin) + + if x == 0 || y == 0 { + if err != nil { + panic(err) + } + } + + err = getCurrentConsoleFont(out, &tmpConsoleFontInfo) + if err != nil { + panic(err) + } + + return int(math.Ceil(float64(x) / float64(tmpConsoleFontInfo.fontSize.x))), int(math.Ceil(float64(y) / float64(tmpConsoleFontInfo.fontSize.y))) +} diff --git a/writer_windows.go b/writer_windows.go index c7a0342..8dafb40 100644 --- a/writer_windows.go +++ b/writer_windows.go @@ -4,8 +4,10 @@ package uilive import ( "fmt" + "strings" "syscall" "unsafe" + "github.com/mattn/go-isatty" ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") @@ -14,11 +16,10 @@ var ( procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") ) // clear the line and move the cursor up -var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC) +var clear = fmt.Sprintf("%c[%dA%c[2K\r", ESC, 0, ESC) type short int16 type dword uint32 @@ -55,12 +56,12 @@ func (w *Writer) clearLines() { } fd := f.Fd() var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&csbi))) + _, _, _ = procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&csbi))) for i := 0; i < w.lineCount; i++ { // move the cursor up csbi.cursorPosition.y-- - procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.cursorPosition)))) + _, _, _ = procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.cursorPosition)))) // clear the line cursor := coord{ x: csbi.window.left, @@ -68,6 +69,6 @@ func (w *Writer) clearLines() { } var count, w dword count = dword(csbi.size.x) - procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) + _, _, _ = procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) } } From 0bb79b828f68c0a8b2bf87808dbd131355a7ab9e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 21:39:01 +0530 Subject: [PATCH 4/7] linter fixes --- terminal_size.go | 2 -- writer.go | 4 ++-- writer_test.go | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/terminal_size.go b/terminal_size.go index c5f48b5..ebef466 100644 --- a/terminal_size.go +++ b/terminal_size.go @@ -12,8 +12,6 @@ import ( type windowSize struct { rows uint16 cols uint16 - xPixels uint16 - yPixels uint16 } var out *os.File diff --git a/writer.go b/writer.go index c7728d9..b1914e0 100644 --- a/writer.go +++ b/writer.go @@ -115,7 +115,7 @@ func (w *Writer) Start() { // Stop stops the listener that updates the terminal func (w *Writer) Stop() { - w.Flush() + _ = w.Flush() close(w.tdone) } @@ -125,7 +125,7 @@ func (w *Writer) Listen() { select { case <-w.ticker.C: if w.ticker != nil { - w.Flush() + _ = w.Flush() } case <-w.tdone: w.mtx.Lock() diff --git a/writer_test.go b/writer_test.go index 5ed200f..3825144 100644 --- a/writer_test.go +++ b/writer_test.go @@ -12,10 +12,10 @@ func TestWriter(t *testing.T) { w.Out = b w.Start() for i := 0; i < 2; i++ { - fmt.Fprintln(w, "foo") + _, _ = fmt.Fprintln(w, "foo") } w.Stop() - fmt.Fprintln(b, "bar") + _, _ = fmt.Fprintln(b, "bar") want := "foo\nfoo\nbar\n" if b.String() != want { From 2972222482499a4985862ede50735299622881bb Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 21:41:22 +0530 Subject: [PATCH 5/7] add support for go modules --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..80083d8 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/gosuri/uilive + +go 1.10 From 145406d9b609c1247cbd2c70b388955985090fce Mon Sep 17 00:00:00 2001 From: Vasily Litvinov Date: Thu, 7 Nov 2019 14:42:31 +0300 Subject: [PATCH 6/7] Fix getTermSize() for Windows --- terminal_size_windows.go | 54 ++++++---------------------------------- 1 file changed, 7 insertions(+), 47 deletions(-) diff --git a/terminal_size_windows.go b/terminal_size_windows.go index 71b8bec..390a525 100644 --- a/terminal_size_windows.go +++ b/terminal_size_windows.go @@ -3,61 +3,21 @@ package uilive import ( - "math" - "syscall" + "os" "unsafe" ) -type consoleFontInfo struct { - font uint32 - fontSize coord -} - -const ( - SmCxMin = 28 - SmCyMin = 29 -) - -var ( - tmpConsoleFontInfo consoleFontInfo - moduleUser32 = syscall.NewLazyDLL("user32.dll") - procGetCurrentConsoleFont = kernel32.NewProc("GetCurrentConsoleFont") - getSystemMetrics = moduleUser32.NewProc("GetSystemMetrics") -) - -func getCurrentConsoleFont(h syscall.Handle, info *consoleFontInfo) (err error) { - r0, _, e1 := syscall.Syscall( - procGetCurrentConsoleFont.Addr(), 3, uintptr(h), 0, uintptr(unsafe.Pointer(info)), - ) - if int(r0) == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - func getTermSize() (int, int) { - out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + out, err := os.Open("CONOUT$") if err != nil { return 0, 0 } - x, _, err := getSystemMetrics.Call(SmCxMin) - y, _, err := getSystemMetrics.Call(SmCyMin) - - if x == 0 || y == 0 { - if err != nil { - panic(err) - } - } - - err = getCurrentConsoleFont(out, &tmpConsoleFontInfo) - if err != nil { - panic(err) + var csbi consoleScreenBufferInfo + ret, _, _ := procGetConsoleScreenBufferInfo.Call(out.Fd(), uintptr(unsafe.Pointer(&csbi))) + if ret == 0 { + return 0, 0 } - return int(math.Ceil(float64(x) / float64(tmpConsoleFontInfo.fontSize.x))), int(math.Ceil(float64(y) / float64(tmpConsoleFontInfo.fontSize.y))) + return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1) } From eafd45c022863b0ad01a61a000d0b19ea60d80a6 Mon Sep 17 00:00:00 2001 From: Vasily Litvinov Date: Thu, 7 Nov 2019 14:48:30 +0300 Subject: [PATCH 7/7] Add forgotten Close() for opened console output --- terminal_size_windows.go | 1 + 1 file changed, 1 insertion(+) diff --git a/terminal_size_windows.go b/terminal_size_windows.go index 390a525..94b7ed0 100644 --- a/terminal_size_windows.go +++ b/terminal_size_windows.go @@ -12,6 +12,7 @@ func getTermSize() (int, int) { if err != nil { return 0, 0 } + defer out.Close() var csbi consoleScreenBufferInfo ret, _, _ := procGetConsoleScreenBufferInfo.Call(out.Fd(), uintptr(unsafe.Pointer(&csbi)))