Code cleanup
This commit is contained in:
parent
e79f0e0a83
commit
47a98de44b
12
README.md
12
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
|
- increase height of sparkline depending on widget size
|
||||||
* Process List
|
* Process List
|
||||||
- memory total goes above 100%
|
- memory total goes above 100%
|
||||||
* general
|
* Graphs
|
||||||
- command line option to set polling interval for CPU and mem
|
|
||||||
- command line option to only show processes, CPU, and mem
|
|
||||||
- zooming in and out of 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
|
- gopsutil cross-compiling issue on linux_amd64 for darwin_i386
|
||||||
* code cleanup
|
* try to get the drawille fork merged upstream
|
||||||
- more comments
|
* termui code cleanup
|
||||||
- termui buffers should ignore characters set outside the widget area
|
- termui buffers should ignore characters set outside the widget area
|
||||||
- ignore writes or give an error?
|
- ignore writes or give an error?
|
||||||
- termui Blocks should be indexed at 0, and maybe change their X and Y variables too
|
- 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
|
- draw borders and label after widget contents
|
||||||
- only merge buffers that are within the original's area
|
- only merge buffers that are within the original's area
|
||||||
|
|
|
@ -5,7 +5,7 @@ package colorschemes
|
||||||
|
|
||||||
-1 = clear
|
-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'.
|
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
|
Once you've created a colorscheme, add an entry for it in the `handleColorscheme` function
|
||||||
|
@ -29,7 +29,7 @@ type Colorscheme struct {
|
||||||
BorderLabel int
|
BorderLabel int
|
||||||
BorderLine int
|
BorderLine int
|
||||||
|
|
||||||
// Try to add at least 8
|
// should add at least 8 here
|
||||||
CPULines []int
|
CPULines []int
|
||||||
|
|
||||||
MainMem int
|
MainMem int
|
||||||
|
@ -41,7 +41,7 @@ type Colorscheme struct {
|
||||||
|
|
||||||
DiskBar int
|
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
|
TempLow int
|
||||||
TempHigh int
|
TempHigh int
|
||||||
}
|
}
|
||||||
|
|
35
gotop.go
35
gotop.go
|
@ -16,13 +16,10 @@ import (
|
||||||
const VERSION = "1.0.1"
|
const VERSION = "1.0.1"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// for when terminal is resized
|
termResized = make(chan bool, 1)
|
||||||
resized = make(chan bool, 1)
|
|
||||||
|
|
||||||
// for when help menu is toggled
|
|
||||||
helpToggled = make(chan bool, 1)
|
helpToggled = make(chan bool, 1)
|
||||||
// whether help menu is toggled
|
helpVisible = false
|
||||||
helpStatus = false
|
|
||||||
|
|
||||||
// proc widget takes longer to load, wait to render until it loads data
|
// proc widget takes longer to load, wait to render until it loads data
|
||||||
procLoaded = make(chan bool, 1)
|
procLoaded = make(chan bool, 1)
|
||||||
|
@ -41,7 +38,6 @@ var (
|
||||||
help *w.HelpMenu
|
help *w.HelpMenu
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sets up docopt which is a command line argument parser
|
|
||||||
func cliArguments() {
|
func cliArguments() {
|
||||||
usage := `
|
usage := `
|
||||||
Usage: gotop [options]
|
Usage: gotop [options]
|
||||||
|
@ -101,13 +97,13 @@ func keyBinds() {
|
||||||
// toggles help menu
|
// toggles help menu
|
||||||
ui.On("?", func(e ui.Event) {
|
ui.On("?", func(e ui.Event) {
|
||||||
helpToggled <- true
|
helpToggled <- true
|
||||||
helpStatus = !helpStatus
|
helpVisible = !helpVisible
|
||||||
})
|
})
|
||||||
// hides help menu
|
// hides help menu
|
||||||
ui.On("<escape>", func(e ui.Event) {
|
ui.On("<escape>", func(e ui.Event) {
|
||||||
if helpStatus {
|
if helpVisible {
|
||||||
helpToggled <- true
|
helpToggled <- true
|
||||||
helpStatus = false
|
helpVisible = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -126,11 +122,9 @@ func termuiColors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func widgetColors() {
|
func widgetColors() {
|
||||||
// memory widget colors
|
|
||||||
mem.LineColor["Main"] = ui.Color(colorscheme.MainMem)
|
mem.LineColor["Main"] = ui.Color(colorscheme.MainMem)
|
||||||
mem.LineColor["Swap"] = ui.Color(colorscheme.SwapMem)
|
mem.LineColor["Swap"] = ui.Color(colorscheme.SwapMem)
|
||||||
|
|
||||||
// cpu widget colors
|
|
||||||
LineColor := make(map[string]ui.Color)
|
LineColor := make(map[string]ui.Color)
|
||||||
for i := 0; i < len(cpu.Data); i++ {
|
for i := 0; i < len(cpu.Data); i++ {
|
||||||
LineColor[fmt.Sprintf("CPU%d", i+1)] = ui.Color(colorscheme.CPULines[i])
|
LineColor[fmt.Sprintf("CPU%d", i+1)] = ui.Color(colorscheme.CPULines[i])
|
||||||
|
@ -158,7 +152,6 @@ func main() {
|
||||||
|
|
||||||
widgetColors()
|
widgetColors()
|
||||||
|
|
||||||
// blocks till loaded
|
|
||||||
<-procLoaded
|
<-procLoaded
|
||||||
|
|
||||||
// inits termui
|
// inits termui
|
||||||
|
@ -180,43 +173,43 @@ func main() {
|
||||||
help.XOffset = (ui.Body.Width - help.X) / 2
|
help.XOffset = (ui.Body.Width - help.X) / 2
|
||||||
help.YOffset = (ui.Body.Height - help.Y) / 2
|
help.YOffset = (ui.Body.Height - help.Y) / 2
|
||||||
|
|
||||||
resized <- true
|
termResized <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
// All rendering done here
|
// all rendering done here
|
||||||
go func() {
|
go func() {
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
drawTick := time.NewTicker(time.Second)
|
drawTick := time.NewTicker(time.Second)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-helpToggled:
|
case <-helpToggled:
|
||||||
if helpStatus {
|
if helpVisible {
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(help)
|
ui.Render(help)
|
||||||
} else {
|
} else {
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
}
|
}
|
||||||
case <-resized:
|
case <-termResized:
|
||||||
if !helpStatus {
|
if !helpVisible {
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
} else if helpStatus {
|
} else if helpVisible {
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(help)
|
ui.Render(help)
|
||||||
}
|
}
|
||||||
case <-keyPressed:
|
case <-keyPressed:
|
||||||
if !helpStatus {
|
if !helpVisible {
|
||||||
ui.Render(proc)
|
ui.Render(proc)
|
||||||
}
|
}
|
||||||
case <-drawTick.C:
|
case <-drawTick.C:
|
||||||
if !helpStatus {
|
if !helpVisible {
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// handles kill signal
|
// handles os kill signal
|
||||||
c := make(chan os.Signal, 2)
|
c := make(chan os.Signal, 2)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -77,10 +77,12 @@ func (b *Block) SetGrid(c0, r0, c1, r1 int) {
|
||||||
b.Grid = image.Rect(c0, r0, c1, r1)
|
b.Grid = image.Rect(c0, r0, c1, r1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetXOffset implements Bufferer interface.
|
||||||
func (b *Block) GetXOffset() int {
|
func (b *Block) GetXOffset() int {
|
||||||
return b.XOffset
|
return b.XOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetYOffset implements Bufferer interface.
|
||||||
func (b *Block) GetYOffset() int {
|
func (b *Block) GetYOffset() int {
|
||||||
return b.YOffset
|
return b.YOffset
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,17 @@ type Buffer struct {
|
||||||
CellMap map[image.Point]Cell
|
CellMap map[image.Point]Cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCell returne a new Cell given all necessary fields.
|
||||||
func NewCell(ch rune, Fg, Bg Color) Cell {
|
func NewCell(ch rune, Fg, Bg Color) Cell {
|
||||||
return Cell{ch, Fg, Bg}
|
return Cell{ch, Fg, Bg}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBuffer returns a new empty Buffer.
|
||||||
func NewBuffer() *Buffer {
|
func NewBuffer() *Buffer {
|
||||||
return &Buffer{
|
return &Buffer{
|
||||||
CellMap: make(map[image.Point]Cell),
|
CellMap: make(map[image.Point]Cell),
|
||||||
Area: image.Rectangle{}}
|
Area: image.Rectangle{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFilledBuffer returns a new Buffer filled with the given Cell.
|
// 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 := NewBuffer()
|
||||||
buf.Area.Min = image.Pt(x0, y0)
|
buf.Area.Min = image.Pt(x0, y0)
|
||||||
buf.Area.Max = image.Pt(x1, y1)
|
buf.Area.Max = image.Pt(x1, y1)
|
||||||
|
buf.Fill(c)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf
|
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) {
|
func (b *Buffer) SetCell(x, y int, c Cell) {
|
||||||
b.CellMap[image.Pt(x, y)] = c
|
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) {
|
func (b *Buffer) MergeWithOffset(buf *Buffer, xOffset, yOffset int) {
|
||||||
for p, c := range buf.CellMap {
|
for p, c := range buf.CellMap {
|
||||||
b.SetCell(p.X+xOffset, p.Y+yOffset, c)
|
b.SetCell(p.X+xOffset, p.Y+yOffset, c)
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
package termui
|
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
|
type Color int
|
||||||
|
|
||||||
// ColorDefault = clear
|
// ColorDefault = clear
|
||||||
const ColorDefault = -1
|
const ColorDefault = -1
|
||||||
|
|
||||||
// Copied from termbox
|
// Copied from termbox. Attributes that can be bitwise OR'ed with a color.
|
||||||
const (
|
const (
|
||||||
AttrBold Color = 1 << (iota + 9)
|
AttrBold Color = 1 << (iota + 9)
|
||||||
AttrUnderline
|
AttrUnderline
|
||||||
AttrReverse
|
AttrReverse
|
||||||
)
|
)
|
||||||
|
|
||||||
// Theme is assigned to the current theme
|
// Theme is assigned to the current theme.
|
||||||
var Theme = DefaultTheme
|
var Theme = DefaultTheme
|
||||||
|
|
||||||
|
// DefaultTheme implements a generic set of colors to use by default.
|
||||||
var DefaultTheme = Colorscheme{
|
var DefaultTheme = Colorscheme{
|
||||||
Fg: 7,
|
Fg: 7,
|
||||||
Bg: -1,
|
Bg: -1,
|
||||||
|
|
|
@ -20,7 +20,7 @@ type EventStream struct {
|
||||||
eventQueue chan tb.Event // list of events from termbox
|
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 {
|
type Event struct {
|
||||||
Key string
|
Key string
|
||||||
Width int
|
Width int
|
||||||
|
@ -76,7 +76,7 @@ func Loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopLoop stops the events Loop
|
// StopLoop stops the event loop.
|
||||||
func StopLoop() {
|
func StopLoop() {
|
||||||
eventStream.stopLoop <- true
|
eventStream.stopLoop <- true
|
||||||
}
|
}
|
||||||
|
@ -156,21 +156,27 @@ func convertTermboxMouseValue(e tb.Event) string {
|
||||||
return ""
|
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 {
|
func convertTermboxEvent(e tb.Event) Event {
|
||||||
ne := Event{} // new event
|
var ne Event
|
||||||
|
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case tb.EventKey:
|
case tb.EventKey:
|
||||||
ne.Key = convertTermboxKeyValue(e)
|
ne = Event{
|
||||||
|
Key: convertTermboxKeyValue(e),
|
||||||
|
}
|
||||||
case tb.EventMouse:
|
case tb.EventMouse:
|
||||||
ne.Key = convertTermboxMouseValue(e)
|
ne = Event{
|
||||||
ne.MouseX = e.MouseX
|
Key: convertTermboxMouseValue(e),
|
||||||
ne.MouseY = e.MouseY
|
MouseX: e.MouseX,
|
||||||
|
MouseY: e.MouseY,
|
||||||
|
}
|
||||||
case tb.EventResize:
|
case tb.EventResize:
|
||||||
ne.Key = "resize"
|
ne = Event{
|
||||||
ne.Width = e.Width
|
Key: "resize",
|
||||||
ne.Height = e.Height
|
Width: e.Width,
|
||||||
|
Height: e.Height,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ne
|
return ne
|
||||||
|
|
|
@ -35,10 +35,8 @@ func (g *Gauge) Buffer() *Buffer {
|
||||||
// plot percentage
|
// plot percentage
|
||||||
s := strconv.Itoa(g.Percent) + "%" + g.Description
|
s := strconv.Itoa(g.Percent) + "%" + g.Description
|
||||||
s = MaxString(s, g.X)
|
s = MaxString(s, g.X)
|
||||||
|
|
||||||
y := (g.Y + 1) / 2
|
y := (g.Y + 1) / 2
|
||||||
x := ((g.X - len(s)) + 1) / 2
|
x := ((g.X - len(s)) + 1) / 2
|
||||||
|
|
||||||
for i, char := range s {
|
for i, char := range s {
|
||||||
bg := g.Bg
|
bg := g.Bg
|
||||||
fg := g.Fg
|
fg := g.Fg
|
||||||
|
|
|
@ -19,11 +19,12 @@ type Grid struct {
|
||||||
Rows int
|
Rows int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewGrid creates an empty Grid.
|
||||||
func NewGrid() *Grid {
|
func NewGrid() *Grid {
|
||||||
return &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) {
|
func (g *Grid) Set(x0, y0, x1, y1 int, widget GridBufferer) {
|
||||||
if widget == nil {
|
if widget == nil {
|
||||||
return
|
return
|
||||||
|
@ -38,14 +39,14 @@ func (g *Grid) Set(x0, y0, x1, y1 int, widget GridBufferer) {
|
||||||
g.Widgets = append(g.Widgets, widget)
|
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() {
|
func (g *Grid) Resize() {
|
||||||
for _, w := range g.Widgets {
|
for _, w := range g.Widgets {
|
||||||
w.Resize(g.Width, g.Height, g.Cols, g.Rows)
|
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 {
|
func (g *Grid) Buffer() *Buffer {
|
||||||
buf := NewFilledBuffer(0, 0, g.Width, g.Height, Cell{' ', ColorDefault, Theme.Bg})
|
buf := NewFilledBuffer(0, 0, g.Width, g.Height, Cell{' ', ColorDefault, Theme.Bg})
|
||||||
for _, w := range g.Widgets {
|
for _, w := range g.Widgets {
|
||||||
|
@ -54,10 +55,12 @@ func (g *Grid) Buffer() *Buffer {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetXOffset implements Bufferer interface.
|
||||||
func (g *Grid) GetXOffset() int {
|
func (g *Grid) GetXOffset() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetYOffset implements Bufferer interface.
|
||||||
func (g *Grid) GetYOffset() int {
|
func (g *Grid) GetYOffset() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
drawille "github.com/cjbassi/drawille-go"
|
drawille "github.com/cjbassi/drawille-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LineGraph implements a graph of data points.
|
// LineGraph implements a line graph of data points.
|
||||||
type LineGraph struct {
|
type LineGraph struct {
|
||||||
*Block
|
*Block
|
||||||
Data map[string][]float64
|
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 {
|
func (lc *LineGraph) Buffer() *Buffer {
|
||||||
buf := lc.Block.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()
|
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)
|
colors := make([][]Color, lc.X+2)
|
||||||
for i := range colors {
|
for i := range colors {
|
||||||
colors[i] = make([]Color, lc.Y+2)
|
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))
|
seriesList := make([]string, len(lc.Data))
|
||||||
i := 0
|
i := 0
|
||||||
for seriesName := range lc.Data {
|
for seriesName := range lc.Data {
|
||||||
|
@ -46,7 +48,7 @@ func (lc *LineGraph) Buffer() *Buffer {
|
||||||
}
|
}
|
||||||
sort.Strings(seriesList)
|
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-- {
|
for i := len(seriesList) - 1; i >= 0; i-- {
|
||||||
seriesName := seriesList[i]
|
seriesName := seriesList[i]
|
||||||
seriesData := lc.Data[seriesName]
|
seriesData := lc.Data[seriesName]
|
||||||
|
@ -61,13 +63,12 @@ func (lc *LineGraph) Buffer() *Buffer {
|
||||||
for i := len(seriesData) - 1; i >= 0; i-- {
|
for i := len(seriesData) - 1; i >= 0; i-- {
|
||||||
x := ((lc.X + 1) * 2) - 1 - (((len(seriesData) - 1) - i) * 5)
|
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))
|
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 { // stop rendering at the left-most wall
|
||||||
if x < 0 {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if lastY == -1 { // if this is the first point
|
if lastY == -1 { // if this is the first point
|
||||||
c.Set(x, y)
|
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 {
|
} else {
|
||||||
c.DrawLine(lastX, lastY, x, y)
|
c.DrawLine(lastX, lastY, x, y)
|
||||||
for _, p := range drawille.Line(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 {
|
for j, seriesName := range seriesList {
|
||||||
|
// sorts lines again
|
||||||
seriesData := lc.Data[seriesName]
|
seriesData := lc.Data[seriesName]
|
||||||
seriesLineColor, ok := lc.LineColor[seriesName]
|
seriesLineColor, ok := lc.LineColor[seriesName]
|
||||||
if !ok {
|
if !ok {
|
||||||
seriesLineColor = lc.DefaultLineColor
|
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])
|
str := fmt.Sprintf("%s %3.0f%%", seriesName, seriesData[len(seriesData)-1])
|
||||||
for k, char := range str {
|
for k, char := range str {
|
||||||
if char != ' ' {
|
if char != ' ' {
|
||||||
|
|
|
@ -17,12 +17,12 @@ type Sparklines struct {
|
||||||
Lines []*Sparkline
|
Lines []*Sparkline
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add appends a given Sparkline to s *Sparklines.
|
// Add appends a given Sparkline to the *Sparklines.
|
||||||
func (s *Sparklines) Add(sl Sparkline) {
|
func (s *Sparklines) Add(sl Sparkline) {
|
||||||
s.Lines = append(s.Lines, &sl)
|
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 {
|
func NewSparkline() *Sparkline {
|
||||||
return &Sparkline{
|
return &Sparkline{
|
||||||
TitleColor: Theme.Fg,
|
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 {
|
func NewSparklines(ss ...*Sparkline) *Sparklines {
|
||||||
return &Sparklines{
|
return &Sparklines{
|
||||||
Block: NewBlock(),
|
Block: NewBlock(),
|
||||||
|
@ -44,19 +44,18 @@ func (sl *Sparklines) Buffer() *Buffer {
|
||||||
|
|
||||||
lc := len(sl.Lines) // lineCount
|
lc := len(sl.Lines) // lineCount
|
||||||
|
|
||||||
// for each line
|
// renders each sparkline and its titles
|
||||||
for i, line := range sl.Lines {
|
for i, line := range sl.Lines {
|
||||||
|
|
||||||
|
// prints titles
|
||||||
title1Y := 2 + (sl.Y/lc)*i
|
title1Y := 2 + (sl.Y/lc)*i
|
||||||
title2Y := (2 + (sl.Y/lc)*i) + 1
|
title2Y := (2 + (sl.Y/lc)*i) + 1
|
||||||
|
|
||||||
title1 := MaxString(line.Title1, sl.X)
|
title1 := MaxString(line.Title1, sl.X)
|
||||||
title2 := MaxString(line.Title2, sl.X)
|
title2 := MaxString(line.Title2, sl.X)
|
||||||
buf.SetString(1, title1Y, title1, line.TitleColor|AttrBold, sl.Bg)
|
buf.SetString(1, title1Y, title1, line.TitleColor|AttrBold, sl.Bg)
|
||||||
buf.SetString(1, title2Y, title2, line.TitleColor|AttrBold, sl.Bg)
|
buf.SetString(1, title2Y, title2, line.TitleColor|AttrBold, sl.Bg)
|
||||||
|
|
||||||
sparkY := (sl.Y / lc) * (i + 1)
|
sparkY := (sl.Y / lc) * (i + 1)
|
||||||
|
|
||||||
// finds max data in current view used for relative heights
|
// finds max data in current view used for relative heights
|
||||||
max := 1
|
max := 1
|
||||||
for i := len(line.Data) - 1; i >= 0 && sl.X-((len(line.Data)-1)-i) >= 1; i-- {
|
for i := len(line.Data) - 1; i >= 0 && sl.X-((len(line.Data)-1)-i) >= 1; i-- {
|
||||||
|
|
|
@ -10,7 +10,7 @@ type Table struct {
|
||||||
Header []string
|
Header []string
|
||||||
Rows [][]string
|
Rows [][]string
|
||||||
ColWidths []int
|
ColWidths []int
|
||||||
Cp []int // column position
|
CellXPos []int // column position
|
||||||
Gap int // gap between columns
|
Gap int // gap between columns
|
||||||
Cursor Color
|
Cursor Color
|
||||||
UniqueCol int // the column used to identify the selected item
|
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 is the default column resizer, but can be overriden.
|
||||||
|
// ColResize calculates the width of each column.
|
||||||
func (t *Table) ColResize() {
|
func (t *Table) ColResize() {
|
||||||
// calculate gap size based on total width
|
// calculate gap size based on total width
|
||||||
t.Gap = 3
|
t.Gap = 3
|
||||||
|
@ -46,7 +47,7 @@ func (t *Table) ColResize() {
|
||||||
cur := 0
|
cur := 0
|
||||||
for _, w := range t.ColWidths {
|
for _, w := range t.ColWidths {
|
||||||
cur += t.Gap
|
cur += t.Gap
|
||||||
t.Cp = append(t.Cp, cur)
|
t.CellXPos = append(t.CellXPos, cur)
|
||||||
cur += w
|
cur += w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,21 +56,20 @@ func (t *Table) ColResize() {
|
||||||
func (t *Table) Buffer() *Buffer {
|
func (t *Table) Buffer() *Buffer {
|
||||||
buf := t.Block.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) {
|
if t.TopRow > len(t.Rows)-(t.Y-1) {
|
||||||
t.TopRow = len(t.Rows) - (t.Y - 1)
|
t.TopRow = len(t.Rows) - (t.Y - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.ColResizer()
|
t.ColResizer()
|
||||||
|
|
||||||
// print header
|
// prints header
|
||||||
// for i := 0; i < render; i++ {
|
|
||||||
for i, width := range t.ColWidths {
|
for i, width := range t.ColWidths {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
r := MaxString(t.Header[i], t.X-6)
|
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
|
// prints each row
|
||||||
|
@ -77,7 +77,7 @@ func (t *Table) Buffer() *Buffer {
|
||||||
row := t.Rows[rowNum]
|
row := t.Rows[rowNum]
|
||||||
y := (rowNum + 2) - t.TopRow
|
y := (rowNum + 2) - t.TopRow
|
||||||
|
|
||||||
// cursor
|
// prints cursor
|
||||||
bg := t.Bg
|
bg := t.Bg
|
||||||
if (t.SelectedItem == "" && rowNum == t.SelectedRow) || (t.SelectedItem != "" && t.SelectedItem == row[t.UniqueCol]) {
|
if (t.SelectedItem == "" && rowNum == t.SelectedRow) || (t.SelectedItem != "" && t.SelectedItem == row[t.UniqueCol]) {
|
||||||
bg = t.Cursor
|
bg = t.Cursor
|
||||||
|
@ -97,7 +97,7 @@ func (t *Table) Buffer() *Buffer {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
r := MaxString(row[i], t.X-6)
|
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 //
|
// 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() {
|
func (t *Table) calcPos() {
|
||||||
t.SelectedItem = ""
|
t.SelectedItem = ""
|
||||||
|
|
||||||
|
@ -147,6 +147,8 @@ func (t *Table) Bottom() {
|
||||||
t.calcPos()
|
t.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The number of lines in a page is equal to the height of the widget.
|
||||||
|
|
||||||
func (t *Table) HalfPageUp() {
|
func (t *Table) HalfPageUp() {
|
||||||
t.SelectedRow = t.SelectedRow - (t.Y-2)/2
|
t.SelectedRow = t.SelectedRow - (t.Y-2)/2
|
||||||
t.calcPos()
|
t.calcPos()
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
cpu "github.com/shirou/gopsutil/cpu"
|
psCPU "github.com/shirou/gopsutil/cpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CPU struct {
|
type CPU struct {
|
||||||
|
@ -15,8 +15,12 @@ type CPU struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCPU() *CPU {
|
func NewCPU() *CPU {
|
||||||
count, _ := cpu.Counts(false)
|
count, _ := psCPU.Counts(false)
|
||||||
c := &CPU{ui.NewLineGraph(), count, time.Second}
|
c := &CPU{
|
||||||
|
LineGraph: ui.NewLineGraph(),
|
||||||
|
count: count,
|
||||||
|
interval: time.Second,
|
||||||
|
}
|
||||||
c.Label = "CPU Usage"
|
c.Label = "CPU Usage"
|
||||||
for i := 0; i < c.count; i++ {
|
for i := 0; i < c.count; i++ {
|
||||||
key := "CPU" + strconv.Itoa(i+1)
|
key := "CPU" + strconv.Itoa(i+1)
|
||||||
|
@ -37,7 +41,7 @@ func NewCPU() *CPU {
|
||||||
func (c *CPU) update() {
|
func (c *CPU) update() {
|
||||||
// psutil calculates the CPU usage over a 1 second interval, therefore it blocks for 1 second
|
// 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
|
// `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++ {
|
for i := 0; i < c.count; i++ {
|
||||||
key := "CPU" + strconv.Itoa(i+1)
|
key := "CPU" + strconv.Itoa(i+1)
|
||||||
c.Data[key] = append(c.Data[key], percent[i])
|
c.Data[key] = append(c.Data[key], percent[i])
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
"github.com/cjbassi/gotop/utils"
|
"github.com/cjbassi/gotop/utils"
|
||||||
disk "github.com/shirou/gopsutil/disk"
|
psDisk "github.com/shirou/gopsutil/disk"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Disk struct {
|
type Disk struct {
|
||||||
|
@ -16,8 +16,11 @@ type Disk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDisk() *Disk {
|
func NewDisk() *Disk {
|
||||||
// get root filesystem usage
|
d := &Disk{
|
||||||
d := &Disk{ui.NewGauge(), "/", time.Second * 5}
|
Gauge: ui.NewGauge(),
|
||||||
|
fs: "/",
|
||||||
|
interval: time.Second * 5,
|
||||||
|
}
|
||||||
d.Label = "Disk Usage"
|
d.Label = "Disk Usage"
|
||||||
|
|
||||||
go d.update()
|
go d.update()
|
||||||
|
@ -32,7 +35,7 @@ func NewDisk() *Disk {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Disk) update() {
|
func (d *Disk) update() {
|
||||||
disk, _ := disk.Usage(d.fs)
|
usage, _ := psDisk.Usage(d.fs)
|
||||||
d.Percent = int(disk.UsedPercent)
|
d.Percent = int(usage.UsedPercent)
|
||||||
d.Description = fmt.Sprintf(" (%dGB free)", int(utils.BytesToGB(disk.Free)))
|
d.Description = fmt.Sprintf(" (%dGB free)", int(utils.BytesToGB(usage.Free)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ type HelpMenu struct {
|
||||||
|
|
||||||
func NewHelpMenu() *HelpMenu {
|
func NewHelpMenu() *HelpMenu {
|
||||||
block := ui.NewBlock()
|
block := ui.NewBlock()
|
||||||
block.X = 48 // width
|
block.X = 48 // width - 1
|
||||||
block.Y = 15 // height
|
block.Y = 15 // height - 1
|
||||||
block.XOffset = (ui.Body.Width - block.X) / 2 // X coordinate
|
block.XOffset = (ui.Body.Width - block.X) / 2 // X coordinate
|
||||||
block.YOffset = (ui.Body.Height - block.Y) / 2 // Y coordinate
|
block.YOffset = (ui.Body.Height - block.Y) / 2 // Y coordinate
|
||||||
return &HelpMenu{block}
|
return &HelpMenu{block}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
mem "github.com/shirou/gopsutil/mem"
|
psMem "github.com/shirou/gopsutil/mem"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mem struct {
|
type Mem struct {
|
||||||
|
@ -13,7 +13,10 @@ type Mem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMem() *Mem {
|
func NewMem() *Mem {
|
||||||
m := &Mem{ui.NewLineGraph(), time.Second}
|
m := &Mem{
|
||||||
|
LineGraph: ui.NewLineGraph(),
|
||||||
|
interval: time.Second,
|
||||||
|
}
|
||||||
m.Label = "Memory Usage"
|
m.Label = "Memory Usage"
|
||||||
m.Data["Main"] = []float64{0}
|
m.Data["Main"] = []float64{0}
|
||||||
m.Data["Swap"] = []float64{0}
|
m.Data["Swap"] = []float64{0}
|
||||||
|
@ -30,8 +33,8 @@ func NewMem() *Mem {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mem) update() {
|
func (m *Mem) update() {
|
||||||
main, _ := mem.VirtualMemory()
|
main, _ := psMem.VirtualMemory()
|
||||||
swap, _ := mem.SwapMemory()
|
swap, _ := psMem.SwapMemory()
|
||||||
m.Data["Main"] = append(m.Data["Main"], main.UsedPercent)
|
m.Data["Main"] = append(m.Data["Main"], main.UsedPercent)
|
||||||
m.Data["Swap"] = append(m.Data["Swap"], swap.UsedPercent)
|
m.Data["Swap"] = append(m.Data["Swap"], swap.UsedPercent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
"github.com/cjbassi/gotop/utils"
|
"github.com/cjbassi/gotop/utils"
|
||||||
net "github.com/shirou/gopsutil/net"
|
psNet "github.com/shirou/gopsutil/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Net struct {
|
type Net struct {
|
||||||
|
@ -25,7 +25,10 @@ func NewNet() *Net {
|
||||||
sent.Data = []int{0}
|
sent.Data = []int{0}
|
||||||
|
|
||||||
spark := ui.NewSparklines(recv, sent)
|
spark := ui.NewSparklines(recv, sent)
|
||||||
n := &Net{spark, time.Second, 0, 0}
|
n := &Net{
|
||||||
|
Sparklines: spark,
|
||||||
|
interval: time.Second,
|
||||||
|
}
|
||||||
n.Label = "Network Usage"
|
n.Label = "Network Usage"
|
||||||
|
|
||||||
go n.update()
|
go n.update()
|
||||||
|
@ -41,56 +44,55 @@ func NewNet() *Net {
|
||||||
|
|
||||||
func (n *Net) update() {
|
func (n *Net) update() {
|
||||||
// `false` causes psutil to group all network activity
|
// `false` causes psutil to group all network activity
|
||||||
interfaces, _ := net.IOCounters(false)
|
interfaces, _ := psNet.IOCounters(false)
|
||||||
recv := interfaces[0].BytesRecv
|
recvTotal := interfaces[0].BytesRecv
|
||||||
sent := interfaces[0].BytesSent
|
sentTotal := interfaces[0].BytesSent
|
||||||
|
|
||||||
if n.recvTotal != 0 { // if this isn't the first update
|
if n.recvTotal != 0 { // if this isn't the first update
|
||||||
curRecv := recv - n.recvTotal
|
recvRecent := recvTotal - n.recvTotal
|
||||||
curSent := sent - n.sentTotal
|
sentRecent := sentTotal - n.sentTotal
|
||||||
|
|
||||||
n.Lines[0].Data = append(n.Lines[0].Data, int(curRecv))
|
n.Lines[0].Data = append(n.Lines[0].Data, int(recvRecent))
|
||||||
n.Lines[1].Data = append(n.Lines[1].Data, int(curSent))
|
n.Lines[1].Data = append(n.Lines[1].Data, int(sentRecent))
|
||||||
}
|
}
|
||||||
|
|
||||||
// used for later calls to update
|
// used in later calls to update
|
||||||
n.recvTotal = recv
|
n.recvTotal = recvTotal
|
||||||
n.sentTotal = sent
|
n.sentTotal = sentTotal
|
||||||
|
|
||||||
// renders net widget titles
|
// renders net widget titles
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
var method string // either 'Rx' or 'Tx'
|
var method string // either 'Rx' or 'Tx'
|
||||||
var total uint64
|
var total float64
|
||||||
cur := n.Lines[i].Data[len(n.Lines[i].Data)-1]
|
recent := n.Lines[i].Data[len(n.Lines[i].Data)-1]
|
||||||
totalUnit := "B"
|
unitTotal := "B"
|
||||||
curUnit := "B"
|
unitRecent := "B"
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
total = recv
|
total = float64(recvTotal)
|
||||||
method = "Rx"
|
method = "Rx"
|
||||||
} else {
|
} else {
|
||||||
total = sent
|
total = float64(sentTotal)
|
||||||
method = "Tx"
|
method = "Tx"
|
||||||
}
|
}
|
||||||
|
|
||||||
if cur >= 1000000 {
|
if recent >= 1000000 {
|
||||||
cur = int(utils.BytesToMB(uint64(cur)))
|
recent = int(utils.BytesToMB(uint64(recent)))
|
||||||
curUnit = "MB"
|
unitRecent = "MB"
|
||||||
} else if cur >= 1000 {
|
} else if recent >= 1000 {
|
||||||
cur = int(utils.BytesToKB(uint64(cur)))
|
recent = int(utils.BytesToKB(uint64(recent)))
|
||||||
curUnit = "kB"
|
unitRecent = "kB"
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCvrt float64
|
|
||||||
if total >= 1000000000 {
|
if total >= 1000000000 {
|
||||||
totalCvrt = utils.BytesToGB(total)
|
total = utils.BytesToGB(uint64(total))
|
||||||
totalUnit = "GB"
|
unitTotal = "GB"
|
||||||
} else if total >= 1000000 {
|
} else if total >= 1000000 {
|
||||||
totalCvrt = utils.BytesToMB(total)
|
total = utils.BytesToMB(uint64(total))
|
||||||
totalUnit = "MB"
|
unitTotal = "MB"
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", method, totalCvrt, totalUnit)
|
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, cur, curUnit)
|
n.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9d %2s/s", method, recent, unitRecent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
cpu "github.com/shirou/gopsutil/cpu"
|
psCPU "github.com/shirou/gopsutil/cpu"
|
||||||
proc "github.com/shirou/gopsutil/process"
|
psProc "github.com/shirou/gopsutil/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DOWN = "▼"
|
|
||||||
UP = "▲"
|
UP = "▲"
|
||||||
|
DOWN = "▼"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process represents each process.
|
// Process represents each process.
|
||||||
|
@ -24,7 +24,6 @@ type Process struct {
|
||||||
Mem float32
|
Mem float32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proc widget.
|
|
||||||
type Proc struct {
|
type Proc struct {
|
||||||
*ui.Table
|
*ui.Table
|
||||||
cpuCount int
|
cpuCount int
|
||||||
|
@ -36,9 +35,8 @@ type Proc struct {
|
||||||
KeyPressed chan bool
|
KeyPressed chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProc creates a new Proc widget.
|
|
||||||
func NewProc(loaded, keyPressed chan bool) *Proc {
|
func NewProc(loaded, keyPressed chan bool) *Proc {
|
||||||
cpuCount, _ := cpu.Counts(false)
|
cpuCount, _ := psCPU.Counts(false)
|
||||||
p := &Proc{
|
p := &Proc{
|
||||||
Table: ui.NewTable(),
|
Table: ui.NewTable(),
|
||||||
interval: time.Second,
|
interval: time.Second,
|
||||||
|
@ -73,15 +71,14 @@ func NewProc(loaded, keyPressed chan bool) *Proc {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// update updates proc widget.
|
|
||||||
func (p *Proc) update() {
|
func (p *Proc) update() {
|
||||||
psProcs, _ := proc.Processes()
|
psProcesses, _ := psProc.Processes()
|
||||||
processes := make([]Process, len(psProcs))
|
processes := make([]Process, len(psProcesses))
|
||||||
for i, pr := range psProcs {
|
for i, psProcess := range psProcesses {
|
||||||
pid := pr.Pid
|
pid := psProcess.Pid
|
||||||
command, _ := pr.Name()
|
command, _ := psProcess.Name()
|
||||||
cpu, _ := pr.CPUPercent()
|
cpu, _ := psProcess.CPUPercent()
|
||||||
mem, _ := pr.MemoryPercent()
|
mem, _ := psProcess.MemoryPercent()
|
||||||
|
|
||||||
processes[i] = Process{
|
processes[i] = Process{
|
||||||
pid,
|
pid,
|
||||||
|
@ -139,21 +136,21 @@ func (p *Proc) ColResize() {
|
||||||
p.Gap = 2
|
p.Gap = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Cp = []int{
|
p.CellXPos = []int{
|
||||||
p.Gap,
|
p.Gap,
|
||||||
p.Gap + p.ColWidths[0] + 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] - p.Gap - p.ColWidths[2],
|
||||||
p.X - p.Gap - p.ColWidths[3],
|
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
|
// 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[2] = 0
|
||||||
p.ColWidths[3] = 0
|
p.ColWidths[3] = 0
|
||||||
} else if p.X < contentWidth {
|
} else if p.X < rowWidth {
|
||||||
p.Cp[2] = p.Cp[3]
|
p.CellXPos[2] = p.CellXPos[3]
|
||||||
p.ColWidths[3] = 0
|
p.ColWidths[3] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,33 +235,33 @@ func (p *Proc) keyBinds() {
|
||||||
// The first field changes from PID to count.
|
// The first field changes from PID to count.
|
||||||
// CPU and Mem are added together for each Process.
|
// CPU and Mem are added together for each Process.
|
||||||
func Group(P []Process) []Process {
|
func Group(P []Process) []Process {
|
||||||
groupMap := make(map[string]Process)
|
groupedP := make(map[string]Process)
|
||||||
for _, p := range P {
|
for _, process := range P {
|
||||||
val, ok := groupMap[p.Command]
|
val, ok := groupedP[process.Command]
|
||||||
if ok {
|
if ok {
|
||||||
newP := Process{
|
groupedP[process.Command] = Process{
|
||||||
val.PID + 1,
|
val.PID + 1,
|
||||||
val.Command,
|
val.Command,
|
||||||
val.CPU + p.CPU,
|
val.CPU + process.CPU,
|
||||||
val.Mem + p.Mem,
|
val.Mem + process.Mem,
|
||||||
}
|
}
|
||||||
groupMap[p.Command] = newP
|
|
||||||
} else {
|
} else {
|
||||||
newP := Process{
|
groupedP[process.Command] = Process{
|
||||||
1,
|
1,
|
||||||
p.Command,
|
process.Command,
|
||||||
p.CPU,
|
process.CPU,
|
||||||
p.Mem,
|
process.Mem,
|
||||||
}
|
}
|
||||||
groupMap[p.Command] = newP
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupList := make([]Process, len(groupMap))
|
|
||||||
i := 0
|
groupList := make([]Process, len(groupedP))
|
||||||
for _, val := range groupMap {
|
var i int
|
||||||
|
for _, val := range groupedP {
|
||||||
groupList[i] = val
|
groupList[i] = val
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupList
|
return groupList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,28 +5,29 @@ package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/termui"
|
ui "github.com/cjbassi/gotop/termui"
|
||||||
ps "github.com/shirou/gopsutil/host"
|
psHost "github.com/shirou/gopsutil/host"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Temp struct {
|
type Temp struct {
|
||||||
*ui.Block
|
*ui.Block
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
Data []int
|
Data map[string]int
|
||||||
DataLabels []string
|
Threshold int
|
||||||
Threshold int
|
TempLow ui.Color
|
||||||
TempLow ui.Color
|
TempHigh ui.Color
|
||||||
TempHigh ui.Color
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTemp() *Temp {
|
func NewTemp() *Temp {
|
||||||
t := &Temp{
|
t := &Temp{
|
||||||
Block: ui.NewBlock(),
|
Block: ui.NewBlock(),
|
||||||
interval: time.Second * 5,
|
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"
|
t.Label = "Temperatures"
|
||||||
|
|
||||||
|
@ -42,38 +43,41 @@ func NewTemp() *Temp {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Temp) update() {
|
func (t *Temp) update() {
|
||||||
sensors, _ := ps.SensorsTemperatures()
|
sensors, _ := psHost.SensorsTemperatures()
|
||||||
temps := []int{}
|
for _, sensor := range sensors {
|
||||||
labels := []string{}
|
|
||||||
for _, temp := range sensors {
|
|
||||||
// only sensors with input in their name are giving us live temp info
|
// only sensors with input in their name are giving us live temp info
|
||||||
if strings.Contains(temp.SensorKey, "input") {
|
if strings.Contains(sensor.SensorKey, "input") {
|
||||||
temps = append(temps, int(temp.Temperature))
|
|
||||||
// removes '_input' from the end of the sensor name
|
// 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.
|
// Buffer implements ui.Bufferer interface.
|
||||||
func (t *Temp) Buffer() *ui.Buffer {
|
func (t *Temp) Buffer() *ui.Buffer {
|
||||||
buf := t.Block.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 {
|
if y+1 > t.Y {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
fg := t.TempLow
|
fg := t.TempLow
|
||||||
if t.Data[y] >= t.Threshold {
|
if t.Data[key] >= t.Threshold {
|
||||||
fg = t.TempHigh
|
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(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
|
return buf
|
||||||
|
|
Loading…
Reference in New Issue
Block a user