package main import ( "bufio" "github.com/jackmordaunt/ghw" "github.com/mitchellh/go-ps" "log" "os" "os/exec" "path" "regexp" "slices" "strconv" "strings" ) type DistroInfo struct { ID string LongName string ShortName string } func getDistroInfo() DistroInfo { distroID := "" var releaseMap = make(map[string]string) if _, err := os.Stat("/etc/os-release"); err == nil { releaseMap, err = readKeyValueFile("/etc/os-release") if err != nil { return DistroInfo{ ID: "unknown", LongName: "Unknown", ShortName: "Unknown", } } if value, ok := releaseMap["ID"]; ok { distroID = value } } switch distroID { default: if id, ok := releaseMap["ID"]; ok { if longName, ok := releaseMap["PRETTY_NAME"]; ok { if shortName, ok := releaseMap["NAME"]; ok { return DistroInfo{ ID: id, LongName: longName, ShortName: shortName, } } } } return DistroInfo{ ID: "unknown", LongName: "Unknown", ShortName: "Unknown", } } } func getDistroAsciiArt() string { defaultAscii := ` .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/'\ \___)=(___/ ` var id string if config.Ascii == "auto" { id = getDistroInfo().ID } else { id = config.Ascii } userConfDir, err := os.UserConfigDir() if err != nil { if _, err := os.Stat(path.Join("/etc/stormfetch/ascii/", id)); err == nil { bytes, err := os.ReadFile(path.Join("/etc/stormfetch/ascii/", id)) if err != nil { 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("/etc/stormfetch/ascii/", id)); err == nil { bytes, err := os.ReadFile(path.Join("/etc/stormfetch/ascii/", id)) if err != nil { return defaultAscii } return string(bytes) } else { return defaultAscii } } func getCPUName() string { cpu, err := ghw.CPU() if err != nil { return "" } return cpu.Processors[0].Model } func getCPUThreads() int { cpu, err := ghw.CPU() if err != nil { return 0 } return int(cpu.TotalThreads) } func getGPUName() string { null, _ := os.Open(os.DevNull) serr := os.Stderr os.Stderr = null gpu, err := ghw.GPU() defer null.Close() os.Stderr = serr if err != nil { return "" } if len(gpu.GraphicsCards) == 0 { return "" } return gpu.GraphicsCards[0].DeviceInfo.Product.Name } type Memory struct { MemTotal int MemFree int MemAvailable int } func GetMemoryInfo() Memory { toInt := func(raw string) int { if raw == "" { return 0 } res, err := strconv.Atoi(raw) if err != nil { panic(err) } return res } parseLine := func(raw string) (key string, value int) { text := strings.ReplaceAll(raw[:len(raw)-2], " ", "") keyValue := strings.Split(text, ":") return keyValue[0], toInt(keyValue[1]) } file, err := os.Open("/proc/meminfo") if err != nil { panic(err) } defer file.Close() bufio.NewScanner(file) scanner := bufio.NewScanner(file) res := Memory{} for scanner.Scan() { key, value := parseLine(scanner.Text()) switch key { case "MemTotal": res.MemTotal = value / 1024 case "MemFree": res.MemFree = value / 1024 case "MemAvailable": res.MemAvailable = value / 1024 } } return res } func GetDEWM() string { processes, err := ps.Processes() if err != nil { log.Fatal(err) } var executables []string for _, process := range processes { executables = append(executables, process.Executable()) } processExists := func(process string) bool { return slices.Contains(executables, process) } runCommand := func(command string) string { cmd := exec.Command("/bin/bash", "-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 string(out) } if processExists("plasmashell") { return strings.TrimSpace("KDE Plasma " + runCommand("plasmashell --version | awk '{print $2}'")) } else if processExists("gnome-session") { return strings.TrimSpace("Gnome " + runCommand("gnome-shell --version | awk '{print $3}'")) } else if processExists("xfce4-session") { return strings.TrimSpace("XFCE " + runCommand("xfce4-session --version | grep xfce4-session | awk '{print $2}'")) } else if processExists("cinnamon") { return strings.TrimSpace("Cinnamon " + runCommand("cinnamon --version | awk '{print $3}'")) } else if processExists("mate-panel") { return strings.TrimSpace("MATE " + runCommand("mate-about --version | awk '{print $4}'")) } else if processExists("lxsession") { return "LXDE" } else if processExists("sway") { return strings.TrimSpace("Sway " + runCommand("sway --version | awk '{print $3}'")) } else if processExists("bspwm") { return strings.TrimSpace("Bspwm " + runCommand("bspwm -v")) } else if processExists("icewm-session") { return strings.TrimSpace("IceWM " + runCommand("icewm --version | awk '{print $2}'")) } return "" } func stripAnsii(str string) string { const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" var re = regexp.MustCompile(ansi) return re.ReplaceAllString(str, "") } func readKeyValueFile(filepath string) (map[string]string, error) { ret := make(map[string]string) if _, err := os.Stat(filepath); err != nil { return nil, err } bytes, err := os.ReadFile(filepath) if err != nil { return nil, err } str := string(bytes) lines := strings.Split(str, "\n") for _, line := range lines { if len(strings.Split(line, "=")) >= 2 { key := strings.SplitN(line, "=", 2)[0] value := strings.SplitN(line, "=", 2)[1] if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") { value = value[1 : len(value)-1] } ret[key] = value } } return ret, nil }