diff --git a/README.md b/README.md index dba0d19..75eb0db 100644 --- a/README.md +++ b/README.md @@ -69,16 +69,16 @@ Feel free to add a new one. You can use 256 colors, bold, underline, and reverse - increase height of sparkline depending on widget size * Process List - memory total goes above 100% -* general - - command line option to set polling interval for CPU and mem - - command line option to only show processes, CPU, and mem +* Graphs - zooming in and out of graphs + - command line option to set polling interval for CPU and mem +* general + - command line option to only show processes, CPU, and mem - gopsutil cross-compiling issue on linux_amd64 for darwin_i386 -* code cleanup - - more comments +* try to get the drawille fork merged upstream +* termui code cleanup - termui buffers should ignore characters set outside the widget area - ignore writes or give an error? - termui Blocks should be indexed at 0, and maybe change their X and Y variables too - - try to get the drawille fork merged upstream - draw borders and label after widget contents - only merge buffers that are within the original's area diff --git a/colorschemes/template.go b/colorschemes/template.go index 8d65a70..f5a84bb 100644 --- a/colorschemes/template.go +++ b/colorschemes/template.go @@ -5,7 +5,7 @@ package colorschemes -1 = clear - You can combine a color with 'Bold', 'Underline', or 'Reverse' by using bitwise OR ('|'). + You can combine a color with 'Bold', 'Underline', or 'Reverse' by using bitwise OR ('|') and the name of the attribute. For example, to get Bold red Labels, you would do 'Labels: 2 | Bold'. Once you've created a colorscheme, add an entry for it in the `handleColorscheme` function @@ -29,7 +29,7 @@ type Colorscheme struct { BorderLabel int BorderLine int - // Try to add at least 8 + // should add at least 8 here CPULines []int MainMem int @@ -41,7 +41,7 @@ type Colorscheme struct { DiskBar int - // Temperature colors depending on if it's over a certain threshold + // colors the temperature number a different color if it's over a certain threshold TempLow int TempHigh int } diff --git a/gotop.go b/gotop.go index e498d96..de9ddca 100644 --- a/gotop.go +++ b/gotop.go @@ -16,13 +16,10 @@ import ( const VERSION = "1.0.1" var ( - // for when terminal is resized - resized = make(chan bool, 1) + termResized = make(chan bool, 1) - // for when help menu is toggled helpToggled = make(chan bool, 1) - // whether help menu is toggled - helpStatus = false + helpVisible = false // proc widget takes longer to load, wait to render until it loads data procLoaded = make(chan bool, 1) @@ -41,7 +38,6 @@ var ( help *w.HelpMenu ) -// Sets up docopt which is a command line argument parser func cliArguments() { usage := ` Usage: gotop [options] @@ -101,13 +97,13 @@ func keyBinds() { // toggles help menu ui.On("?", func(e ui.Event) { helpToggled <- true - helpStatus = !helpStatus + helpVisible = !helpVisible }) // hides help menu ui.On("", func(e ui.Event) { - if helpStatus { + if helpVisible { helpToggled <- true - helpStatus = false + helpVisible = false } }) } @@ -126,11 +122,9 @@ func termuiColors() { } func widgetColors() { - // memory widget colors mem.LineColor["Main"] = ui.Color(colorscheme.MainMem) mem.LineColor["Swap"] = ui.Color(colorscheme.SwapMem) - // cpu widget colors LineColor := make(map[string]ui.Color) for i := 0; i < len(cpu.Data); i++ { LineColor[fmt.Sprintf("CPU%d", i+1)] = ui.Color(colorscheme.CPULines[i]) @@ -158,7 +152,6 @@ func main() { widgetColors() - // blocks till loaded <-procLoaded // inits termui @@ -180,43 +173,43 @@ func main() { help.XOffset = (ui.Body.Width - help.X) / 2 help.YOffset = (ui.Body.Height - help.Y) / 2 - resized <- true + termResized <- true }) - // All rendering done here + // all rendering done here go func() { ui.Render(ui.Body) drawTick := time.NewTicker(time.Second) for { select { case <-helpToggled: - if helpStatus { + if helpVisible { ui.Clear() ui.Render(help) } else { ui.Render(ui.Body) } - case <-resized: - if !helpStatus { + case <-termResized: + if !helpVisible { ui.Clear() ui.Render(ui.Body) - } else if helpStatus { + } else if helpVisible { ui.Clear() ui.Render(help) } case <-keyPressed: - if !helpStatus { + if !helpVisible { ui.Render(proc) } case <-drawTick.C: - if !helpStatus { + if !helpVisible { ui.Render(ui.Body) } } } }() - // handles kill signal + // handles os kill signal c := make(chan os.Signal, 2) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { diff --git a/termui/block.go b/termui/block.go index cc3a15c..5d1ab8e 100644 --- a/termui/block.go +++ b/termui/block.go @@ -77,10 +77,12 @@ func (b *Block) SetGrid(c0, r0, c1, r1 int) { b.Grid = image.Rect(c0, r0, c1, r1) } +// GetXOffset implements Bufferer interface. func (b *Block) GetXOffset() int { return b.XOffset } +// GetYOffset implements Bufferer interface. func (b *Block) GetYOffset() int { return b.YOffset } diff --git a/termui/buffer.go b/termui/buffer.go index bab7e30..47c234c 100644 --- a/termui/buffer.go +++ b/termui/buffer.go @@ -17,14 +17,17 @@ type Buffer struct { CellMap map[image.Point]Cell } +// NewCell returne a new Cell given all necessary fields. func NewCell(ch rune, Fg, Bg Color) Cell { return Cell{ch, Fg, Bg} } +// NewBuffer returns a new empty Buffer. func NewBuffer() *Buffer { return &Buffer{ CellMap: make(map[image.Point]Cell), - Area: image.Rectangle{}} + Area: image.Rectangle{}, + } } // NewFilledBuffer returns a new Buffer filled with the given Cell. @@ -32,16 +35,11 @@ func NewFilledBuffer(x0, y0, x1, y1 int, c Cell) *Buffer { buf := NewBuffer() buf.Area.Min = image.Pt(x0, y0) buf.Area.Max = image.Pt(x1, y1) - - for x := buf.Area.Min.X; x < buf.Area.Max.X; x++ { - for y := buf.Area.Min.Y; y < buf.Area.Max.Y; y++ { - buf.SetCell(x, y, c) - } - } + buf.Fill(c) return buf } -// Set assigns a Cell to (x,y). +// SetCell assigns a Cell to (x,y). func (b *Buffer) SetCell(x, y int, c Cell) { b.CellMap[image.Pt(x, y)] = c } @@ -82,7 +80,7 @@ func (b *Buffer) Merge(bs ...*Buffer) { } } -// MergeWithOffset merges the given buffer at a certain position on the given buffer. +// MergeWithOffset merges a Buffer onto another with an offset. func (b *Buffer) MergeWithOffset(buf *Buffer, xOffset, yOffset int) { for p, c := range buf.CellMap { b.SetCell(p.X+xOffset, p.Y+yOffset, c) diff --git a/termui/colors.go b/termui/colors.go index f1de7bf..3af2469 100644 --- a/termui/colors.go +++ b/termui/colors.go @@ -1,21 +1,22 @@ package termui -// Color is an integer in the range -1 to 255 +// Color is an integer in the range -1 to 255. type Color int // ColorDefault = clear const ColorDefault = -1 -// Copied from termbox +// Copied from termbox. Attributes that can be bitwise OR'ed with a color. const ( AttrBold Color = 1 << (iota + 9) AttrUnderline AttrReverse ) -// Theme is assigned to the current theme +// Theme is assigned to the current theme. var Theme = DefaultTheme +// DefaultTheme implements a generic set of colors to use by default. var DefaultTheme = Colorscheme{ Fg: 7, Bg: -1, diff --git a/termui/events.go b/termui/events.go index 30c6581..8849101 100644 --- a/termui/events.go +++ b/termui/events.go @@ -20,7 +20,7 @@ type EventStream struct { eventQueue chan tb.Event // list of events from termbox } -// Event includes only the termbox.Event attributes we need. +// Event is a copy of termbox.Event that only contains the fields we need. type Event struct { Key string Width int @@ -76,7 +76,7 @@ func Loop() { } } -// StopLoop stops the events Loop +// StopLoop stops the event loop. func StopLoop() { eventStream.stopLoop <- true } @@ -156,21 +156,27 @@ func convertTermboxMouseValue(e tb.Event) string { return "" } -// convertTermboxEvent turns a termbox event into a termui event +// convertTermboxEvent turns a termbox event into a termui event. func convertTermboxEvent(e tb.Event) Event { - ne := Event{} // new event + var ne Event switch e.Type { case tb.EventKey: - ne.Key = convertTermboxKeyValue(e) + ne = Event{ + Key: convertTermboxKeyValue(e), + } case tb.EventMouse: - ne.Key = convertTermboxMouseValue(e) - ne.MouseX = e.MouseX - ne.MouseY = e.MouseY + ne = Event{ + Key: convertTermboxMouseValue(e), + MouseX: e.MouseX, + MouseY: e.MouseY, + } case tb.EventResize: - ne.Key = "resize" - ne.Width = e.Width - ne.Height = e.Height + ne = Event{ + Key: "resize", + Width: e.Width, + Height: e.Height, + } } return ne diff --git a/termui/gauge.go b/termui/gauge.go index 5667686..1896871 100644 --- a/termui/gauge.go +++ b/termui/gauge.go @@ -35,10 +35,8 @@ func (g *Gauge) Buffer() *Buffer { // plot percentage s := strconv.Itoa(g.Percent) + "%" + g.Description s = MaxString(s, g.X) - y := (g.Y + 1) / 2 x := ((g.X - len(s)) + 1) / 2 - for i, char := range s { bg := g.Bg fg := g.Fg diff --git a/termui/grid.go b/termui/grid.go index c8321a2..a005234 100644 --- a/termui/grid.go +++ b/termui/grid.go @@ -19,11 +19,12 @@ type Grid struct { Rows int } +// NewGrid creates an empty Grid. func NewGrid() *Grid { return &Grid{} } -// Set takes a widget along with it's grid dimensions to be controlled by the grid. +// Set assigns a widget and its grid dimensions to Grid. func (g *Grid) Set(x0, y0, x1, y1 int, widget GridBufferer) { if widget == nil { return @@ -38,14 +39,14 @@ func (g *Grid) Set(x0, y0, x1, y1 int, widget GridBufferer) { g.Widgets = append(g.Widgets, widget) } -// Resize resizes each widget in the grid's control. +// Resize resizes each widget in the grid. func (g *Grid) Resize() { for _, w := range g.Widgets { w.Resize(g.Width, g.Height, g.Cols, g.Rows) } } -// Buffer implements Bufferer interface and merges each widget into one buffer. +// Buffer implements the Bufferer interface by merging each widget in Grid into one buffer. func (g *Grid) Buffer() *Buffer { buf := NewFilledBuffer(0, 0, g.Width, g.Height, Cell{' ', ColorDefault, Theme.Bg}) for _, w := range g.Widgets { @@ -54,10 +55,12 @@ func (g *Grid) Buffer() *Buffer { return buf } +// GetXOffset implements Bufferer interface. func (g *Grid) GetXOffset() int { return 0 } +// GetYOffset implements Bufferer interface. func (g *Grid) GetYOffset() int { return 0 } diff --git a/termui/linegraph.go b/termui/linegraph.go index 1aebeb3..4b01691 100644 --- a/termui/linegraph.go +++ b/termui/linegraph.go @@ -7,7 +7,7 @@ import ( drawille "github.com/cjbassi/drawille-go" ) -// LineGraph implements a graph of data points. +// LineGraph implements a line graph of data points. type LineGraph struct { *Block Data map[string][]float64 @@ -27,17 +27,19 @@ func NewLineGraph() *LineGraph { } } -// renderPoints plots and interpolates data points. +// Buffer implements Bufferer interface. func (lc *LineGraph) Buffer() *Buffer { buf := lc.Block.Buffer() + // we render each data point on to the canvas then copy over the braille to the buffer at the end + // fyi braille characters have 2x4 dots for each character c := drawille.NewCanvas() - // used to keep track of colors but not write them to the buffer until the end + // used to keep track of the braille colors until the end when we render the braille to the buffer colors := make([][]Color, lc.X+2) for i := range colors { colors[i] = make([]Color, lc.Y+2) } - // Sort the series so that overlapping data will overlap the same way each time + // sort the series so that overlapping data will overlap the same way each time seriesList := make([]string, len(lc.Data)) i := 0 for seriesName := range lc.Data { @@ -46,7 +48,7 @@ func (lc *LineGraph) Buffer() *Buffer { } sort.Strings(seriesList) - // draw lines in reverse order so the first one is on top + // draw lines in reverse order so that the first color defined in the colorscheme is on top for i := len(seriesList) - 1; i >= 0; i-- { seriesName := seriesList[i] seriesData := lc.Data[seriesName] @@ -61,13 +63,12 @@ func (lc *LineGraph) Buffer() *Buffer { for i := len(seriesData) - 1; i >= 0; i-- { x := ((lc.X + 1) * 2) - 1 - (((len(seriesData) - 1) - i) * 5) y := ((lc.Y + 1) * 4) - 1 - int((float64((lc.Y)*4)-1)*(seriesData[i]/100)) - // stop rendering at the left-most wall - if x < 0 { + if x < 0 { // stop rendering at the left-most wall break } if lastY == -1 { // if this is the first point c.Set(x, y) - colors[x/2][y/4] = seriesLineColor // divide by 2 and 4 due to 2x4 dots in braille characters + colors[x/2][y/4] = seriesLineColor } else { c.DrawLine(lastX, lastY, x, y) for _, p := range drawille.Line(lastX, lastY, x, y) { @@ -91,14 +92,16 @@ func (lc *LineGraph) Buffer() *Buffer { } } + // renders key ontop for j, seriesName := range seriesList { + // sorts lines again seriesData := lc.Data[seriesName] seriesLineColor, ok := lc.LineColor[seriesName] if !ok { seriesLineColor = lc.DefaultLineColor } - // Render key ontop, but let braille be drawn between words + // render key ontop, but let braille be drawn over space characters str := fmt.Sprintf("%s %3.0f%%", seriesName, seriesData[len(seriesData)-1]) for k, char := range str { if char != ' ' { diff --git a/termui/sparkline.go b/termui/sparkline.go index 5de952f..6beeb8d 100644 --- a/termui/sparkline.go +++ b/termui/sparkline.go @@ -17,12 +17,12 @@ type Sparklines struct { Lines []*Sparkline } -// Add appends a given Sparkline to s *Sparklines. +// Add appends a given Sparkline to the *Sparklines. func (s *Sparklines) Add(sl Sparkline) { s.Lines = append(s.Lines, &sl) } -// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines. +// NewSparkline returns an unrenderable single sparkline that intended to be added into a Sparklines. func NewSparkline() *Sparkline { return &Sparkline{ TitleColor: Theme.Fg, @@ -30,7 +30,7 @@ func NewSparkline() *Sparkline { } } -// NewSparklines return a new *Sparklines with given Sparkline(s), you can always add a new Sparkline later. +// NewSparklines return a new *Sparklines with given Sparklines, you can always add a new Sparkline later. func NewSparklines(ss ...*Sparkline) *Sparklines { return &Sparklines{ Block: NewBlock(), @@ -44,19 +44,18 @@ func (sl *Sparklines) Buffer() *Buffer { lc := len(sl.Lines) // lineCount - // for each line + // renders each sparkline and its titles for i, line := range sl.Lines { + // prints titles title1Y := 2 + (sl.Y/lc)*i title2Y := (2 + (sl.Y/lc)*i) + 1 - title1 := MaxString(line.Title1, sl.X) title2 := MaxString(line.Title2, sl.X) buf.SetString(1, title1Y, title1, line.TitleColor|AttrBold, sl.Bg) buf.SetString(1, title2Y, title2, line.TitleColor|AttrBold, sl.Bg) sparkY := (sl.Y / lc) * (i + 1) - // finds max data in current view used for relative heights max := 1 for i := len(line.Data) - 1; i >= 0 && sl.X-((len(line.Data)-1)-i) >= 1; i-- { diff --git a/termui/table.go b/termui/table.go index c5d8909..7e377d2 100644 --- a/termui/table.go +++ b/termui/table.go @@ -10,7 +10,7 @@ type Table struct { Header []string Rows [][]string ColWidths []int - Cp []int // column position + CellXPos []int // column position Gap int // gap between columns Cursor Color UniqueCol int // the column used to identify the selected item @@ -34,6 +34,7 @@ func NewTable() *Table { } // ColResize is the default column resizer, but can be overriden. +// ColResize calculates the width of each column. func (t *Table) ColResize() { // calculate gap size based on total width t.Gap = 3 @@ -46,7 +47,7 @@ func (t *Table) ColResize() { cur := 0 for _, w := range t.ColWidths { cur += t.Gap - t.Cp = append(t.Cp, cur) + t.CellXPos = append(t.CellXPos, cur) cur += w } } @@ -55,21 +56,20 @@ func (t *Table) ColResize() { func (t *Table) Buffer() *Buffer { buf := t.Block.Buffer() - // makes sure there isn't a gap at the bottom of the table view + // removes gap at the bottom of the current view if there is one if t.TopRow > len(t.Rows)-(t.Y-1) { t.TopRow = len(t.Rows) - (t.Y - 1) } t.ColResizer() - // print header - // for i := 0; i < render; i++ { + // prints header for i, width := range t.ColWidths { if width == 0 { break } r := MaxString(t.Header[i], t.X-6) - buf.SetString(t.Cp[i], 1, r, t.Fg|AttrBold, t.Bg) + buf.SetString(t.CellXPos[i], 1, r, t.Fg|AttrBold, t.Bg) } // prints each row @@ -77,7 +77,7 @@ func (t *Table) Buffer() *Buffer { row := t.Rows[rowNum] y := (rowNum + 2) - t.TopRow - // cursor + // prints cursor bg := t.Bg if (t.SelectedItem == "" && rowNum == t.SelectedRow) || (t.SelectedItem != "" && t.SelectedItem == row[t.UniqueCol]) { bg = t.Cursor @@ -97,7 +97,7 @@ func (t *Table) Buffer() *Buffer { break } r := MaxString(row[i], t.X-6) - buf.SetString(t.Cp[i], y, r, t.Fg, bg) + buf.SetString(t.CellXPos[i], y, r, t.Fg, bg) } } @@ -108,7 +108,7 @@ func (t *Table) Buffer() *Buffer { // Cursor Movement // ///////////////////////////////////////////////////////////////////////////////// -// calcPos is used to calculate the cursor position and where in the process list we are located. +// calcPos is used to calculate the cursor position and the current view. func (t *Table) calcPos() { t.SelectedItem = "" @@ -147,6 +147,8 @@ func (t *Table) Bottom() { t.calcPos() } +// The number of lines in a page is equal to the height of the widget. + func (t *Table) HalfPageUp() { t.SelectedRow = t.SelectedRow - (t.Y-2)/2 t.calcPos() diff --git a/widgets/cpu.go b/widgets/cpu.go index 95cc829..cc98bf8 100644 --- a/widgets/cpu.go +++ b/widgets/cpu.go @@ -5,7 +5,7 @@ import ( "time" ui "github.com/cjbassi/gotop/termui" - cpu "github.com/shirou/gopsutil/cpu" + psCPU "github.com/shirou/gopsutil/cpu" ) type CPU struct { @@ -15,8 +15,12 @@ type CPU struct { } func NewCPU() *CPU { - count, _ := cpu.Counts(false) - c := &CPU{ui.NewLineGraph(), count, time.Second} + count, _ := psCPU.Counts(false) + c := &CPU{ + LineGraph: ui.NewLineGraph(), + count: count, + interval: time.Second, + } c.Label = "CPU Usage" for i := 0; i < c.count; i++ { key := "CPU" + strconv.Itoa(i+1) @@ -37,7 +41,7 @@ func NewCPU() *CPU { func (c *CPU) update() { // psutil calculates the CPU usage over a 1 second interval, therefore it blocks for 1 second // `true` makes it so psutil doesn't group CPU usage percentages - percent, _ := cpu.Percent(time.Second, true) + percent, _ := psCPU.Percent(time.Second, true) for i := 0; i < c.count; i++ { key := "CPU" + strconv.Itoa(i+1) c.Data[key] = append(c.Data[key], percent[i]) diff --git a/widgets/disk.go b/widgets/disk.go index 000c936..cab0135 100644 --- a/widgets/disk.go +++ b/widgets/disk.go @@ -6,7 +6,7 @@ import ( ui "github.com/cjbassi/gotop/termui" "github.com/cjbassi/gotop/utils" - disk "github.com/shirou/gopsutil/disk" + psDisk "github.com/shirou/gopsutil/disk" ) type Disk struct { @@ -16,8 +16,11 @@ type Disk struct { } func NewDisk() *Disk { - // get root filesystem usage - d := &Disk{ui.NewGauge(), "/", time.Second * 5} + d := &Disk{ + Gauge: ui.NewGauge(), + fs: "/", + interval: time.Second * 5, + } d.Label = "Disk Usage" go d.update() @@ -32,7 +35,7 @@ func NewDisk() *Disk { } func (d *Disk) update() { - disk, _ := disk.Usage(d.fs) - d.Percent = int(disk.UsedPercent) - d.Description = fmt.Sprintf(" (%dGB free)", int(utils.BytesToGB(disk.Free))) + usage, _ := psDisk.Usage(d.fs) + d.Percent = int(usage.UsedPercent) + d.Description = fmt.Sprintf(" (%dGB free)", int(utils.BytesToGB(usage.Free))) } diff --git a/widgets/help.go b/widgets/help.go index 1a8a569..6c0f592 100644 --- a/widgets/help.go +++ b/widgets/help.go @@ -30,8 +30,8 @@ type HelpMenu struct { func NewHelpMenu() *HelpMenu { block := ui.NewBlock() - block.X = 48 // width - block.Y = 15 // height + block.X = 48 // width - 1 + block.Y = 15 // height - 1 block.XOffset = (ui.Body.Width - block.X) / 2 // X coordinate block.YOffset = (ui.Body.Height - block.Y) / 2 // Y coordinate return &HelpMenu{block} diff --git a/widgets/mem.go b/widgets/mem.go index 75b39c7..06a885c 100644 --- a/widgets/mem.go +++ b/widgets/mem.go @@ -4,7 +4,7 @@ import ( "time" ui "github.com/cjbassi/gotop/termui" - mem "github.com/shirou/gopsutil/mem" + psMem "github.com/shirou/gopsutil/mem" ) type Mem struct { @@ -13,7 +13,10 @@ type Mem struct { } func NewMem() *Mem { - m := &Mem{ui.NewLineGraph(), time.Second} + m := &Mem{ + LineGraph: ui.NewLineGraph(), + interval: time.Second, + } m.Label = "Memory Usage" m.Data["Main"] = []float64{0} m.Data["Swap"] = []float64{0} @@ -30,8 +33,8 @@ func NewMem() *Mem { } func (m *Mem) update() { - main, _ := mem.VirtualMemory() - swap, _ := mem.SwapMemory() + main, _ := psMem.VirtualMemory() + swap, _ := psMem.SwapMemory() m.Data["Main"] = append(m.Data["Main"], main.UsedPercent) m.Data["Swap"] = append(m.Data["Swap"], swap.UsedPercent) } diff --git a/widgets/net.go b/widgets/net.go index 7417d99..d0fa097 100644 --- a/widgets/net.go +++ b/widgets/net.go @@ -6,7 +6,7 @@ import ( ui "github.com/cjbassi/gotop/termui" "github.com/cjbassi/gotop/utils" - net "github.com/shirou/gopsutil/net" + psNet "github.com/shirou/gopsutil/net" ) type Net struct { @@ -25,7 +25,10 @@ func NewNet() *Net { sent.Data = []int{0} spark := ui.NewSparklines(recv, sent) - n := &Net{spark, time.Second, 0, 0} + n := &Net{ + Sparklines: spark, + interval: time.Second, + } n.Label = "Network Usage" go n.update() @@ -41,56 +44,55 @@ func NewNet() *Net { func (n *Net) update() { // `false` causes psutil to group all network activity - interfaces, _ := net.IOCounters(false) - recv := interfaces[0].BytesRecv - sent := interfaces[0].BytesSent + interfaces, _ := psNet.IOCounters(false) + recvTotal := interfaces[0].BytesRecv + sentTotal := interfaces[0].BytesSent if n.recvTotal != 0 { // if this isn't the first update - curRecv := recv - n.recvTotal - curSent := sent - n.sentTotal + recvRecent := recvTotal - n.recvTotal + sentRecent := sentTotal - n.sentTotal - n.Lines[0].Data = append(n.Lines[0].Data, int(curRecv)) - n.Lines[1].Data = append(n.Lines[1].Data, int(curSent)) + n.Lines[0].Data = append(n.Lines[0].Data, int(recvRecent)) + n.Lines[1].Data = append(n.Lines[1].Data, int(sentRecent)) } - // used for later calls to update - n.recvTotal = recv - n.sentTotal = sent + // used in later calls to update + n.recvTotal = recvTotal + n.sentTotal = sentTotal // renders net widget titles for i := 0; i < 2; i++ { var method string // either 'Rx' or 'Tx' - var total uint64 - cur := n.Lines[i].Data[len(n.Lines[i].Data)-1] - totalUnit := "B" - curUnit := "B" + var total float64 + recent := n.Lines[i].Data[len(n.Lines[i].Data)-1] + unitTotal := "B" + unitRecent := "B" if i == 0 { - total = recv + total = float64(recvTotal) method = "Rx" } else { - total = sent + total = float64(sentTotal) method = "Tx" } - if cur >= 1000000 { - cur = int(utils.BytesToMB(uint64(cur))) - curUnit = "MB" - } else if cur >= 1000 { - cur = int(utils.BytesToKB(uint64(cur))) - curUnit = "kB" + if recent >= 1000000 { + recent = int(utils.BytesToMB(uint64(recent))) + unitRecent = "MB" + } else if recent >= 1000 { + recent = int(utils.BytesToKB(uint64(recent))) + unitRecent = "kB" } - var totalCvrt float64 if total >= 1000000000 { - totalCvrt = utils.BytesToGB(total) - totalUnit = "GB" + total = utils.BytesToGB(uint64(total)) + unitTotal = "GB" } else if total >= 1000000 { - totalCvrt = utils.BytesToMB(total) - totalUnit = "MB" + total = utils.BytesToMB(uint64(total)) + unitTotal = "MB" } - n.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", method, totalCvrt, totalUnit) - n.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9d %2s/s", method, cur, curUnit) + n.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", method, total, unitTotal) + n.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9d %2s/s", method, recent, unitRecent) } } diff --git a/widgets/proc.go b/widgets/proc.go index d47f69f..7f8d583 100644 --- a/widgets/proc.go +++ b/widgets/proc.go @@ -7,13 +7,13 @@ import ( "time" ui "github.com/cjbassi/gotop/termui" - cpu "github.com/shirou/gopsutil/cpu" - proc "github.com/shirou/gopsutil/process" + psCPU "github.com/shirou/gopsutil/cpu" + psProc "github.com/shirou/gopsutil/process" ) const ( - DOWN = "▼" UP = "▲" + DOWN = "▼" ) // Process represents each process. @@ -24,7 +24,6 @@ type Process struct { Mem float32 } -// Proc widget. type Proc struct { *ui.Table cpuCount int @@ -36,9 +35,8 @@ type Proc struct { KeyPressed chan bool } -// NewProc creates a new Proc widget. func NewProc(loaded, keyPressed chan bool) *Proc { - cpuCount, _ := cpu.Counts(false) + cpuCount, _ := psCPU.Counts(false) p := &Proc{ Table: ui.NewTable(), interval: time.Second, @@ -73,15 +71,14 @@ func NewProc(loaded, keyPressed chan bool) *Proc { return p } -// update updates proc widget. func (p *Proc) update() { - psProcs, _ := proc.Processes() - processes := make([]Process, len(psProcs)) - for i, pr := range psProcs { - pid := pr.Pid - command, _ := pr.Name() - cpu, _ := pr.CPUPercent() - mem, _ := pr.MemoryPercent() + psProcesses, _ := psProc.Processes() + processes := make([]Process, len(psProcesses)) + for i, psProcess := range psProcesses { + pid := psProcess.Pid + command, _ := psProcess.Name() + cpu, _ := psProcess.CPUPercent() + mem, _ := psProcess.MemoryPercent() processes[i] = Process{ pid, @@ -139,21 +136,21 @@ func (p *Proc) ColResize() { p.Gap = 2 } - p.Cp = []int{ + p.CellXPos = []int{ p.Gap, p.Gap + p.ColWidths[0] + p.Gap, p.X - p.Gap - p.ColWidths[3] - p.Gap - p.ColWidths[2], p.X - p.Gap - p.ColWidths[3], } - contentWidth := p.Gap + p.ColWidths[0] + p.Gap + p.ColWidths[1] + p.Gap + p.ColWidths[2] + p.Gap + p.ColWidths[3] + p.Gap + rowWidth := p.Gap + p.ColWidths[0] + p.Gap + p.ColWidths[1] + p.Gap + p.ColWidths[2] + p.Gap + p.ColWidths[3] + p.Gap // only renders a column if it fits - if p.X < (contentWidth - p.Gap - p.ColWidths[3]) { + if p.X < (rowWidth - p.Gap - p.ColWidths[3]) { p.ColWidths[2] = 0 p.ColWidths[3] = 0 - } else if p.X < contentWidth { - p.Cp[2] = p.Cp[3] + } else if p.X < rowWidth { + p.CellXPos[2] = p.CellXPos[3] p.ColWidths[3] = 0 } } @@ -238,33 +235,33 @@ func (p *Proc) keyBinds() { // The first field changes from PID to count. // CPU and Mem are added together for each Process. func Group(P []Process) []Process { - groupMap := make(map[string]Process) - for _, p := range P { - val, ok := groupMap[p.Command] + groupedP := make(map[string]Process) + for _, process := range P { + val, ok := groupedP[process.Command] if ok { - newP := Process{ + groupedP[process.Command] = Process{ val.PID + 1, val.Command, - val.CPU + p.CPU, - val.Mem + p.Mem, + val.CPU + process.CPU, + val.Mem + process.Mem, } - groupMap[p.Command] = newP } else { - newP := Process{ + groupedP[process.Command] = Process{ 1, - p.Command, - p.CPU, - p.Mem, + process.Command, + process.CPU, + process.Mem, } - groupMap[p.Command] = newP } } - groupList := make([]Process, len(groupMap)) - i := 0 - for _, val := range groupMap { + + groupList := make([]Process, len(groupedP)) + var i int + for _, val := range groupedP { groupList[i] = val i++ } + return groupList } diff --git a/widgets/temp.go b/widgets/temp.go index d5bf520..c630613 100644 --- a/widgets/temp.go +++ b/widgets/temp.go @@ -5,28 +5,29 @@ package widgets import ( "fmt" + "sort" "strings" "time" ui "github.com/cjbassi/gotop/termui" - ps "github.com/shirou/gopsutil/host" + psHost "github.com/shirou/gopsutil/host" ) type Temp struct { *ui.Block - interval time.Duration - Data []int - DataLabels []string - Threshold int - TempLow ui.Color - TempHigh ui.Color + interval time.Duration + Data map[string]int + Threshold int + TempLow ui.Color + TempHigh ui.Color } func NewTemp() *Temp { t := &Temp{ Block: ui.NewBlock(), interval: time.Second * 5, - Threshold: 80, // temp at which color should change to red + Data: make(map[string]int), + Threshold: 80, // temp at which color should change } t.Label = "Temperatures" @@ -42,38 +43,41 @@ func NewTemp() *Temp { } func (t *Temp) update() { - sensors, _ := ps.SensorsTemperatures() - temps := []int{} - labels := []string{} - for _, temp := range sensors { + sensors, _ := psHost.SensorsTemperatures() + for _, sensor := range sensors { // only sensors with input in their name are giving us live temp info - if strings.Contains(temp.SensorKey, "input") { - temps = append(temps, int(temp.Temperature)) + if strings.Contains(sensor.SensorKey, "input") { // removes '_input' from the end of the sensor name - labels = append(labels, temp.SensorKey[:strings.Index(temp.SensorKey, "_input")]) + label := sensor.SensorKey[:strings.Index(sensor.SensorKey, "_input")] + t.Data[label] = int(sensor.Temperature) } } - t.Data = temps - t.DataLabels = labels } // Buffer implements ui.Bufferer interface. func (t *Temp) Buffer() *ui.Buffer { buf := t.Block.Buffer() - for y, text := range t.DataLabels { + var keys []string + for k := range t.Data { + keys = append(keys, k) + } + sort.Strings(keys) + + for y, key := range keys { if y+1 > t.Y { break } fg := t.TempLow - if t.Data[y] >= t.Threshold { + if t.Data[key] >= t.Threshold { fg = t.TempHigh } - s := ui.MaxString(text, (t.X - 4)) + s := ui.MaxString(key, (t.X - 4)) buf.SetString(1, y+1, s, t.Fg, t.Bg) - buf.SetString(t.X-2, y+1, fmt.Sprintf("%dC", t.Data[y]), fg, t.Bg) + buf.SetString(t.X-2, y+1, fmt.Sprintf("%dC", t.Data[key]), fg, t.Bg) + } return buf