mirror of
https://github.com/bcicen/ctop.git
synced 2024-08-30 18:23:19 +00:00
add statusline widget, status messages to logging
This commit is contained in:
parent
d743472b16
commit
d46ce783c2
@ -53,10 +53,10 @@ func Read() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Write() error {
|
||||
path, err := getConfigPath()
|
||||
func Write() (path string, err error) {
|
||||
path, err = getConfigPath()
|
||||
if err != nil {
|
||||
return err
|
||||
return path, err
|
||||
}
|
||||
|
||||
cfgdir := basedir(path)
|
||||
@ -64,22 +64,22 @@ func Write() error {
|
||||
if _, err := os.Stat(cfgdir); err != nil {
|
||||
err = os.MkdirAll(cfgdir, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
|
||||
return path, fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open config for writing: %s", err)
|
||||
return path, fmt.Errorf("failed to open config for writing: %s", err)
|
||||
}
|
||||
|
||||
writer := toml.NewEncoder(file)
|
||||
err = writer.Encode(exportConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write config: %s", err)
|
||||
return path, fmt.Errorf("failed to write config: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// determine config path from environment
|
||||
|
23
grid.go
23
grid.go
@ -17,6 +17,7 @@ func RedrawRows(clr bool) {
|
||||
header.SetFilter(config.GetVal("filterStr"))
|
||||
y += header.Height()
|
||||
}
|
||||
|
||||
cGrid.SetY(y)
|
||||
|
||||
for _, c := range cursor.filtered {
|
||||
@ -32,6 +33,7 @@ func RedrawRows(clr bool) {
|
||||
}
|
||||
cGrid.Align()
|
||||
ui.Render(cGrid)
|
||||
|
||||
}
|
||||
|
||||
func SingleView() MenuFn {
|
||||
@ -82,6 +84,7 @@ func Display() bool {
|
||||
|
||||
// initial draw
|
||||
header.Align()
|
||||
status.Align()
|
||||
cursor.RefreshContainers()
|
||||
RedrawRows(true)
|
||||
|
||||
@ -132,7 +135,13 @@ func Display() bool {
|
||||
ui.StopLoop()
|
||||
})
|
||||
ui.Handle("/sys/kbd/S", func(ui.Event) {
|
||||
config.Write()
|
||||
path, err := config.Write()
|
||||
if err == nil {
|
||||
log.Statusf("wrote config to %s", path)
|
||||
} else {
|
||||
log.StatusErr(err)
|
||||
}
|
||||
ui.StopLoop()
|
||||
})
|
||||
|
||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
||||
@ -141,6 +150,7 @@ func Display() bool {
|
||||
|
||||
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
||||
header.Align()
|
||||
status.Align()
|
||||
cursor.ScrollPage()
|
||||
cGrid.SetWidth(ui.TermWidth())
|
||||
log.Infof("resize: width=%v max-rows=%v", cGrid.Width, cGrid.MaxRows())
|
||||
@ -149,6 +159,17 @@ func Display() bool {
|
||||
|
||||
ui.Loop()
|
||||
|
||||
if log.StatusQueued() {
|
||||
for sm := range log.FlushStatus() {
|
||||
if sm.IsError {
|
||||
status.ShowErr(sm.Text)
|
||||
} else {
|
||||
status.Show(sm.Text)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if menu != nil {
|
||||
for menu != nil {
|
||||
menu = menu()
|
||||
|
@ -1,6 +1,7 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@ -20,11 +21,36 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
type statusMsg struct {
|
||||
Text string
|
||||
IsError bool
|
||||
}
|
||||
|
||||
type CTopLogger struct {
|
||||
*logging.Logger
|
||||
backend *logging.MemoryBackend
|
||||
sLog []statusMsg
|
||||
}
|
||||
|
||||
func (c *CTopLogger) FlushStatus() chan statusMsg {
|
||||
ch := make(chan statusMsg)
|
||||
go func() {
|
||||
for _, sm := range c.sLog {
|
||||
ch <- sm
|
||||
}
|
||||
close(ch)
|
||||
c.sLog = []statusMsg{}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *CTopLogger) StatusQueued() bool { return len(c.sLog) > 0 }
|
||||
func (c *CTopLogger) Status(s string) { c.addStatus(statusMsg{s, false}) }
|
||||
func (c *CTopLogger) StatusErr(err error) { c.addStatus(statusMsg{err.Error(), true}) }
|
||||
func (c *CTopLogger) addStatus(sm statusMsg) { c.sLog = append(c.sLog, sm) }
|
||||
|
||||
func (c *CTopLogger) Statusf(s string, a ...interface{}) { c.Status(fmt.Sprintf(s, a...)) }
|
||||
|
||||
func Init() *CTopLogger {
|
||||
if Log == nil {
|
||||
logging.SetFormatter(format) // setup default formatter
|
||||
@ -32,6 +58,7 @@ func Init() *CTopLogger {
|
||||
Log = &CTopLogger{
|
||||
logging.MustGetLogger("ctop"),
|
||||
logging.NewMemoryBackend(size),
|
||||
[]statusMsg{},
|
||||
}
|
||||
|
||||
if debugMode() {
|
||||
|
5
main.go
5
main.go
@ -25,6 +25,7 @@ var (
|
||||
cursor *GridCursor
|
||||
cGrid *compact.CompactGrid
|
||||
header *widgets.CTopHeader
|
||||
status *widgets.StatusLine
|
||||
|
||||
versionStr = fmt.Sprintf("ctop version %v, build %v %v", version, build, goVersion)
|
||||
)
|
||||
@ -59,8 +60,9 @@ func main() {
|
||||
// init logger
|
||||
log = logging.Init()
|
||||
|
||||
// init global config
|
||||
// init global config and read config file if exists
|
||||
config.Init()
|
||||
config.Read()
|
||||
|
||||
// override default config values with command line flags
|
||||
if *filterFlag != "" {
|
||||
@ -102,6 +104,7 @@ func main() {
|
||||
cursor = &GridCursor{cSource: conn}
|
||||
cGrid = compact.NewCompactGrid()
|
||||
header = widgets.NewCTopHeader()
|
||||
status = widgets.NewStatusLine()
|
||||
|
||||
for {
|
||||
exit := Display()
|
||||
|
@ -17,8 +17,8 @@ type CTopHeader struct {
|
||||
func NewCTopHeader() *CTopHeader {
|
||||
return &CTopHeader{
|
||||
Time: headerPar(2, timeStr()),
|
||||
Count: headerPar(27, "-"),
|
||||
Filter: headerPar(47, ""),
|
||||
Count: headerPar(24, "-"),
|
||||
Filter: headerPar(40, ""),
|
||||
bg: headerBg(),
|
||||
}
|
||||
}
|
||||
|
87
widgets/status.go
Normal file
87
widgets/status.go
Normal file
@ -0,0 +1,87 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
ui "github.com/gizak/termui"
|
||||
)
|
||||
|
||||
var (
|
||||
statusHeight = 1
|
||||
statusIter = 3
|
||||
)
|
||||
|
||||
type StatusLine struct {
|
||||
Message *ui.Par
|
||||
bg *ui.Par
|
||||
}
|
||||
|
||||
func NewStatusLine() *StatusLine {
|
||||
p := ui.NewPar("")
|
||||
p.X = 2
|
||||
p.Border = false
|
||||
p.Height = statusHeight
|
||||
p.Bg = ui.ThemeAttr("header.bg")
|
||||
p.TextFgColor = ui.ThemeAttr("header.fg")
|
||||
p.TextBgColor = ui.ThemeAttr("header.bg")
|
||||
return &StatusLine{
|
||||
Message: p,
|
||||
bg: statusBg(),
|
||||
}
|
||||
}
|
||||
|
||||
func (sl *StatusLine) Display() {
|
||||
ui.DefaultEvtStream.ResetHandlers()
|
||||
defer ui.DefaultEvtStream.ResetHandlers()
|
||||
|
||||
iter := statusIter
|
||||
ui.Handle("/sys/kbd/", func(ui.Event) {
|
||||
ui.StopLoop()
|
||||
})
|
||||
ui.Handle("/timer/1s", func(ui.Event) {
|
||||
iter--
|
||||
if iter <= 0 {
|
||||
ui.StopLoop()
|
||||
}
|
||||
})
|
||||
|
||||
ui.Render(sl)
|
||||
ui.Loop()
|
||||
}
|
||||
|
||||
// change given message on the status line
|
||||
func (sl *StatusLine) Show(s string) {
|
||||
sl.Message.TextFgColor = ui.ThemeAttr("header.fg")
|
||||
sl.Message.Text = s
|
||||
sl.Display()
|
||||
}
|
||||
|
||||
func (sl *StatusLine) ShowErr(s string) {
|
||||
sl.Message.TextFgColor = ui.ThemeAttr("status.danger")
|
||||
sl.Message.Text = s
|
||||
sl.Display()
|
||||
}
|
||||
|
||||
func (sl *StatusLine) Buffer() ui.Buffer {
|
||||
buf := ui.NewBuffer()
|
||||
buf.Merge(sl.bg.Buffer())
|
||||
buf.Merge(sl.Message.Buffer())
|
||||
return buf
|
||||
}
|
||||
|
||||
func (sl *StatusLine) Align() {
|
||||
sl.bg.SetWidth(ui.TermWidth() - 1)
|
||||
sl.Message.SetWidth(ui.TermWidth() - 2)
|
||||
|
||||
sl.bg.Y = ui.TermHeight() - 1
|
||||
sl.Message.Y = ui.TermHeight() - 1
|
||||
}
|
||||
|
||||
func (sl *StatusLine) Height() int { return statusHeight }
|
||||
|
||||
func statusBg() *ui.Par {
|
||||
bg := ui.NewPar("")
|
||||
bg.X = 1
|
||||
bg.Height = statusHeight
|
||||
bg.Border = false
|
||||
bg.Bg = ui.ThemeAttr("header.bg")
|
||||
return bg
|
||||
}
|
Loading…
Reference in New Issue
Block a user