From 0c2c473c36997d4dfe38f957adeffffad39a6b48 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 17:48:21 +0900 Subject: [PATCH 01/19] support DRCS Sixel Graphics --- main.go | 10 ++++-- osc/iterm2.go | 99 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index a70529a..9ff46f3 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,10 @@ import ( "github.com/rs/jplot/osc" ) +var ( + supportSixel = false +) + func main() { flag.Usage = func() { out := os.Stderr @@ -39,7 +43,9 @@ func main() { flag.Parse() if os.Getenv("TERM_PROGRAM") != "iTerm.app" { - fatal("iTerm2 required") + if !osc.IsSixelSupported() { + fatal("iTerm2 or DRCS Sixel graphics required") + } } if os.Getenv("TERM") == "screen" { fatal("screen and tmux not supported") @@ -149,7 +155,7 @@ func render(dash graph.Dash, rows int) { rows = size.Row } // Use iTerm2 image display feature. - term := &osc.ImageWriter{} + term := osc.NewImageWriter() defer term.Close() if err := dash.Render(term, width, height); err != nil { fatal(fmt.Sprintf("cannot render graph: %v", err.Error())) diff --git a/osc/iterm2.go b/osc/iterm2.go index 337df96..55e42d5 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -5,11 +5,14 @@ import ( "encoding/base64" "errors" "fmt" + "image/png" "io" "os" "sync" "time" + "github.com/mattn/go-sixel" + "golang.org/x/crypto/ssh/terminal" ) @@ -19,11 +22,46 @@ var st = "\007" var cellSizeOnce sync.Once var cellWidth, cellHeight float64 +var sixelEnabled = false + func init() { if os.Getenv("TERM") == "screen" { ecsi = "\033Ptmux;\033" + ecsi st += "\033\\" } + sixelEnabled = checkSixel() +} + +func checkSixel() bool { + s, err := terminal.MakeRaw(1) + if err != nil { + return false + } + defer terminal.Restore(1, s) + _, err = os.Stdout.Write([]byte("\x1b[c")) + if err != nil { + return false + } + defer fileSetReadDeadline(os.Stdout, time.Time{}) + + var b [100]byte + n, err := os.Stdout.Read(b[:]) + if err != nil { + return false + } + if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { + return false + } + for _, t := range bytes.Split(b[4:n], []byte(";")) { + if len(t) == 1 && t[0] == '4' { + return true + } + } + return false +} + +func IsSixelSupported() bool { + return sixelEnabled } // ClearScrollback clears iTerm2 scrollback. @@ -45,10 +83,15 @@ func initCellSize() { return } defer terminal.Restore(1, s) - fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) - fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) - defer fileSetReadDeadline(os.Stdout, time.Time{}) - fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + if !sixelEnabled { + fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + } else { + // FIXME Way to get sizes from terminal response? + cellWidth, cellHeight = 8, 8 + } } // Size gathers sizing information of the current session's controling terminal. @@ -59,7 +102,7 @@ func Size() (size TermSize, err error) { } cellSizeOnce.Do(initCellSize) if cellWidth+cellHeight == 0 { - err = errors.New("cannot get iTerm2 cell size") + err = errors.New("cannot get terminal cell size") } size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) return @@ -71,9 +114,16 @@ func Rows() (rows int, err error) { return } -// ImageWriter is a writer that write into iTerm2 terminal the PNG data written +func NewImageWriter() io.WriteCloser { + if !sixelEnabled { + return &imageWriter{} + } + return &sixelWriter{} +} + +// imageWriter is a writer that write into iTerm2 terminal the PNG data written // to it. -type ImageWriter struct { +type imageWriter struct { Name string once sync.Once @@ -81,20 +131,47 @@ type ImageWriter struct { buf *bytes.Buffer } -func (w *ImageWriter) init() { +func (w *imageWriter) init() { w.buf = &bytes.Buffer{} w.b66enc = base64.NewEncoder(base64.StdEncoding, w.buf) } -// Write writes the PNG image data into the ImageWriter buffer. -func (w *ImageWriter) Write(p []byte) (n int, err error) { +// Write writes the PNG image data into the imageWriter buffer. +func (w *imageWriter) Write(p []byte) (n int, err error) { w.once.Do(w.init) return w.b66enc.Write(p) } // Close flushes the image to the terminal and close the writer. -func (w *ImageWriter) Close() error { +func (w *imageWriter) Close() error { w.once.Do(w.init) fmt.Printf("%s1337;File=preserveAspectRatio=1;inline=1:%s%s", ecsi, w.buf.Bytes(), st) return w.b66enc.Close() } + +type sixelWriter struct { + once sync.Once + enc *sixel.Encoder + buf *bytes.Buffer +} + +func (w *sixelWriter) init() { + w.buf = &bytes.Buffer{} + w.enc = sixel.NewEncoder(os.Stdout) +} + +// Write writes the PNG image data into the imageWriter buffer. +func (w *sixelWriter) Write(p []byte) (n int, err error) { + w.once.Do(w.init) + return w.buf.Write(p) +} + +// Close flushes the image to the terminal and close the writer. +func (w *sixelWriter) Close() error { + w.once.Do(w.init) + img, err := png.Decode(w.buf) + if err != nil { + return err + } + return w.enc.Encode(img) +} From 7b67044ed982b94dbe186f12d50fee628e16cc72 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 18:02:32 +0900 Subject: [PATCH 02/19] remove needless variable --- main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main.go b/main.go index 9ff46f3..1764f78 100644 --- a/main.go +++ b/main.go @@ -16,10 +16,6 @@ import ( "github.com/rs/jplot/osc" ) -var ( - supportSixel = false -) - func main() { flag.Usage = func() { out := os.Stderr From 1cc9a93f9cc6138449056db7d6b016adbc2312ab Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 18:18:09 +0900 Subject: [PATCH 03/19] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04590c5..707c026 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # jplot [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/jplot/master/LICENSE) [![Build Status](https://travis-ci.org/rs/jplot.svg?branch=master)](https://travis-ci.org/rs/jplot) -Jplot tracks expvar-like (JSON) metrics and plot their evolution over time right into your iTerm2 terminal. +Jplot tracks expvar-like (JSON) metrics and plot their evolution over time right into your iTerm2 terminal (or DRCS Sixel Graphics). ![](doc/demo.gif) @@ -33,7 +33,7 @@ From source: go get -u github.com/rs/jplot ``` -This tool does only work with [iTerm2](https://www.iterm2.com). +This tool does only work with [iTerm2](https://www.iterm2.com), or terminals support DRCS Sixel Graphics. ## Usage From e9127485bb85f3f982625a5b78423c86fb1d3e9f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:04:32 +0900 Subject: [PATCH 04/19] move condition into osc.HasGraphicsSupport --- main.go | 6 ++---- osc/iterm2.go | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 1764f78..fca2273 100644 --- a/main.go +++ b/main.go @@ -38,10 +38,8 @@ func main() { rows := flag.Int("rows", 0, "Limits the height of the graph output.") flag.Parse() - if os.Getenv("TERM_PROGRAM") != "iTerm.app" { - if !osc.IsSixelSupported() { - fatal("iTerm2 or DRCS Sixel graphics required") - } + if !osc.HasGraphicsSupport() { + fatal("iTerm2 or DRCS Sixel graphics required") } if os.Getenv("TERM") == "screen" { fatal("screen and tmux not supported") diff --git a/osc/iterm2.go b/osc/iterm2.go index 55e42d5..4e32564 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -60,8 +60,8 @@ func checkSixel() bool { return false } -func IsSixelSupported() bool { - return sixelEnabled +func HasGraphicsSupport() bool { + return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled } // ClearScrollback clears iTerm2 scrollback. From 50d5719ec06cd7c4bbc1b054cbe4162135854c4b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:09:41 +0900 Subject: [PATCH 05/19] isolate sixel support in a sixel.go and common.go --- osc/common.go | 79 ++++++++++++++++++++++++++++++ osc/iterm2.go | 132 -------------------------------------------------- osc/sixel.go | 76 +++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 132 deletions(-) create mode 100644 osc/common.go create mode 100644 osc/sixel.go diff --git a/osc/common.go b/osc/common.go new file mode 100644 index 0000000..fac259d --- /dev/null +++ b/osc/common.go @@ -0,0 +1,79 @@ +package osc + +import ( + "errors" + "fmt" + "io" + "os" + "sync" + "time" + + "golang.org/x/crypto/ssh/terminal" +) + +var ecsi = "\033]" +var st = "\007" + +var cellSizeOnce sync.Once +var cellWidth, cellHeight float64 + +func HasGraphicsSupport() bool { + return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled +} + +// ClearScrollback clears iTerm2 scrollback. +func ClearScrollback() { + print(ecsi + "1337;ClearScrollback" + st) +} + +// TermSize contains sizing information of the terminal. +type TermSize struct { + Row int + Col int + Width int + Height int +} + +func initCellSize() { + s, err := terminal.MakeRaw(1) + if err != nil { + return + } + defer terminal.Restore(1, s) + if !sixelEnabled { + fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + } else { + // FIXME Way to get sizes from terminal response? + cellWidth, cellHeight = 8, 8 + } +} + +// Size gathers sizing information of the current session's controling terminal. +func Size() (size TermSize, err error) { + size.Col, size.Row, err = terminal.GetSize(1) + if err != nil { + return + } + cellSizeOnce.Do(initCellSize) + if cellWidth+cellHeight == 0 { + err = errors.New("cannot get terminal cell size") + } + size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) + return +} + +// Rows returns the number of rows for the controling terminal. +func Rows() (rows int, err error) { + _, rows, err = terminal.GetSize(1) + return +} + +func NewImageWriter() io.WriteCloser { + if !sixelEnabled { + return &imageWriter{} + } + return &sixelWriter{} +} diff --git a/osc/iterm2.go b/osc/iterm2.go index 4e32564..92a1232 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -3,122 +3,17 @@ package osc import ( "bytes" "encoding/base64" - "errors" "fmt" - "image/png" "io" "os" "sync" - "time" - - "github.com/mattn/go-sixel" - - "golang.org/x/crypto/ssh/terminal" ) -var ecsi = "\033]" -var st = "\007" - -var cellSizeOnce sync.Once -var cellWidth, cellHeight float64 - -var sixelEnabled = false - func init() { if os.Getenv("TERM") == "screen" { ecsi = "\033Ptmux;\033" + ecsi st += "\033\\" } - sixelEnabled = checkSixel() -} - -func checkSixel() bool { - s, err := terminal.MakeRaw(1) - if err != nil { - return false - } - defer terminal.Restore(1, s) - _, err = os.Stdout.Write([]byte("\x1b[c")) - if err != nil { - return false - } - defer fileSetReadDeadline(os.Stdout, time.Time{}) - - var b [100]byte - n, err := os.Stdout.Read(b[:]) - if err != nil { - return false - } - if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { - return false - } - for _, t := range bytes.Split(b[4:n], []byte(";")) { - if len(t) == 1 && t[0] == '4' { - return true - } - } - return false -} - -func HasGraphicsSupport() bool { - return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled -} - -// ClearScrollback clears iTerm2 scrollback. -func ClearScrollback() { - print(ecsi + "1337;ClearScrollback" + st) -} - -// TermSize contains sizing information of the terminal. -type TermSize struct { - Row int - Col int - Width int - Height int -} - -func initCellSize() { - s, err := terminal.MakeRaw(1) - if err != nil { - return - } - defer terminal.Restore(1, s) - if !sixelEnabled { - fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) - fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) - defer fileSetReadDeadline(os.Stdout, time.Time{}) - fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) - } else { - // FIXME Way to get sizes from terminal response? - cellWidth, cellHeight = 8, 8 - } -} - -// Size gathers sizing information of the current session's controling terminal. -func Size() (size TermSize, err error) { - size.Col, size.Row, err = terminal.GetSize(1) - if err != nil { - return - } - cellSizeOnce.Do(initCellSize) - if cellWidth+cellHeight == 0 { - err = errors.New("cannot get terminal cell size") - } - size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) - return -} - -// Rows returns the number of rows for the controling terminal. -func Rows() (rows int, err error) { - _, rows, err = terminal.GetSize(1) - return -} - -func NewImageWriter() io.WriteCloser { - if !sixelEnabled { - return &imageWriter{} - } - return &sixelWriter{} } // imageWriter is a writer that write into iTerm2 terminal the PNG data written @@ -148,30 +43,3 @@ func (w *imageWriter) Close() error { fmt.Printf("%s1337;File=preserveAspectRatio=1;inline=1:%s%s", ecsi, w.buf.Bytes(), st) return w.b66enc.Close() } - -type sixelWriter struct { - once sync.Once - enc *sixel.Encoder - buf *bytes.Buffer -} - -func (w *sixelWriter) init() { - w.buf = &bytes.Buffer{} - w.enc = sixel.NewEncoder(os.Stdout) -} - -// Write writes the PNG image data into the imageWriter buffer. -func (w *sixelWriter) Write(p []byte) (n int, err error) { - w.once.Do(w.init) - return w.buf.Write(p) -} - -// Close flushes the image to the terminal and close the writer. -func (w *sixelWriter) Close() error { - w.once.Do(w.init) - img, err := png.Decode(w.buf) - if err != nil { - return err - } - return w.enc.Encode(img) -} diff --git a/osc/sixel.go b/osc/sixel.go new file mode 100644 index 0000000..ebda021 --- /dev/null +++ b/osc/sixel.go @@ -0,0 +1,76 @@ +package osc + +import ( + "bytes" + "image/png" + "os" + "sync" + "time" + + "github.com/mattn/go-sixel" + + "golang.org/x/crypto/ssh/terminal" +) + +var sixelEnabled = false + +func init() { + if os.Getenv("TERM_PROGRAM") != "iTerm.app" { + sixelEnabled = checkSixel() + } +} + +func checkSixel() bool { + s, err := terminal.MakeRaw(1) + if err != nil { + return false + } + defer terminal.Restore(1, s) + _, err = os.Stdout.Write([]byte("\x1b[c")) + if err != nil { + return false + } + defer fileSetReadDeadline(os.Stdout, time.Time{}) + + var b [100]byte + n, err := os.Stdout.Read(b[:]) + if err != nil { + return false + } + if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { + return false + } + for _, t := range bytes.Split(b[4:n], []byte(";")) { + if len(t) == 1 && t[0] == '4' { + return true + } + } + return false +} + +type sixelWriter struct { + once sync.Once + enc *sixel.Encoder + buf *bytes.Buffer +} + +func (w *sixelWriter) init() { + w.buf = &bytes.Buffer{} + w.enc = sixel.NewEncoder(os.Stdout) +} + +// Write writes the PNG image data into the imageWriter buffer. +func (w *sixelWriter) Write(p []byte) (n int, err error) { + w.once.Do(w.init) + return w.buf.Write(p) +} + +// Close flushes the image to the terminal and close the writer. +func (w *sixelWriter) Close() error { + w.once.Do(w.init) + img, err := png.Decode(w.buf) + if err != nil { + return err + } + return w.enc.Encode(img) +} From e631e8858d7508726cf22271f3cf7807d8dd4902 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:24:57 +0900 Subject: [PATCH 06/19] update README.md with info about supported terminals --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 707c026..de686c9 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,14 @@ echo 'GET http://localhost:8080' | \ ``` ![](doc/vegeta.gif) + +### Supported Terminals + +* [xterm](http://invisible-island.net/xterm/) +* [iTerm2](https://www.iterm2.com/) on OSX +* [mintty](https://mintty.github.io/) on UNIX OSs via SSH +* [mlterm](https://sourceforge.net/projects/mlterm/) on Linux and Windows +* [RLogin](http://nanno.dip.jp/softlib/man/rlogin/) on Windows +* [yaft](http://uobikiemukot.github.io/yaft/) on Linux console +* [yaft-android](https://github.com/uobikiemukot/yaft-android) on Android +* [Tanasinn](http://saitoha.github.io/tanasinn/) on Firefox/Thunderbird From 8cd00e312468318733ec915c51709b811c1e731f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 17:48:21 +0900 Subject: [PATCH 07/19] support DRCS Sixel Graphics --- main.go | 13 ++++--- osc/iterm2.go | 100 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/main.go b/main.go index 7b10283..9ff46f3 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,10 @@ import ( "github.com/rs/jplot/osc" ) +var ( + supportSixel = false +) + func main() { flag.Usage = func() { out := os.Stderr @@ -39,7 +43,9 @@ func main() { flag.Parse() if os.Getenv("TERM_PROGRAM") != "iTerm.app" { - fatal("iTerm2 required") + if !osc.IsSixelSupported() { + fatal("iTerm2 or DRCS Sixel graphics required") + } } if os.Getenv("TERM") == "screen" { fatal("screen and tmux not supported") @@ -149,10 +155,7 @@ func render(dash graph.Dash, rows int) { rows = size.Row } // Use iTerm2 image display feature. - term := &osc.ImageWriter{ - Width: width, - Height: height, - } + term := osc.NewImageWriter() defer term.Close() if err := dash.Render(term, width, height); err != nil { fatal(fmt.Sprintf("cannot render graph: %v", err.Error())) diff --git a/osc/iterm2.go b/osc/iterm2.go index 89d184b..dddfc07 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -5,11 +5,14 @@ import ( "encoding/base64" "errors" "fmt" + "image/png" "io" "os" "sync" "time" + "github.com/mattn/go-sixel" + "golang.org/x/crypto/ssh/terminal" ) @@ -19,11 +22,46 @@ var st = "\007" var cellSizeOnce sync.Once var cellWidth, cellHeight float64 +var sixelEnabled = false + func init() { if os.Getenv("TERM") == "screen" { ecsi = "\033Ptmux;\033" + ecsi st += "\033\\" } + sixelEnabled = checkSixel() +} + +func checkSixel() bool { + s, err := terminal.MakeRaw(1) + if err != nil { + return false + } + defer terminal.Restore(1, s) + _, err = os.Stdout.Write([]byte("\x1b[c")) + if err != nil { + return false + } + defer fileSetReadDeadline(os.Stdout, time.Time{}) + + var b [100]byte + n, err := os.Stdout.Read(b[:]) + if err != nil { + return false + } + if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { + return false + } + for _, t := range bytes.Split(b[4:n], []byte(";")) { + if len(t) == 1 && t[0] == '4' { + return true + } + } + return false +} + +func IsSixelSupported() bool { + return sixelEnabled } // ClearScrollback clears iTerm2 scrollback. @@ -45,10 +83,15 @@ func initCellSize() { return } defer terminal.Restore(1, s) - fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) - fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) - defer fileSetReadDeadline(os.Stdout, time.Time{}) - fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + if !sixelEnabled { + fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + } else { + // FIXME Way to get sizes from terminal response? + cellWidth, cellHeight = 8, 8 + } } // Size gathers sizing information of the current session's controling terminal. @@ -59,7 +102,7 @@ func Size() (size TermSize, err error) { } cellSizeOnce.Do(initCellSize) if cellWidth+cellHeight == 0 { - err = errors.New("cannot get iTerm2 cell size") + err = errors.New("cannot get terminal cell size") } size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) return @@ -71,9 +114,15 @@ func Rows() (rows int, err error) { return } -// ImageWriter is a writer that write into iTerm2 terminal the PNG data written -// to it. -type ImageWriter struct { +func NewImageWriter() io.WriteCloser { + if !sixelEnabled { + return &imageWriter{} + } + return &sixelWriter{} +} + +// imageWriter is a writer that write into iTerm2 terminal the PNG data written +type imageWriter struct { Name string Width int Height int @@ -83,20 +132,47 @@ type ImageWriter struct { buf *bytes.Buffer } -func (w *ImageWriter) init() { +func (w *imageWriter) init() { w.buf = &bytes.Buffer{} w.b64enc = base64.NewEncoder(base64.StdEncoding, w.buf) } -// Write writes the PNG image data into the ImageWriter buffer. -func (w *ImageWriter) Write(p []byte) (n int, err error) { +// Write writes the PNG image data into the imageWriter buffer. +func (w *imageWriter) Write(p []byte) (n int, err error) { w.once.Do(w.init) return w.b64enc.Write(p) } // Close flushes the image to the terminal and close the writer. -func (w *ImageWriter) Close() error { +func (w *imageWriter) Close() error { w.once.Do(w.init) fmt.Printf("%s1337;File=preserveAspectRatio=1;width=%dpx;height=%dpx;inline=1:%s%s", ecsi, w.Width, w.Height, w.buf.Bytes(), st) return w.b64enc.Close() } + +type sixelWriter struct { + once sync.Once + enc *sixel.Encoder + buf *bytes.Buffer +} + +func (w *sixelWriter) init() { + w.buf = &bytes.Buffer{} + w.enc = sixel.NewEncoder(os.Stdout) +} + +// Write writes the PNG image data into the imageWriter buffer. +func (w *sixelWriter) Write(p []byte) (n int, err error) { + w.once.Do(w.init) + return w.buf.Write(p) +} + +// Close flushes the image to the terminal and close the writer. +func (w *sixelWriter) Close() error { + w.once.Do(w.init) + img, err := png.Decode(w.buf) + if err != nil { + return err + } + return w.enc.Encode(img) +} From 4b809884c49219e170f9e28de63830b5e2e09d3f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 18:02:32 +0900 Subject: [PATCH 08/19] remove needless variable --- main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main.go b/main.go index 9ff46f3..1764f78 100644 --- a/main.go +++ b/main.go @@ -16,10 +16,6 @@ import ( "github.com/rs/jplot/osc" ) -var ( - supportSixel = false -) - func main() { flag.Usage = func() { out := os.Stderr From 0ca8ad2efa79553e1b34b98c10df673b716a1457 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 28 Mar 2018 18:18:09 +0900 Subject: [PATCH 09/19] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04590c5..707c026 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # jplot [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/jplot/master/LICENSE) [![Build Status](https://travis-ci.org/rs/jplot.svg?branch=master)](https://travis-ci.org/rs/jplot) -Jplot tracks expvar-like (JSON) metrics and plot their evolution over time right into your iTerm2 terminal. +Jplot tracks expvar-like (JSON) metrics and plot their evolution over time right into your iTerm2 terminal (or DRCS Sixel Graphics). ![](doc/demo.gif) @@ -33,7 +33,7 @@ From source: go get -u github.com/rs/jplot ``` -This tool does only work with [iTerm2](https://www.iterm2.com). +This tool does only work with [iTerm2](https://www.iterm2.com), or terminals support DRCS Sixel Graphics. ## Usage From e8672addf2f4a5f0d00d62f832ffffb28fd5e276 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:04:32 +0900 Subject: [PATCH 10/19] move condition into osc.HasGraphicsSupport --- main.go | 6 ++---- osc/iterm2.go | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 1764f78..fca2273 100644 --- a/main.go +++ b/main.go @@ -38,10 +38,8 @@ func main() { rows := flag.Int("rows", 0, "Limits the height of the graph output.") flag.Parse() - if os.Getenv("TERM_PROGRAM") != "iTerm.app" { - if !osc.IsSixelSupported() { - fatal("iTerm2 or DRCS Sixel graphics required") - } + if !osc.HasGraphicsSupport() { + fatal("iTerm2 or DRCS Sixel graphics required") } if os.Getenv("TERM") == "screen" { fatal("screen and tmux not supported") diff --git a/osc/iterm2.go b/osc/iterm2.go index dddfc07..fa1a6a0 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -60,8 +60,8 @@ func checkSixel() bool { return false } -func IsSixelSupported() bool { - return sixelEnabled +func HasGraphicsSupport() bool { + return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled } // ClearScrollback clears iTerm2 scrollback. From bf88131a1fcfa5064af8db340f2d1174d3b690ce Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:09:41 +0900 Subject: [PATCH 11/19] isolate sixel support in a sixel.go and common.go --- osc/common.go | 79 ++++++++++++++++++++++++++++++ osc/iterm2.go | 132 -------------------------------------------------- osc/sixel.go | 76 +++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 132 deletions(-) create mode 100644 osc/common.go create mode 100644 osc/sixel.go diff --git a/osc/common.go b/osc/common.go new file mode 100644 index 0000000..fac259d --- /dev/null +++ b/osc/common.go @@ -0,0 +1,79 @@ +package osc + +import ( + "errors" + "fmt" + "io" + "os" + "sync" + "time" + + "golang.org/x/crypto/ssh/terminal" +) + +var ecsi = "\033]" +var st = "\007" + +var cellSizeOnce sync.Once +var cellWidth, cellHeight float64 + +func HasGraphicsSupport() bool { + return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled +} + +// ClearScrollback clears iTerm2 scrollback. +func ClearScrollback() { + print(ecsi + "1337;ClearScrollback" + st) +} + +// TermSize contains sizing information of the terminal. +type TermSize struct { + Row int + Col int + Width int + Height int +} + +func initCellSize() { + s, err := terminal.MakeRaw(1) + if err != nil { + return + } + defer terminal.Restore(1, s) + if !sixelEnabled { + fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) + } else { + // FIXME Way to get sizes from terminal response? + cellWidth, cellHeight = 8, 8 + } +} + +// Size gathers sizing information of the current session's controling terminal. +func Size() (size TermSize, err error) { + size.Col, size.Row, err = terminal.GetSize(1) + if err != nil { + return + } + cellSizeOnce.Do(initCellSize) + if cellWidth+cellHeight == 0 { + err = errors.New("cannot get terminal cell size") + } + size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) + return +} + +// Rows returns the number of rows for the controling terminal. +func Rows() (rows int, err error) { + _, rows, err = terminal.GetSize(1) + return +} + +func NewImageWriter() io.WriteCloser { + if !sixelEnabled { + return &imageWriter{} + } + return &sixelWriter{} +} diff --git a/osc/iterm2.go b/osc/iterm2.go index fa1a6a0..fdc1828 100644 --- a/osc/iterm2.go +++ b/osc/iterm2.go @@ -3,122 +3,17 @@ package osc import ( "bytes" "encoding/base64" - "errors" "fmt" - "image/png" "io" "os" "sync" - "time" - - "github.com/mattn/go-sixel" - - "golang.org/x/crypto/ssh/terminal" ) -var ecsi = "\033]" -var st = "\007" - -var cellSizeOnce sync.Once -var cellWidth, cellHeight float64 - -var sixelEnabled = false - func init() { if os.Getenv("TERM") == "screen" { ecsi = "\033Ptmux;\033" + ecsi st += "\033\\" } - sixelEnabled = checkSixel() -} - -func checkSixel() bool { - s, err := terminal.MakeRaw(1) - if err != nil { - return false - } - defer terminal.Restore(1, s) - _, err = os.Stdout.Write([]byte("\x1b[c")) - if err != nil { - return false - } - defer fileSetReadDeadline(os.Stdout, time.Time{}) - - var b [100]byte - n, err := os.Stdout.Read(b[:]) - if err != nil { - return false - } - if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { - return false - } - for _, t := range bytes.Split(b[4:n], []byte(";")) { - if len(t) == 1 && t[0] == '4' { - return true - } - } - return false -} - -func HasGraphicsSupport() bool { - return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled -} - -// ClearScrollback clears iTerm2 scrollback. -func ClearScrollback() { - print(ecsi + "1337;ClearScrollback" + st) -} - -// TermSize contains sizing information of the terminal. -type TermSize struct { - Row int - Col int - Width int - Height int -} - -func initCellSize() { - s, err := terminal.MakeRaw(1) - if err != nil { - return - } - defer terminal.Restore(1, s) - if !sixelEnabled { - fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) - fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) - defer fileSetReadDeadline(os.Stdout, time.Time{}) - fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) - } else { - // FIXME Way to get sizes from terminal response? - cellWidth, cellHeight = 8, 8 - } -} - -// Size gathers sizing information of the current session's controling terminal. -func Size() (size TermSize, err error) { - size.Col, size.Row, err = terminal.GetSize(1) - if err != nil { - return - } - cellSizeOnce.Do(initCellSize) - if cellWidth+cellHeight == 0 { - err = errors.New("cannot get terminal cell size") - } - size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) - return -} - -// Rows returns the number of rows for the controling terminal. -func Rows() (rows int, err error) { - _, rows, err = terminal.GetSize(1) - return -} - -func NewImageWriter() io.WriteCloser { - if !sixelEnabled { - return &imageWriter{} - } - return &sixelWriter{} } // imageWriter is a writer that write into iTerm2 terminal the PNG data written @@ -149,30 +44,3 @@ func (w *imageWriter) Close() error { fmt.Printf("%s1337;File=preserveAspectRatio=1;width=%dpx;height=%dpx;inline=1:%s%s", ecsi, w.Width, w.Height, w.buf.Bytes(), st) return w.b64enc.Close() } - -type sixelWriter struct { - once sync.Once - enc *sixel.Encoder - buf *bytes.Buffer -} - -func (w *sixelWriter) init() { - w.buf = &bytes.Buffer{} - w.enc = sixel.NewEncoder(os.Stdout) -} - -// Write writes the PNG image data into the imageWriter buffer. -func (w *sixelWriter) Write(p []byte) (n int, err error) { - w.once.Do(w.init) - return w.buf.Write(p) -} - -// Close flushes the image to the terminal and close the writer. -func (w *sixelWriter) Close() error { - w.once.Do(w.init) - img, err := png.Decode(w.buf) - if err != nil { - return err - } - return w.enc.Encode(img) -} diff --git a/osc/sixel.go b/osc/sixel.go new file mode 100644 index 0000000..ebda021 --- /dev/null +++ b/osc/sixel.go @@ -0,0 +1,76 @@ +package osc + +import ( + "bytes" + "image/png" + "os" + "sync" + "time" + + "github.com/mattn/go-sixel" + + "golang.org/x/crypto/ssh/terminal" +) + +var sixelEnabled = false + +func init() { + if os.Getenv("TERM_PROGRAM") != "iTerm.app" { + sixelEnabled = checkSixel() + } +} + +func checkSixel() bool { + s, err := terminal.MakeRaw(1) + if err != nil { + return false + } + defer terminal.Restore(1, s) + _, err = os.Stdout.Write([]byte("\x1b[c")) + if err != nil { + return false + } + defer fileSetReadDeadline(os.Stdout, time.Time{}) + + var b [100]byte + n, err := os.Stdout.Read(b[:]) + if err != nil { + return false + } + if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { + return false + } + for _, t := range bytes.Split(b[4:n], []byte(";")) { + if len(t) == 1 && t[0] == '4' { + return true + } + } + return false +} + +type sixelWriter struct { + once sync.Once + enc *sixel.Encoder + buf *bytes.Buffer +} + +func (w *sixelWriter) init() { + w.buf = &bytes.Buffer{} + w.enc = sixel.NewEncoder(os.Stdout) +} + +// Write writes the PNG image data into the imageWriter buffer. +func (w *sixelWriter) Write(p []byte) (n int, err error) { + w.once.Do(w.init) + return w.buf.Write(p) +} + +// Close flushes the image to the terminal and close the writer. +func (w *sixelWriter) Close() error { + w.once.Do(w.init) + img, err := png.Decode(w.buf) + if err != nil { + return err + } + return w.enc.Encode(img) +} From 9e20d5433c5ee8dcbb9184ed101a2ed28119529c Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 29 Mar 2018 09:24:57 +0900 Subject: [PATCH 12/19] update README.md with info about supported terminals --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 707c026..de686c9 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,14 @@ echo 'GET http://localhost:8080' | \ ``` ![](doc/vegeta.gif) + +### Supported Terminals + +* [xterm](http://invisible-island.net/xterm/) +* [iTerm2](https://www.iterm2.com/) on OSX +* [mintty](https://mintty.github.io/) on UNIX OSs via SSH +* [mlterm](https://sourceforge.net/projects/mlterm/) on Linux and Windows +* [RLogin](http://nanno.dip.jp/softlib/man/rlogin/) on Windows +* [yaft](http://uobikiemukot.github.io/yaft/) on Linux console +* [yaft-android](https://github.com/uobikiemukot/yaft-android) on Android +* [Tanasinn](http://saitoha.github.io/tanasinn/) on Firefox/Thunderbird From d4f919fa5209fab38eb0d23a8c9a829a3e90eb04 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 15 Oct 2018 16:36:39 +0900 Subject: [PATCH 13/19] get terminal width/height --- osc/common.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osc/common.go b/osc/common.go index fac259d..e38661c 100644 --- a/osc/common.go +++ b/osc/common.go @@ -16,6 +16,7 @@ var st = "\007" var cellSizeOnce sync.Once var cellWidth, cellHeight float64 +var termWidth, termHeight int func HasGraphicsSupport() bool { return os.Getenv("TERM_PROGRAM") == "iTerm.app" || sixelEnabled @@ -23,7 +24,9 @@ func HasGraphicsSupport() bool { // ClearScrollback clears iTerm2 scrollback. func ClearScrollback() { - print(ecsi + "1337;ClearScrollback" + st) + if !sixelEnabled { + print(ecsi + "1337;ClearScrollback" + st) + } } // TermSize contains sizing information of the terminal. @@ -46,8 +49,10 @@ func initCellSize() { defer fileSetReadDeadline(os.Stdout, time.Time{}) fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) } else { - // FIXME Way to get sizes from terminal response? - cellWidth, cellHeight = 8, 8 + fmt.Fprint(os.Stdout, "\033[14t") + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033[4;%d;%dt", &termHeight, &termWidth) } } @@ -58,7 +63,11 @@ func Size() (size TermSize, err error) { return } cellSizeOnce.Do(initCellSize) - if cellWidth+cellHeight == 0 { + if termWidth > 0 && termHeight > 0 { + size.Width = int(termWidth/(size.Col-1)) * (size.Col - 1) + size.Height = int(termHeight/(size.Row-1)) * (size.Row - 1) + return + } else if cellWidth+cellHeight == 0 { err = errors.New("cannot get terminal cell size") } size.Width, size.Height = size.Col*int(cellWidth), size.Row*int(cellHeight) From c14a0d587f1a3e9b2bea3ea6f412e1f3fab893b3 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 15 Oct 2018 17:20:04 +0900 Subject: [PATCH 14/19] remove extra empty line --- osc/sixel.go | 1 - 1 file changed, 1 deletion(-) diff --git a/osc/sixel.go b/osc/sixel.go index 2e11a81..6ad7e52 100644 --- a/osc/sixel.go +++ b/osc/sixel.go @@ -8,7 +8,6 @@ import ( "time" "github.com/mattn/go-sixel" - "golang.org/x/crypto/ssh/terminal" ) From c9196d9c67041f0bb346c8b0a63345deaad211b7 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 17 Oct 2018 11:42:33 +0900 Subject: [PATCH 15/19] rename package to term --- main.go | 26 +++++++++++++------------- {osc => term}/common.go | 2 +- {osc => term}/go110.go | 2 +- {osc => term}/iterm2.go | 2 +- {osc => term}/not_go110.go | 2 +- {osc => term}/sixel.go | 2 +- {osc => term}/std.go | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) rename {osc => term}/common.go (99%) rename {osc => term}/go110.go (91%) rename {osc => term}/iterm2.go (98%) rename {osc => term}/not_go110.go (90%) rename {osc => term}/sixel.go (99%) rename {osc => term}/std.go (99%) diff --git a/main.go b/main.go index ce437b4..e7f35e5 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( "github.com/monochromegane/terminal" "github.com/rs/jplot/data" "github.com/rs/jplot/graph" - "github.com/rs/jplot/osc" + "github.com/rs/jplot/term" ) func main() { @@ -38,7 +38,7 @@ func main() { rows := flag.Int("rows", 0, "Limits the height of the graph output.") flag.Parse() - if !osc.HasGraphicsSupport() { + if !term.HasGraphicsSupport() { fatal("iTerm2 or DRCS Sixel graphics required") } if os.Getenv("TERM") == "screen" { @@ -89,11 +89,11 @@ func main() { i++ if i%120 == 0 { // Clear scrollback to avoid iTerm from eating all the memory. - osc.ClearScrollback() + term.ClearScrollback() } - osc.CursorSavePosition() + term.CursorSavePosition() render(dash, *rows) - osc.CursorRestorePosition() + term.CursorRestorePosition() case <-exit: if i == 0 { render(dash, *rows) @@ -117,28 +117,28 @@ func fatal(a ...interface{}) { } func prepare(rows int) { - osc.HideCursor() + term.HideCursor() if rows == 0 { var err error - if rows, err = osc.Rows(); err != nil { + if rows, err = term.Rows(); err != nil { fatal("Cannot get window size: ", err) } } print(strings.Repeat("\n", rows)) - osc.CursorMove(osc.Up, rows) + term.CursorMove(term.Up, rows) } func cleanup(rows int) { - osc.ShowCursor() + term.ShowCursor() if rows == 0 { - rows, _ = osc.Rows() + rows, _ = term.Rows() } - osc.CursorMove(osc.Down, rows) + term.CursorMove(term.Down, rows) print("\n") } func render(dash graph.Dash, rows int) { - size, err := osc.Size() + size, err := term.Size() if err != nil { fatal("Cannot get window size: ", err) } @@ -149,7 +149,7 @@ func render(dash graph.Dash, rows int) { rows = size.Row } // Use iTerm2 image display feature. - term := osc.NewImageWriter(width, height) + term := term.NewImageWriter(width, height) defer term.Close() if err := dash.Render(term, width, height); err != nil { fatal(fmt.Sprintf("cannot render graph: %v", err.Error())) diff --git a/osc/common.go b/term/common.go similarity index 99% rename from osc/common.go rename to term/common.go index 0eae564..051c495 100644 --- a/osc/common.go +++ b/term/common.go @@ -1,4 +1,4 @@ -package osc +package term import ( "errors" diff --git a/osc/go110.go b/term/go110.go similarity index 91% rename from osc/go110.go rename to term/go110.go index 18de60a..563f0ef 100644 --- a/osc/go110.go +++ b/term/go110.go @@ -1,6 +1,6 @@ // +build go1.10 -package osc +package term import ( "os" diff --git a/osc/iterm2.go b/term/iterm2.go similarity index 98% rename from osc/iterm2.go rename to term/iterm2.go index fdc1828..409ec37 100644 --- a/osc/iterm2.go +++ b/term/iterm2.go @@ -1,4 +1,4 @@ -package osc +package term import ( "bytes" diff --git a/osc/not_go110.go b/term/not_go110.go similarity index 90% rename from osc/not_go110.go rename to term/not_go110.go index b965363..b6ba899 100644 --- a/osc/not_go110.go +++ b/term/not_go110.go @@ -1,6 +1,6 @@ // +build !go1.10 -package osc +package term import ( "os" diff --git a/osc/sixel.go b/term/sixel.go similarity index 99% rename from osc/sixel.go rename to term/sixel.go index 6ad7e52..3c37f98 100644 --- a/osc/sixel.go +++ b/term/sixel.go @@ -1,4 +1,4 @@ -package osc +package term import ( "bytes" diff --git a/osc/std.go b/term/std.go similarity index 99% rename from osc/std.go rename to term/std.go index f2bedee..d73d3b8 100644 --- a/osc/std.go +++ b/term/std.go @@ -1,4 +1,4 @@ -package osc +package term import "fmt" From 383a726bad9a147dd146623332cb3c1b324acbd9 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 17 Oct 2018 11:44:34 +0900 Subject: [PATCH 16/19] reverse condition --- term/common.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/term/common.go b/term/common.go index 051c495..8bbdcc4 100644 --- a/term/common.go +++ b/term/common.go @@ -43,17 +43,17 @@ func initCellSize() { return } defer terminal.Restore(1, s) - if !sixelEnabled { - fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) - fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) - defer fileSetReadDeadline(os.Stdout, time.Time{}) - fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) - } else { + if sixelEnabled { fmt.Fprint(os.Stdout, "\033[14t") fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) defer fileSetReadDeadline(os.Stdout, time.Time{}) fmt.Fscanf(os.Stdout, "\033[4;%d;%dt", &termHeight, &termWidth) + return } + fmt.Fprint(os.Stdout, ecsi+"1337;ReportCellSize"+st) + fileSetReadDeadline(os.Stdout, time.Now().Add(time.Second)) + defer fileSetReadDeadline(os.Stdout, time.Time{}) + fmt.Fscanf(os.Stdout, "\033]1337;ReportCellSize=%f;%f\033\\", &cellHeight, &cellWidth) } // Size gathers sizing information of the current session's controling terminal. @@ -82,13 +82,13 @@ func Rows() (rows int, err error) { } func NewImageWriter(width, height int) io.WriteCloser { - if !sixelEnabled { - return &imageWriter{ + if sixelEnabled { + return &sixelWriter{ Width: width, Height: height, } } - return &sixelWriter{ + return &imageWriter{ Width: width, Height: height, } From d4499cf41493f096e13bb3ec6c8164db8dde3da9 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 18 Sep 2019 23:27:40 +0900 Subject: [PATCH 17/19] Fix build --- graph/graph.go | 13 ++++++------- graph/legend.go | 2 +- term/sixel.go | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/graph/graph.go b/graph/graph.go index 194648c..ed56a42 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -8,7 +8,6 @@ import ( "github.com/rs/jplot/data" chart "github.com/wcharczuk/go-chart" "github.com/wcharczuk/go-chart/drawing" - "github.com/wcharczuk/go-chart/seq" ) func init() { @@ -46,17 +45,17 @@ func newChart(series []chart.Series, markers []chart.GridLine, width, height int for i, s := range series { if s, ok := s.(chart.ContinuousSeries); ok { min, max = minMax(s.YValues, min, max) - s.XValues = seq.Range(0, float64(len(s.YValues)-1)) + s.XValues = chart.LinearRange(0, float64(len(s.YValues)-1)) c := chart.GetAlternateColor(i + 4) s.Style = chart.Style{ - Show: true, + Hidden: false, StrokeWidth: 2, StrokeColor: c, FillColor: c.WithAlpha(20), FontSize: 9, } series[i] = s - last := chart.LastValueAnnotation(s, siValueFormater) + last := chart.LastValueAnnotationSeries(s, siValueFormater) last.Style.FillColor = c last.Style.FontColor = textColor(c) last.Style.FontSize = 9 @@ -71,7 +70,7 @@ func newChart(series []chart.Series, markers []chart.GridLine, width, height int Padding: chart.NewBox(5, 0, 0, 5), }, YAxis: chart.YAxis{ - Style: chart.StyleShow(), + Style: chart.Shown(), ValueFormatter: siValueFormater, }, Series: series, @@ -88,13 +87,13 @@ func newChart(series []chart.Series, markers []chart.GridLine, width, height int if len(markers) > 0 { graph.Background.Padding.Bottom = 0 // compensate transparent tick space graph.XAxis = chart.XAxis{ - Style: chart.StyleShow(), + Style: chart.Shown(), TickStyle: chart.Style{ StrokeColor: chart.ColorTransparent, }, TickPosition: 10, // hide text with non-existing position GridMajorStyle: chart.Style{ - Show: true, + Hidden: false, StrokeColor: chart.ColorAlternateGray.WithAlpha(100), StrokeWidth: 2.0, StrokeDashArray: []float64{2.0, 2.0}, diff --git a/graph/legend.go b/graph/legend.go index 467d9d3..7cc7bfe 100644 --- a/graph/legend.go +++ b/graph/legend.go @@ -37,7 +37,7 @@ func legend(c *chart.Chart, userDefaults ...chart.Style) chart.Renderable { var labels []string var lines []chart.Style for _, s := range c.Series { - if s.GetStyle().IsZero() || s.GetStyle().Show { + if s.GetStyle().IsZero() || !s.GetStyle().Hidden { if _, isAnnotationSeries := s.(chart.AnnotationSeries); !isAnnotationSeries { labels = append(labels, s.GetName()) lines = append(lines, s.GetStyle()) diff --git a/term/sixel.go b/term/sixel.go index 3c37f98..ccb277a 100644 --- a/term/sixel.go +++ b/term/sixel.go @@ -36,10 +36,23 @@ func checkSixel() bool { if err != nil { return false } - if !bytes.HasPrefix(b[:n], []byte("\x1b[?63;")) { + var supportedTerminals = []string{ + "\x1b[?62;", // VT240 + "\x1b[?63;", // wsltty + "\x1b[?64;", // mintty + "\x1b[?65;", // RLogin + } + supported := false + for _, supportedTerminal := range supportedTerminals { + if bytes.HasPrefix(b[:n], []byte(supportedTerminal)) { + supported = true + break + } + } + if !supported { return false } - for _, t := range bytes.Split(b[4:n], []byte(";")) { + for _, t := range bytes.Split(b[6:n], []byte(";")) { if len(t) == 1 && t[0] == '4' { return true } From 9329a5ebd58fa5f08249757b1fe50acaf046c80f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 18 Sep 2019 23:55:06 +0900 Subject: [PATCH 18/19] Check msys2/cygwin terminal --- term/sixel.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/term/sixel.go b/term/sixel.go index ccb277a..8af2308 100644 --- a/term/sixel.go +++ b/term/sixel.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/mattn/go-isatty" "github.com/mattn/go-sixel" "golang.org/x/crypto/ssh/terminal" ) @@ -20,6 +21,9 @@ func init() { } func checkSixel() bool { + if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + return true + } s, err := terminal.MakeRaw(1) if err != nil { return false From c5a1488db2eb4402c28a10fa02068aa7e0fa9314 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 19 Sep 2019 00:21:49 +0900 Subject: [PATCH 19/19] Update .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4db10c3..2680a73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: -- '1.8' -- '1.9' - '1.10' +- '1.11' +- '1.12' - master matrix: allow_failures: