From 2edf53dd12877081fc68f4d31545514579ececcf Mon Sep 17 00:00:00 2001 From: Graham Clark Date: Thu, 10 Sep 2020 23:58:20 -0400 Subject: [PATCH] Load the configured theme at startup Termshark will now look in termshark.toml for a main.theme key. If set to e.g. foobar, then termshark will attempt to load (1) ~/.config/termshark/themes/foobar.toml, and failing that (2) see if it has a built-in theme called /themes/foobar.toml via its statik filesystem. If it finds the toml, regardless of the contents, the theme will be active and called foobar. Then when loading its color palette, any config-file references to colors e.g. dark.progress-bar - will look up a key dark.progress bar in the toml loaded from foobar.toml. If, eventually, a value is found that can be parsed as a color, termshark will use it for that UI element. --- cmd/termshark/termshark.go | 9 ++++ theme/utils.go | 87 ++++++++++++++++++++++++++++++-------- ui/palette.go | 13 +++++- 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/cmd/termshark/termshark.go b/cmd/termshark/termshark.go index b188562..940e346 100644 --- a/cmd/termshark/termshark.go +++ b/cmd/termshark/termshark.go @@ -26,6 +26,7 @@ import ( "github.com/gcla/termshark/v2/pcap" "github.com/gcla/termshark/v2/streams" "github.com/gcla/termshark/v2/system" + "github.com/gcla/termshark/v2/theme" "github.com/gcla/termshark/v2/tty" "github.com/gcla/termshark/v2/ui" "github.com/gcla/termshark/v2/widgets/filter" @@ -675,6 +676,14 @@ func cmain() int { ui.AutoScroll = termshark.ConfBool("main.auto-scroll", true) ui.PacketColors = termshark.ConfBool("main.packet-colors", true) + themeName := termshark.ConfString("main.theme", "") + if themeName != "" { + err = theme.Load(themeName) + if err != nil { + log.Warnf("Theme %s could not be loaded: %v", themeName, err) + } + } + // Set them up here so they have access to any command-line flags that // need to be passed to the tshark commands used pdmlArgs := termshark.ConfStringSlice("main.pdml-args", []string{}) diff --git a/theme/utils.go b/theme/utils.go index 53dd9d2..a2af271 100644 --- a/theme/utils.go +++ b/theme/utils.go @@ -6,9 +6,17 @@ package theme import ( + "fmt" + "io" + "os" + "path/filepath" + "github.com/gcla/gowid" - "github.com/gcla/termshark/v2" - log "github.com/sirupsen/logrus" + "github.com/rakyll/statik/fs" + "github.com/shibukawa/configdir" + "github.com/spf13/viper" + + _ "github.com/gcla/termshark/v2/assets/statik" ) //====================================================================== @@ -20,6 +28,8 @@ const ( Background Layer = iota ) +var theme *viper.Viper + // MakeColorSafe extends gowid's MakeColorSafe function, prefering to interpret // its string argument as a toml file config key lookup first; if this fails, then // fall back to gowid.MakeColorSafe, which will then read colors as urwid color names, @@ -27,32 +37,73 @@ const ( func MakeColorSafe(s string, l Layer) (gowid.Color, error) { loops := 10 cur := s - for { - next := termshark.ConfString(cur, "") - if next != "" { - cur = next - } else { - next := termshark.ConfStringSlice(cur, []string{}) - if len(next) != 2 { - break + if theme != nil { + for { + next := theme.GetString(cur) + if next != "" { + cur = next } else { - cur = next[l] + next := theme.GetStringSlice(cur) + if next == nil || len(next) != 2 { + break + } else { + cur = next[l] + } + } + loops -= 1 + if loops == 0 { + break } - } - loops -= 1 - if loops == 0 { - break } } col, err := gowid.MakeColorSafe(cur) if err == nil { return gowid.Color{IColor: col, Id: s}, nil } - col, err = gowid.MakeColorSafe(s) + return gowid.MakeColorSafe(s) +} + +// Clear resets the package-level theme object. Next time ui.SetupColors is called, +// the theme-connected colors won't be found, and termshark will fall back to its +// programmed default colors. +func Clear() { + theme = nil +} + +// Load will set the package-level theme object to a viper object representing the +// toml file either (a) read from disk, or failing that (b) built-in to termshark. +// Disk themes are prefered and take precedence. +func Load(name string) error { + theme = viper.New() + theme.SetConfigType("toml") + stdConf := configdir.New("", "termshark") + dirs := stdConf.QueryFolders(configdir.Global) + + // Prefer to load from disk + themeFileName := filepath.Join(dirs[0].Path, "themes", fmt.Sprintf("%s.toml", name)) + + var file io.ReadCloser + var err error + + file, err = os.Open(themeFileName) + if err == nil { + defer file.Close() + return theme.ReadConfig(file) + } + + // Fall back to built-in themes + statikFS, err := fs.New() if err != nil { - log.Infof("Could not understand configured theme color '%s'", s) + return err } - return col, err + + file, err = statikFS.Open(filepath.Join("/themes", fmt.Sprintf("%s.toml", name))) + if err != nil { + return err + } + defer file.Close() + + return theme.ReadConfig(file) } //====================================================================== diff --git a/ui/palette.go b/ui/palette.go index 26fb4a8..32bb507 100644 --- a/ui/palette.go +++ b/ui/palette.go @@ -9,8 +9,10 @@ import ( "fmt" "github.com/gcla/gowid" + "github.com/gcla/termshark/v2" "github.com/gcla/termshark/v2/theme" "github.com/gcla/termshark/v2/theme/modeswap" + log "github.com/sirupsen/logrus" ) //====================================================================== @@ -280,10 +282,19 @@ func lbg(key string, fb gowid.IColor) gowid.IColor { } func tomlCol(key string, layer theme.Layer, hue string, fb gowid.IColor) gowid.IColor { - col, err := theme.MakeColorSafe(fmt.Sprintf("themes.rules.%s.%s", hue, key), layer) + rule := fmt.Sprintf("%s.%s", hue, key) + col, err := theme.MakeColorSafe(rule, layer) if err == nil { return col + } else { + // Warn if the user has defined themes.rules.etcetc, but the resulting + // color can't be resolved. If no key is present, it means the user hasn't + // set up themes, so ignore. + if termshark.ConfString("main.theme", "") != "" { + log.Infof("Could not understand configured theme color '%s'", key) + } } + return fb }