Refactor
This commit is contained in:
parent
3a804eb666
commit
71e10bb6be
4
go.sum
4
go.sum
|
@ -10,7 +10,9 @@ github.com/gizak/termui v0.0.0-20190124041613-958a28575d74 h1:gQbT+IgWIflxp7EQax
|
||||||
github.com/gizak/termui v0.0.0-20190124041613-958a28575d74/go.mod h1:hJmkzz29zwvMdxina9wLc5fWN7bZuougH5YR93VrJtA=
|
github.com/gizak/termui v0.0.0-20190124041613-958a28575d74/go.mod h1:hJmkzz29zwvMdxina9wLc5fWN7bZuougH5YR93VrJtA=
|
||||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||||
|
github.com/google/pprof v0.0.0-20190109223431-e84dfd68c163 h1:beB+Da4k9B1zmgag78k3k1Bx4L/fdWr5FwNa0f8RxmY=
|
||||||
github.com/google/pprof v0.0.0-20190109223431-e84dfd68c163/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190109223431-e84dfd68c163/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
@ -36,7 +38,9 @@ github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+D
|
||||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU=
|
||||||
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
||||||
|
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M=
|
||||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0=
|
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0=
|
||||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|
73
main.go
73
main.go
|
@ -44,19 +44,19 @@ var (
|
||||||
minimalMode = false
|
minimalMode = false
|
||||||
averageLoad = false
|
averageLoad = false
|
||||||
percpuLoad = false
|
percpuLoad = false
|
||||||
fahrenheit = false
|
tempScale = w.Celcius
|
||||||
battery = false
|
battery = false
|
||||||
statusbar = false
|
statusbar = false
|
||||||
|
|
||||||
renderLock sync.RWMutex
|
renderLock sync.RWMutex
|
||||||
|
|
||||||
cpu *w.CPU
|
cpu *w.CpuWidget
|
||||||
batt *w.Batt
|
batt *w.BatteryWidget
|
||||||
mem *w.Mem
|
mem *w.MemWidget
|
||||||
proc *w.Proc
|
proc *w.ProcWidget
|
||||||
net *w.Net
|
net *w.NetWidget
|
||||||
disk *w.Disk
|
disk *w.DiskWidget
|
||||||
temp *w.Temp
|
temp *w.TempWidget
|
||||||
help *w.HelpMenu
|
help *w.HelpMenu
|
||||||
grid *ui.Grid
|
grid *ui.Grid
|
||||||
bar *w.StatusBar
|
bar *w.StatusBar
|
||||||
|
@ -134,7 +134,10 @@ Colorschemes:
|
||||||
} else {
|
} else {
|
||||||
updateInterval = time.Second / time.Duration(rate)
|
updateInterval = time.Second / time.Duration(rate)
|
||||||
}
|
}
|
||||||
fahrenheit, _ = args["--fahrenheit"].(bool)
|
fahrenheit, _ := args["--fahrenheit"].(bool)
|
||||||
|
if fahrenheit {
|
||||||
|
tempScale = w.Fahrenheit
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -221,8 +224,8 @@ func setDefaultTermuiColors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setWidgetColors() {
|
func setWidgetColors() {
|
||||||
mem.LineColor["Main"] = ui.Color(colorscheme.MainMem)
|
mem.LineColors["Main"] = ui.Color(colorscheme.MainMem)
|
||||||
mem.LineColor["Swap"] = ui.Color(colorscheme.SwapMem)
|
mem.LineColors["Swap"] = ui.Color(colorscheme.SwapMem)
|
||||||
|
|
||||||
proc.CursorColor = ui.Color(colorscheme.ProcCursor)
|
proc.CursorColor = ui.Color(colorscheme.ProcCursor)
|
||||||
|
|
||||||
|
@ -238,7 +241,7 @@ func setWidgetColors() {
|
||||||
i = 0
|
i = 0
|
||||||
}
|
}
|
||||||
c := colorscheme.CPULines[i]
|
c := colorscheme.CPULines[i]
|
||||||
cpu.LineColor[v] = ui.Color(c)
|
cpu.LineColors[v] = ui.Color(c)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,13 +259,13 @@ func setWidgetColors() {
|
||||||
i = 0
|
i = 0
|
||||||
}
|
}
|
||||||
c := colorscheme.BattLines[i]
|
c := colorscheme.BattLines[i]
|
||||||
batt.LineColor[v] = ui.Color(c)
|
batt.LineColors[v] = ui.Color(c)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
temp.TempLow = ui.Color(colorscheme.TempLow)
|
temp.TempLowColor = ui.Color(colorscheme.TempLow)
|
||||||
temp.TempHigh = ui.Color(colorscheme.TempHigh)
|
temp.TempHighColor = ui.Color(colorscheme.TempHigh)
|
||||||
|
|
||||||
net.Lines[0].LineColor = ui.Color(colorscheme.Sparkline)
|
net.Lines[0].LineColor = ui.Color(colorscheme.Sparkline)
|
||||||
net.Lines[0].TitleColor = ui.Color(colorscheme.BorderLabel)
|
net.Lines[0].TitleColor = ui.Color(colorscheme.BorderLabel)
|
||||||
|
@ -272,17 +275,17 @@ func setWidgetColors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initWidgets() {
|
func initWidgets() {
|
||||||
cpu = w.NewCPU(&renderLock, updateInterval, graphHorizontalScale, averageLoad, percpuLoad)
|
cpu = w.NewCpuWidget(&renderLock, updateInterval, graphHorizontalScale, averageLoad, percpuLoad)
|
||||||
mem = w.NewMem(&renderLock, updateInterval, graphHorizontalScale)
|
mem = w.NewMemWidget(&renderLock, updateInterval, graphHorizontalScale)
|
||||||
proc = w.NewProc(&renderLock)
|
proc = w.NewProcWidget(&renderLock)
|
||||||
help = w.NewHelpMenu()
|
help = w.NewHelpMenu()
|
||||||
if !minimalMode {
|
if !minimalMode {
|
||||||
if battery {
|
if battery {
|
||||||
batt = w.NewBatt(&renderLock, graphHorizontalScale)
|
batt = w.NewBatteryWidget(&renderLock, graphHorizontalScale)
|
||||||
}
|
}
|
||||||
net = w.NewNet(&renderLock)
|
net = w.NewNetWidget(&renderLock)
|
||||||
disk = w.NewDisk(&renderLock)
|
disk = w.NewDiskWidget(&renderLock)
|
||||||
temp = w.NewTemp(&renderLock, fahrenheit)
|
temp = w.NewTempWidget(&renderLock, tempScale)
|
||||||
}
|
}
|
||||||
if statusbar {
|
if statusbar {
|
||||||
bar = w.NewStatusBar()
|
bar = w.NewStatusBar()
|
||||||
|
@ -367,46 +370,46 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
case "<MouseLeft>":
|
case "<MouseLeft>":
|
||||||
payload := e.Payload.(ui.Mouse)
|
payload := e.Payload.(ui.Mouse)
|
||||||
proc.Click(payload.X, payload.Y)
|
proc.HandleClick(payload.X, payload.Y)
|
||||||
render(proc)
|
render(proc)
|
||||||
case "k", "<Up>", "<MouseWheelUp>":
|
case "k", "<Up>", "<MouseWheelUp>":
|
||||||
proc.Up()
|
proc.ScrollUp()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "j", "<Down>", "<MouseWheelDown>":
|
case "j", "<Down>", "<MouseWheelDown>":
|
||||||
proc.Down()
|
proc.ScrollDown()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "<Home>":
|
case "<Home>":
|
||||||
proc.Top()
|
proc.ScrollTop()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "g":
|
case "g":
|
||||||
if previousKey == "g" {
|
if previousKey == "g" {
|
||||||
proc.Top()
|
proc.ScrollTop()
|
||||||
render(proc)
|
render(proc)
|
||||||
}
|
}
|
||||||
case "G", "<End>":
|
case "G", "<End>":
|
||||||
proc.Bottom()
|
proc.ScrollBottom()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "<C-d>":
|
case "<C-d>":
|
||||||
proc.HalfPageDown()
|
proc.ScrollHalfPageDown()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "<C-u>":
|
case "<C-u>":
|
||||||
proc.HalfPageUp()
|
proc.ScrollHalfPageUp()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "<C-f>":
|
case "<C-f>":
|
||||||
proc.PageDown()
|
proc.ScrollPageDown()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "<C-b>":
|
case "<C-b>":
|
||||||
proc.PageUp()
|
proc.ScrollPageUp()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "d":
|
case "d":
|
||||||
if previousKey == "d" {
|
if previousKey == "d" {
|
||||||
proc.Kill()
|
proc.KillProc()
|
||||||
}
|
}
|
||||||
case "<Tab>":
|
case "<Tab>":
|
||||||
proc.Tab()
|
proc.ToggleShowingGroupedProcs()
|
||||||
render(proc)
|
render(proc)
|
||||||
case "m", "c", "p":
|
case "m", "c", "p":
|
||||||
proc.ChangeSort(e)
|
proc.ChangeProcSortMethod(w.ProcSortMethod(e.ID))
|
||||||
render(proc)
|
render(proc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StderrToLogfile(lf *os.File) {
|
func StderrToLogfile(logfile *os.File) {
|
||||||
syscall.Dup3(int(lf.Fd()), 2, 0)
|
syscall.Dup3(int(logfile.Fd()), 2, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StderrToLogfile(lf *os.File) {
|
func StderrToLogfile(logfile *os.File) {
|
||||||
syscall.Dup2(int(lf.Fd()), 2)
|
syscall.Dup2(int(logfile.Fd()), 2)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,21 +11,26 @@ import (
|
||||||
// LineGraph implements a line graph of data points.
|
// LineGraph implements a line graph of data points.
|
||||||
type LineGraph struct {
|
type LineGraph struct {
|
||||||
*Block
|
*Block
|
||||||
Data map[string][]float64
|
|
||||||
LineColor map[string]Color
|
|
||||||
HorizontalScale int
|
|
||||||
Labels map[string]string
|
|
||||||
|
|
||||||
|
Data map[string][]float64
|
||||||
|
Labels map[string]string
|
||||||
|
|
||||||
|
HorizontalScale int
|
||||||
|
|
||||||
|
LineColors map[string]Color
|
||||||
DefaultLineColor Color
|
DefaultLineColor Color
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLineGraph() *LineGraph {
|
func NewLineGraph() *LineGraph {
|
||||||
return &LineGraph{
|
return &LineGraph{
|
||||||
Block: NewBlock(),
|
Block: NewBlock(),
|
||||||
Data: make(map[string][]float64),
|
|
||||||
LineColor: make(map[string]Color),
|
Data: make(map[string][]float64),
|
||||||
Labels: make(map[string]string),
|
Labels: make(map[string]string),
|
||||||
|
|
||||||
HorizontalScale: 5,
|
HorizontalScale: 5,
|
||||||
|
|
||||||
|
LineColors: make(map[string]Color),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +58,7 @@ func (self *LineGraph) Draw(buf *Buffer) {
|
||||||
for i := len(seriesList) - 1; i >= 0; i-- {
|
for i := len(seriesList) - 1; i >= 0; i-- {
|
||||||
seriesName := seriesList[i]
|
seriesName := seriesList[i]
|
||||||
seriesData := self.Data[seriesName]
|
seriesData := self.Data[seriesName]
|
||||||
seriesLineColor, ok := self.LineColor[seriesName]
|
seriesLineColor, ok := self.LineColors[seriesName]
|
||||||
if !ok {
|
if !ok {
|
||||||
seriesLineColor = self.DefaultLineColor
|
seriesLineColor = self.DefaultLineColor
|
||||||
}
|
}
|
||||||
|
@ -110,7 +115,7 @@ func (self *LineGraph) Draw(buf *Buffer) {
|
||||||
if i+2 > self.Inner.Dy() {
|
if i+2 > self.Inner.Dy() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
seriesLineColor, ok := self.LineColor[seriesName]
|
seriesLineColor, ok := self.LineColors[seriesName]
|
||||||
if !ok {
|
if !ok {
|
||||||
seriesLineColor = self.DefaultLineColor
|
seriesLineColor = self.DefaultLineColor
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,31 +16,31 @@ type Sparkline struct {
|
||||||
LineColor Color
|
LineColor Color
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sparklines is a renderable widget which groups together the given sparklines.
|
// SparklineGroup is a renderable widget which groups together the given sparklines.
|
||||||
type Sparklines struct {
|
type SparklineGroup struct {
|
||||||
*Block
|
*Block
|
||||||
Lines []*Sparkline
|
Lines []*Sparkline
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add appends a given Sparkline to the *Sparklines.
|
// Add appends a given Sparkline to the *SparklineGroup.
|
||||||
func (self *Sparklines) Add(sl Sparkline) {
|
func (self *SparklineGroup) Add(sl Sparkline) {
|
||||||
self.Lines = append(self.Lines, &sl)
|
self.Lines = append(self.Lines, &sl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSparkline returns an unrenderable single sparkline that intended to be added into a Sparklines.
|
// NewSparkline returns an unrenderable single sparkline that intended to be added into a SparklineGroup.
|
||||||
func NewSparkline() *Sparkline {
|
func NewSparkline() *Sparkline {
|
||||||
return &Sparkline{}
|
return &Sparkline{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSparklines return a new *Sparklines with given Sparklines, you can always add a new Sparkline later.
|
// NewSparklineGroup return a new *SparklineGroup with given Sparklines, you can always add a new Sparkline later.
|
||||||
func NewSparklines(ss ...*Sparkline) *Sparklines {
|
func NewSparklineGroup(ss ...*Sparkline) *SparklineGroup {
|
||||||
return &Sparklines{
|
return &SparklineGroup{
|
||||||
Block: NewBlock(),
|
Block: NewBlock(),
|
||||||
Lines: ss,
|
Lines: ss,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Sparklines) Draw(buf *Buffer) {
|
func (self *SparklineGroup) Draw(buf *Buffer) {
|
||||||
self.Block.Draw(buf)
|
self.Block.Draw(buf)
|
||||||
|
|
||||||
lc := len(self.Lines) // lineCount
|
lc := len(self.Lines) // lineCount
|
||||||
|
|
|
@ -14,37 +14,30 @@ type Table struct {
|
||||||
Header []string
|
Header []string
|
||||||
Rows [][]string
|
Rows [][]string
|
||||||
|
|
||||||
ColWidths []int
|
ColWidths []int
|
||||||
CellXPos []int // column position
|
ColGap int
|
||||||
ColResizer func() // for widgets that inherit a Table and want to overload the ColResize method
|
PadLeft int
|
||||||
Gap int // gap between columns
|
|
||||||
PadLeft int
|
|
||||||
|
|
||||||
Cursor bool
|
ShowCursor bool
|
||||||
CursorColor Color
|
CursorColor Color
|
||||||
|
|
||||||
UniqueCol int // the column used to identify the selected item
|
UniqueCol int // the column used to uniquely identify each table row
|
||||||
SelectedItem string // used to keep the cursor on the correct item if the data changes
|
SelectedItem string // used to keep the cursor on the correct item if the data changes
|
||||||
SelectedRow int
|
SelectedRow int
|
||||||
TopRow int // used to indicate where in the table we are scrolled at
|
TopRow int // used to indicate where in the table we are scrolled at
|
||||||
|
|
||||||
|
ColResizer func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new Table instance
|
// NewTable returns a new Table instance
|
||||||
func NewTable() *Table {
|
func NewTable() *Table {
|
||||||
self := &Table{
|
return &Table{
|
||||||
Block: NewBlock(),
|
Block: NewBlock(),
|
||||||
// CursorColor: Theme.TableCursor,
|
|
||||||
SelectedRow: 0,
|
SelectedRow: 0,
|
||||||
TopRow: 0,
|
TopRow: 0,
|
||||||
UniqueCol: 0,
|
UniqueCol: 0,
|
||||||
|
ColResizer: func() {},
|
||||||
}
|
}
|
||||||
self.ColResizer = self.ColResize
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColResize is the default column resizer, but can be overriden.
|
|
||||||
// ColResize calculates the width of each column.
|
|
||||||
func (self *Table) ColResize() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Draw(buf *Buffer) {
|
func (self *Table) Draw(buf *Buffer) {
|
||||||
|
@ -53,12 +46,12 @@ func (self *Table) Draw(buf *Buffer) {
|
||||||
self.ColResizer()
|
self.ColResizer()
|
||||||
|
|
||||||
// finds exact column starting position
|
// finds exact column starting position
|
||||||
self.CellXPos = []int{}
|
colXPos := []int{}
|
||||||
cur := 1 + self.PadLeft
|
cur := 1 + self.PadLeft
|
||||||
for _, w := range self.ColWidths {
|
for _, w := range self.ColWidths {
|
||||||
self.CellXPos = append(self.CellXPos, cur)
|
colXPos = append(colXPos, cur)
|
||||||
cur += w
|
cur += w
|
||||||
cur += self.Gap
|
cur += self.ColGap
|
||||||
}
|
}
|
||||||
|
|
||||||
// prints header
|
// prints header
|
||||||
|
@ -68,13 +61,13 @@ func (self *Table) Draw(buf *Buffer) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// don't render column if it doesn't fit in widget
|
// don't render column if it doesn't fit in widget
|
||||||
if width > (self.Inner.Dx()-self.CellXPos[i])+1 {
|
if width > (self.Inner.Dx()-colXPos[i])+1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
h,
|
h,
|
||||||
NewStyle(Theme.Default.Fg, ColorClear, ModifierBold),
|
NewStyle(Theme.Default.Fg, ColorClear, ModifierBold),
|
||||||
image.Pt(self.Inner.Min.X+self.CellXPos[i]-1, self.Inner.Min.Y),
|
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +83,7 @@ func (self *Table) Draw(buf *Buffer) {
|
||||||
|
|
||||||
// prints cursor
|
// prints cursor
|
||||||
style := NewStyle(Theme.Default.Fg)
|
style := NewStyle(Theme.Default.Fg)
|
||||||
if self.Cursor {
|
if self.ShowCursor {
|
||||||
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
|
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
|
||||||
style.Fg = self.CursorColor
|
style.Fg = self.CursorColor
|
||||||
style.Modifier = ModifierReverse
|
style.Modifier = ModifierReverse
|
||||||
|
@ -115,24 +108,22 @@ func (self *Table) Draw(buf *Buffer) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// don't render column if width is greater than distance to end of widget
|
// don't render column if width is greater than distance to end of widget
|
||||||
if width > (self.Inner.Dx()-self.CellXPos[i])+1 {
|
if width > (self.Inner.Dx()-colXPos[i])+1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r := TrimString(row[i], width)
|
r := TrimString(row[i], width)
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
r,
|
r,
|
||||||
style,
|
style,
|
||||||
image.Pt(self.Inner.Min.X+self.CellXPos[i]-1, self.Inner.Min.Y+y-1),
|
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y+y-1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
// Scrolling ///////////////////////////////////////////////////////////////////
|
||||||
// Cursor Movement //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// calcPos is used to calculate the cursor position and the current view.
|
// calcPos is used to calculate the cursor position and the current view into the table.
|
||||||
func (self *Table) calcPos() {
|
func (self *Table) calcPos() {
|
||||||
self.SelectedItem = ""
|
self.SelectedItem = ""
|
||||||
|
|
||||||
|
@ -151,49 +142,47 @@ func (self *Table) calcPos() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Up() {
|
func (self *Table) ScrollUp() {
|
||||||
self.SelectedRow--
|
self.SelectedRow--
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Down() {
|
func (self *Table) ScrollDown() {
|
||||||
self.SelectedRow++
|
self.SelectedRow++
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Top() {
|
func (self *Table) ScrollTop() {
|
||||||
self.SelectedRow = 0
|
self.SelectedRow = 0
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Bottom() {
|
func (self *Table) ScrollBottom() {
|
||||||
self.SelectedRow = len(self.Rows) - 1
|
self.SelectedRow = len(self.Rows) - 1
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The number of lines in a page is equal to the height of the widgeself.
|
func (self *Table) ScrollHalfPageUp() {
|
||||||
|
|
||||||
func (self *Table) HalfPageUp() {
|
|
||||||
self.SelectedRow = self.SelectedRow - (self.Inner.Dy()-2)/2
|
self.SelectedRow = self.SelectedRow - (self.Inner.Dy()-2)/2
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) HalfPageDown() {
|
func (self *Table) ScrollHalfPageDown() {
|
||||||
self.SelectedRow = self.SelectedRow + (self.Inner.Dy()-2)/2
|
self.SelectedRow = self.SelectedRow + (self.Inner.Dy()-2)/2
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) PageUp() {
|
func (self *Table) ScrollPageUp() {
|
||||||
self.SelectedRow -= (self.Inner.Dy() - 2)
|
self.SelectedRow -= (self.Inner.Dy() - 2)
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) PageDown() {
|
func (self *Table) ScrollPageDown() {
|
||||||
self.SelectedRow += (self.Inner.Dy() - 2)
|
self.SelectedRow += (self.Inner.Dy() - 2)
|
||||||
self.calcPos()
|
self.calcPos()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Table) Click(x, y int) {
|
func (self *Table) HandleClick(x, y int) {
|
||||||
x = x - self.Min.X
|
x = x - self.Min.X
|
||||||
y = y - self.Min.Y
|
y = y - self.Min.Y
|
||||||
if (x > 0 && x <= self.Inner.Dx()) && (y > 0 && y <= self.Inner.Dy()) {
|
if (x > 0 && x <= self.Inner.Dx()) && (y > 0 && y <= self.Inner.Dy()) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func ConvertBytes(b uint64) (float64, string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Max(a, b int) int {
|
func MaxInt(a, b int) int {
|
||||||
if a > b {
|
if a > b {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,25 +13,26 @@ import (
|
||||||
ui "github.com/cjbassi/gotop/src/termui"
|
ui "github.com/cjbassi/gotop/src/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Batt struct {
|
type BatteryWidget struct {
|
||||||
*ui.LineGraph
|
*ui.LineGraph
|
||||||
interval time.Duration
|
updateInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBatt(renderLock *sync.RWMutex, horizontalScale int) *Batt {
|
func NewBatteryWidget(renderLock *sync.RWMutex, horizontalScale int) *BatteryWidget {
|
||||||
self := &Batt{
|
self := &BatteryWidget{
|
||||||
LineGraph: ui.NewLineGraph(),
|
LineGraph: ui.NewLineGraph(),
|
||||||
interval: time.Minute,
|
updateInterval: time.Minute,
|
||||||
}
|
}
|
||||||
self.Title = " Battery Status "
|
self.Title = " Battery Status "
|
||||||
self.HorizontalScale = horizontalScale
|
self.HorizontalScale = horizontalScale
|
||||||
|
|
||||||
// intentional duplicate
|
// intentional duplicate
|
||||||
|
// adds 2 datapoints to the graph, otherwise the dot is difficult to see
|
||||||
self.update()
|
self.update()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -41,20 +42,20 @@ func NewBatt(renderLock *sync.RWMutex, horizontalScale int) *Batt {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkId(i int) string {
|
func makeId(i int) string {
|
||||||
return "Batt" + strconv.Itoa(i)
|
return "Batt" + strconv.Itoa(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Batt) update() {
|
func (self *BatteryWidget) update() {
|
||||||
batts, err := battery.GetAll()
|
batteries, err := battery.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get battery info from system: %v", err)
|
log.Printf("failed to get battery info: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i, b := range batts {
|
for i, battery := range batteries {
|
||||||
n := mkId(i)
|
id := makeId(i)
|
||||||
pc := math.Abs(b.Current/b.Full) * 100.0
|
percentFull := math.Abs(battery.Current/battery.Full) * 100.0
|
||||||
self.Data[n] = append(self.Data[n], pc)
|
self.Data[id] = append(self.Data[id], percentFull)
|
||||||
self.Labels[n] = fmt.Sprintf("%3.0f%% %.0f/%.0f", pc, math.Abs(b.Current), math.Abs(b.Full))
|
self.Labels[id] = fmt.Sprintf("%3.0f%% %.0f/%.0f", percentFull, math.Abs(battery.Current), math.Abs(battery.Full))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,66 +6,66 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
psCPU "github.com/shirou/gopsutil/cpu"
|
psCpu "github.com/shirou/gopsutil/cpu"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/src/termui"
|
ui "github.com/cjbassi/gotop/src/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CPU struct {
|
type CpuWidget struct {
|
||||||
*ui.LineGraph
|
*ui.LineGraph
|
||||||
Count int // number of cores
|
CpuCount int
|
||||||
Average bool // show average load
|
ShowAverageLoad bool
|
||||||
PerCPU bool // show per-core load
|
ShowPerCpuLoad bool
|
||||||
interval time.Duration
|
updateInterval time.Duration
|
||||||
formatString string
|
formatString string
|
||||||
renderLock *sync.RWMutex
|
renderLock *sync.RWMutex
|
||||||
updateLock sync.Mutex
|
updateLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCPU(renderLock *sync.RWMutex, interval time.Duration, horizontalScale int, average bool, percpu bool) *CPU {
|
func NewCpuWidget(renderLock *sync.RWMutex, updateInterval time.Duration, horizontalScale int, showAverageLoad bool, showPerCpuLoad bool) *CpuWidget {
|
||||||
count, err := psCPU.Counts(false)
|
cpuCount, err := psCpu.Counts(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get CPU count from gopsutil: %v", err)
|
log.Printf("failed to get CPU count from gopsutil: %v", err)
|
||||||
}
|
}
|
||||||
formatString := "CPU%1d"
|
formatString := "CPU%1d"
|
||||||
if count > 10 {
|
if cpuCount > 10 {
|
||||||
formatString = "CPU%02d"
|
formatString = "CPU%02d"
|
||||||
}
|
}
|
||||||
self := &CPU{
|
self := &CpuWidget{
|
||||||
LineGraph: ui.NewLineGraph(),
|
LineGraph: ui.NewLineGraph(),
|
||||||
Count: count,
|
CpuCount: cpuCount,
|
||||||
interval: interval,
|
updateInterval: updateInterval,
|
||||||
Average: average,
|
ShowAverageLoad: showAverageLoad,
|
||||||
PerCPU: percpu,
|
ShowPerCpuLoad: showPerCpuLoad,
|
||||||
formatString: formatString,
|
formatString: formatString,
|
||||||
renderLock: renderLock,
|
renderLock: renderLock,
|
||||||
}
|
}
|
||||||
self.Title = " CPU Usage "
|
self.Title = " CPU Usage "
|
||||||
self.HorizontalScale = horizontalScale
|
self.HorizontalScale = horizontalScale
|
||||||
|
|
||||||
if !(self.Average || self.PerCPU) {
|
if !(self.ShowAverageLoad || self.ShowPerCpuLoad) {
|
||||||
if self.Count <= 8 {
|
if self.CpuCount <= 8 {
|
||||||
self.PerCPU = true
|
self.ShowPerCpuLoad = true
|
||||||
} else {
|
} else {
|
||||||
self.Average = true
|
self.ShowAverageLoad = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.Average {
|
if self.ShowAverageLoad {
|
||||||
self.Data["AVRG"] = []float64{0}
|
self.Data["AVRG"] = []float64{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.PerCPU {
|
if self.ShowPerCpuLoad {
|
||||||
for i := 0; i < self.Count; i++ {
|
for i := 0; i < int(self.CpuCount); i++ {
|
||||||
k := fmt.Sprintf(formatString, i)
|
key := fmt.Sprintf(formatString, i)
|
||||||
self.Data[k] = []float64{0}
|
self.Data[key] = []float64{0}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
self.update()
|
self.update()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -73,12 +73,12 @@ func NewCPU(renderLock *sync.RWMutex, interval time.Duration, horizontalScale in
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CPU) update() {
|
func (self *CpuWidget) update() {
|
||||||
if self.Average {
|
if self.ShowAverageLoad {
|
||||||
go func() {
|
go func() {
|
||||||
percent, err := psCPU.Percent(self.interval, false)
|
percent, err := psCpu.Percent(self.updateInterval, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get average CPU usage percent from gopsutil: %v. self.interval: %v. percpu: %v", err, self.interval, false)
|
log.Printf("failed to get average CPU usage percent from gopsutil: %v. self.updateInterval: %v. percpu: %v", err, self.updateInterval, false)
|
||||||
} else {
|
} else {
|
||||||
self.renderLock.RLock()
|
self.renderLock.RLock()
|
||||||
defer self.renderLock.RUnlock()
|
defer self.renderLock.RUnlock()
|
||||||
|
@ -90,23 +90,23 @@ func (self *CPU) update() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.PerCPU {
|
if self.ShowPerCpuLoad {
|
||||||
go func() {
|
go func() {
|
||||||
percents, err := psCPU.Percent(self.interval, true)
|
percents, err := psCpu.Percent(self.updateInterval, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get CPU usage percents from gopsutil: %v. self.interval: %v. percpu: %v", err, self.interval, true)
|
log.Printf("failed to get CPU usage percents from gopsutil: %v. self.updateInterval: %v. percpu: %v", err, self.updateInterval, true)
|
||||||
} else {
|
} else {
|
||||||
if len(percents) != self.Count {
|
if len(percents) != int(self.CpuCount) {
|
||||||
log.Printf("error: number of CPU usage percents from gopsutil doesn't match CPU count. percents: %v. self.Count: %v", percents, self.Count)
|
log.Printf("error: number of CPU usage percents from gopsutil doesn't match CPU count. percents: %v. self.Count: %v", percents, self.CpuCount)
|
||||||
} else {
|
} else {
|
||||||
self.renderLock.RLock()
|
self.renderLock.RLock()
|
||||||
defer self.renderLock.RUnlock()
|
defer self.renderLock.RUnlock()
|
||||||
self.updateLock.Lock()
|
self.updateLock.Lock()
|
||||||
defer self.updateLock.Unlock()
|
defer self.updateLock.Unlock()
|
||||||
for i, percent := range percents {
|
for i, percent := range percents {
|
||||||
k := fmt.Sprintf(self.formatString, i)
|
key := fmt.Sprintf(self.formatString, i)
|
||||||
self.Data[k] = append(self.Data[k], percent)
|
self.Data[key] = append(self.Data[key], percent)
|
||||||
self.Labels[k] = fmt.Sprintf("%3.0f%%", percent)
|
self.Labels[key] = fmt.Sprintf("%3.0f%%", percent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,37 +15,43 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Partition struct {
|
type Partition struct {
|
||||||
Device string
|
Device string
|
||||||
Mount string
|
MountPoint string
|
||||||
TotalRead uint64
|
BytesRead uint64
|
||||||
TotalWrite uint64
|
BytesWritten uint64
|
||||||
CurRead string
|
BytesReadRecently string
|
||||||
CurWrite string
|
BytesWrittenRecently string
|
||||||
UsedPercent int
|
UsedPercent uint32
|
||||||
Free string
|
Free string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Disk struct {
|
type DiskWidget struct {
|
||||||
*ui.Table
|
*ui.Table
|
||||||
interval time.Duration
|
updateInterval time.Duration
|
||||||
Partitions map[string]*Partition
|
Partitions map[string]*Partition
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDisk(renderLock *sync.RWMutex) *Disk {
|
func NewDiskWidget(renderLock *sync.RWMutex) *DiskWidget {
|
||||||
self := &Disk{
|
self := &DiskWidget{
|
||||||
Table: ui.NewTable(),
|
Table: ui.NewTable(),
|
||||||
interval: time.Second,
|
updateInterval: time.Second,
|
||||||
Partitions: make(map[string]*Partition),
|
Partitions: make(map[string]*Partition),
|
||||||
}
|
}
|
||||||
self.Title = " Disk Usage "
|
self.Title = " Disk Usage "
|
||||||
self.Header = []string{"Disk", "Mount", "Used", "Free", "R/s", "W/s"}
|
self.Header = []string{"Disk", "Mount", "Used", "Free", "R/s", "W/s"}
|
||||||
self.Gap = 2
|
self.ColGap = 2
|
||||||
self.ColResizer = self.ColResize
|
self.ColResizer = func() {
|
||||||
|
self.ColWidths = []int{
|
||||||
|
utils.MaxInt(4, (self.Inner.Dx()-29)/2),
|
||||||
|
utils.MaxInt(5, (self.Inner.Dx()-29)/2),
|
||||||
|
4, 5, 5, 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -55,86 +61,87 @@ func NewDisk(renderLock *sync.RWMutex) *Disk {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Disk) update() {
|
func (self *DiskWidget) update() {
|
||||||
Partitions, err := psDisk.Partitions(false)
|
partitions, err := psDisk.Partitions(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get disk partitions from gopsutil: %v", err)
|
log.Printf("failed to get disk partitions from gopsutil: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// add partition if it's new
|
// add partition if it's new
|
||||||
for _, Part := range Partitions {
|
for _, partition := range partitions {
|
||||||
// don't show loop devices
|
// don't show loop devices
|
||||||
if strings.HasPrefix(Part.Device, "/dev/loop") {
|
if strings.HasPrefix(partition.Device, "/dev/loop") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// don't show docker container filesystems
|
// don't show docker container filesystems
|
||||||
if strings.HasPrefix(Part.Mountpoint, "/var/lib/docker/") {
|
if strings.HasPrefix(partition.Mountpoint, "/var/lib/docker/") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if partition doesn't already exist in our list
|
// check if partition doesn't already exist in our list
|
||||||
if _, ok := self.Partitions[Part.Device]; !ok {
|
if _, ok := self.Partitions[partition.Device]; !ok {
|
||||||
self.Partitions[Part.Device] = &Partition{
|
self.Partitions[partition.Device] = &Partition{
|
||||||
Device: Part.Device,
|
Device: partition.Device,
|
||||||
Mount: Part.Mountpoint,
|
MountPoint: partition.Mountpoint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete a partition if it no longer exists
|
// delete a partition if it no longer exists
|
||||||
todelete := []string{}
|
toDelete := []string{}
|
||||||
for key := range self.Partitions {
|
for device := range self.Partitions {
|
||||||
exists := false
|
exists := false
|
||||||
for _, Part := range Partitions {
|
for _, partition := range partitions {
|
||||||
if key == Part.Device {
|
if device == partition.Device {
|
||||||
exists = true
|
exists = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
todelete = append(todelete, key)
|
toDelete = append(toDelete, device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, val := range todelete {
|
for _, device := range toDelete {
|
||||||
delete(self.Partitions, val)
|
delete(self.Partitions, device)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updates partition info
|
// updates partition info
|
||||||
for _, Part := range self.Partitions {
|
for _, partition := range self.Partitions {
|
||||||
usage, err := psDisk.Usage(Part.Mount)
|
usage, err := psDisk.Usage(partition.MountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get partition usage statistics from gopsutil: %v. Part: %v", err, Part)
|
log.Printf("failed to get partition usage statistics from gopsutil: %v. partition: %v", err, partition)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Part.UsedPercent = int(usage.UsedPercent)
|
partition.UsedPercent = uint32(usage.UsedPercent)
|
||||||
|
|
||||||
Free, Mag := utils.ConvertBytes(usage.Free)
|
bytesFree, magnitudeFree := utils.ConvertBytes(usage.Free)
|
||||||
Part.Free = fmt.Sprintf("%3d%s", uint64(Free), Mag)
|
partition.Free = fmt.Sprintf("%3d%s", uint64(bytesFree), magnitudeFree)
|
||||||
|
|
||||||
ret, err := psDisk.IOCounters(Part.Device)
|
ioCounters, err := psDisk.IOCounters(partition.Device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get partition read/write info from gopsutil: %v. Part: %v", err, Part)
|
log.Printf("failed to get partition read/write info from gopsutil: %v. partition: %v", err, partition)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data := ret[strings.Replace(Part.Device, "/dev/", "", -1)]
|
ioCounter := ioCounters[strings.Replace(partition.Device, "/dev/", "", -1)]
|
||||||
curRead, curWrite := data.ReadBytes, data.WriteBytes
|
bytesRead, bytesWritten := ioCounter.ReadBytes, ioCounter.WriteBytes
|
||||||
if Part.TotalRead != 0 { // if this isn't the first update
|
if partition.BytesRead != 0 { // if this isn't the first update
|
||||||
readRecent := curRead - Part.TotalRead
|
bytesReadRecently := bytesRead - partition.BytesRead
|
||||||
writeRecent := curWrite - Part.TotalWrite
|
bytesWrittenRecently := bytesWritten - partition.BytesWritten
|
||||||
|
|
||||||
readFloat, unitRead := utils.ConvertBytes(readRecent)
|
readFloat, readMagnitude := utils.ConvertBytes(bytesReadRecently)
|
||||||
writeFloat, unitWrite := utils.ConvertBytes(writeRecent)
|
writeFloat, writeMagnitude := utils.ConvertBytes(bytesWrittenRecently)
|
||||||
readRecent, writeRecent = uint64(readFloat), uint64(writeFloat)
|
bytesReadRecently, bytesWrittenRecently = uint64(readFloat), uint64(writeFloat)
|
||||||
Part.CurRead = fmt.Sprintf("%d%s", readRecent, unitRead)
|
partition.BytesReadRecently = fmt.Sprintf("%d%s", bytesReadRecently, readMagnitude)
|
||||||
Part.CurWrite = fmt.Sprintf("%d%s", writeRecent, unitWrite)
|
partition.BytesWrittenRecently = fmt.Sprintf("%d%s", bytesWrittenRecently, writeMagnitude)
|
||||||
} else {
|
} else {
|
||||||
Part.CurRead = fmt.Sprintf("%d%s", 0, "B")
|
partition.BytesReadRecently = fmt.Sprintf("%d%s", 0, "B")
|
||||||
Part.CurWrite = fmt.Sprintf("%d%s", 0, "B")
|
partition.BytesWrittenRecently = fmt.Sprintf("%d%s", 0, "B")
|
||||||
}
|
}
|
||||||
Part.TotalRead, Part.TotalWrite = curRead, curWrite
|
partition.BytesRead, partition.BytesWritten = bytesRead, bytesWritten
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts self.Partitions into self.Rows which is a [][]String
|
// converts self.Partitions into self.Rows which is a [][]String
|
||||||
|
|
||||||
sortedPartitions := []string{}
|
sortedPartitions := []string{}
|
||||||
for seriesName := range self.Partitions {
|
for seriesName := range self.Partitions {
|
||||||
sortedPartitions = append(sortedPartitions, seriesName)
|
sortedPartitions = append(sortedPartitions, seriesName)
|
||||||
|
@ -142,31 +149,15 @@ func (self *Disk) update() {
|
||||||
sort.Strings(sortedPartitions)
|
sort.Strings(sortedPartitions)
|
||||||
|
|
||||||
self.Rows = make([][]string, len(self.Partitions))
|
self.Rows = make([][]string, len(self.Partitions))
|
||||||
|
|
||||||
for i, key := range sortedPartitions {
|
for i, key := range sortedPartitions {
|
||||||
Part := self.Partitions[key]
|
partition := self.Partitions[key]
|
||||||
self.Rows[i] = make([]string, 6)
|
self.Rows[i] = make([]string, 6)
|
||||||
self.Rows[i][0] = strings.Replace(strings.Replace(Part.Device, "/dev/", "", -1), "mapper/", "", -1)
|
self.Rows[i][0] = strings.Replace(strings.Replace(partition.Device, "/dev/", "", -1), "mapper/", "", -1)
|
||||||
self.Rows[i][1] = Part.Mount
|
self.Rows[i][1] = partition.MountPoint
|
||||||
self.Rows[i][2] = fmt.Sprintf("%d%%", Part.UsedPercent)
|
self.Rows[i][2] = fmt.Sprintf("%d%%", partition.UsedPercent)
|
||||||
self.Rows[i][3] = Part.Free
|
self.Rows[i][3] = partition.Free
|
||||||
self.Rows[i][4] = Part.CurRead
|
self.Rows[i][4] = partition.BytesReadRecently
|
||||||
self.Rows[i][5] = Part.CurWrite
|
self.Rows[i][5] = partition.BytesWrittenRecently
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColResize overrides the default ColResize in the termui table.
|
|
||||||
func (self *Disk) ColResize() {
|
|
||||||
self.ColWidths = []int{
|
|
||||||
utils.Max(4, (self.Inner.Dx()-29)/2),
|
|
||||||
utils.Max(5, (self.Inner.Dx()-29)/2),
|
|
||||||
4, 5, 5, 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.CellXPos = []int{}
|
|
||||||
cur := 1
|
|
||||||
for _, w := range self.ColWidths {
|
|
||||||
self.CellXPos = append(self.CellXPos, cur)
|
|
||||||
cur += w
|
|
||||||
cur += self.Gap
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,9 @@ func (self *HelpMenu) Draw(buf *ui.Buffer) {
|
||||||
self.Block.Draw(buf)
|
self.Block.Draw(buf)
|
||||||
|
|
||||||
for y, line := range strings.Split(KEYBINDS, "\n") {
|
for y, line := range strings.Split(KEYBINDS, "\n") {
|
||||||
for x, char := range line {
|
for x, rune := range line {
|
||||||
buf.SetCell(
|
buf.SetCell(
|
||||||
ui.NewCell(char, ui.NewStyle(7)),
|
ui.NewCell(rune, ui.NewStyle(7)),
|
||||||
image.Pt(self.Inner.Min.X+x, self.Inner.Min.Y+y-1),
|
image.Pt(self.Inner.Min.X+x, self.Inner.Min.Y+y-1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,15 +12,15 @@ import (
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mem struct {
|
type MemWidget struct {
|
||||||
*ui.LineGraph
|
*ui.LineGraph
|
||||||
interval time.Duration
|
updateInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMem(renderLock *sync.RWMutex, interval time.Duration, horizontalScale int) *Mem {
|
func NewMemWidget(renderLock *sync.RWMutex, updateInterval time.Duration, horizontalScale int) *MemWidget {
|
||||||
self := &Mem{
|
self := &MemWidget{
|
||||||
LineGraph: ui.NewLineGraph(),
|
LineGraph: ui.NewLineGraph(),
|
||||||
interval: interval,
|
updateInterval: updateInterval,
|
||||||
}
|
}
|
||||||
self.Title = " Memory Usage "
|
self.Title = " Memory Usage "
|
||||||
self.HorizontalScale = horizontalScale
|
self.HorizontalScale = horizontalScale
|
||||||
|
@ -30,7 +30,7 @@ func NewMem(renderLock *sync.RWMutex, interval time.Duration, horizontalScale in
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -40,24 +40,36 @@ func NewMem(renderLock *sync.RWMutex, interval time.Duration, horizontalScale in
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Mem) update() {
|
func (self *MemWidget) update() {
|
||||||
main, err := psMem.VirtualMemory()
|
mainMemory, err := psMem.VirtualMemory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get main memory info from gopsutil: %v", err)
|
log.Printf("failed to get main memory info from gopsutil: %v", err)
|
||||||
} else {
|
} else {
|
||||||
self.Data["Main"] = append(self.Data["Main"], main.UsedPercent)
|
self.Data["Main"] = append(self.Data["Main"], mainMemory.UsedPercent)
|
||||||
mainTotalBytes, mainTotalMagnitude := utils.ConvertBytes(main.Total)
|
mainMemoryTotalBytes, mainMemoryTotalMagnitude := utils.ConvertBytes(mainMemory.Total)
|
||||||
mainUsedBytes, mainUsedMagnitude := utils.ConvertBytes(main.Used)
|
mainMemoryUsedBytes, mainMemoryUsedMagnitude := utils.ConvertBytes(mainMemory.Used)
|
||||||
self.Labels["Main"] = fmt.Sprintf("%3.0f%% %5.1f%s/%.0f%s", main.UsedPercent, mainUsedBytes, mainUsedMagnitude, mainTotalBytes, mainTotalMagnitude)
|
self.Labels["Main"] = fmt.Sprintf("%3.0f%% %5.1f%s/%.0f%s",
|
||||||
|
mainMemory.UsedPercent,
|
||||||
|
mainMemoryUsedBytes,
|
||||||
|
mainMemoryUsedMagnitude,
|
||||||
|
mainMemoryTotalBytes,
|
||||||
|
mainMemoryTotalMagnitude,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
swap, err := psMem.SwapMemory()
|
swapMemory, err := psMem.SwapMemory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get swap memory info from gopsutil: %v", err)
|
log.Printf("failed to get swap memory info from gopsutil: %v", err)
|
||||||
} else {
|
} else {
|
||||||
self.Data["Swap"] = append(self.Data["Swap"], swap.UsedPercent)
|
self.Data["Swap"] = append(self.Data["Swap"], swapMemory.UsedPercent)
|
||||||
swapTotalBytes, swapTotalMagnitude := utils.ConvertBytes(swap.Total)
|
swapMemoryTotalBytes, swapMemoryTotalMagnitude := utils.ConvertBytes(swapMemory.Total)
|
||||||
swapUsedBytes, swapUsedMagnitude := utils.ConvertBytes(swap.Used)
|
swapMemoryUsedBytes, swapMemoryUsedMagnitude := utils.ConvertBytes(swapMemory.Used)
|
||||||
self.Labels["Swap"] = fmt.Sprintf("%3.0f%% %5.1f%s/%.0f%s", swap.UsedPercent, swapUsedBytes, swapUsedMagnitude, swapTotalBytes, swapTotalMagnitude)
|
self.Labels["Swap"] = fmt.Sprintf("%3.0f%% %5.1f%s/%.0f%s",
|
||||||
|
swapMemory.UsedPercent,
|
||||||
|
swapMemoryUsedBytes,
|
||||||
|
swapMemoryUsedMagnitude,
|
||||||
|
swapMemoryTotalBytes,
|
||||||
|
swapMemoryTotalMagnitude,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,33 +12,33 @@ import (
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Net struct {
|
type NetWidget struct {
|
||||||
*ui.Sparklines
|
*ui.SparklineGroup
|
||||||
interval time.Duration
|
updateInterval time.Duration
|
||||||
|
|
||||||
// used to calculate recent network activity
|
// used to calculate recent network activity
|
||||||
prevRecvTotal uint64
|
totalBytesRecv uint64
|
||||||
prevSentTotal uint64
|
totalBytesSent uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNet(renderLock *sync.RWMutex) *Net {
|
func NewNetWidget(renderLock *sync.RWMutex) *NetWidget {
|
||||||
recv := ui.NewSparkline()
|
recvSparkline := ui.NewSparkline()
|
||||||
recv.Data = []int{}
|
recvSparkline.Data = []int{}
|
||||||
|
|
||||||
sent := ui.NewSparkline()
|
sentSparkline := ui.NewSparkline()
|
||||||
sent.Data = []int{}
|
sentSparkline.Data = []int{}
|
||||||
|
|
||||||
spark := ui.NewSparklines(recv, sent)
|
spark := ui.NewSparklineGroup(recvSparkline, sentSparkline)
|
||||||
self := &Net{
|
self := &NetWidget{
|
||||||
Sparklines: spark,
|
SparklineGroup: spark,
|
||||||
interval: time.Second,
|
updateInterval: time.Second,
|
||||||
}
|
}
|
||||||
self.Title = " Network Usage "
|
self.Title = " Network Usage "
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -48,60 +48,62 @@ func NewNet(renderLock *sync.RWMutex) *Net {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Net) update() {
|
func (self *NetWidget) update() {
|
||||||
interfaces, err := psNet.IOCounters(true)
|
interfaces, err := psNet.IOCounters(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get network activity from gopsutil: %v", err)
|
log.Printf("failed to get network activity from gopsutil: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var curRecvTotal uint64
|
|
||||||
var curSentTotal uint64
|
var totalBytesRecv uint64
|
||||||
|
var totalBytesSent uint64
|
||||||
for _, _interface := range interfaces {
|
for _, _interface := range interfaces {
|
||||||
// ignore VPN interface
|
// ignore VPN interface
|
||||||
if _interface.Name != "tun0" {
|
if _interface.Name != "tun0" {
|
||||||
curRecvTotal += _interface.BytesRecv
|
totalBytesRecv += _interface.BytesRecv
|
||||||
curSentTotal += _interface.BytesSent
|
totalBytesSent += _interface.BytesSent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var recvRecent uint64
|
|
||||||
var sentRecent uint64
|
|
||||||
|
|
||||||
if self.prevRecvTotal != 0 { // if this isn't the first update
|
var recentBytesRecv uint64
|
||||||
recvRecent = curRecvTotal - self.prevRecvTotal
|
var recentBytesSent uint64
|
||||||
sentRecent = curSentTotal - self.prevSentTotal
|
|
||||||
|
|
||||||
if int(recvRecent) < 0 {
|
if self.totalBytesRecv != 0 { // if this isn't the first update
|
||||||
log.Printf("error: negative value for recently received network data from gopsutil. recvRecent: %v", recvRecent)
|
recentBytesRecv = totalBytesRecv - self.totalBytesRecv
|
||||||
|
recentBytesSent = totalBytesSent - self.totalBytesSent
|
||||||
|
|
||||||
|
if int(recentBytesRecv) < 0 {
|
||||||
|
log.Printf("error: negative value for recently received network data from gopsutil. recentBytesRecv: %v", recentBytesRecv)
|
||||||
// recover from error
|
// recover from error
|
||||||
recvRecent = 0
|
recentBytesRecv = 0
|
||||||
}
|
}
|
||||||
if int(sentRecent) < 0 {
|
if int(recentBytesSent) < 0 {
|
||||||
log.Printf("error: negative value for recently sent network data from gopsutil. sentRecent: %v", sentRecent)
|
log.Printf("error: negative value for recently sent network data from gopsutil. recentBytesSent: %v", recentBytesSent)
|
||||||
// recover from error
|
// recover from error
|
||||||
sentRecent = 0
|
recentBytesSent = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
self.Lines[0].Data = append(self.Lines[0].Data, int(recvRecent))
|
self.Lines[0].Data = append(self.Lines[0].Data, int(recentBytesRecv))
|
||||||
self.Lines[1].Data = append(self.Lines[1].Data, int(sentRecent))
|
self.Lines[1].Data = append(self.Lines[1].Data, int(recentBytesSent))
|
||||||
}
|
}
|
||||||
|
|
||||||
// used in later calls to update
|
// used in later calls to update
|
||||||
self.prevRecvTotal = curRecvTotal
|
self.totalBytesRecv = totalBytesRecv
|
||||||
self.prevSentTotal = curSentTotal
|
self.totalBytesSent = totalBytesSent
|
||||||
|
|
||||||
// render widget titles
|
// render widget titles
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
total, label, recent := func() (uint64, string, uint64) {
|
total, label, recent := func() (uint64, string, uint64) {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return curRecvTotal, "RX", recvRecent
|
return totalBytesRecv, "RX", recentBytesRecv
|
||||||
}
|
}
|
||||||
return curSentTotal, "Tx", sentRecent
|
return totalBytesSent, "Tx", recentBytesSent
|
||||||
}()
|
}()
|
||||||
|
|
||||||
recentConv, unitRecent := utils.ConvertBytes(uint64(recent))
|
recentConverted, unitRecent := utils.ConvertBytes(uint64(recent))
|
||||||
totalConv, unitTotal := utils.ConvertBytes(uint64(total))
|
totalConverted, unitTotal := utils.ConvertBytes(uint64(total))
|
||||||
|
|
||||||
self.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", label, totalConv, unitTotal)
|
self.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", label, totalConverted, unitTotal)
|
||||||
self.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9.1f %2s/s", label, recentConv, unitRecent)
|
self.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9.1f %2s/s", label, recentConverted, unitRecent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gizak/termui"
|
|
||||||
psCPU "github.com/shirou/gopsutil/cpu"
|
psCPU "github.com/shirou/gopsutil/cpu"
|
||||||
|
|
||||||
ui "github.com/cjbassi/gotop/src/termui"
|
ui "github.com/cjbassi/gotop/src/termui"
|
||||||
|
@ -17,56 +16,67 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UP = "▲"
|
UP_ARROW = "▲"
|
||||||
DOWN = "▼"
|
DOWN_ARROW = "▼"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process represents each process.
|
type ProcSortMethod string
|
||||||
type Process struct {
|
|
||||||
PID int
|
const (
|
||||||
Command string
|
ProcSortCpu ProcSortMethod = "c"
|
||||||
CPU float64
|
ProcSortMem = "m"
|
||||||
Mem float64
|
ProcSortPid = "p"
|
||||||
Args string
|
)
|
||||||
}
|
|
||||||
|
|
||||||
type Proc struct {
|
type Proc struct {
|
||||||
*ui.Table
|
Pid int
|
||||||
cpuCount float64
|
CommandName string
|
||||||
interval time.Duration
|
FullCommand string
|
||||||
sortMethod string
|
Cpu float64
|
||||||
groupedProcs []Process
|
Mem float64
|
||||||
ungroupedProcs []Process
|
|
||||||
group bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProc(renderLock *sync.RWMutex) *Proc {
|
type ProcWidget struct {
|
||||||
|
*ui.Table
|
||||||
|
cpuCount float64
|
||||||
|
updateInterval time.Duration
|
||||||
|
sortMethod ProcSortMethod
|
||||||
|
groupedProcs []Proc
|
||||||
|
ungroupedProcs []Proc
|
||||||
|
showGroupedProcs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProcWidget(renderLock *sync.RWMutex) *ProcWidget {
|
||||||
cpuCount, err := psCPU.Counts(false)
|
cpuCount, err := psCPU.Counts(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get CPU count from gopsutil: %v", err)
|
log.Printf("failed to get CPU count from gopsutil: %v", err)
|
||||||
}
|
}
|
||||||
self := &Proc{
|
self := &ProcWidget{
|
||||||
Table: ui.NewTable(),
|
Table: ui.NewTable(),
|
||||||
interval: time.Second,
|
updateInterval: time.Second,
|
||||||
cpuCount: float64(cpuCount),
|
cpuCount: float64(cpuCount),
|
||||||
sortMethod: "c",
|
sortMethod: ProcSortCpu,
|
||||||
group: true,
|
showGroupedProcs: true,
|
||||||
}
|
}
|
||||||
self.Title = " Processes "
|
self.Title = " Processes "
|
||||||
self.ColResizer = self.ColResize
|
self.ShowCursor = true
|
||||||
self.Cursor = true
|
self.ColGap = 3
|
||||||
self.Gap = 3
|
|
||||||
self.PadLeft = 2
|
self.PadLeft = 2
|
||||||
|
self.ColResizer = func() {
|
||||||
|
self.ColWidths = []int{
|
||||||
|
5, utils.MaxInt(self.Inner.Dx()-26, 10), 4, 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.UniqueCol = 0
|
self.UniqueCol = 0
|
||||||
if self.group {
|
if self.showGroupedProcs {
|
||||||
self.UniqueCol = 1
|
self.UniqueCol = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -76,120 +86,104 @@ func NewProc(renderLock *sync.RWMutex) *Proc {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts either the grouped or ungrouped []Process based on the sortMethod.
|
func (self *ProcWidget) update() {
|
||||||
|
procs, err := getProcs()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to retrieve processes: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't iterate on the entries directly since we can't modify them that way
|
||||||
|
for i := range procs {
|
||||||
|
procs[i].Cpu /= self.cpuCount
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ungroupedProcs = procs
|
||||||
|
self.groupedProcs = groupProcs(procs)
|
||||||
|
|
||||||
|
self.sortProcs()
|
||||||
|
self.convertProcsToTableRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortProcs sorts either the grouped or ungrouped []Process based on the sortMethod.
|
||||||
// Called with every update, when the sort method is changed, and when processes are grouped and ungrouped.
|
// Called with every update, when the sort method is changed, and when processes are grouped and ungrouped.
|
||||||
func (self *Proc) Sort() {
|
func (self *ProcWidget) sortProcs() {
|
||||||
self.Header = []string{"Count", "Command", "CPU%", "Mem%"}
|
self.Header = []string{"Count", "Command", "CPU%", "Mem%"}
|
||||||
|
|
||||||
if !self.group {
|
if !self.showGroupedProcs {
|
||||||
self.Header[0] = "PID"
|
self.Header[0] = "PID"
|
||||||
}
|
}
|
||||||
|
|
||||||
processes := &self.ungroupedProcs
|
var procs *[]Proc
|
||||||
if self.group {
|
if self.showGroupedProcs {
|
||||||
processes = &self.groupedProcs
|
procs = &self.groupedProcs
|
||||||
|
} else {
|
||||||
|
procs = &self.ungroupedProcs
|
||||||
}
|
}
|
||||||
|
|
||||||
switch self.sortMethod {
|
switch self.sortMethod {
|
||||||
case "c":
|
case ProcSortCpu:
|
||||||
sort.Sort(sort.Reverse(ProcessByCPU(*processes)))
|
sort.Sort(sort.Reverse(SortProcsByCpu(*procs)))
|
||||||
self.Header[2] += DOWN
|
self.Header[2] += DOWN_ARROW
|
||||||
case "p":
|
case ProcSortPid:
|
||||||
if self.group {
|
if self.showGroupedProcs {
|
||||||
sort.Sort(sort.Reverse(ProcessByPID(*processes)))
|
sort.Sort(sort.Reverse(SortProcsByPid(*procs)))
|
||||||
} else {
|
} else {
|
||||||
sort.Sort(ProcessByPID(*processes))
|
sort.Sort(SortProcsByPid(*procs))
|
||||||
}
|
}
|
||||||
self.Header[0] += DOWN
|
self.Header[0] += DOWN_ARROW
|
||||||
case "m":
|
case ProcSortMem:
|
||||||
sort.Sort(sort.Reverse(ProcessByMem(*processes)))
|
sort.Sort(sort.Reverse(SortProcsByMem(*procs)))
|
||||||
self.Header[3] += DOWN
|
self.Header[3] += DOWN_ARROW
|
||||||
}
|
|
||||||
|
|
||||||
self.Rows = FieldsToStrings(*processes, self.group)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColResize overrides the default ColResize in the termui table.
|
|
||||||
func (self *Proc) ColResize() {
|
|
||||||
self.ColWidths = []int{
|
|
||||||
5, utils.Max(self.Inner.Dx()-26, 10), 4, 4,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Proc) ChangeSort(e termui.Event) {
|
// convertProcsToTableRows converts a []Proc to a [][]string and sets it to the table Rows
|
||||||
if self.sortMethod != e.ID {
|
func (self *ProcWidget) convertProcsToTableRows() {
|
||||||
self.sortMethod = e.ID
|
var procs *[]Proc
|
||||||
self.Top()
|
if self.showGroupedProcs {
|
||||||
self.Sort()
|
procs = &self.groupedProcs
|
||||||
|
} else {
|
||||||
|
procs = &self.ungroupedProcs
|
||||||
|
}
|
||||||
|
strings := make([][]string, len(*procs))
|
||||||
|
for i := range *procs {
|
||||||
|
strings[i] = make([]string, 4)
|
||||||
|
strings[i][0] = strconv.Itoa(int((*procs)[i].Pid))
|
||||||
|
if self.showGroupedProcs {
|
||||||
|
strings[i][1] = (*procs)[i].CommandName
|
||||||
|
} else {
|
||||||
|
strings[i][1] = (*procs)[i].FullCommand
|
||||||
|
}
|
||||||
|
strings[i][2] = fmt.Sprintf("%4s", strconv.FormatFloat((*procs)[i].Cpu, 'f', 1, 64))
|
||||||
|
strings[i][3] = fmt.Sprintf("%4s", strconv.FormatFloat(float64((*procs)[i].Mem), 'f', 1, 64))
|
||||||
|
}
|
||||||
|
self.Rows = strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ProcWidget) ChangeProcSortMethod(method ProcSortMethod) {
|
||||||
|
if self.sortMethod != method {
|
||||||
|
self.sortMethod = method
|
||||||
|
self.ScrollTop()
|
||||||
|
self.sortProcs()
|
||||||
|
self.convertProcsToTableRows()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Proc) Tab() {
|
func (self *ProcWidget) ToggleShowingGroupedProcs() {
|
||||||
self.group = !self.group
|
self.showGroupedProcs = !self.showGroupedProcs
|
||||||
if self.group {
|
if self.showGroupedProcs {
|
||||||
self.UniqueCol = 1
|
self.UniqueCol = 1
|
||||||
} else {
|
} else {
|
||||||
self.UniqueCol = 0
|
self.UniqueCol = 0
|
||||||
}
|
}
|
||||||
self.Sort()
|
self.ScrollTop()
|
||||||
self.Top()
|
self.sortProcs()
|
||||||
|
self.convertProcsToTableRows()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group groupes a []Process based on command name.
|
// KillProc kills a process or group of processes depending on if we're displaying the processes grouped or not.
|
||||||
// The first field changes from PID to count.
|
func (self *ProcWidget) KillProc() {
|
||||||
// CPU and Mem are added together for each Process.
|
|
||||||
func Group(P []Process) []Process {
|
|
||||||
groupedP := make(map[string]Process)
|
|
||||||
for _, process := range P {
|
|
||||||
val, ok := groupedP[process.Command]
|
|
||||||
if ok {
|
|
||||||
groupedP[process.Command] = Process{
|
|
||||||
val.PID + 1,
|
|
||||||
val.Command,
|
|
||||||
val.CPU + process.CPU,
|
|
||||||
val.Mem + process.Mem,
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
groupedP[process.Command] = Process{
|
|
||||||
1,
|
|
||||||
process.Command,
|
|
||||||
process.CPU,
|
|
||||||
process.Mem,
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
groupList := make([]Process, len(groupedP))
|
|
||||||
var i int
|
|
||||||
for _, val := range groupedP {
|
|
||||||
groupList[i] = val
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupList
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldsToStrings converts a []Process to a [][]string
|
|
||||||
func FieldsToStrings(P []Process, grouped bool) [][]string {
|
|
||||||
strings := make([][]string, len(P))
|
|
||||||
for i, p := range P {
|
|
||||||
strings[i] = make([]string, 4)
|
|
||||||
strings[i][0] = strconv.Itoa(int(p.PID))
|
|
||||||
if grouped {
|
|
||||||
strings[i][1] = p.Command
|
|
||||||
} else {
|
|
||||||
strings[i][1] = p.Args
|
|
||||||
}
|
|
||||||
strings[i][2] = fmt.Sprintf("%4s", strconv.FormatFloat(p.CPU, 'f', 1, 64))
|
|
||||||
strings[i][3] = fmt.Sprintf("%4s", strconv.FormatFloat(float64(p.Mem), 'f', 1, 64))
|
|
||||||
}
|
|
||||||
return strings
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill kills process or group of processes.
|
|
||||||
func (self *Proc) Kill() {
|
|
||||||
self.SelectedItem = ""
|
self.SelectedItem = ""
|
||||||
command := "kill"
|
command := "kill"
|
||||||
if self.UniqueCol == 1 {
|
if self.UniqueCol == 1 {
|
||||||
|
@ -200,57 +194,91 @@ func (self *Proc) Kill() {
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
// groupProcs groupes a []Proc based on command name.
|
||||||
// []Process Sorting //
|
// The first field changes from PID to count.
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
// Cpu and Mem are added together for each Proc.
|
||||||
|
func groupProcs(procs []Proc) []Proc {
|
||||||
|
groupedProcsMap := make(map[string]Proc)
|
||||||
|
for _, proc := range procs {
|
||||||
|
val, ok := groupedProcsMap[proc.CommandName]
|
||||||
|
if ok {
|
||||||
|
groupedProcsMap[proc.CommandName] = Proc{
|
||||||
|
val.Pid + 1,
|
||||||
|
val.CommandName,
|
||||||
|
"",
|
||||||
|
val.Cpu + proc.Cpu,
|
||||||
|
val.Mem + proc.Mem,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groupedProcsMap[proc.CommandName] = Proc{
|
||||||
|
1,
|
||||||
|
proc.CommandName,
|
||||||
|
"",
|
||||||
|
proc.Cpu,
|
||||||
|
proc.Mem,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ProcessByCPU []Process
|
groupedProcsList := make([]Proc, len(groupedProcsMap))
|
||||||
|
i := 0
|
||||||
|
for _, val := range groupedProcsMap {
|
||||||
|
groupedProcsList[i] = val
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedProcsList
|
||||||
|
}
|
||||||
|
|
||||||
|
// []Proc Sorting //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type SortProcsByCpu []Proc
|
||||||
|
|
||||||
// Len implements Sort interface
|
// Len implements Sort interface
|
||||||
func (P ProcessByCPU) Len() int {
|
func (self SortProcsByCpu) Len() int {
|
||||||
return len(P)
|
return len(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap implements Sort interface
|
// Swap implements Sort interface
|
||||||
func (P ProcessByCPU) Swap(i, j int) {
|
func (self SortProcsByCpu) Swap(i, j int) {
|
||||||
P[i], P[j] = P[j], P[i]
|
self[i], self[j] = self[j], self[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less implements Sort interface
|
// Less implements Sort interface
|
||||||
func (P ProcessByCPU) Less(i, j int) bool {
|
func (self SortProcsByCpu) Less(i, j int) bool {
|
||||||
return P[i].CPU < P[j].CPU
|
return self[i].Cpu < self[j].Cpu
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProcessByPID []Process
|
type SortProcsByPid []Proc
|
||||||
|
|
||||||
// Len implements Sort interface
|
// Len implements Sort interface
|
||||||
func (P ProcessByPID) Len() int {
|
func (self SortProcsByPid) Len() int {
|
||||||
return len(P)
|
return len(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap implements Sort interface
|
// Swap implements Sort interface
|
||||||
func (P ProcessByPID) Swap(i, j int) {
|
func (self SortProcsByPid) Swap(i, j int) {
|
||||||
P[i], P[j] = P[j], P[i]
|
self[i], self[j] = self[j], self[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less implements Sort interface
|
// Less implements Sort interface
|
||||||
func (P ProcessByPID) Less(i, j int) bool {
|
func (self SortProcsByPid) Less(i, j int) bool {
|
||||||
return P[i].PID < P[j].PID
|
return self[i].Pid < self[j].Pid
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProcessByMem []Process
|
type SortProcsByMem []Proc
|
||||||
|
|
||||||
// Len implements Sort interface
|
// Len implements Sort interface
|
||||||
func (P ProcessByMem) Len() int {
|
func (self SortProcsByMem) Len() int {
|
||||||
return len(P)
|
return len(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap implements Sort interface
|
// Swap implements Sort interface
|
||||||
func (P ProcessByMem) Swap(i, j int) {
|
func (self SortProcsByMem) Swap(i, j int) {
|
||||||
P[i], P[j] = P[j], P[i]
|
self[i], self[j] = self[j], self[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less implements Sort interface
|
// Less implements Sort interface
|
||||||
func (P ProcessByMem) Less(i, j int) bool {
|
func (self SortProcsByMem) Less(i, j int) bool {
|
||||||
return P[i].Mem < P[j].Mem
|
return self[i].Mem < self[j].Mem
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,35 +8,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *Proc) update() {
|
func getProcs() ([]Proc, error) {
|
||||||
processes, err := Processes()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to retrieve processes: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// have to iterate like this in order to actually change the value
|
|
||||||
for i := range processes {
|
|
||||||
processes[i].CPU /= self.cpuCount
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ungroupedProcs = processes
|
|
||||||
self.groupedProcs = Group(processes)
|
|
||||||
|
|
||||||
self.Sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Processes() ([]Process, error) {
|
|
||||||
output, err := exec.Command("ps", "-axo", "pid:10,comm:50,pcpu:5,pmem:5,args").Output()
|
output, err := exec.Command("ps", "-axo", "pid:10,comm:50,pcpu:5,pmem:5,args").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute 'ps' command: %v", err)
|
return nil, fmt.Errorf("failed to execute 'ps' command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts to []string, removing trailing newline and header
|
// converts to []string, removing trailing newline and header
|
||||||
processStrArr := strings.Split(strings.TrimSuffix(string(output), "\n"), "\n")[1:]
|
linesOfProcStrings := strings.Split(strings.TrimSuffix(string(output), "\n"), "\n")[1:]
|
||||||
|
|
||||||
processes := []Process{}
|
procs := []Proc{}
|
||||||
for _, line := range processStrArr {
|
for _, line := range linesOfProcStrings {
|
||||||
pid, err := strconv.Atoi(strings.TrimSpace(line[0:10]))
|
pid, err := strconv.Atoi(strings.TrimSpace(line[0:10]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to convert PID to int: %v. line: %v", err, line)
|
log.Printf("failed to convert PID to int: %v. line: %v", err, line)
|
||||||
|
@ -49,14 +31,15 @@ func Processes() ([]Process, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to convert Mem usage to float: %v. line: %v", err, line)
|
log.Printf("failed to convert Mem usage to float: %v. line: %v", err, line)
|
||||||
}
|
}
|
||||||
process := Process{
|
proc := Proc{
|
||||||
PID: pid,
|
Pid: pid,
|
||||||
Command: strings.TrimSpace(line[11:61]),
|
CommandName: strings.TrimSpace(line[11:61]),
|
||||||
CPU: cpu,
|
FullCommand: line[74:],
|
||||||
Mem: mem,
|
Cpu: cpu,
|
||||||
Args: line[74:],
|
Mem: mem,
|
||||||
}
|
}
|
||||||
processes = append(processes, process)
|
procs = append(procs, proc)
|
||||||
}
|
}
|
||||||
return processes, nil
|
|
||||||
|
return procs, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,31 +11,31 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Define column widths for ps output used in Processes()
|
// Define column widths for ps output used in Procs()
|
||||||
five = "12345"
|
five = "12345"
|
||||||
ten = five + five
|
ten = five + five
|
||||||
fifty = ten + ten + ten + ten + ten
|
fifty = ten + ten + ten + ten + ten
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *Proc) update() {
|
func (self *ProcWidget) update() {
|
||||||
processes, err := Processes()
|
procs, err := GetProcs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to retrieve processes: %v", err)
|
log.Printf("failed to retrieve processes: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// have to iterate like this in order to actually change the value
|
// have to iterate like this in order to actually change the value
|
||||||
for i := range processes {
|
for i := range procs {
|
||||||
processes[i].CPU /= self.cpuCount
|
procs[i].CPU /= self.cpuCount
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ungroupedProcs = processes
|
self.ungroupedProcs = procs
|
||||||
self.groupedProcs = Group(processes)
|
self.groupedProcs = GroupProcs(procs)
|
||||||
|
|
||||||
self.Sort()
|
self.SortProcesses()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Processes() ([]Process, error) {
|
func GetProcs() ([]Process, error) {
|
||||||
keywords := fmt.Sprintf("pid=%s,comm=%s,pcpu=%s,pmem=%s,args", ten, fifty, five, five)
|
keywords := fmt.Sprintf("pid=%s,comm=%s,pcpu=%s,pmem=%s,args", ten, fifty, five, five)
|
||||||
output, err := exec.Command("ps", "-caxo", keywords).Output()
|
output, err := exec.Command("ps", "-caxo", keywords).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -43,10 +43,10 @@ func Processes() ([]Process, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts to []string and removes the header
|
// converts to []string and removes the header
|
||||||
strOutput := strings.Split(strings.TrimSpace(string(output)), "\n")[1:]
|
linesOfProcStrings := strings.Split(strings.TrimSpace(string(output)), "\n")[1:]
|
||||||
|
|
||||||
processes := []Process{}
|
procs := []Proc{}
|
||||||
for _, line := range strOutput {
|
for _, line := range linesOfProcStrings {
|
||||||
pid, err := strconv.Atoi(strings.TrimSpace(line[0:10]))
|
pid, err := strconv.Atoi(strings.TrimSpace(line[0:10]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to convert first field to int: %v. split: %v", err, line)
|
log.Printf("failed to convert first field to int: %v. split: %v", err, line)
|
||||||
|
@ -59,14 +59,15 @@ func Processes() ([]Process, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to convert fourth field to float: %v. split: %v", err, line)
|
log.Printf("failed to convert fourth field to float: %v. split: %v", err, line)
|
||||||
}
|
}
|
||||||
process := Process{
|
proc := Proc{
|
||||||
PID: pid,
|
PID: pid,
|
||||||
Command: strings.TrimSpace(line[11:61]),
|
Command: strings.TrimSpace(line[11:61]),
|
||||||
CPU: cpu,
|
CPU: cpu,
|
||||||
Mem: mem,
|
Mem: mem,
|
||||||
Args: line[74:],
|
Args: line[74:],
|
||||||
}
|
}
|
||||||
processes = append(processes, process)
|
procs = append(procs, proc)
|
||||||
}
|
}
|
||||||
return processes, nil
|
|
||||||
|
return procs, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,35 @@
|
||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
psProc "github.com/shirou/gopsutil/process"
|
psProc "github.com/shirou/gopsutil/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *Proc) update() {
|
func getProcs() ([]Proc, error) {
|
||||||
psProcesses, err := psProc.Processes()
|
psProcs, err := psProc.Processes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get processes from gopsutil: %v", err)
|
return nil, fmt.Errorf("failed to get processes from gopsutil: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
processes := make([]Process, len(psProcesses))
|
|
||||||
for i, psProcess := range psProcesses {
|
procs := make([]Proc, len(psProcs))
|
||||||
pid := psProcess.Pid
|
for i, psProc := range psProcs {
|
||||||
command, err := psProcess.Name()
|
pid := psProc.Pid
|
||||||
|
command, err := psProc.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get process command from gopsutil: %v. psProcess: %v. i: %v. pid: %v", err, psProcess, i, pid)
|
log.Printf("failed to get process command from gopsutil: %v. psProc: %v. i: %v. pid: %v", err, psProc, i, pid)
|
||||||
}
|
}
|
||||||
cpu, err := psProcess.CPUPercent()
|
cpu, err := psProc.CPUPercent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get process cpu usage from gopsutil: %v. psProcess: %v. i: %v. pid: %v", err, psProcess, i, pid)
|
log.Printf("failed to get process cpu usage from gopsutil: %v. psProc: %v. i: %v. pid: %v", err, psProc, i, pid)
|
||||||
}
|
}
|
||||||
mem, err := psProcess.MemoryPercent()
|
mem, err := psProc.MemoryPercent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get process memeory usage from gopsutil: %v. psProcess: %v. i: %v. pid: %v", err, psProcess, i, pid)
|
log.Printf("failed to get process memeory usage from gopsutil: %v. psProc: %v. i: %v. pid: %v", err, psProc, i, pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
processes[i] = Process{
|
procs[i] = Proc{
|
||||||
int(pid),
|
int(pid),
|
||||||
command,
|
command,
|
||||||
cpu / self.cpuCount,
|
cpu / self.cpuCount,
|
||||||
|
@ -39,8 +40,5 @@ func (self *Proc) update() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ungroupedProcs = processes
|
return procs, nil
|
||||||
self.groupedProcs = Group(processes)
|
|
||||||
|
|
||||||
self.Sort()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -21,27 +22,31 @@ func NewStatusBar() *StatusBar {
|
||||||
func (self *StatusBar) Draw(buf *ui.Buffer) {
|
func (self *StatusBar) Draw(buf *ui.Buffer) {
|
||||||
self.Block.Draw(buf)
|
self.Block.Draw(buf)
|
||||||
|
|
||||||
hostname, _ := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("could not get hostname: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
hostname,
|
hostname,
|
||||||
ui.NewStyle(7),
|
ui.NewStyle(ui.ColorWhite),
|
||||||
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+(self.Inner.Dy()/2)),
|
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+(self.Inner.Dy()/2)),
|
||||||
)
|
)
|
||||||
|
|
||||||
t := time.Now()
|
currentTime := time.Now()
|
||||||
_time := t.Format("15:04:05")
|
formattedTime := currentTime.Format("15:04:05")
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
_time,
|
formattedTime,
|
||||||
ui.NewStyle(7),
|
ui.NewStyle(ui.ColorWhite),
|
||||||
image.Pt(
|
image.Pt(
|
||||||
self.Inner.Min.X+(self.Inner.Dx()/2)-len(_time)/2,
|
self.Inner.Min.X+(self.Inner.Dx()/2)-len(formattedTime)/2,
|
||||||
self.Inner.Min.Y+(self.Inner.Dy()/2),
|
self.Inner.Min.Y+(self.Inner.Dy()/2),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
"gotop",
|
"gotop",
|
||||||
ui.NewStyle(7),
|
ui.NewStyle(ui.ColorWhite),
|
||||||
image.Pt(
|
image.Pt(
|
||||||
self.Inner.Max.X-6,
|
self.Inner.Max.X-6,
|
||||||
self.Inner.Min.Y+(self.Inner.Dy()/2),
|
self.Inner.Min.Y+(self.Inner.Dy()/2),
|
||||||
|
|
|
@ -8,36 +8,45 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
|
|
||||||
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Temp struct {
|
type TempScale int
|
||||||
*ui.Block // inherits from Block instead of a premade Widget
|
|
||||||
interval time.Duration
|
const (
|
||||||
Data map[string]int
|
Celcius TempScale = 0
|
||||||
Threshold int
|
Fahrenheit = 1
|
||||||
TempLow ui.Color
|
)
|
||||||
TempHigh ui.Color
|
|
||||||
Fahrenheit bool
|
type TempWidget struct {
|
||||||
|
*ui.Block // inherits from Block instead of a premade Widget
|
||||||
|
updateInterval time.Duration
|
||||||
|
Data map[string]int
|
||||||
|
TempThreshold int
|
||||||
|
TempLowColor ui.Color
|
||||||
|
TempHighColor ui.Color
|
||||||
|
TempScale TempScale
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTemp(renderLock *sync.RWMutex, fahrenheit bool) *Temp {
|
func NewTempWidget(renderLock *sync.RWMutex, tempScale TempScale) *TempWidget {
|
||||||
self := &Temp{
|
self := &TempWidget{
|
||||||
Block: ui.NewBlock(),
|
Block: ui.NewBlock(),
|
||||||
interval: time.Second * 5,
|
updateInterval: time.Second * 5,
|
||||||
Data: make(map[string]int),
|
Data: make(map[string]int),
|
||||||
Threshold: 80, // temp at which color should change
|
TempThreshold: 80,
|
||||||
|
TempScale: tempScale,
|
||||||
}
|
}
|
||||||
self.Title = " Temperatures "
|
self.Title = " Temperatures "
|
||||||
|
|
||||||
if fahrenheit {
|
if tempScale == Fahrenheit {
|
||||||
self.Fahrenheit = true
|
self.TempThreshold = utils.CelsiusToFahrenheit(self.TempThreshold)
|
||||||
self.Threshold = int(self.Threshold*9/5 + 32)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.NewTicker(self.interval).C {
|
for range time.NewTicker(self.updateInterval).C {
|
||||||
renderLock.RLock()
|
renderLock.RLock()
|
||||||
self.update()
|
self.update()
|
||||||
renderLock.RUnlock()
|
renderLock.RUnlock()
|
||||||
|
@ -47,8 +56,8 @@ func NewTemp(renderLock *sync.RWMutex, fahrenheit bool) *Temp {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
// We implement a custom Draw method instead of inheriting from a generic Widget.
|
// Custom Draw method instead of inheriting from a generic Widget.
|
||||||
func (self *Temp) Draw(buf *ui.Buffer) {
|
func (self *TempWidget) Draw(buf *ui.Buffer) {
|
||||||
self.Block.Draw(buf)
|
self.Block.Draw(buf)
|
||||||
|
|
||||||
var keys []string
|
var keys []string
|
||||||
|
@ -62,9 +71,11 @@ func (self *Temp) Draw(buf *ui.Buffer) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
fg := self.TempLow
|
var fg ui.Color
|
||||||
if self.Data[key] >= self.Threshold {
|
if self.Data[key] < self.TempThreshold {
|
||||||
fg = self.TempHigh
|
fg = self.TempLowColor
|
||||||
|
} else {
|
||||||
|
fg = self.TempHighColor
|
||||||
}
|
}
|
||||||
|
|
||||||
s := ui.TrimString(key, (self.Inner.Dx() - 4))
|
s := ui.TrimString(key, (self.Inner.Dx() - 4))
|
||||||
|
@ -72,13 +83,15 @@ func (self *Temp) Draw(buf *ui.Buffer) {
|
||||||
ui.Theme.Default,
|
ui.Theme.Default,
|
||||||
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+y),
|
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+y),
|
||||||
)
|
)
|
||||||
if self.Fahrenheit {
|
|
||||||
|
switch self.TempScale {
|
||||||
|
case Fahrenheit:
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
fmt.Sprintf("%3dF", self.Data[key]),
|
fmt.Sprintf("%3dF", self.Data[key]),
|
||||||
ui.NewStyle(fg),
|
ui.NewStyle(fg),
|
||||||
image.Pt(self.Inner.Max.X-4, self.Inner.Min.Y+y),
|
image.Pt(self.Inner.Max.X-4, self.Inner.Min.Y+y),
|
||||||
)
|
)
|
||||||
} else {
|
case Celcius:
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
fmt.Sprintf("%3dC", self.Data[key]),
|
fmt.Sprintf("%3dC", self.Data[key]),
|
||||||
ui.NewStyle(fg),
|
ui.NewStyle(fg),
|
||||||
|
|
|
@ -53,7 +53,7 @@ func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||||
return temperatures, nil
|
return temperatures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Temp) update() {
|
func (self *TempWidget) update() {
|
||||||
sensors, err := SensorsTemperatures()
|
sensors, err := SensorsTemperatures()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get sensors from CGO: %v", err)
|
log.Printf("failed to get sensors from CGO: %v", err)
|
||||||
|
@ -61,9 +61,10 @@ func (self *Temp) update() {
|
||||||
}
|
}
|
||||||
for _, sensor := range sensors {
|
for _, sensor := range sensors {
|
||||||
if sensor.Temperature != 0 {
|
if sensor.Temperature != 0 {
|
||||||
if self.Fahrenheit {
|
switch self.TempScale {
|
||||||
|
case Fahrenheit:
|
||||||
self.Data[sensor.SensorKey] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
self.Data[sensor.SensorKey] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
||||||
} else {
|
case Celcius:
|
||||||
self.Data[sensor.SensorKey] = int(sensor.Temperature)
|
self.Data[sensor.SensorKey] = int(sensor.Temperature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
|
// loosely based on https://github.com/openbsd/src/blob/master/sbin/sysctl/sysctl.c#L2517
|
||||||
|
|
||||||
// #include <sys/time.h>
|
// #include <sys/time.h>
|
||||||
// #include <sys/sysctl.h>
|
// #include <sys/sysctl.h>
|
||||||
// #include <sys/sensors.h>
|
// #include <sys/sensors.h>
|
||||||
|
@ -13,14 +15,14 @@ import (
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getTemp(t *Temp, mib []C.int, mlen int, snsrdev *C.struct_sensordev, index int) {
|
func (self *TempWidget) getTemp(mib []C.int, mlen int, snsrdev *C.struct_sensordev, index int) {
|
||||||
switch mlen {
|
switch mlen {
|
||||||
case 4:
|
case 4:
|
||||||
k := mib[3]
|
k := mib[3]
|
||||||
var numt C.int
|
var numt C.int
|
||||||
for numt = 0; numt < snsrdev.maxnumt[k]; numt++ {
|
for numt = 0; numt < snsrdev.maxnumt[k]; numt++ {
|
||||||
mib[4] = numt
|
mib[4] = numt
|
||||||
getTemp(t, mib, mlen+1, snsrdev, int(numt))
|
self.getTemp(mib, mlen+1, snsrdev, int(numt))
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
var snsr C.struct_sensor
|
var snsr C.struct_sensor
|
||||||
|
@ -34,16 +36,17 @@ func getTemp(t *Temp, mib []C.int, mlen int, snsrdev *C.struct_sensordev, index
|
||||||
key := C.GoString(&snsrdev.xname[0]) + ".temp" + strconv.Itoa(index)
|
key := C.GoString(&snsrdev.xname[0]) + ".temp" + strconv.Itoa(index)
|
||||||
temp := int((snsr.value - 273150000.0) / 1000000.0)
|
temp := int((snsr.value - 273150000.0) / 1000000.0)
|
||||||
|
|
||||||
if t.Fahrenheit {
|
switch self.TempScale {
|
||||||
t.Data[key] = utils.CelsiusToFahrenheit(temp)
|
case Fahrenheit:
|
||||||
} else {
|
self.Data[key] = utils.CelsiusToFahrenheit(temp)
|
||||||
t.Data[key] = temp
|
case Celcius:
|
||||||
|
self.Data[key] = temp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Temp) update() {
|
func (self *TempWidget) update() {
|
||||||
mib := []C.int{0, 1, 2, 3, 4}
|
mib := []C.int{0, 1, 2, 3, 4}
|
||||||
|
|
||||||
var snsrdev C.struct_sensordev
|
var snsrdev C.struct_sensordev
|
||||||
|
@ -56,17 +59,14 @@ func (self *Temp) update() {
|
||||||
var i C.int
|
var i C.int
|
||||||
for i = 0; ; i++ {
|
for i = 0; ; i++ {
|
||||||
mib[2] = i
|
mib[2] = i
|
||||||
|
|
||||||
if v, e := C.sysctl(&mib[0], 3, unsafe.Pointer(&snsrdev), &len, nil, 0); v == -1 {
|
if v, e := C.sysctl(&mib[0], 3, unsafe.Pointer(&snsrdev), &len, nil, 0); v == -1 {
|
||||||
if e == syscall.ENXIO {
|
if e == syscall.ENXIO {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if e == syscall.ENOENT {
|
if e == syscall.ENOENT {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.getTemp(mib, 4, &snsrdev, 0)
|
||||||
getTemp(self, mib, 4, &snsrdev, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *Temp) update() {
|
func (self *TempWidget) update() {
|
||||||
sensors, err := psHost.SensorsTemperatures()
|
sensors, err := psHost.SensorsTemperatures()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error recieved from gopsutil: %v", err)
|
log.Printf("error recieved from gopsutil: %v", err)
|
||||||
|
@ -21,9 +21,10 @@ func (self *Temp) update() {
|
||||||
if strings.Contains(sensor.SensorKey, "input") && sensor.Temperature != 0 {
|
if strings.Contains(sensor.SensorKey, "input") && sensor.Temperature != 0 {
|
||||||
// removes '_input' from the end of the sensor name
|
// removes '_input' from the end of the sensor name
|
||||||
label := sensor.SensorKey[:strings.Index(sensor.SensorKey, "_input")]
|
label := sensor.SensorKey[:strings.Index(sensor.SensorKey, "_input")]
|
||||||
if self.Fahrenheit {
|
switch self.TempScale {
|
||||||
|
case Fahrenheit:
|
||||||
self.Data[label] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
self.Data[label] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
||||||
} else {
|
case Celcius:
|
||||||
self.Data[label] = int(sensor.Temperature)
|
self.Data[label] = int(sensor.Temperature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *Temp) update() {
|
func (self *TempWidget) update() {
|
||||||
sensors, err := psHost.SensorsTemperatures()
|
sensors, err := psHost.SensorsTemperatures()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to get sensors from gopsutil: %v", err)
|
log.Printf("failed to get sensors from gopsutil: %v", err)
|
||||||
|
@ -16,9 +16,10 @@ func (self *Temp) update() {
|
||||||
}
|
}
|
||||||
for _, sensor := range sensors {
|
for _, sensor := range sensors {
|
||||||
if sensor.Temperature != 0 {
|
if sensor.Temperature != 0 {
|
||||||
if self.Fahrenheit {
|
switch self.TempScale {
|
||||||
|
case Fahrenheit:
|
||||||
self.Data[sensor.SensorKey] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
self.Data[sensor.SensorKey] = utils.CelsiusToFahrenheit(int(sensor.Temperature))
|
||||||
} else {
|
case Celcius:
|
||||||
self.Data[sensor.SensorKey] = int(sensor.Temperature)
|
self.Data[sensor.SensorKey] = int(sensor.Temperature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user