Add Toshiba Tec LIUST-A00 utilities
This commit is contained in:
146
liust-50/cmd/liustatus/status.go
Normal file
146
liust-50/cmd/liustatus/status.go
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user