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 }