From de4379ca2c3a85615237e7a51f9283b740e0c91e Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch Date: Sun, 5 Oct 2025 17:02:02 +0200 Subject: Add Toshiba Tec LIUST-A00 utilities --- liust-50/cmd/liustatus/status.go | 146 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 liust-50/cmd/liustatus/status.go (limited to 'liust-50/cmd/liustatus/status.go') diff --git a/liust-50/cmd/liustatus/status.go b/liust-50/cmd/liustatus/status.go new file mode 100644 index 0000000..06784ba --- /dev/null +++ b/liust-50/cmd/liustatus/status.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "math/rand" + "strings" + "time" + + "janouch.name/desktop-tools/liust-50/charset" +) + +const ( + displayWidth = 20 + displayHeight = 2 + targetCharset = 0x63 +) + +type DisplayState struct { + Display [displayHeight][displayWidth]uint8 +} + +type Display struct { + Current, Last DisplayState +} + +func NewDisplay() *Display { + t := &Display{} + for y := 0; y < displayHeight; y++ { + for x := 0; x < displayWidth; x++ { + t.Current.Display[y][x] = ' ' + t.Last.Display[y][x] = ' ' + } + } + return t +} + +func (t *Display) SetLine(row int, content string) { + if row < 0 || row >= displayHeight { + return + } + + runes := []rune(content) + for x := 0; x < displayWidth; x++ { + if x < len(runes) { + b, ok := charset.ResolveRune(runes[x], targetCharset) + if ok { + t.Current.Display[row][x] = b + } else { + t.Current.Display[row][x] = '?' + } + } else { + t.Current.Display[row][x] = ' ' + } + } +} + +func (t *Display) HasChanges() bool { + for y := 0; y < displayHeight; y++ { + for x := 0; x < displayWidth; x++ { + if t.Current.Display[y][x] != t.Last.Display[y][x] { + return true + } + } + } + return false +} + +func (t *Display) Update() { + for y := 0; y < displayHeight; y++ { + start := -1 + for x := 0; x < displayWidth; x++ { + if t.Current.Display[y][x] != t.Last.Display[y][x] { + start = x + break + } + } + if start >= 0 { + fmt.Printf("\x1b[%d;%dH%s", + y+1, start+1, []byte(t.Current.Display[y][start:])) + copy(t.Last.Display[y][start:], t.Current.Display[y][start:]) + } + } +} + +func statusProducer(lines chan<- string) { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + temperature, fetcher := "", NewWeatherFetcher() + temperatureChan := make(chan string) + go fetcher.Run(5*time.Minute, temperatureChan) + + for { + select { + case newTemperature := <-temperatureChan: + temperature = newTemperature + default: + } + + now := time.Now() + status := fmt.Sprintf("%s %3s %s", + now.Format("Mon _2 Jan"), temperature, now.Format("15:04")) + + // Ensure exactly 20 characters. + runes := []rune(status) + if len(runes) > displayWidth { + status = string(runes[:displayWidth]) + } else if len(runes) < displayWidth { + status = status + strings.Repeat(" ", displayWidth-len(runes)) + } + + lines <- status + <-ticker.C + } +} + +func main() { + rand.Seed(time.Now().UTC().UnixNano()) + terminal := NewDisplay() + + kaomojiChan := make(chan string, 1) + statusChan := make(chan string, 1) + go func() { + kaomojiChan <- strings.Repeat(" ", displayWidth) + statusChan <- strings.Repeat(" ", displayWidth) + }() + + go kaomojiProducer(kaomojiChan) + go statusProducer(statusChan) + + // TODO(p): And we might want to disable cursor visibility as well. + fmt.Printf("\x1bR%c", targetCharset) + fmt.Print("\x1b[2J") // Clear display + + for { + select { + case line := <-kaomojiChan: + terminal.SetLine(0, line) + case line := <-statusChan: + terminal.SetLine(1, line) + } + if terminal.HasChanges() { + terminal.Update() + } + } +} -- cgit v1.2.3-70-g09d2