Updated event management based on changes to termui
This commit is contained in:
Caleb Bassi 2018-11-29 18:17:13 -08:00
parent ce5342cfc9
commit 8ee8950ba2
11 changed files with 190 additions and 228 deletions

View File

@ -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 `<C-c>`
- Process Navigation:
- `<up>`/`<down>` and `j`/`k`: up and down
- `<C-d>` and `<C-u>`: up and down half a page
- `<C-f>` and `<C-b>`: up and down a full page
- `gg` and `G`: jump to top and bottom
- Process Sorting:
- Process navigation
- `k` and `<Up>`: up
- `j` and `<Down`: down
- `<C-u>`: half page up
- `<C-d>`: half page down
- `<C-b>`: full page up
- `<C-f>`: full page down
- `gg` and `<Home>`: jump to top
- `G` and `<End>`: jump to bottom
- Process actions:
- `<Tab>`: toggle process grouping
- `dd`: kill selected process or group of processes
- Process sorting
- `c`: CPU
- `m`: Mem
- `p`: PID
- `<tab>`: 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

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

238
main.go
View File

@ -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", "<C-c>", func(e ui.Event) {
ui.StopLoop()
})
// toggles help menu
ui.On("?", func(e ui.Event) {
helpToggled <- true
helpVisible = !helpVisible
})
// hides help menu
ui.On("<escape>", 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,18 +212,122 @@ func initWidgets() {
wg.Wait()
}
func eventLoop() {
drawTicker := time.NewTicker(interval).C
// handles kill signal sent to gotop
sigTerm := make(chan os.Signal, 2)
signal.Notify(sigTerm, os.Interrupt, syscall.SIGTERM)
uiEvents := ui.PollEvents()
previousKey := ""
for {
select {
case <-sigTerm:
return
case <-drawTicker:
if !helpVisible {
ui.Render(ui.Body)
}
case e := <-uiEvents:
switch e.ID {
case "q", "<C-c>":
return
case "?":
helpVisible = !helpVisible
if helpVisible {
ui.Clear()
ui.Render(help)
} else {
ui.Render(ui.Body)
}
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 "<Escape>":
if helpVisible {
helpVisible = false
ui.Render(ui.Body)
}
case "<Resize>":
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)
} else {
ui.Render(ui.Body)
}
case "<MouseLeft>":
payload := e.Payload.(ui.Mouse)
proc.Click(payload.X, payload.Y)
ui.Render(proc)
case "<MouseWheelUp>", "<Up>", "k":
proc.Up()
ui.Render(proc)
case "<MouseWheelDown>", "<Down>", "j":
proc.Down()
ui.Render(proc)
case "g", "<Home>":
if previousKey == "g" {
proc.Top()
ui.Render(proc)
previousKey = ""
}
case "G", "<End>":
proc.Bottom()
ui.Render(proc)
case "<C-d>":
proc.HalfPageDown()
ui.Render(proc)
case "<C-u>":
proc.HalfPageUp()
ui.Render(proc)
case "<C-f>":
proc.PageDown()
ui.Render(proc)
case "<C-b>":
proc.PageUp()
ui.Render(proc)
case "d":
if previousKey == "d" {
proc.Kill()
previousKey = ""
}
case "<Tab>":
proc.Tab()
ui.Render(proc)
case "m", "c", "p":
proc.ChangeSort(e)
ui.Render(proc)
}
previousKey = e.ID
}
}
}
func main() {
cliArguments()
keyBinds()
// need to do this before initializing widgets so that they can inherit the colors
termuiColors()
termuiColors() // need to do this before initializing widgets so that they can inherit the colors
initWidgets()
widgetColors()
help = w.NewHelpMenu()
// inits termui
@ -283,53 +338,6 @@ func main() {
defer ui.Close()
setupGrid()
ui.On("<resize>", func(e ui.Event) {
ui.Body.Width, ui.Body.Height = e.Width, e.Height
ui.Body.Resize()
termResized <- true
})
// 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:
ui.Clear()
ui.Render(help)
}
} else {
select {
case <-helpToggled:
ui.Clear()
ui.Render(help)
case <-termResized:
ui.Clear()
ui.Render(ui.Body)
case <-keyPressed:
ui.Render(proc)
case <-zoomed:
ui.Render(cpu, mem)
case <-drawTick.C:
ui.Render(ui.Body)
}
}
}
}()
// handles kill signal sent to gotop
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
ui.StopLoop()
}()
ui.Loop()
ui.Render(ui.Body)
eventLoop()
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -9,21 +9,28 @@ import (
const KEYBINDS = `
Quit: q or <C-c>
Process Navigation
- <up>/<down> and j/k: up and down
- <C-d> and <C-u>: up and down half a page
- <C-f> and <C-b>: up and down a full page
- gg and G: jump to top and bottom
Process navigation
- k and <Up>: up
- j and <Down>: down
- <C-u>: half page up
- <C-d>: half page down
- <C-b>: full page up
- <C-f>: full page down
- gg and <Home>: jump to top
- G and <End>: jump to bottom
Process Sorting
Process actions:
- <Tab>: toggle process grouping
- dd: kill selected process or group of processes
Process sorting
- c: CPU
- m: Mem
- p: PID
<tab>: 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}
}

View File

@ -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()
}

View File

@ -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

View File

@ -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("<MouseLeft>", func(e ui.Event) {
self.Click(e.MouseX, e.MouseY)
self.KeyPressed <- true
})
ui.On("<MouseWheelUp>", "<MouseWheelDown>", func(e ui.Event) {
switch e.Key {
case "<MouseWheelDown>":
self.Down()
case "<MouseWheelUp>":
self.Up()
}
self.KeyPressed <- true
})
ui.On("<up>", "<down>", func(e ui.Event) {
switch e.Key {
case "<up>":
self.Up()
case "<down>":
self.Down()
}
self.KeyPressed <- true
})
viKeys := []string{"j", "k", "gg", "G", "<C-d>", "<C-u>", "<C-f>", "<C-b>", "<home>", "<end>"}
ui.On(viKeys, func(e ui.Event) {
switch e.Key {
case "j":
self.Down()
case "k":
self.Up()
case "<home>":
fallthrough
case "gg":
self.Top()
case "<end>":
fallthrough
case "G":
self.Bottom()
case "<C-d>":
self.HalfPageDown()
case "<C-u>":
self.HalfPageUp()
case "<C-f>":
self.PageDown()
case "<C-b>":
self.PageUp()
}
self.KeyPressed <- true
})
ui.On("dd", func(e ui.Event) {
self.Kill()
})
ui.On("<tab>", 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
})
self.Sort()
}
}
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
}
})
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.

View File

@ -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()
}