correct GetSize on Windows
Vladimir Bauer
6 years ago
| 15 | 15 | |
| 16 | 16 | func main() { |
| 17 | 17 | doneWg := new(sync.WaitGroup) |
| 18 | p := mpb.New(mpb.WithWidth(64), mpb.WithWaitGroup(doneWg)) | |
| 18 | p := mpb.New(mpb.WithWaitGroup(doneWg)) | |
| 19 | 19 | numBars := 4 |
| 20 | 20 | |
| 21 | 21 | var bars []*mpb.Bar |
| 43 | 43 | i := i |
| 44 | 44 | go func() { |
| 45 | 45 | task := fmt.Sprintf("Task#%02d:", i) |
| 46 | // ANSI escape sequences are not supported on Windows OS | |
| 46 | 47 | job := "\x1b[31;1;4minstalling\x1b[0m" |
| 47 | 48 | // preparing delayed bars |
| 48 | 49 | b := p.AddBar(rand.Int63n(101)+100, |
| 6 | 6 | "io" |
| 7 | 7 | "os" |
| 8 | 8 | |
| 9 | "golang.org/x/crypto/ssh/terminal" | |
| 9 | "github.com/mattn/go-isatty" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | // NotATTY not a TeleTYpewriter error. |
| 29 | 29 | w := &Writer{out: out} |
| 30 | 30 | if f, ok := out.(*os.File); ok { |
| 31 | 31 | w.fd = f.Fd() |
| 32 | w.isTerminal = terminal.IsTerminal(int(w.fd)) | |
| 32 | w.isTerminal = isatty.IsTerminal(w.fd) | |
| 33 | 33 | } |
| 34 | 34 | return w |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 37 | // Flush flushes the underlying buffer. |
| 38 | 38 | func (w *Writer) Flush(lineCount int) (err error) { |
| 39 | // some terminals interpret clear 0 lines as clear 1 | |
| 39 | 40 | if w.lineCount > 0 { |
| 40 | 41 | w.clearLines() |
| 41 | 42 | } |
| 62 | 63 | |
| 63 | 64 | // GetWidth returns width of underlying terminal. |
| 64 | 65 | func (w *Writer) GetWidth() (int, error) { |
| 65 | if w.isTerminal { | |
| 66 | tw, _, err := terminal.GetSize(int(w.fd)) | |
| 67 | return tw, err | |
| 66 | if !w.isTerminal { | |
| 67 | return -1, NotATTY | |
| 68 | 68 | } |
| 69 | return -1, NotATTY | |
| 69 | tw, _, err := GetSize(w.fd) | |
| 70 | return tw, err | |
| 70 | 71 | } |
| 1 | 1 | |
| 2 | 2 | package cwriter |
| 3 | 3 | |
| 4 | import "fmt" | |
| 4 | import ( | |
| 5 | "fmt" | |
| 6 | ||
| 7 | "golang.org/x/sys/unix" | |
| 8 | ) | |
| 5 | 9 | |
| 6 | 10 | func (w *Writer) clearLines() { |
| 7 | 11 | fmt.Fprintf(w.out, cuuAndEd, w.lineCount) |
| 8 | 12 | } |
| 13 | ||
| 14 | // GetSize returns the dimensions of the given terminal. | |
| 15 | func GetSize(fd uintptr) (width, height int, err error) { | |
| 16 | ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) | |
| 17 | if err != nil { | |
| 18 | return -1, -1, err | |
| 19 | } | |
| 20 | return int(ws.Col), int(ws.Row), nil | |
| 21 | } |
| 13 | 13 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") |
| 14 | 14 | procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") |
| 15 | 15 | procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") |
| 16 | procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") | |
| 17 | 16 | ) |
| 18 | 17 | |
| 19 | 18 | type coord struct { |
| 40 | 39 | if !w.isTerminal { |
| 41 | 40 | fmt.Fprintf(w.out, cuuAndEd, w.lineCount) |
| 42 | 41 | } |
| 43 | var info consoleScreenBufferInfo | |
| 44 | procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(&info))) | |
| 42 | ||
| 43 | info := new(consoleScreenBufferInfo) | |
| 44 | procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(info))) | |
| 45 | 45 | |
| 46 | 46 | info.cursorPosition.y -= int16(w.lineCount) |
| 47 | 47 | if info.cursorPosition.y < 0 { |
| 50 | 50 | procSetConsoleCursorPosition.Call(w.fd, uintptr(uint32(uint16(info.cursorPosition.y))<<16|uint32(uint16(info.cursorPosition.x)))) |
| 51 | 51 | |
| 52 | 52 | // clear the lines |
| 53 | cursor := coord{ | |
| 53 | cursor := &coord{ | |
| 54 | 54 | x: info.window.left, |
| 55 | 55 | y: info.cursorPosition.y, |
| 56 | 56 | } |
| 57 | 57 | count := uint32(info.size.x) * uint32(w.lineCount) |
| 58 | procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(new(uint32)))) | |
| 58 | procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(cursor)), uintptr(unsafe.Pointer(new(uint32)))) | |
| 59 | 59 | } |
| 60 | ||
| 61 | // GetSize returns the visible dimensions of the given terminal. | |
| 62 | // | |
| 63 | // These dimensions don't include any scrollback buffer height. | |
| 64 | func GetSize(fd uintptr) (width, height int, err error) { | |
| 65 | info := new(consoleScreenBufferInfo) | |
| 66 | procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(info))) | |
| 67 | return int(info.window.right - info.window.left), int(info.window.bottom - info.window.top), nil | |
| 68 | } | |
| 2 | 2 | require ( |
| 3 | 3 | github.com/VividCortex/ewma v1.1.1 |
| 4 | 4 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d |
| 5 | golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 | |
| 6 | golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect | |
| 5 | github.com/mattn/go-isatty v0.0.12 | |
| 6 | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f | |
| 7 | 7 | ) |
| 8 | 8 | |
| 9 | 9 | go 1.14 |
| 1 | 1 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= |
| 2 | 2 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= |
| 3 | 3 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= |
| 4 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |
| 5 | golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= | |
| 6 | golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |
| 7 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |
| 8 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 9 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 10 | golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= | |
| 11 | golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 12 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |
| 4 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | |
| 5 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | |
| 6 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 7 | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI= | |
| 8 | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |