summaryrefslogtreecommitdiff
path: root/cmd/orca/main.go
diff options
context:
space:
mode:
authorKevin Wallace <kevin@pentabarf.net>2026-04-25 06:13:06 -0700
committerKevin Wallace <kevin@pentabarf.net>2026-04-25 06:13:06 -0700
commit37301efe606906e390d91c5938aa8dc9dfc8be7e (patch)
tree00838b83be90c8d891a6c9682a86816d4c27aa24 /cmd/orca/main.go
parentorca ui replica wip (diff)
wait for cvend at startup, only redraw on state changes
Diffstat (limited to 'cmd/orca/main.go')
-rw-r--r--cmd/orca/main.go159
1 files changed, 69 insertions, 90 deletions
diff --git a/cmd/orca/main.go b/cmd/orca/main.go
index 79c5eab..f840d22 100644
--- a/cmd/orca/main.go
+++ b/cmd/orca/main.go
@@ -1,125 +1,104 @@
package main
import (
- "embed"
"flag"
"image"
- "image/color"
- "image/draw"
- _ "image/png"
- "io"
- "io/fs"
"log"
"os"
"time"
- "golang.org/x/image/font"
- "golang.org/x/image/font/opentype"
- "golang.org/x/image/font/sfnt"
- "golang.org/x/image/math/fixed"
+ "pm3.dev/cvend"
"pm3.dev/fb"
+ "pm3.dev/ipp"
)
-// openSFNT opens the specified font file
-// It keeps the file open indefinitely for lazy loading when the font is used.
-// It never attempts to close the file and is meant only for loading fonts permanently at init time.
-func openSFNT(fs fs.FS, path string) *sfnt.Font {
- f, err := fs.Open(path)
- if err != nil {
- panic(err)
- }
- font, err := sfnt.ParseReaderAt(f.(io.ReaderAt))
- if err != nil {
- panic(err)
- }
- return font
+type cvendState struct {
+ ready bool
}
-func openImage(fs fs.FS, path string) image.Image {
- f, err := fs.Open(path)
- if err != nil {
- panic(err)
- }
- img, _, err := image.Decode(f)
- if err != nil {
- panic(err)
- }
- return img
+type displayState struct {
+ now time.Time
+ cvend cvendState
}
-//go:embed *.ttf *.png
-var embedFS embed.FS
-var qtFonts = os.DirFS("/usr/lib/fonts")
+func main() {
+ flag.Parse()
+ log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
+ log.Println(os.Args[0], "start")
-var (
- dejaVuSans = openSFNT(qtFonts, "DejaVuSans.ttf")
- notoSans_Regular = openSFNT(embedFS, "NotoSans-Regular.ttf")
- notoSans_Bold = openSFNT(embedFS, "NotoSans-Bold.ttf")
-)
+ displayStateCh := make(chan displayState)
+ go displayTask(displayStateCh)
-var (
- orcaImg = openImage(embedFS, "orca.png")
- belowImg = openImage(embedFS, "below.png")
-)
+ displayState := displayState{
+ now: time.Now(),
+ }
+ displayStateCh <- displayState
-type justify int
+ cvendStateCh := make(chan cvendState)
+ go cvendTask(cvendStateCh)
-const (
- _ justify = iota
- justifyLeft
- justifyCenter
- justifyRight
-)
+ tickerCh := wallTicker(time.Minute).C
-func drawText(img draw.Image, sfnt *sfnt.Font, size float64, just justify, c color.Color, dot fixed.Point26_6, text string) {
- face, err := opentype.NewFace(sfnt, &opentype.FaceOptions{
- Size: size,
- DPI: 72,
- Hinting: font.HintingNone,
- })
- if err != nil {
- panic(err)
- }
- defer face.Close()
- drawer := font.Drawer{
- Dst: img,
- Src: &image.Uniform{c},
- Face: face,
- Dot: dot,
- }
- _, adv := drawer.BoundString(text)
- var offset fixed.Int26_6
- switch just {
- case justifyCenter:
- offset = adv / 2
- case justifyRight:
- offset = adv
+ for {
+ select {
+ case now := <-tickerCh:
+ displayState.now = now
+ case cvendState := <-cvendStateCh:
+ displayState.cvend = cvendState
+ }
+ displayStateCh <- displayState
}
- drawer.Dot.X -= offset
- drawer.DrawString(text)
}
-func main() {
- flag.Parse()
- log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
- log.Println(os.Args[0], "start")
+// wallTicker returns a ticker that fires whenever time.Now().Truncate(d) changes
+func wallTicker(d time.Duration) *time.Ticker {
+ first := time.Until(time.Now().Add(d).Truncate(d))
+ ticker := time.NewTicker(first)
+ go func() {
+ time.Sleep(first)
+ ticker.Reset(d)
+ }()
+ return ticker
+}
+func displayTask(stateCh <-chan displayState) {
fb, err := fb.Open()
if err != nil {
log.Fatalf("open fb: %v", err)
}
defer fb.Close()
- for {
- now := time.Now()
+ for state := range stateCh {
+ start := time.Now()
img := image.NewRGBA(fb.Bounds())
- draw.Draw(img, img.Bounds(), &image.Uniform{color.RGBA{34, 31, 32, 255}}, image.Point{}, draw.Src)
- drawText(img, dejaVuSans, 20, justifyLeft, color.White, fixed.P(50, 25), now.Format("Jan 02, 2006"))
- drawText(img, dejaVuSans, 20, justifyRight, color.White, fixed.P(750, 25), now.Format("3:04 pm"))
- drawText(img, notoSans_Regular, 16, justifyCenter, color.White, fixed.P(400, 25), "V1234567-D98765")
- draw.Draw(img, orcaImg.Bounds().Add(image.Point{400 - orcaImg.Bounds().Dx()/2, 75}), orcaImg, image.Point{}, draw.Over)
- drawText(img, notoSans_Bold, 48, justifyCenter, color.White, fixed.P(400, 330), "Tap below")
- draw.Draw(img, belowImg.Bounds().Add(image.Point{400 - belowImg.Bounds().Dx()/2, 380}), belowImg, image.Point{}, draw.Over)
+ drawDisplay(img, state)
fb.WriteRGBA(img)
+ dur := time.Now().Sub(start)
+ log.Printf("display update %s %+v", dur, state)
+ }
+
+}
+
+func cvendTask(out chan<- cvendState) {
+ var state cvendState
+ var cv ipp.Session
+ var err error
+ cv, err = cvend.OpenIPP(func(msgType byte, msgData []byte) {
+ switch msgType {
+ case 0x05: // StatusReply
+ if !state.ready {
+ state.ready = true
+ out <- state
+ }
+ default:
+ cvend.LogIPP(msgType, msgData)
+ }
+ })
+ if err != nil {
+ panic(err)
+ }
+ for {
+ cv.SendIPP(0x04, nil) // Status
+ time.Sleep(1 * time.Second)
}
}