abstract terminal interface

This commit is contained in:
alu
2026-08-07 14:49:21 -07:00
parent 691dba7687
commit 5c4d16857b
10 changed files with 91 additions and 40 deletions
+6 -9
View File
@@ -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
}
+50
View File
@@ -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
}
+1 -3
View File
@@ -1,8 +1,6 @@
package common
import "strings"
type Driver interface {
Read() (any, error)
Render(*strings.Builder) error
Render(Terminal) error
}
+7 -5
View File
@@ -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
+6 -5
View File
@@ -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
}
+3 -5
View File
@@ -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)
}
}
+7 -4
View File
@@ -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,
})
}
+4 -3
View File
@@ -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
+4 -3
View File
@@ -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
+3 -3
View File
@@ -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)
}
}