diff --git a/README.md b/README.md index 7021e93c..0dfc2bbf 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ git clone --depth 1 https://github.com/cjbassi/gotop /tmp/gotop /tmp/gotop/scripts/download.sh ``` -Then move `gotop` into your $PATH somewhere. +Then move `gotop` into your \$PATH somewhere. ### Arch Linux @@ -47,18 +47,25 @@ go get github.com/cjbassi/gotop ### Keybinds - Quit: `q` or `` -- Process Navigation: - - ``/`` and `j`/`k`: up and down - - `` and ``: up and down half a page - - `` and ``: up and down a full page - - `gg` and `G`: jump to top and bottom -- Process Sorting: +- Process navigation + - `k` and ``: up + - `j` and ``: half page up + - ``: half page down + - ``: full page up + - ``: full page down + - `gg` and ``: jump to top + - `G` and ``: jump to bottom +- Process actions: + - ``: toggle process grouping + - `dd`: kill selected process or group of processes +- Process sorting - `c`: CPU - `m`: Mem - `p`: PID -- ``: toggle process grouping -- `dd`: kill the selected process or process group -- `h` and `l`: zoom in and out of CPU and Mem graphs +- CPU and Mem graph scaling: + - `h`: scale in + - `l`: scale out - `?`: toggles keybind help menu ### Mouse diff --git a/go.mod b/go.mod index 18ae0500..5cd7656f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cjbassi/gotop require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/cjbassi/drawille-go v0.0.0-20180329221028-ad535d0f92cd // indirect - github.com/cjbassi/termui v0.0.0-20180823181054-5edfcb3a441f + github.com/cjbassi/termui v0.0.0-20181129231847-3a3db079d9dd github.com/davecgh/go-spew v1.1.1 // indirect github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 github.com/go-ole/go-ole v1.2.1 // indirect diff --git a/go.sum b/go.sum index f3622d12..136220ce 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ github.com/cjbassi/drawille-go v0.0.0-20180329221028-ad535d0f92cd h1:nSJpATLVvFa github.com/cjbassi/drawille-go v0.0.0-20180329221028-ad535d0f92cd/go.mod h1:vjcQJUZJYD3MeVGhtZXSMnCHfUNZxsyYzJt90eCYxK4= github.com/cjbassi/termui v0.0.0-20180823181054-5edfcb3a441f h1:t8d9FIPBeDHClPJBkB8yJyIBcMIxzdMAY2xB1vWHi48= github.com/cjbassi/termui v0.0.0-20180823181054-5edfcb3a441f/go.mod h1:rqXckrwz+i0fH/zNwU6AdBNULHwmZsgehnSlhKP5i2Q= +github.com/cjbassi/termui v0.0.0-20181129202454-e08bceac6d82 h1:Nohf7C2tEJfEtfJ2mAF244MPJUj6JT9Quzf4ZrkmEfE= +github.com/cjbassi/termui v0.0.0-20181129202454-e08bceac6d82/go.mod h1:rqXckrwz+i0fH/zNwU6AdBNULHwmZsgehnSlhKP5i2Q= +github.com/cjbassi/termui v0.0.0-20181129231847-3a3db079d9dd h1:12/9RCEyFB4mnNlafhySzhfPgFIfTxbjHrkhEX0SgDQ= +github.com/cjbassi/termui v0.0.0-20181129231847-3a3db079d9dd/go.mod h1:rqXckrwz+i0fH/zNwU6AdBNULHwmZsgehnSlhKP5i2Q= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= diff --git a/main.go b/main.go index c74e7158..53997e84 100644 --- a/main.go +++ b/main.go @@ -21,27 +21,15 @@ import ( var version = "1.5.1" var ( - termResized = make(chan bool, 1) - - helpToggled = make(chan bool, 1) - helpVisible = false - - wg sync.WaitGroup - // used to render the proc widget whenever a key is pressed for it - keyPressed = make(chan bool, 1) - // used to render cpu and mem when zoom has changed - zoomed = make(chan bool, 1) - - colorscheme = colorschemes.Default - + colorscheme = colorschemes.Default minimal = false - widgetCount = 6 interval = time.Second zoom = 7 zoomInterval = 3 - - averageLoad = false - percpuLoad = false + helpVisible = false + averageLoad = false + percpuLoad = false + widgetCount = 6 cpu *w.CPU mem *w.Mem @@ -49,7 +37,6 @@ var ( net *w.Net disk *w.Disk temp *w.Temp - help *w.HelpMenu ) @@ -78,6 +65,8 @@ Colorschemes: if val, _ := args["--color"]; val != nil { handleColorscheme(val.(string)) } + averageLoad, _ = args["--averagecpu"].(bool) + percpuLoad, _ = args["--percpu"].(bool) minimal, _ = args["--minimal"].(bool) if minimal { @@ -91,9 +80,6 @@ Colorschemes: } else { interval = time.Second / time.Duration(rate) } - - averageLoad, _ = args["--averagecpu"].(bool) - percpuLoad, _ = args["--percpu"].(bool) } func handleColorscheme(cs string) { @@ -153,41 +139,6 @@ func setupGrid() { } } -func keyBinds() { - // quits - ui.On("q", "", func(e ui.Event) { - ui.StopLoop() - }) - - // toggles help menu - ui.On("?", func(e ui.Event) { - helpToggled <- true - helpVisible = !helpVisible - }) - // hides help menu - ui.On("", func(e ui.Event) { - if helpVisible { - helpToggled <- true - helpVisible = false - } - }) - - ui.On("h", func(e ui.Event) { - zoom += zoomInterval - cpu.Zoom = zoom - mem.Zoom = zoom - zoomed <- true - }) - ui.On("l", func(e ui.Event) { - if zoom > zoomInterval { - zoom -= zoomInterval - cpu.Zoom = zoom - mem.Zoom = zoom - zoomed <- true - } - }) -} - func termuiColors() { ui.Theme.Fg = ui.Color(colorscheme.Fg) ui.Theme.Bg = ui.Color(colorscheme.Bg) @@ -227,8 +178,8 @@ func widgetColors() { } } -// load widgets asynchronously but wait till they are all finished func initWidgets() { + var wg sync.WaitGroup wg.Add(widgetCount) go func() { @@ -240,7 +191,7 @@ func initWidgets() { wg.Done() }() go func() { - proc = w.NewProc(keyPressed) + proc = w.NewProc() wg.Done() }() if !minimal { @@ -261,75 +212,132 @@ func initWidgets() { wg.Wait() } -func main() { - cliArguments() - - keyBinds() - - // need to do this before initializing widgets so that they can inherit the colors - termuiColors() - - initWidgets() - - widgetColors() - - help = w.NewHelpMenu() - - // inits termui - err := ui.Init() - if err != nil { - panic(err) - } - defer ui.Close() +func eventLoop() { + drawTicker := time.NewTicker(interval).C - setupGrid() + // handles kill signal sent to gotop + sigTerm := make(chan os.Signal, 2) + signal.Notify(sigTerm, os.Interrupt, syscall.SIGTERM) - ui.On("", func(e ui.Event) { - ui.Body.Width, ui.Body.Height = e.Width, e.Height - ui.Body.Resize() + uiEvents := ui.PollEvents() - termResized <- true - }) + previousKey := "" - // all rendering done here - go func() { - ui.Render(ui.Body) - drawTick := time.NewTicker(interval) - for { - if helpVisible { - select { - case <-helpToggled: - ui.Render(ui.Body) - case <-termResized: + for { + select { + case <-sigTerm: + return + case <-drawTicker: + if !helpVisible { + ui.Render(ui.Body) + } + case e := <-uiEvents: + switch e.ID { + case "q", "": + return + case "?": + helpVisible = !helpVisible + if helpVisible { ui.Clear() ui.Render(help) + } else { + ui.Render(ui.Body) } - } else { - select { - case <-helpToggled: - ui.Clear() + case "h": + if !helpVisible { + zoom += zoomInterval + cpu.Zoom = zoom + mem.Zoom = zoom + ui.Render(cpu, mem) + } + case "l": + if !helpVisible { + if zoom > zoomInterval { + zoom -= zoomInterval + cpu.Zoom = zoom + mem.Zoom = zoom + ui.Render(cpu, mem) + } + } + case "": + if helpVisible { + helpVisible = false + ui.Render(ui.Body) + } + case "": + payload := e.Payload.(ui.Resize) + ui.Body.Width, ui.Body.Height = payload.Width, payload.Height + ui.Body.Resize() + ui.Clear() + if helpVisible { ui.Render(help) - case <-termResized: - ui.Clear() + } else { ui.Render(ui.Body) - case <-keyPressed: + } + + case "": + payload := e.Payload.(ui.Mouse) + proc.Click(payload.X, payload.Y) + ui.Render(proc) + case "", "", "k": + proc.Up() + ui.Render(proc) + case "", "", "j": + proc.Down() + ui.Render(proc) + case "g", "": + if previousKey == "g" { + proc.Top() ui.Render(proc) - case <-zoomed: - ui.Render(cpu, mem) - case <-drawTick.C: - ui.Render(ui.Body) + previousKey = "" + } + case "G", "": + proc.Bottom() + ui.Render(proc) + case "": + proc.HalfPageDown() + ui.Render(proc) + case "": + proc.HalfPageUp() + ui.Render(proc) + case "": + proc.PageDown() + ui.Render(proc) + case "": + proc.PageUp() + ui.Render(proc) + case "d": + if previousKey == "d" { + proc.Kill() + previousKey = "" } + case "": + proc.Tab() + ui.Render(proc) + case "m", "c", "p": + proc.ChangeSort(e) + ui.Render(proc) } + previousKey = e.ID } - }() + } +} - // handles kill signal sent to gotop - c := make(chan os.Signal, 2) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - <-c - ui.StopLoop() - }() +func main() { + cliArguments() + termuiColors() // need to do this before initializing widgets so that they can inherit the colors + initWidgets() + widgetColors() + help = w.NewHelpMenu() - ui.Loop() + // inits termui + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + setupGrid() + ui.Render(ui.Body) + eventLoop() } diff --git a/src/widgets/cpu.go b/src/widgets/cpu.go index 043aa01c..1079bbb0 100644 --- a/src/widgets/cpu.go +++ b/src/widgets/cpu.go @@ -48,10 +48,10 @@ func NewCPU(interval time.Duration, zoom int, average bool, percpu bool) *CPU { } } - ticker := time.NewTicker(self.interval) + go self.update() // update asynchronously because of 1 second blocking period + go func() { - // update asynchronously because of 1 second blocking period - go self.update() + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() } diff --git a/src/widgets/disk.go b/src/widgets/disk.go index a6c8b44e..82b416ee 100644 --- a/src/widgets/disk.go +++ b/src/widgets/disk.go @@ -41,8 +41,8 @@ func NewDisk() *Disk { self.update() - ticker := time.NewTicker(self.interval) go func() { + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() } diff --git a/src/widgets/help.go b/src/widgets/help.go index ed1bf773..19fa302c 100644 --- a/src/widgets/help.go +++ b/src/widgets/help.go @@ -9,21 +9,28 @@ import ( const KEYBINDS = ` Quit: q or -Process Navigation - - / and j/k: up and down - - and : up and down half a page - - and : up and down a full page - - gg and G: jump to top and bottom - -Process Sorting +Process navigation + - k and : up + - j and : down + - : half page up + - : half page down + - : full page up + - : full page down + - gg and : jump to top + - G and : jump to bottom + +Process actions: + - : toggle process grouping + - dd: kill selected process or group of processes + +Process sorting - c: CPU - m: Mem - p: PID -: toggle process grouping -dd: kill the selected process or process group - -h and l: zoom in and out of CPU and Mem graphs +CPU and Mem graph scaling: + - h: scale in + - l: scale out ` type HelpMenu struct { @@ -32,8 +39,8 @@ type HelpMenu struct { func NewHelpMenu() *HelpMenu { block := ui.NewBlock() - block.X = 48 // width - 1 - block.Y = 17 // height - 1 + block.X = 51 // width - 1 + block.Y = 24 // height - 1 return &HelpMenu{block} } diff --git a/src/widgets/mem.go b/src/widgets/mem.go index a3f1c65b..63a6a789 100644 --- a/src/widgets/mem.go +++ b/src/widgets/mem.go @@ -26,8 +26,8 @@ func NewMem(interval time.Duration, zoom int) *Mem { self.update() - ticker := time.NewTicker(self.interval) go func() { + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() } diff --git a/src/widgets/net.go b/src/widgets/net.go index 21a675ec..9e928da8 100644 --- a/src/widgets/net.go +++ b/src/widgets/net.go @@ -33,8 +33,8 @@ func NewNet() *Net { self.update() - ticker := time.NewTicker(self.interval) go func() { + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() } @@ -48,8 +48,8 @@ func (self *Net) update() { interfaces, _ := psNet.IOCounters(false) curRecvTotal := interfaces[0].BytesRecv curSentTotal := interfaces[0].BytesSent - var recvRecent uint64 = 0 - var sentRecent uint64 = 0 + var recvRecent uint64 + var sentRecent uint64 if self.prevRecvTotal != 0 { // if this isn't the first update recvRecent = curRecvTotal - self.prevRecvTotal diff --git a/src/widgets/proc.go b/src/widgets/proc.go index 4ec636de..f6e4ec56 100644 --- a/src/widgets/proc.go +++ b/src/widgets/proc.go @@ -34,10 +34,9 @@ type Proc struct { groupedProcs []Process ungroupedProcs []Process group bool - KeyPressed chan bool } -func NewProc(keyPressed chan bool) *Proc { +func NewProc() *Proc { cpuCount, _ := psCPU.Counts(false) self := &Proc{ Table: ui.NewTable(), @@ -45,7 +44,6 @@ func NewProc(keyPressed chan bool) *Proc { cpuCount: float64(cpuCount), sortMethod: "c", group: true, - KeyPressed: keyPressed, } self.Label = "Processes" self.ColResizer = self.ColResize @@ -58,12 +56,10 @@ func NewProc(keyPressed chan bool) *Proc { self.UniqueCol = 1 } - self.keyBinds() - self.update() - ticker := time.NewTicker(self.interval) go func() { + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() } @@ -112,83 +108,23 @@ func (self *Proc) ColResize() { } } -func (self *Proc) keyBinds() { - ui.On("", func(e ui.Event) { - self.Click(e.MouseX, e.MouseY) - self.KeyPressed <- true - }) - - ui.On("", "", func(e ui.Event) { - switch e.Key { - case "": - self.Down() - case "": - self.Up() - } - self.KeyPressed <- true - }) - - ui.On("", "", func(e ui.Event) { - switch e.Key { - case "": - self.Up() - case "": - self.Down() - } - self.KeyPressed <- true - }) - - viKeys := []string{"j", "k", "gg", "G", "", "", "", "", "", ""} - ui.On(viKeys, func(e ui.Event) { - switch e.Key { - case "j": - self.Down() - case "k": - self.Up() - case "": - fallthrough - case "gg": - self.Top() - case "": - fallthrough - case "G": - self.Bottom() - case "": - self.HalfPageDown() - case "": - self.HalfPageUp() - case "": - self.PageDown() - case "": - self.PageUp() - } - self.KeyPressed <- true - }) - - ui.On("dd", func(e ui.Event) { - self.Kill() - }) - - ui.On("", func(e ui.Event) { - self.group = !self.group - if self.group { - self.UniqueCol = 1 - } else { - self.UniqueCol = 0 - } - self.Sort() +func (self *Proc) ChangeSort(e ui.Event) { + if self.sortMethod != e.ID { + self.sortMethod = e.ID self.Top() - self.KeyPressed <- true - }) - - ui.On("m", "c", "p", func(e ui.Event) { - if self.sortMethod != e.Key { - self.sortMethod = e.Key - self.Top() - self.Sort() - self.KeyPressed <- true - } - }) + self.Sort() + } +} + +func (self *Proc) Tab() { + self.group = !self.group + if self.group { + self.UniqueCol = 1 + } else { + self.UniqueCol = 0 + } + self.Sort() + self.Top() } // Group groupes a []Process based on command name. diff --git a/src/widgets/temp.go b/src/widgets/temp.go index c366cb2f..2185a0f7 100644 --- a/src/widgets/temp.go +++ b/src/widgets/temp.go @@ -31,8 +31,8 @@ func NewTemp() *Temp { self.update() - ticker := time.NewTicker(self.interval) go func() { + ticker := time.NewTicker(self.interval) for range ticker.C { self.update() }