forked from lesovsky/pgcenter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathui.go
141 lines (120 loc) · 3.63 KB
/
ui.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Stuff related to user interface.
package top
import (
"fmt"
"github.com/jroimartin/gocui"
"time"
)
var (
errSaved error // keep error during program lifecycle
cmdTimer *time.Timer // show cmdline's messages until timer is not expired
)
// Create and run UI.
func uiLoop() error {
var e ErrorRate
var errInterval time.Duration = 1 * time.Second
var errMaxcount int = 5
for {
// init UI
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
return fmt.Errorf("FATAL: gui creating failed with %s.\n", err)
}
// construct UI
g.SetManagerFunc(layout)
// setup key shortcuts and bindings
if err := keybindings(g); err != nil {
return fmt.Errorf("FATAL: %s.\n", err)
}
// initial read of stats
doWork(g)
// run stats loop, that reads and display stats
wg.Add(1)
go statLoop(g)
// run UI loop, that handle UI events
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
// check errors rate and quit if them too much - allow no more than 5 errors within 1 second
if err := e.Check(errInterval, errMaxcount); err != nil {
return fmt.Errorf("FATAL: gui main loop failed with %s (%d errors within %.0f seconds).", err, e.errCnt, e.timeElapsed.Seconds())
}
// ignore errors if they are seldom - just rebuild UI
}
wg.Wait()
}
}
// Defines UI layout - views and their location.
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
// Sysstat view
if v, err := g.SetView("sysstat", -1, -1, maxX-1/2, 4); err != nil {
if err != gocui.ErrUnknownView {
return fmt.Errorf("set sysstat view on layout failed: %s", err)
}
if _, err := g.SetCurrentView("sysstat"); err != nil {
return fmt.Errorf("set sysstat view as current on layout failed: %s", err)
}
v.Frame = false
}
// Postgres activity view
if v, err := g.SetView("pgstat", maxX/2, -1, maxX-1, 4); err != nil {
if err != gocui.ErrUnknownView {
return fmt.Errorf("set pgstat view on layout failed: %s", err)
}
v.Frame = false
}
// Command line
if v, err := g.SetView("cmdline", -1, 3, maxX-1, 5); err != nil {
if err != gocui.ErrUnknownView {
return fmt.Errorf("set cmdline view on layout failed: %s", err)
}
// show saved error to user if any
if errSaved != nil {
printCmdline(g, "%s", errSaved)
errSaved = nil
}
v.Frame = false
}
// Postgres' stats view
if v, err := g.SetView("dbstat", -1, 4, maxX-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return fmt.Errorf("set dbstat view on layout failed: %s", err)
}
v.Frame = false
}
// Aux stats view
if ctx.aux > auxNone {
if v, err := g.SetView("aux", -1, 3*maxY/5-1, maxX-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return fmt.Errorf("set aux view on layout failed: %s", err)
}
fmt.Fprintln(v, "")
v.Frame = false
}
}
return nil
}
// Wrapper function for printing messages in cmdline.
func printCmdline(g *gocui.Gui, format string, s ...interface{}) {
g.Update(func(g *gocui.Gui) error {
v, err := g.View("cmdline")
if err != nil {
return fmt.Errorf("Set focus on cmdline view failed: %s", err)
}
v.Clear()
fmt.Fprintf(v, format, s...)
// Clear the message after 1 second. Use timer here because it helps to show message a constant time and avoid blinking.
if format != "" {
// When user pushes buttons quickly and messages should be displayed a constant period of time, in that case
// if there is a non-expired timer, refresh it (just stop existing and create new one)
if cmdTimer != nil {
cmdTimer.Stop()
}
cmdTimer = time.NewTimer(time.Second)
go func() {
<-cmdTimer.C
printCmdline(g, "") // timer expired - wipe message.
}()
}
return nil
})
}