Files
desktop-tools/liust-50/cmd/liustatus/status.go
Přemysl Eric Janouch de4379ca2c
All checks were successful
Alpine 3.22 Success
OpenBSD 7.8 Success
Add Toshiba Tec LIUST-A00 utilities
2025-11-17 15:16:21 +01:00

147 lines
2.9 KiB
Go

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()
}
}
}