147 lines
2.9 KiB
Go
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()
|
|
}
|
|
}
|
|
}
|