//----------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. // See LICENSE.txt file in the project root for full license information. //----------------------------------------------------------------------------- package main import ( "fmt" "math" "time" "github.com/mattn/go-runewidth" tm "github.com/nsf/termbox-go" ) const ( lefttop = iota horizontal righttop vertical leftbottom rightbottom middlebottom middletop middleleft middleright middlemiddle space box1 box2 box3 box4 uparrow dnarrow ) var symbols = []rune{'┌', '─', '┐', '│', '└', '┘', '┴', '┬', '├', '┤', '┼', ' ', '░', '▒', '▓', '█', '↑', '↓'} const ( justifyLeft = iota justifyRight justifyCenter ) const ( border = iota noBorder ) type table struct { ccount int cwidth []int x int y int cr int justify int border int } func init() { if runewidth.IsEastAsian() { symbols = []rune{'+', '-', '+', '|', '+', '+', '+', '+', '+', '+', '+', ' ', '░', '▒', '▓', '█', '^', 'v'} } } func (t *table) drawTblRow(ledge, redge, middle, spr rune, fg, bg tm.Attribute) { twidth := t.ccount + 1 for _, w := range t.cwidth { twidth += w } for i := 0; i < twidth; i++ { tm.SetCell(t.x+i, t.y+t.cr, middle, fg, bg) } if t.border == border { tm.SetCell(t.x, t.y+t.cr, ledge, fg, bg) tm.SetCell(t.x+twidth, t.y+t.cr, redge, fg, bg) } o := 0 for c, w := range t.cwidth { o += w + 1 if c < t.ccount-1 { tm.SetCell(t.x+o, t.y+t.cr, spr, fg, bg) } } t.cr++ } func (t *table) addTblRow(row []string) { t.drawTblRow(symbols[vertical], symbols[vertical], symbols[space], symbols[vertical], tm.ColorDefault, tm.ColorDefault) t.cr-- o := 1 alignOffset := 0 for i := 0; i < t.ccount; i++ { w := t.cwidth[i] var s string if t.justify == justifyLeft { s = fmt.Sprintf("%-*s", w, row[i]) if i == 0 && t.border == noBorder { alignOffset = -1 } } else { s = fmt.Sprintf("%*s", w, row[i]) } printText(t.x+o+alignOffset, t.y+t.cr, w, s, tm.ColorDefault, tm.ColorDefault) o += w + 1 alignOffset = 0 } t.cr++ } func (t *table) addTblSpr() { t.drawTblRow(symbols[middleleft], symbols[middleright], symbols[horizontal], symbols[middlemiddle], tm.ColorDefault, tm.ColorDefault) } func (t *table) addTblHdr() { t.drawTblRow(symbols[lefttop], symbols[righttop], symbols[horizontal], symbols[middletop], tm.ColorDefault, tm.ColorDefault) } func (t *table) addTblFtr() { t.drawTblRow(symbols[leftbottom], symbols[rightbottom], symbols[horizontal], symbols[middlebottom], tm.ColorDefault, tm.ColorDefault) } func printHLineText(x, y int, w int, text string) { for i := 0; i < w; i++ { tm.SetCell(x+i, y, symbols[horizontal], tm.ColorWhite, tm.ColorDefault) } offset := (w - runewidth.StringWidth(text)) / 2 textArr := []rune(text) xoff := 0 for i := 0; i < len(text); i++ { tm.SetCell(x+offset+i+xoff, y, textArr[i], tm.ColorWhite, tm.ColorDefault) if runewidth.RuneWidth(textArr[i]) == 2 { xoff++ } } } func printVLine(x, y int, h int) { tm.SetCell(x, y, symbols[middletop], tm.ColorWhite, tm.ColorDefault) for i := 1; i < h; i++ { tm.SetCell(x, y+i, symbols[vertical], tm.ColorWhite, tm.ColorDefault) } } func printText(x, y, w int, text string, fg, bg tm.Attribute) { textArr := []rune(text) for i := 0; i < w; i++ { tm.SetCell(x+i, y, ' ', fg, bg) } xoff := 0 for i := 0; i < len(textArr); i++ { tm.SetCell(x+i+xoff, y, textArr[i], fg, bg) if runewidth.RuneWidth(textArr[i]) == 2 { xoff++ } } } func printCenterText(x, y, w int, text string, fg, bg tm.Attribute) { offset := (w - runewidth.StringWidth(text)) / 2 textArr := []rune(text) for i := 0; i < w; i++ { tm.SetCell(x+i, y, ' ', fg, bg) } xoff := 0 for i := 0; i < len(textArr); i++ { tm.SetCell(x+offset+i+xoff, y, textArr[i], fg, bg) if runewidth.RuneWidth(textArr[i]) == 2 { xoff++ } } } func printHLine(x, y int, w int) { for i := 0; i < w; i++ { tm.SetCell(x+i, y, symbols[horizontal], tm.ColorWhite, tm.ColorDefault) } } func printUsageBar(x, y, w int, usage, scale uint64, clr tm.Attribute) { barw := int(math.Log10(float64(uint64((usage + scale - 1) / (scale / 10))))) if barw > w { barw = w } else if barw < 0 { barw = 0 } for j := 0; j < w; j++ { tm.SetCell(x+j, y, symbols[box3], clr, tm.ColorDefault) } for j := 0; j < barw; j++ { tm.SetCell(x+j, y, symbols[box3], clr|tm.AttrBold, clr) } } func printDivider() { ui.printMsg("-----------------------------------------------------------") } func printDivider2() { ui.printMsg("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -") } type ethrUI interface { fini() printMsg(format string, a ...interface{}) printErr(format string, a ...interface{}) printDbg(format string, a ...interface{}) paint(uint64) emitTestHdr() emitLatencyHdr() emitLatencyResults(remote, proto string, avg, min, max, p50, p90, p95, p99, p999, p9999 time.Duration) emitTestResultBegin() emitTestResult(*ethrSession, EthrProtocol, uint64) printTestResults([]string) emitTestResultEnd() emitStats(ethrNetStat) } var ui ethrUI