11 Commits
9 changed files with 151 additions and 128 deletions
+8 -8
View File
@@ -1,23 +1,23 @@
#/43;45;45;15
${C3}.%1-------------------------:
%3.%1-------------------------:
.%2+=%1========================.
:%2++%1===%2++===%1===============- :%2++%1-
:%2*++%1====%2+++++==%1===========- .==:
-%2*+++%1=====%2+***++=%1=========:
=%2*++++=%1=======------------:
=%2*+++++=%1====- ${C3}...%1
=%2*+++++=%1====- %3...%1
.%2+*+++++%1=-===: .%2=+++=%1:
:%2++++%1=====-==: -***%2**%1+
:%2++=%1=======-=. .=+**+${C3}.%1
.%2+%1==========-. ${C3}.%1
:%2+++++++%1====- ${C3}.%1--==-${C3}.%1
:%2++%1==========. ${C3}:%2+++++++%1${C3}:
:%2++=%1=======-=. .=+**+%3.%1
.%2+%1==========-. %3.%1
:%2+++++++%1====- %3.%1--==-%3.%1
:%2++%1==========. %3:%2+++++++%1%3:
%1.-===========. =*****+*+
%1.-===========: .+*****+:
%1-=======%2++++%1:::::::::::::::::::::::::-: ${C3}.%1---:
%1-=======%2++++%1:::::::::::::::::::::::::-: %3.%1---:
:======%2++++%1====%2+++******************=.
%1:=====%2+++%1==========%2++++++++++++++*-
%1.====%2++%1==============%2++++++++++*-
%1.===%2+%1==================%2+++++++:
%1.-=======================%2+++:
${C3}..........................
%3..........................
+1 -1
View File
@@ -5,7 +5,7 @@ modules:
- name: kernel
- name: packages
- name: shell
- name: init
- name: init_system
- name: motherboard
- name: cpus
- name: gpus
+9 -5
View File
@@ -5,9 +5,13 @@ import (
"strings"
)
func setupColorMap(asciiArtHeader string) map[int]string {
colorMap := make(map[int]string)
colorMap[0] = "\033[0m"
func setupColorMap(asciiArtHeader string) []string {
colorMap := make([]string, 10)
// Set default color map values
for i := range 10 {
colorMap[i] = "\033[0m"
}
// Return if header is empty
if asciiArtHeader == "" {
@@ -16,8 +20,8 @@ func setupColorMap(asciiArtHeader string) map[int]string {
// Read colors from ascii art header
ansiColors := strings.Split(strings.TrimPrefix(asciiArtHeader, "#/"), ";")
for i, ansiColor := range ansiColors {
colorMap[i+1] = fmt.Sprintf("\033[38;5;%sm", ansiColor)
for i := 0; i < 9 && i < len(ansiColors); i++ {
colorMap[i+1] = fmt.Sprintf("\033[38;5;%sm", ansiColors[i])
}
return colorMap
+5 -4
View File
@@ -10,7 +10,6 @@ import (
type StormfetchConfig struct {
Ascii string `yaml:"distro_ascii"`
DistroName string `yaml:"distro_name"`
Modules []stormfetchModuleConfig `yaml:"modules"`
AnsiiColors []int `yaml:"ansii_colors"`
ForceConfigAnsii bool `yaml:"force_config_ansii"`
@@ -22,20 +21,22 @@ var config = StormfetchConfig{
}
func readConfig() {
if ConfigPath == "" {
// Get home directory
userConfigDir, _ := os.UserConfigDir()
// Find valid config directory
if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/config.yml")); err == nil {
configPath = path.Join(userConfigDir, "stormfetch/config.yml")
ConfigPath = path.Join(userConfigDir, "stormfetch/config.yml")
} else if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/config.yml")); err == nil {
configPath = path.Join(SystemConfigDir, "stormfetch/config.yml")
ConfigPath = path.Join(SystemConfigDir, "stormfetch/config.yml")
} else {
log.Fatalf("Config file not found: %s", err.Error())
}
}
// Parse config
bytes, err := os.ReadFile(configPath)
bytes, err := os.ReadFile(ConfigPath)
if err != nil {
log.Fatal(err)
}
+25 -7
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"strings"
"time"
"unicode"
)
// Build-time variables
@@ -14,22 +15,22 @@ var SystemConfigDir = "/etc/"
// Flag variables
var ShowVersion = false
var ConfigPath = ""
var Ascii = ""
var ShowModuleTimeTaken = false
var configPath = ""
func main() {
readConfig()
parseFlags()
readConfig()
initializeModuleMap()
run()
}
func parseFlags() {
flag.BoolVar(&ShowVersion, "version", false, "Show Stormfetch version")
flag.StringVar(&ConfigPath, "config", "", "Use the specified config file")
flag.BoolVar(&ShowModuleTimeTaken, "time-taken", false, "Show time taken to execute each module")
flag.StringVar(&config.Ascii, "ascii", config.Ascii, "Set distro ascii")
flag.StringVar(&config.DistroName, "distro-name", config.DistroName, "Set distro name")
flag.StringVar(&Ascii, "ascii", "", "Set distro ascii")
flag.Parse()
}
@@ -48,6 +49,20 @@ func run() {
}
asciiArtNoColor := asciiArt
// Rewrite last color code to the start of the next line
lastColor := "0"
for i := 1; i < len(asciiArt); i++ {
rune := rune(asciiArt[i])
if unicode.IsDigit(rune) && asciiArt[i-1] == '%' {
lastColor = string(rune)
}
if rune == '\n' {
asciiArt = asciiArt[:i+1] + "%" + lastColor + asciiArt[i+1:]
}
}
// Setup color map and replace colors in ascii art
colorMap := setupColorMap(asciiArtHeader)
for key, value := range colorMap {
@@ -76,6 +91,9 @@ func run() {
text := module.Execute(module)
end := time.Now().UnixMilli()
// Insert default color at the start of the module's text
text = colorMap[0] + text
// Show time taken
if ShowModuleTimeTaken {
fmt.Printf("Module '%s' took %d milliseconds\n", module.Name, end-start)
@@ -89,7 +107,7 @@ func run() {
}
// Continue if text length is 0
if len(textNoColor) == 0 {
if len(textNoColor)-len(colorMap[0]) == 0 {
continue
}
@@ -107,7 +125,7 @@ func run() {
}
}
// Split ascii art into each lien
// Split ascii art into each line
asciiArtSplit := strings.Split(asciiArt, "\n")
asciiArtNoColorSplit := strings.Split(asciiArtNoColor, "\n")
+33
View File
@@ -399,4 +399,37 @@ func initializeModuleMap() {
return builder.String()
}}
RegisterModule(monitorsModule)
// Custom module
customModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "custom"}, Execute: func(sm StormfetchModule) string {
shell, _ := sm.GetData("shell", "/bin/sh")
commandList, _ := sm.GetData("commands", make([]any, 0))
// Exeucte all commands
commandOutput := make(map[int]string)
for i, value := range commandList.([]any) {
command, ok := value.(string)
if !ok {
continue
}
commandOutput[i+1] = runCommand(command, shell.(string))
}
return os.Expand(sm.Format, func(s string) string {
if len(s) <= 4 || !strings.HasPrefix(s, "CMD_") {
return ""
}
commandIndexStr := strings.Split(s, "CMD_")[1]
commandIndex, err := strconv.Atoi(commandIndexStr)
if err != nil {
return ""
}
return commandOutput[commandIndex]
})
}}
RegisterModule(customModule)
}
+35 -55
View File
@@ -22,10 +22,6 @@ func GetDistroInfo() DistroInfo {
LongName: "Unknown",
ShortName: "Unknown",
}
if strings.TrimSpace(config.DistroName) != "" {
info.LongName = strings.TrimSpace(config.DistroName)
info.ShortName = strings.TrimSpace(config.DistroName)
}
// Detect release file location
var releaseFile string
@@ -39,20 +35,23 @@ func GetDistroInfo() DistroInfo {
return info
}
// Read release file
releaseMap, err := ReadKeyValueFile(releaseFile)
if err != nil {
return info
}
// Set struct fields
if id, ok := releaseMap["ID"]; ok {
info.ID = id
}
if longName, ok := releaseMap["PRETTY_NAME"]; ok && info.LongName == "Unknown" {
if longName, ok := releaseMap["PRETTY_NAME"]; ok {
info.LongName = longName
}
if shortName, ok := releaseMap["NAME"]; ok && info.ShortName == "Unknown" {
if shortName, ok := releaseMap["NAME"]; ok {
info.ShortName = shortName
}
return info
}
@@ -64,40 +63,36 @@ func GetDistroAsciiArt() string {
// \ \
(| | )
/'\_ _/'\
\___)=(___/ `
var id string
if config.Ascii == "auto" {
id = GetDistroInfo().ID
\___)=(___/`
// Get ascii name to use
var asciiName string
if Ascii != "" {
asciiName = Ascii
} else if config.Ascii == "auto" {
asciiName = GetDistroInfo().ID
} else {
id = config.Ascii
asciiName = config.Ascii
}
// Check for ascii art in home directory
userConfDir, err := os.UserConfigDir()
if err != nil {
if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/ascii/", id)); err == nil {
bytes, err := os.ReadFile(path.Join(SystemConfigDir, "stormfetch/ascii/", id))
if err != nil {
if err == nil {
if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", asciiName)); err == nil {
if bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", asciiName)); err == nil {
return strings.TrimRight(string(bytes), "\n")
}
}
}
// Check for ascii art in system config directory
if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/ascii/", asciiName)); err == nil {
if bytes, err := os.ReadFile(path.Join(SystemConfigDir, "stormfetch/ascii/", asciiName)); err == nil {
return strings.TrimRight(string(bytes), "\n")
}
}
return defaultAscii
}
return string(bytes)
} else {
return defaultAscii
}
}
if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", id)); err == nil {
bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", id))
if err != nil {
return defaultAscii
}
return string(bytes)
} else if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/ascii/", id)); err == nil {
bytes, err := os.ReadFile(path.Join(SystemConfigDir, "stormfetch/ascii/", id))
if err != nil {
return defaultAscii
}
return strings.TrimRight(string(bytes), "\n\t ")
} else {
return defaultAscii
}
}
func GetArch() string {
@@ -138,21 +133,6 @@ func GetKernel() (string, string) {
}
func GetInitSystem() string {
runCommand := func(command string) string {
cmd := exec.Command("/bin/sh", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
process, err := ps.FindProcess(1)
if err != nil {
return ""
@@ -161,19 +141,19 @@ func GetInitSystem() string {
// Special cases
// OpenRC check
if _, err := os.Stat("/usr/sbin/openrc"); err == nil {
return "OpenRC " + runCommand("openrc --version | awk '{print $3}'")
return "OpenRC " + runCommand("openrc --version | awk '{print $3}'", "/bin/sh")
}
// Default PID 1 process name checking
switch process.Executable() {
case "systemd":
return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'")
return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'", "/bin/sh")
case "runit":
return "Runit"
case "dinit":
return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'")
return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'", "/bin/sh")
case "enit":
return "Enit " + runCommand("enit --version | awk '{print $3}'")
return "Enit " + runCommand("enit --version | awk '{print $3}'", "/bin/sh")
default:
return process.Executable()
}
+16 -45
View File
@@ -3,7 +3,6 @@ package main
import (
"log"
"os"
"os/exec"
"path/filepath"
"slices"
"strconv"
@@ -19,20 +18,6 @@ type DEWM struct {
}
func GetShell() string {
runCommand := func(command string) string {
cmd := exec.Command("/bin/sh", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
file, err := os.ReadFile("/etc/passwd")
if err != nil {
return ""
@@ -54,13 +39,13 @@ func GetShell() string {
case "dash":
return "Dash"
case "bash":
return "Bash " + runCommand("echo $BASH_VERSION")
return "Bash " + runCommand("echo $BASH_VERSION", "/bin/sh")
case "zsh":
return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'")
return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'", "/bin/sh")
case "fish":
return "Fish " + runCommand("$SHELL --version | awk '{print $3}'")
return "Fish " + runCommand("$SHELL --version | awk '{print $3}'", "/bin/sh")
case "nu":
return "Nushell " + runCommand("$SHELL --version")
return "Nushell " + runCommand("$SHELL --version", "/bin/sh")
default:
return "Unknown"
}
@@ -79,53 +64,39 @@ func GetDEWM() DEWM {
processExists := func(process string) bool {
return slices.Contains(executables, process)
}
runCommand := func(command string) string {
cmd := exec.Command("/bin/sh", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
if processExists("plasmashell") {
dewm := DEWM{
Name: "KDE Plasma",
Type: "DE",
Version: runCommand("plasmashell --version | awk '{print $2}'"),
Version: runCommand("plasmashell --version | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("gnome-session") {
dewm := DEWM{
Name: "Gnome",
Type: "DE",
Version: runCommand("gnome-shell --version | awk '{print $3}'"),
Version: runCommand("gnome-shell --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("xfce4-session") {
dewm := DEWM{
Name: "XFCE",
Type: "DE",
Version: runCommand("xfce4-session --version | head -n1 | awk '{print $2}'"),
Version: runCommand("xfce4-session --version | head -n1 | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("cinnamon") {
dewm := DEWM{
Name: "Cinnamon",
Type: "DE",
Version: runCommand("cinnamon --version | awk '{print $3}'"),
Version: runCommand("cinnamon --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("mate-panel") {
dewm := DEWM{
Name: "MATE",
Type: "DE",
Version: runCommand("mate-about --version | awk '{print $4}'"),
Version: runCommand("mate-about --version | awk '{print $4}'", "/bin/sh"),
}
return dewm
} else if processExists("lxsession") {
@@ -139,23 +110,23 @@ func GetDEWM() DEWM {
dewm := DEWM{
Name: "LXQt",
Type: "DE",
Version: runCommand("lxqt-session --version | head -n1 | awk '{print $2}'"),
Version: runCommand("lxqt-session --version | head -n1 | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("i3") || processExists("i3-with-shmlog") {
dewm := DEWM{
Name: "i3",
Type: "WM",
Version: runCommand("i3 --version | awk '{print $3}'"),
Version: runCommand("i3 --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("sway") {
dewm := DEWM{
Name: "Sway",
Type: "WM",
Version: runCommand("sway --version | awk '{print $3}'"),
Version: runCommand("sway --version | awk '{print $3}'", "/bin/sh"),
}
if runCommand("sway --version | awk '{print $1}'") == "swayfx" {
if runCommand("sway --version | awk '{print $1}'", "/bin/sh") == "swayfx" {
dewm.Name = "SwayFX"
} else {
dewm.Name = "Sway"
@@ -165,21 +136,21 @@ func GetDEWM() DEWM {
dewm := DEWM{
Name: "Bspwm",
Type: "WM",
Version: runCommand("bspwm -v"),
Version: runCommand("bspwm -v", "/bin/sh"),
}
return dewm
} else if processExists("Hyprland") {
dewm := DEWM{
Name: "Hyprland",
Type: "WM",
Version: runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'"),
Version: runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'", "/bin/sh"),
}
return dewm
} else if processExists("icewm-session") {
dewm := DEWM{
Name: "IceWM",
Type: "WM",
Version: runCommand("icewm --version | awk '{print $2}'"),
Version: runCommand("icewm --version | awk '{print $2}'", "/bin/sh"),
}
return dewm
}
+16
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"math"
"os"
"os/exec"
"regexp"
"strings"
)
@@ -56,3 +57,18 @@ func ReadKeyValueFile(filepath string) (map[string]string, error) {
}
return ret, nil
}
func runCommand(command string, shell string) string {
cmd := exec.Command(shell, "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}