diff --git a/app/app.go b/app/app.go index e8e63a6..b2a6d24 100644 --- a/app/app.go +++ b/app/app.go @@ -1,12 +1,12 @@ package app import ( + "finally-a-monolithic-linux-hw-monitor/app/common" "finally-a-monolithic-linux-hw-monitor/app/mem" "finally-a-monolithic-linux-hw-monitor/app/proc" "fmt" "os" "os/signal" - "strings" "syscall" "time" ) @@ -25,22 +25,19 @@ func Run() { os.Exit(0) }() + term := common.NewTerminal() proc, _ := proc.GetProc() mem, _ := mem.GetMem() ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { - console := &strings.Builder{} - // Move cursor to top-left (0,0) without clearing full buffer (flicker-free) - fmt.Fprint(console, "\033[H") + term.Start() - proc.RenderProc(console) - mem.Render(console) + proc.Render(term) + mem.Render(term) - fmt.Fprint(console, "\nPress Ctrl+C to exit.\033[K\n") - fmt.Fprint(console, "\033[0J") - fmt.Print(console.String()) + term.End() <-ticker.C } diff --git a/app/common/console.go b/app/common/console.go new file mode 100644 index 0000000..8e5439c --- /dev/null +++ b/app/common/console.go @@ -0,0 +1,50 @@ +package common + +import ( + "fmt" + "strings" +) + +type Terminal interface { + Start() error + Header(string) error + Subheader(string) error + Print(string) error + End() error +} + +type StringBuilderTerminal struct { + sb *strings.Builder +} + +func NewTerminal() Terminal { + return &StringBuilderTerminal{ + sb: &strings.Builder{}, + } +} + +func (t *StringBuilderTerminal) Start() error { + _, err := fmt.Fprint(t.sb, "\033[H") + return err +} + +func (t *StringBuilderTerminal) Header(header string) error { + _, err := fmt.Fprintf(t.sb, "=== %s ===\033[K\n", header) + return err +} + +func (t *StringBuilderTerminal) Subheader(subheader string) error { + _, err := fmt.Fprintf(t.sb, "--- %s ---\033[K\n", subheader) + return err +} + +func (t *StringBuilderTerminal) Print(line string) error { + _, err := fmt.Fprintf(t.sb, "%s\033[K\n", line) + return err +} + +func (t *StringBuilderTerminal) End() error { + fmt.Fprint(t.sb, "\033[0J") + _, err := fmt.Print(t.sb.String()) + return err +} diff --git a/app/common/drivers.go b/app/common/drivers.go index 477d509..fbe64be 100644 --- a/app/common/drivers.go +++ b/app/common/drivers.go @@ -1,8 +1,6 @@ package common -import "strings" - type Driver interface { Read() (any, error) - Render(*strings.Builder) error + Render(Terminal) error } diff --git a/app/mem/drivers/dmidecode.go b/app/mem/drivers/dmidecode.go index 312130b..7808a26 100644 --- a/app/mem/drivers/dmidecode.go +++ b/app/mem/drivers/dmidecode.go @@ -2,6 +2,7 @@ package drivers import ( "bufio" + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os/exec" "strconv" @@ -16,23 +17,24 @@ func NewDmiDecodeDriver() (*DmiDecodeDriver, error) { return &r, err } -func (r *DmiDecodeDriver) Render(console *strings.Builder) error { +func (r *DmiDecodeDriver) Render(term common.Terminal) error { h, err := r.Read() if err != nil { return err } hardware := h.(MemoryHardware) + term.Subheader("Modules") for i := range len(hardware.ModelNames) { - fmt.Fprintf(console, "--- RAM Module %d ---\033[K\n", i) + term.Print(fmt.Sprintf(" Module %d ", i)) modelName := hardware.ModelNames[i] - fmt.Fprintf(console, "%-16s | %-16s \033[K\n", "Model Name", modelName) + term.Print(fmt.Sprintf("%-16s | %-16s", "Model Name", modelName)) generation := hardware.Generations[i] speed := hardware.Speeds[i] - fmt.Fprintf(console, "%-16s | %s@%s \033[K\n", "Speed", generation, speed) + term.Print(fmt.Sprintf("%-16s | %s@%s", "Speed", generation, speed)) formFactor := hardware.FormFactors[i] - fmt.Fprintf(console, "%-16s | %s \033[K\n", "Form Factor", formFactor) + term.Print(fmt.Sprintf("%-16s | %s", "Form Factor", formFactor)) } return nil diff --git a/app/mem/drivers/meminfo.go b/app/mem/drivers/meminfo.go index 97e443c..5b178c1 100644 --- a/app/mem/drivers/meminfo.go +++ b/app/mem/drivers/meminfo.go @@ -2,6 +2,7 @@ package drivers import ( "bufio" + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os" "strconv" @@ -16,7 +17,7 @@ func NewMemInfoDriver() (*MemInfoDriver, error) { return &r, err } -func (r *MemInfoDriver) Render(console *strings.Builder) error { +func (r *MemInfoDriver) Render(term common.Terminal) error { u, err := r.Read() if err != nil { return err @@ -24,10 +25,10 @@ func (r *MemInfoDriver) Render(console *strings.Builder) error { usage := u.(MemoryUsage) - fmt.Fprintf(console, "--- Usage ---\033[K\n") - fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Total", usage.TotalGB) - fmt.Fprintf(console, "%-16s | %.2f GiB (%.2f%%) \033[K\n", "Used", usage.UsedGB, usage.UsedPercent) - fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Avail", usage.AvailableGB) + term.Subheader("Usage") + term.Print(fmt.Sprintf("%-16s | %.2f GiB", "Total", usage.TotalGB)) + term.Print(fmt.Sprintf("%-16s | %.2f GiB (%.2f%%)", "Used", usage.UsedGB, usage.UsedPercent)) + term.Print(fmt.Sprintf("%-16s | %.2f GiB", "Avail", usage.AvailableGB)) return nil } diff --git a/app/mem/mem.go b/app/mem/mem.go index a28ad68..7d0132d 100644 --- a/app/mem/mem.go +++ b/app/mem/mem.go @@ -3,14 +3,12 @@ package mem import ( "finally-a-monolithic-linux-hw-monitor/app/common" "finally-a-monolithic-linux-hw-monitor/app/mem/drivers" - "fmt" - "strings" ) -func (mem *Mem) Render(console *strings.Builder) { - fmt.Fprintf(console, "=== %s ===\033[K\n", "Memory") +func (mem *Mem) Render(term common.Terminal) { + term.Header("Memory") for _, driver := range mem.drivers { - driver.Render(console) + driver.Render(term) } } diff --git a/app/proc/drivers/hwmon.go b/app/proc/drivers/hwmon.go index abaa1bf..90466d7 100644 --- a/app/proc/drivers/hwmon.go +++ b/app/proc/drivers/hwmon.go @@ -1,6 +1,7 @@ package drivers import ( + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os" "path/filepath" @@ -8,6 +9,8 @@ import ( "strings" ) +const milliCperC = 1000.0 + type HwmonDriver struct { hwmonDir string hwmonDriver string @@ -38,20 +41,20 @@ func NewHwmonDriver(expectedDrivers []string) (*HwmonDriver, error) { return nil, fmt.Errorf("could not find %#v in /sys/class/hwmon", expectedDrivers) } -func (r *HwmonDriver) Render(console *strings.Builder) error { +func (r *HwmonDriver) Render(term common.Terminal) error { t, err := r.Read() if err != nil { return err } temps := t.([]TemperatureSensor) + term.Subheader("Temperature") - fmt.Fprint(console, "--- Temperature ---\033[K\n") for _, sensor := range temps { tempC := sensor.TempC tempF := (tempC * 9 / 5) + 32 // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f °C (%.3f °F)\033[K\n", sensor.Name, tempC, tempF) + term.Print(fmt.Sprintf("%-16s | %.2f °C (%.3f °F)", sensor.Name, tempC, tempF)) } return nil @@ -87,7 +90,7 @@ func (r *HwmonDriver) Read() (any, error) { temps = append(temps, TemperatureSensor{ path: inputPath, Name: label, - TempC: milliC / 1000.0, + TempC: milliC / milliCperC, }) } diff --git a/app/proc/drivers/rapl.go b/app/proc/drivers/rapl.go index 55f2b11..c7b3d35 100644 --- a/app/proc/drivers/rapl.go +++ b/app/proc/drivers/rapl.go @@ -1,6 +1,7 @@ package drivers import ( + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os" "path/filepath" @@ -28,18 +29,18 @@ func NewRaplDriver() (*RaplDriver, error) { return &r, err } -func (r *RaplDriver) Render(console *strings.Builder) error { - fmt.Fprint(console, "--- Power ---\033[K\n") +func (r *RaplDriver) Render(term common.Terminal) error { p, err := r.Read() if err != nil { return err } power := p.([]PowerSensor) + term.Subheader("Power") for _, sensor := range power { // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f W\033[K\n", sensor.Name, sensor.Watts) + term.Print(fmt.Sprintf("%-16s | %.2f W", sensor.Name, sensor.Watts)) } return nil diff --git a/app/proc/drivers/sys.go b/app/proc/drivers/sys.go index 1631ddb..5236846 100644 --- a/app/proc/drivers/sys.go +++ b/app/proc/drivers/sys.go @@ -1,6 +1,7 @@ package drivers import ( + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os" "path/filepath" @@ -17,18 +18,18 @@ func NewSysDriver() (*SysDriver, error) { return &r, err } -func (r *SysDriver) Render(console *strings.Builder) error { - fmt.Fprint(console, "--- Clock Frequency ---\033[K\n") +func (r *SysDriver) Render(term common.Terminal) error { c, err := r.Read() if err != nil { return err } clock := c.([]ClockSensor) + term.Subheader("Frequency") for _, sensor := range clock { // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f GHz\033[K\n", sensor.Name, sensor.FrequencyGHz) + term.Print(fmt.Sprintf("%-16s | %.2f GHz", sensor.Name, sensor.FrequencyGHz)) } return nil diff --git a/app/proc/proc.go b/app/proc/proc.go index ab90a67..1fb2df8 100644 --- a/app/proc/proc.go +++ b/app/proc/proc.go @@ -8,10 +8,10 @@ import ( "strings" ) -func (proc *Proc) RenderProc(console *strings.Builder) { - fmt.Fprintf(console, "=== %s ===\033[K\n", proc.Model()) +func (proc *Proc) Render(term common.Terminal) { + term.Header(proc.Model()) for _, driver := range proc.drivers { - driver.Render(console) + driver.Render(term) } }