2018-02-19 15:25:02 +08:00
package main
import (
2018-12-11 13:21:40 +08:00
"fmt"
2020-02-18 01:40:16 +08:00
"io"
2018-12-05 13:44:25 +08:00
"log"
2020-02-18 23:44:29 +08:00
"net/http"
2018-02-19 15:25:02 +08:00
"os"
"os/signal"
2018-12-05 13:44:25 +08:00
"path/filepath"
2018-03-09 16:27:46 +08:00
"strconv"
2020-02-13 23:40:20 +08:00
"strings"
2018-02-19 15:25:02 +08:00
"syscall"
"time"
2019-02-07 09:18:44 +08:00
docopt "github.com/docopt/docopt.go"
2019-03-08 15:15:41 +08:00
ui "github.com/gizak/termui/v3"
2020-02-18 23:44:29 +08:00
"github.com/prometheus/client_golang/prometheus/promhttp"
2020-03-07 23:03:25 +08:00
"github.com/shibukawa/configdir"
2019-02-07 09:18:44 +08:00
2020-02-29 04:48:35 +08:00
"github.com/xxxserxxx/gotop/v3"
"github.com/xxxserxxx/gotop/v3/colorschemes"
"github.com/xxxserxxx/gotop/v3/layout"
"github.com/xxxserxxx/gotop/v3/logging"
w "github.com/xxxserxxx/gotop/v3/widgets"
2018-02-19 15:25:02 +08:00
)
2019-01-13 08:31:37 +08:00
const (
2019-02-16 18:11:05 +08:00
appName = "gotop"
2019-01-13 08:31:37 +08:00
graphHorizontalScaleDelta = 3
2020-03-04 04:16:28 +08:00
defaultUI = "2:cpu\ndisk/1 2:mem/2\ntemp\n2:net 2:procs"
2020-02-14 03:24:41 +08:00
minimalUI = "cpu\nmem procs"
2020-02-16 06:10:45 +08:00
batteryUI = "cpu/2 batt/1\ndisk/1 2:mem/2\ntemp\nnet procs"
2020-02-29 00:03:41 +08:00
procsUI = "cpu 4:procs\ndisk\nmem\nnet"
2020-03-07 19:01:47 +08:00
kitchensink = "3:cpu/2 3:mem/1\n4:temp/1 3:disk/2\npower\n3:net 3:procs"
2019-01-13 08:31:37 +08:00
)
2018-02-19 15:25:02 +08:00
var (
2020-03-03 19:47:52 +08:00
// TODO: Set this at compile time; having to check this in sucks.
2020-04-16 21:54:25 +08:00
Version = "3.6.0"
2020-02-14 00:15:52 +08:00
conf gotop . Config
2020-02-13 10:15:49 +08:00
help * w . HelpMenu
bar * w . StatusBar
statusbar bool
2018-12-05 13:44:25 +08:00
stderrLogger = log . New ( os . Stderr , "" , 0 )
2018-02-19 15:25:02 +08:00
)
2020-02-20 00:40:45 +08:00
// TODO: Add tab completion for Linux https://gist.github.com/icholy/5314423
2020-02-21 08:56:52 +08:00
// TODO: state:merge #135 linux console font (cmatsuoka/console-font)
// TODO: state:deferred 157 FreeBSD fixes & Nvidia GPU support (kraust/master). Significant CPU use impact for NVidia changes.
// TODO: Virtual devices from Prometeus metrics @feature
2020-03-03 03:41:55 +08:00
// TODO: Abstract out the UI toolkit. mum4k/termdash, VladimirMarkelov/clui, gcla/gowid, rivo/tview, marcusolsson/tui-go might work better for some OS/Archs. Performance/memory use comparison would be interesting.
2020-02-18 01:40:16 +08:00
func parseArgs ( conf * gotop . Config ) error {
2020-04-07 00:04:08 +08:00
cds := conf . ConfigDir . QueryFolders ( configdir . All )
cpaths := make ( [ ] string , len ( cds ) )
for i , p := range cds {
cpaths [ i ] = p . Path
}
usage := fmt . Sprintf ( `
2018-02-19 15:25:02 +08:00
Usage : gotop [ options ]
Options :
2020-02-13 23:40:20 +08:00
- c , -- color = NAME Set a colorscheme .
- h , -- help Show this screen .
2020-02-16 06:10:45 +08:00
- m , -- minimal Only show CPU , Mem and Process widgets . Overrides - l . ( DEPRECATED , use - l minimal )
2020-04-16 22:36:10 +08:00
- S , -- graphscale = INT Default is 7 ; 1 is lowest scale - out ; values greater than 30 are probably not useful .
2020-02-13 23:40:20 +08:00
- r , -- rate = RATE Number of times per second to update CPU and Mem widgets [ default : 1 ] .
- V , -- version Print version and exit .
- p , -- percpu Show each CPU in the CPU widget .
- a , -- averagecpu Show average CPU in the CPU widget .
- f , -- fahrenheit Show temperatures in fahrenheit .
- s , -- statusbar Show a statusbar with the time .
2020-02-16 06:10:45 +08:00
- b , -- battery Show battery level widget ( ' minimal ' turns off ) . ( DEPRECATED , use - l battery )
2020-02-14 03:24:41 +08:00
- B , -- bandwidth = bits Specify the number of bits per seconds .
- l , -- layout = NAME Name of layout spec file for the UI . Looks first in $ XDG_CONFIG_HOME / gotop , then as a path . Use "-" to pipe .
2020-02-29 00:03:41 +08:00
- i , -- interface = NAME Select network interface [ default : all ] . Several interfaces can be defined using comma separated values . Interfaces can also be ignored using !
2020-02-18 23:44:29 +08:00
- x , -- export = PORT Enable metrics for export on the specified port .
2020-02-29 00:03:41 +08:00
- X , -- extensions = NAMES Enables the listed extensions . This is a comma - separated list without the . so suffix . The current and config directories will be searched .
2020-03-02 23:09:17 +08:00
-- test Runs tests and exits with success / failure code
2020-03-08 02:14:19 +08:00
-- print - paths List out the paths that gotop will look for gotop . conf , layouts , color schemes , and extensions
2020-03-08 21:50:20 +08:00
-- print - keys Show the keyboard bindings
2020-02-14 21:52:33 +08:00
2020-02-29 00:03:41 +08:00
Built - in layouts :
default
minimal
battery
kitchensink
2018-02-19 15:25:02 +08:00
Colorschemes :
default
2018-04-10 13:00:27 +08:00
default - dark ( for white background )
2018-02-21 18:24:36 +08:00
solarized
2019-11-16 08:23:32 +08:00
solarized16 - dark
solarized16 - light
2018-02-22 11:54:25 +08:00
monokai
2019-02-21 15:51:49 +08:00
vice
2020-04-07 00:15:09 +08:00
Colorschemes and layouts that are not built - in are searched for ( in order ) in :
% s
The first path in this list is always the cwd .
Log files are stored in % s
` , strings . Join ( cpaths , ", " ) , filepath . Join ( conf . ConfigDir . QueryCacheFolder ( ) . Path , logging . LOGFILE ) )
2018-02-19 15:25:02 +08:00
2020-02-18 01:40:16 +08:00
var err error
conf . Colorscheme , err = colorschemes . FromName ( conf . ConfigDir , "default" )
if err != nil {
return err
2020-02-13 10:15:49 +08:00
}
2020-03-03 19:47:52 +08:00
args , err := docopt . ParseArgs ( usage , os . Args [ 1 : ] , Version )
2018-12-05 13:44:25 +08:00
if err != nil {
2020-02-18 01:40:16 +08:00
return err
2018-12-05 13:44:25 +08:00
}
2018-02-21 18:24:36 +08:00
2020-02-13 23:40:20 +08:00
if val , _ := args [ "--layout" ] ; val != nil {
2020-02-18 01:40:16 +08:00
conf . Layout = val . ( string )
2020-02-13 23:40:20 +08:00
}
2018-02-21 18:24:36 +08:00
if val , _ := args [ "--color" ] ; val != nil {
2020-02-18 01:40:16 +08:00
cs , err := colorschemes . FromName ( conf . ConfigDir , val . ( string ) )
2020-02-13 10:15:49 +08:00
if err != nil {
2020-02-18 01:40:16 +08:00
return err
2018-12-11 13:21:40 +08:00
}
2020-02-13 10:15:49 +08:00
conf . Colorscheme = cs
2018-02-21 18:24:36 +08:00
}
2020-02-18 01:40:16 +08:00
if args [ "--averagecpu" ] . ( bool ) {
conf . AverageLoad , _ = args [ "--averagecpu" ] . ( bool )
2020-02-16 06:10:45 +08:00
}
2020-02-18 01:40:16 +08:00
if args [ "--percpu" ] . ( bool ) {
conf . PercpuLoad , _ = args [ "--percpu" ] . ( bool )
2020-02-14 03:24:41 +08:00
}
2020-02-18 01:40:16 +08:00
if args [ "--statusbar" ] . ( bool ) {
statusbar , _ = args [ "--statusbar" ] . ( bool )
2018-12-05 13:44:25 +08:00
}
2020-02-18 01:40:16 +08:00
if args [ "--battery" ] . ( bool ) {
conf . Layout = "battery"
2018-03-09 16:34:26 +08:00
}
2020-02-18 01:40:16 +08:00
if args [ "--minimal" ] . ( bool ) {
conf . Layout = "minimal"
2019-03-01 08:29:52 +08:00
}
2020-02-18 23:44:29 +08:00
if val , _ := args [ "--export" ] ; val != nil {
conf . ExportPort = val . ( string )
}
if val , _ := args [ "--rate" ] ; val != nil {
rateStr , _ := val . ( string )
2020-02-18 01:40:16 +08:00
rate , err := strconv . ParseFloat ( rateStr , 64 )
2019-01-15 11:39:37 +08:00
if err != nil {
2020-02-18 01:40:16 +08:00
return fmt . Errorf ( "invalid rate parameter" )
}
if rate < 1 {
conf . UpdateInterval = time . Second * time . Duration ( 1 / rate )
} else {
conf . UpdateInterval = time . Second / time . Duration ( rate )
2018-12-11 13:21:40 +08:00
}
2018-09-19 04:42:49 +08:00
}
2020-02-18 01:40:16 +08:00
if val , _ := args [ "--fahrenheit" ] ; val != nil {
fahrenheit , _ := val . ( bool )
if fahrenheit {
conf . TempScale = w . Fahrenheit
}
2018-02-19 15:25:02 +08:00
}
2020-02-18 01:40:16 +08:00
if val , _ := args [ "--interface" ] ; val != nil {
conf . NetInterface , _ = args [ "--interface" ] . ( string )
2018-09-19 04:42:49 +08:00
}
2020-02-29 00:03:41 +08:00
if val , _ := args [ "--extensions" ] ; val != nil {
exs , _ := args [ "--extensions" ] . ( string )
conf . Extensions = strings . Split ( exs , "," )
}
2020-03-02 23:09:17 +08:00
if val , _ := args [ "--test" ] ; val != nil {
conf . Test = val . ( bool )
}
2020-04-16 22:36:10 +08:00
if val , _ := args [ "--graphscale" ] ; val != nil {
str , _ := args [ "--graphscale" ] . ( string )
scl , err := strconv . Atoi ( str )
if err != nil {
fmt . Printf ( "invalid value \"%s\" for graphscale; must be an integer\n" , args [ "--graphscale" ] )
os . Exit ( 1 )
}
if scl < 1 {
fmt . Printf ( "graphscale must be greater than 0 [1, ∞); you provided %d. Values > 30 are probably not useful.\n" , scl )
os . Exit ( 1 )
}
conf . GraphHorizontalScale = scl
}
2020-03-07 23:03:25 +08:00
if args [ "--print-paths" ] . ( bool ) {
paths := make ( [ ] string , 0 )
for _ , d := range conf . ConfigDir . QueryFolders ( configdir . All ) {
paths = append ( paths , d . Path )
}
fmt . Println ( strings . Join ( paths , "\n" ) )
os . Exit ( 0 )
}
2020-03-08 02:14:19 +08:00
if args [ "--print-keys" ] . ( bool ) {
fmt . Println ( `
Quit : q or < C - c >
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 with SIGTERM
d3 : kill selected process or group of processes with SIGQUIT
d9 : kill selected process or group of processes with SIGKILL
Process sorting
c : CPU
m : Mem
p : PID
Process filtering :
/ : start editing filter
( while editing ) :
< Enter > accept filter
< C - c > and < Escape > : clear filter
CPU and Mem graph scaling :
h : scale in
l : scale out
? : toggles keybind help menu ` )
os . Exit ( 0 )
}
2020-02-18 01:40:16 +08:00
return nil
2018-02-19 15:25:02 +08:00
}
2020-02-14 00:15:52 +08:00
func setDefaultTermuiColors ( c gotop . Config ) {
2020-02-13 10:15:49 +08:00
ui . Theme . Default = ui . NewStyle ( ui . Color ( c . Colorscheme . Fg ) , ui . Color ( c . Colorscheme . Bg ) )
ui . Theme . Block . Title = ui . NewStyle ( ui . Color ( c . Colorscheme . BorderLabel ) , ui . Color ( c . Colorscheme . Bg ) )
ui . Theme . Block . Border = ui . NewStyle ( ui . Color ( c . Colorscheme . BorderLine ) , ui . Color ( c . Colorscheme . Bg ) )
2018-02-19 15:25:02 +08:00
}
2020-02-14 00:15:52 +08:00
func eventLoop ( c gotop . Config , grid * layout . MyGrid ) {
2020-02-13 10:15:49 +08:00
drawTicker := time . NewTicker ( c . UpdateInterval ) . C
2018-02-19 15:25:02 +08:00
2018-11-30 10:17:13 +08:00
// handles kill signal sent to gotop
sigTerm := make ( chan os . Signal , 2 )
signal . Notify ( sigTerm , os . Interrupt , syscall . SIGTERM )
2018-02-19 15:25:02 +08:00
2018-11-30 10:17:13 +08:00
uiEvents := ui . PollEvents ( )
2018-02-21 19:41:40 +08:00
2018-11-30 10:17:13 +08:00
previousKey := ""
2018-02-19 15:25:02 +08:00
2018-11-30 10:17:13 +08:00
for {
select {
case <- sigTerm :
return
case <- drawTicker :
2020-02-13 10:15:49 +08:00
if ! c . HelpVisible {
2019-03-08 15:43:10 +08:00
ui . Render ( grid )
2019-03-17 22:54:56 +08:00
if statusbar {
ui . Render ( bar )
}
2018-11-30 10:17:13 +08:00
}
case e := <- uiEvents :
2020-02-14 23:35:58 +08:00
if grid . Proc != nil && grid . Proc . HandleEvent ( e ) {
ui . Render ( grid . Proc )
break
}
2018-11-30 10:17:13 +08:00
switch e . ID {
case "q" , "<C-c>" :
return
case "?" :
2020-02-13 10:15:49 +08:00
c . HelpVisible = ! c . HelpVisible
2019-01-08 12:10:09 +08:00
case "<Resize>" :
payload := e . Payload . ( ui . Resize )
2019-02-07 09:18:44 +08:00
termWidth , termHeight := payload . Width , payload . Height
2019-02-02 15:22:27 +08:00
if statusbar {
2019-02-07 09:18:44 +08:00
grid . SetRect ( 0 , 0 , termWidth , termHeight - 1 )
bar . SetRect ( 0 , termHeight - 1 , termWidth , termHeight )
2019-02-02 15:22:27 +08:00
} else {
grid . SetRect ( 0 , 0 , payload . Width , payload . Height )
}
2019-01-08 12:10:09 +08:00
help . Resize ( payload . Width , payload . Height )
ui . Clear ( )
}
2020-02-13 10:15:49 +08:00
if c . HelpVisible {
2019-01-08 12:10:09 +08:00
switch e . ID {
case "?" :
2018-02-19 15:25:02 +08:00
ui . Clear ( )
ui . Render ( help )
2019-01-08 12:10:09 +08:00
case "<Escape>" :
2020-02-13 10:15:49 +08:00
c . HelpVisible = false
2019-03-08 15:43:10 +08:00
ui . Render ( grid )
2019-01-08 12:10:09 +08:00
case "<Resize>" :
ui . Render ( help )
2018-02-19 15:25:02 +08:00
}
2019-01-08 12:10:09 +08:00
} else {
switch e . ID {
case "?" :
2019-03-08 15:43:10 +08:00
ui . Render ( grid )
2019-01-08 12:10:09 +08:00
case "h" :
2020-02-13 10:15:49 +08:00
c . GraphHorizontalScale += graphHorizontalScaleDelta
for _ , item := range grid . Lines {
item . Scale ( c . GraphHorizontalScale )
}
ui . Render ( grid )
2019-01-08 12:10:09 +08:00
case "l" :
2020-02-13 10:15:49 +08:00
if c . GraphHorizontalScale > graphHorizontalScaleDelta {
c . GraphHorizontalScale -= graphHorizontalScaleDelta
for _ , item := range grid . Lines {
item . Scale ( c . GraphHorizontalScale )
ui . Render ( item )
}
2018-11-30 10:17:13 +08:00
}
2019-01-08 12:10:09 +08:00
case "<Resize>" :
2019-03-08 15:43:10 +08:00
ui . Render ( grid )
2019-02-06 18:11:41 +08:00
if statusbar {
2019-03-08 15:43:10 +08:00
ui . Render ( bar )
2019-02-06 18:11:41 +08:00
}
2019-01-08 12:10:09 +08:00
case "<MouseLeft>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
payload := e . Payload . ( ui . Mouse )
grid . Proc . HandleClick ( payload . X , payload . Y )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "k" , "<Up>" , "<MouseWheelUp>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollUp ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "j" , "<Down>" , "<MouseWheelDown>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollDown ( )
ui . Render ( grid . Proc )
}
2019-02-02 15:22:27 +08:00
case "<Home>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollTop ( )
ui . Render ( grid . Proc )
}
2019-02-02 15:06:57 +08:00
case "g" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
if previousKey == "g" {
grid . Proc . ScrollTop ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
}
case "G" , "<End>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollBottom ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "<C-d>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollHalfPageDown ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "<C-u>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollHalfPageUp ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "<C-f>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollPageDown ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "<C-b>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ScrollPageUp ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "d" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
if previousKey == "d" {
2020-02-14 03:38:45 +08:00
grid . Proc . KillProc ( "SIGTERM" )
}
}
case "3" :
if grid . Proc != nil {
if previousKey == "d" {
grid . Proc . KillProc ( "SIGQUIT" )
}
}
case "9" :
if grid . Proc != nil {
if previousKey == "d" {
grid . Proc . KillProc ( "SIGKILL" )
2020-02-13 10:15:49 +08:00
}
2019-01-08 12:10:09 +08:00
}
case "<Tab>" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ToggleShowingGroupedProcs ( )
ui . Render ( grid . Proc )
}
2019-01-08 12:10:09 +08:00
case "m" , "c" , "p" :
2020-02-13 10:15:49 +08:00
if grid . Proc != nil {
grid . Proc . ChangeProcSortMethod ( w . ProcSortMethod ( e . ID ) )
ui . Render ( grid . Proc )
}
2020-02-14 23:35:58 +08:00
case "/" :
if grid . Proc != nil {
grid . Proc . SetEditingFilter ( true )
ui . Render ( grid . Proc )
}
2018-11-30 10:17:13 +08:00
}
2019-01-08 12:10:09 +08:00
if previousKey == e . ID {
previousKey = ""
} else {
previousKey = e . ID
2018-02-19 15:25:02 +08:00
}
}
2018-12-05 13:07:14 +08:00
2018-02-19 15:25:02 +08:00
}
2018-11-30 10:17:13 +08:00
}
}
2018-02-19 15:25:02 +08:00
2020-02-18 01:40:16 +08:00
func makeConfig ( ) gotop . Config {
2020-03-07 23:03:25 +08:00
cd := configdir . New ( "" , appName )
cd . LocalPath , _ = filepath . Abs ( "." )
2020-02-18 01:40:16 +08:00
conf = gotop . Config {
ConfigDir : cd ,
GraphHorizontalScale : 7 ,
HelpVisible : false ,
UpdateInterval : time . Second ,
AverageLoad : false ,
2020-02-18 23:44:29 +08:00
PercpuLoad : true ,
2020-02-18 01:40:16 +08:00
TempScale : w . Celsius ,
Statusbar : false ,
NetInterface : w . NET_INTERFACE_ALL ,
MaxLogSize : 5000000 ,
Layout : "default" ,
}
return conf
}
2020-02-20 00:40:45 +08:00
// TODO: mpd visualizer widget
2018-12-10 13:19:09 +08:00
func main ( ) {
2020-03-08 21:30:06 +08:00
// This is just to make sure gotop returns a useful exit code, but also
// executes all defer statements and so cleans up before exit. Sort of
// annoying work-around for a lack of a clean way to exit Go programs
// with exit codes.
ec := run ( )
2020-03-08 21:50:20 +08:00
if ec > 0 {
if ec < 2 {
fmt . Printf ( "errors encountered; check the log file %s\n" , filepath . Join ( conf . ConfigDir . QueryCacheFolder ( ) . Path , logging . LOGFILE ) )
}
}
2020-03-08 21:30:06 +08:00
os . Exit ( ec )
}
func run ( ) int {
2020-02-18 01:40:16 +08:00
// Set up default config
conf := makeConfig ( )
2020-03-07 23:03:25 +08:00
// Find the config file; look in (1) local, (2) user, (3) global
err := conf . Load ( )
if err != nil {
2020-03-08 21:50:20 +08:00
fmt . Printf ( "failed to parse config file: %s\n" , err )
return 2
2020-02-18 01:40:16 +08:00
}
// Override with command line arguments
2020-03-07 23:03:25 +08:00
err = parseArgs ( & conf )
2020-02-13 10:15:49 +08:00
if err != nil {
2020-03-08 21:50:20 +08:00
fmt . Printf ( "parsing CLI args: %s\n" , err )
return 2
2018-12-11 13:21:40 +08:00
}
2020-02-16 04:27:31 +08:00
logfile , err := logging . New ( conf )
2019-01-20 11:37:31 +08:00
if err != nil {
2020-03-07 23:03:25 +08:00
fmt . Printf ( "failed to setup log file: %v\n" , err )
2020-03-08 21:50:20 +08:00
return 2
2019-01-20 11:37:31 +08:00
}
2019-02-07 09:18:44 +08:00
defer logfile . Close ( )
2019-01-20 11:37:31 +08:00
2020-03-07 23:03:25 +08:00
lstream , err := getLayout ( conf )
if err != nil {
2020-03-08 21:50:20 +08:00
stderrLogger . Print ( err )
2020-03-08 21:30:06 +08:00
return 1
2020-03-07 23:03:25 +08:00
}
2020-03-02 23:09:17 +08:00
ly := layout . ParseLayout ( lstream )
if conf . Test {
2020-03-08 21:30:06 +08:00
return runTests ( conf )
2020-03-02 23:09:17 +08:00
}
2018-12-10 13:19:09 +08:00
if err := ui . Init ( ) ; err != nil {
2020-03-08 21:50:20 +08:00
stderrLogger . Print ( err )
return 1
2018-11-30 10:17:13 +08:00
}
defer ui . Close ( )
2018-12-11 13:21:40 +08:00
2020-02-13 10:15:49 +08:00
setDefaultTermuiColors ( conf ) // done before initializing widgets to allow inheriting colors
help = w . NewHelpMenu ( )
if statusbar {
bar = w . NewStatusBar ( )
}
2019-01-13 08:44:12 +08:00
2020-02-13 10:15:49 +08:00
grid , err := layout . Layout ( ly , conf )
if err != nil {
2020-03-08 21:50:20 +08:00
stderrLogger . Print ( err )
return 1
2020-02-13 10:15:49 +08:00
}
2019-01-20 11:37:31 +08:00
2019-01-24 13:23:35 +08:00
termWidth , termHeight := ui . TerminalDimensions ( )
2019-02-02 15:22:27 +08:00
if statusbar {
grid . SetRect ( 0 , 0 , termWidth , termHeight - 1 )
} else {
grid . SetRect ( 0 , 0 , termWidth , termHeight )
}
2019-01-01 08:55:50 +08:00
help . Resize ( termWidth , termHeight )
ui . Render ( grid )
2019-02-02 15:22:27 +08:00
if statusbar {
bar . SetRect ( 0 , termHeight - 1 , termWidth , termHeight )
ui . Render ( bar )
}
2018-12-11 13:21:40 +08:00
2020-02-18 23:44:29 +08:00
if conf . ExportPort != "" {
go func ( ) {
http . Handle ( "/metrics" , promhttp . Handler ( ) )
http . ListenAndServe ( conf . ExportPort , nil )
} ( )
}
2020-04-16 22:36:10 +08:00
2020-02-13 10:15:49 +08:00
eventLoop ( conf , grid )
2020-03-08 21:30:06 +08:00
return 0
2018-02-19 15:25:02 +08:00
}
2020-02-18 01:40:16 +08:00
2020-03-07 23:03:25 +08:00
func getLayout ( conf gotop . Config ) ( io . Reader , error ) {
2020-02-18 01:40:16 +08:00
switch conf . Layout {
case "-" :
2020-03-07 23:03:25 +08:00
return os . Stdin , nil
2020-02-18 01:40:16 +08:00
case "default" :
2020-03-07 23:03:25 +08:00
return strings . NewReader ( defaultUI ) , nil
2020-02-18 01:40:16 +08:00
case "minimal" :
2020-03-07 23:03:25 +08:00
return strings . NewReader ( minimalUI ) , nil
2020-02-18 01:40:16 +08:00
case "battery" :
2020-03-07 23:03:25 +08:00
return strings . NewReader ( batteryUI ) , nil
2020-02-29 00:03:41 +08:00
case "procs" :
2020-03-07 23:03:25 +08:00
return strings . NewReader ( procsUI ) , nil
2020-03-07 19:01:47 +08:00
case "kitchensink" :
2020-03-07 23:03:25 +08:00
return strings . NewReader ( kitchensink ) , nil
2020-02-18 01:40:16 +08:00
default :
2020-03-07 23:03:25 +08:00
folder := conf . ConfigDir . QueryFolderContainsFile ( conf . Layout )
if folder == nil {
paths := make ( [ ] string , 0 )
for _ , d := range conf . ConfigDir . QueryFolders ( configdir . Existing ) {
paths = append ( paths , d . Path )
2020-02-18 01:40:16 +08:00
}
2020-03-07 23:03:25 +08:00
return nil , fmt . Errorf ( "unable find layout file %s in %s" , conf . Layout , strings . Join ( paths , ", " ) )
}
lo , err := folder . ReadFile ( conf . Layout )
if err != nil {
return nil , err
2020-02-18 01:40:16 +08:00
}
2020-03-07 23:03:25 +08:00
return strings . NewReader ( string ( lo ) ) , nil
2020-02-18 01:40:16 +08:00
}
}
2020-02-29 00:03:41 +08:00
2020-03-02 23:09:17 +08:00
func runTests ( conf gotop . Config ) int {
fmt . Printf ( "PASS" )
return 0
}