Compare commits

8 Commits
4 changed files with 254 additions and 43 deletions
+70 -15
View File
@@ -2,7 +2,6 @@ package main
import (
"os"
"os/exec"
"slices"
"strings"
@@ -11,12 +10,20 @@ import (
)
type CPU struct {
Vendor string
Model string
Cores int
Threads int
}
type GPU struct {
PCIAddress string
Vendor string
Product string
Subsystem string
Driver string
VRAM int64
}
type Monitor struct {
Width int
Height int
@@ -36,9 +43,23 @@ func GetCPUs(hiddenCPUs []int) []CPU {
continue
}
// Remove unnecessary information from the CPU model
model := cpu.Model
stringsToRemove := []string{
" CPU", " FPU", " APU", " Processor",
" Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core",
" 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core",
}
for _, str := range stringsToRemove {
model = strings.ReplaceAll(model, str, "")
}
model = strings.Split(model, "w/ Radeon ")[0]
model = strings.Split(model, "with Radeon ")[0]
model = strings.Split(model, "@")[0]
model = strings.TrimSpace(model)
ret = append(ret, CPU{
Vendor: cpu.Vendor,
Model: cpu.Model,
Model: model,
Cores: int(cpu.NumCores),
Threads: int(cpu.NumThreads),
})
@@ -47,22 +68,52 @@ func GetCPUs(hiddenCPUs []int) []CPU {
return ret
}
func GetGPUModels(hiddenGPUS []int) (ret []string) {
cmd := exec.Command("sh", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:'")
bytes, err := cmd.Output()
func GetGPUModels(hiddenGPUs []int) []GPU {
ret := make([]GPU, 0)
// Set stderr to nil to avoid warnings
stderr := os.Stderr
os.Stderr = nil
gpus, err := ghw.GPU()
if err != nil {
return nil
return ret
}
for i, gpu := range strings.Split(string(bytes), "\n") {
if slices.Contains(hiddenGPUS, i+1) {
// Restore stderr
os.Stderr = stderr
for i, gpu := range gpus.GraphicsCards {
if slices.Contains(hiddenGPUs, i+1) {
continue
}
if gpu == "" {
continue
// Set alternative names for vendors
var vendor string
switch gpu.DeviceInfo.Vendor.ID {
case "1002":
vendor = "AMD"
case "10de":
vendor = "Nvidia"
case "8086":
vendor = "Intel"
default:
vendor = gpu.DeviceInfo.Vendor.Name
}
gpu = strings.TrimPrefix(strings.TrimSpace(gpu), "Device:\t")
ret = append(ret, gpu)
// Fallback subsystem name to product name
subsystem := gpu.DeviceInfo.Subsystem.Name
if subsystem == "" || subsystem == "unknown" {
subsystem = gpu.DeviceInfo.Product.Name
}
ret = append(ret, GPU{
PCIAddress: gpu.Address,
Vendor: vendor,
Product: gpu.DeviceInfo.Product.Name,
Subsystem: subsystem,
Driver: gpu.DeviceInfo.Driver,
})
}
return ret
@@ -73,7 +124,11 @@ func GetMotherboardModel() string {
if err != nil {
return ""
}
return strings.TrimSpace(string(bytes))
// Remove duplicate whitespaces
ret := strings.Join(strings.Fields(string(bytes)), " ")
return ret
}
func GetMonitors() []Monitor {
+38 -11
View File
@@ -100,10 +100,12 @@ func initializeModuleMap() {
// Packages module
packagesModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "packages", Format: "%3Packages: %4$PACKAGES"}, Execute: func(sm StormfetchModule) string {
packages := GetInstalledPackages()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "PACKAGES":
return GetInstalledPackages()
return packages
default:
return ""
}
@@ -113,10 +115,12 @@ func initializeModuleMap() {
// Shell module
shellModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "shell", Format: "%3Shell: %4$SHELL"}, Execute: func(sm StormfetchModule) string {
shell := GetShell()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "SHELL":
return GetShell()
return shell
default:
return ""
}
@@ -126,10 +130,16 @@ func initializeModuleMap() {
// Init system module
initSystemModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "init_system", Format: "%3Init: %4$INIT"}, Execute: func(sm StormfetchModule) string {
initSystem := GetInitSystem()
if initSystem == "" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "INIT":
return GetInitSystem()
return initSystem
default:
return ""
}
@@ -139,10 +149,12 @@ func initializeModuleMap() {
// Libc module
libcModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "libc", Format: "%3Libc: %4$LIBC"}, Execute: func(sm StormfetchModule) string {
libc := GetLibc()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LIBC":
return GetLibc()
return libc
default:
return ""
}
@@ -188,8 +200,6 @@ func initializeModuleMap() {
switch s {
case "CPU_NUM":
return strconv.Itoa(i + 1)
case "CPU_VENDOR":
return cpu.Vendor
case "CPU_MODEL":
return cpu.Model
case "CPU_CORES":
@@ -209,7 +219,7 @@ func initializeModuleMap() {
RegisterModule(cpusModule)
// GPUs module
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_MODEL"}, Execute: func(sm StormfetchModule) string {
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_VENDOR $GPU_SUBSYSTEM"}, Execute: func(sm StormfetchModule) string {
hiddenGPUsInterface, _ := sm.GetData("hidden_gpus", make([]any, 0))
// Convert interface slices to string slices
@@ -222,12 +232,22 @@ func initializeModuleMap() {
gpus := GetGPUModels(hiddenGPUs)
for i, gpu := range gpus {
vram := strconv.FormatInt(gpu.VRAM, 10)
expanded := os.Expand(sm.Format, func(s string) string {
switch s {
case "GPU_NUM":
return strconv.Itoa(i + 1)
case "GPU_MODEL":
return gpu
case "GPU_VENDOR":
return gpu.Vendor
case "GPU_PRODUCT":
return gpu.Product
case "GPU_SUBSYSTEM":
return gpu.Subsystem
case "GPU_DRIVER":
return gpu.Driver
case "GPU_VRAM":
return vram
default:
return ""
}
@@ -244,6 +264,10 @@ func initializeModuleMap() {
memoryModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "memory", Format: "%3Memory: %4$MEM_USED MiB / $MEM_TOTAL MiB"}, Execute: func(sm StormfetchModule) string {
memoryInfo := GetMemoryInfo()
if memoryInfo == nil {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "MEM_TOTAL":
@@ -330,10 +354,12 @@ func initializeModuleMap() {
// Local IP module
localIpModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "local_ip", Format: "%3Local IP: %4$LOCAL_IP"}, Execute: func(sm StormfetchModule) string {
localIP := GetLocalIP()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LOCAL_IP":
return GetLocalIP()
return localIP
default:
return ""
}
@@ -349,6 +375,7 @@ func initializeModuleMap() {
}
dewm := GetDEWM()
displayProtocol := GetDisplayProtocol()
// Return empty string if can't detect DE/WM
if dewm.Name == "Unknown" {
@@ -364,7 +391,7 @@ func initializeModuleMap() {
case "DEWM_VERSION":
return dewm.Version
case "DISPLAY_PROTOCOL":
return GetDisplayProtocol()
return displayProtocol
default:
return ""
}
+139 -15
View File
@@ -2,25 +2,29 @@ package main
import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"strings"
)
type PackageManager struct {
Name string
ExecutableName string
PackageListCommand string
GetPackages func(...any) int
FunctionInput []any
}
var PackageManagers = []PackageManager{
{Name: "dpkg", ExecutableName: "dpkg", PackageListCommand: "dpkg-query -f '${Package}\\n' -W"},
{Name: "pacman", ExecutableName: "pacman", PackageListCommand: "pacman -Q"},
{Name: "rpm", ExecutableName: "rpm", PackageListCommand: "rpm -qa"},
{Name: "xbps", ExecutableName: "xbps-query", PackageListCommand: "xbps-query -l"},
{Name: "bpm", ExecutableName: "bpm", PackageListCommand: "ls /var/lib/bpm/installed/"},
{Name: "portage", ExecutableName: "emerge", PackageListCommand: "find /var/db/pkg/*/ -mindepth 1 -maxdepth 1"},
{Name: "flatpak", ExecutableName: "flatpak", PackageListCommand: "flatpak list"},
{Name: "snap", ExecutableName: "snap", PackageListCommand: "snap list | tail +2"},
{Name: "dpkg", ExecutableName: "dpkg", GetPackages: pmFileLines, FunctionInput: []any{"/var/lib/dpkg/status", "Status: install ok installed"}},
{Name: "pacman", ExecutableName: "pacman", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/pacman/local/", true}},
{Name: "rpm", ExecutableName: "rpm", GetPackages: pmShellCommandLines, FunctionInput: []any{"rpm -qa"}},
{Name: "xbps", ExecutableName: "xbps-query", GetPackages: pmFileLines, FunctionInput: []any{"/var/db/xbps/pkgdb-0.38.plist", "<string>installed</string>"}},
{Name: "bpm", ExecutableName: "bpm", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/bpm/installed/"}},
{Name: "portage", ExecutableName: "emerge", GetPackages: pmPortage},
{Name: "flatpak", ExecutableName: "flatpak", GetPackages: pmFlatpak},
{Name: "snap", ExecutableName: "snap", GetPackages: pmSnap},
}
func (pm *PackageManager) CountPackages() int {
@@ -29,12 +33,7 @@ func (pm *PackageManager) CountPackages() int {
return 0
}
output, err := exec.Command("/bin/sh", "-c", pm.PackageListCommand).Output()
if err != nil {
return 0
}
return strings.Count(string(output), "\n")
return pm.GetPackages(pm.FunctionInput...)
}
func GetInstalledPackages() (ret string) {
@@ -51,3 +50,128 @@ func GetInstalledPackages() (ret string) {
return ret
}
func pmDirectoryElements(args ...any) int {
directory := args[0].(string)
dirsOnly := false
if len(args) >= 2 {
dirsOnly = args[1].(bool)
}
total := 0
dirEntries, _ := os.ReadDir(directory)
for _, entry := range dirEntries {
if !entry.IsDir() && dirsOnly {
continue
}
total++
}
return total
}
func pmFileLines(args ...any) int {
filepath := args[0].(string)
mustContain := ""
if len(args) >= 2 {
mustContain = args[1].(string)
}
total := 0
content, err := os.ReadFile(filepath)
if err == nil {
for _, line := range strings.Split(strings.TrimSpace(string(content)), "\n") {
if mustContain != "" && !strings.Contains(line, mustContain) {
continue
}
total++
}
}
return total
}
func pmShellCommandLines(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
return len(strings.Split(output, "\n"))
}
func pmShellCommandOutput(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
packageCount, _ := strconv.Atoi(output)
return packageCount
}
func pmPortage(args ...any) int {
portageDir := "/var/db/pkg"
total := 0
dirEntries, _ := os.ReadDir(portageDir)
for _, repo := range dirEntries {
if !repo.IsDir() {
continue
}
packages, _ := os.ReadDir(path.Join(portageDir, repo.Name()))
total += len(packages)
}
return total
}
func pmFlatpak(args ...any) int {
arch := GetArch()
flatpakDir := "/var/lib/flatpak"
total := 0
// Count applications
apps, err := os.ReadDir(path.Join(flatpakDir, "app"))
if err == nil {
for _, app := range apps {
if strings.HasSuffix(app.Name(), ".Locale") || strings.HasSuffix(app.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "app", app.Name(), arch))
total += len(dirEntries)
}
}
// Count runtimes
runtimes, err := os.ReadDir(path.Join(flatpakDir, "runtime"))
if err == nil {
for _, runtime := range runtimes {
if strings.HasSuffix(runtime.Name(), ".Locale") || strings.HasSuffix(runtime.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "runtime", runtime.Name(), arch))
total += len(dirEntries)
}
}
return total
}
func pmSnap(args ...any) int {
total := pmDirectoryElements("/snap", true)
if total > 0 {
return total - 1
}
total = pmDirectoryElements("/var/lib/snapd/snap", true)
if total > 0 {
return total - 1
}
return 0
}
+5
View File
@@ -138,6 +138,11 @@ func GetInitSystem() string {
return ""
}
// Return if init system can't be found
if process == nil {
return ""
}
// Special cases
// OpenRC check
if _, err := os.Stat("/usr/sbin/openrc"); err == nil {