summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--autorun.sh9
-rw-r--r--cmd/orca/NotoSans-Bold.ttfbin0 -> 630968 bytes
-rw-r--r--cmd/orca/NotoSans-Regular.ttfbin0 -> 629024 bytes
-rw-r--r--cmd/orca/below.pngbin0 -> 1904 bytes
-rw-r--r--cmd/orca/main.go125
-rw-r--r--cmd/orca/orca.pngbin0 -> 25521 bytes
-rw-r--r--fb/fb.go228
-rw-r--r--go.mod9
-rw-r--r--go.sum8
10 files changed, 372 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index 07e308d..75dc005 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ export GOARM=7
USB_ROOT=/Volumes/MP_SD
GOSOURCES:=$(shell find . -name *.go)
-BINS:=bin/cvend-ipp bin/pic32-ipp bin/beep
+BINS:=bin/cvend-ipp bin/pic32-ipp bin/beep bin/orca
all: ${BINS}
diff --git a/autorun.sh b/autorun.sh
index 4d6697c..30e0382 100644
--- a/autorun.sh
+++ b/autorun.sh
@@ -1,5 +1,6 @@
-#!/bin/bash -x
+set -x
cd "$(dirname "$0")"
-#systemd-run -p StandardOutput=tty -d bin/cvend-ipp
-#systemd-run -p StandardOutput=tty -d bin/pic32-ipp
-systemd-run -p StandardOutput=tty -d bin/beep
+stty -F /dev/ttymxc1 115200 cs8 -cstopb -parenb
+# systemd-run -p StandardOutput=tty -d bin/cvend-ipp
+# systemd-run -p StandardOutput=tty -d bin/pic32-ipp
+systemd-run -p StandardOutput=tty -p TTYPath=/dev/ttymxc1 -d bin/orca
diff --git a/cmd/orca/NotoSans-Bold.ttf b/cmd/orca/NotoSans-Bold.ttf
new file mode 100644
index 0000000..07f0d25
--- /dev/null
+++ b/cmd/orca/NotoSans-Bold.ttf
Binary files differ
diff --git a/cmd/orca/NotoSans-Regular.ttf b/cmd/orca/NotoSans-Regular.ttf
new file mode 100644
index 0000000..4bac02f
--- /dev/null
+++ b/cmd/orca/NotoSans-Regular.ttf
Binary files differ
diff --git a/cmd/orca/below.png b/cmd/orca/below.png
new file mode 100644
index 0000000..d4bf5af
--- /dev/null
+++ b/cmd/orca/below.png
Binary files differ
diff --git a/cmd/orca/main.go b/cmd/orca/main.go
new file mode 100644
index 0000000..79c5eab
--- /dev/null
+++ b/cmd/orca/main.go
@@ -0,0 +1,125 @@
+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/fb"
+)
+
+// 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
+}
+
+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
+}
+
+//go:embed *.ttf *.png
+var embedFS embed.FS
+var qtFonts = os.DirFS("/usr/lib/fonts")
+
+var (
+ dejaVuSans = openSFNT(qtFonts, "DejaVuSans.ttf")
+ notoSans_Regular = openSFNT(embedFS, "NotoSans-Regular.ttf")
+ notoSans_Bold = openSFNT(embedFS, "NotoSans-Bold.ttf")
+)
+
+var (
+ orcaImg = openImage(embedFS, "orca.png")
+ belowImg = openImage(embedFS, "below.png")
+)
+
+type justify int
+
+const (
+ _ justify = iota
+ justifyLeft
+ justifyCenter
+ justifyRight
+)
+
+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
+ }
+ 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")
+
+ fb, err := fb.Open()
+ if err != nil {
+ log.Fatalf("open fb: %v", err)
+ }
+ defer fb.Close()
+
+ for {
+ now := 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)
+ fb.WriteRGBA(img)
+ }
+}
diff --git a/cmd/orca/orca.png b/cmd/orca/orca.png
new file mode 100644
index 0000000..ef3af1d
--- /dev/null
+++ b/cmd/orca/orca.png
Binary files differ
diff --git a/fb/fb.go b/fb/fb.go
new file mode 100644
index 0000000..2c28d6d
--- /dev/null
+++ b/fb/fb.go
@@ -0,0 +1,228 @@
+package fb
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "io"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ Width = 800
+ Height = 480
+)
+
+type fb struct {
+ fd int
+ data []byte
+}
+
+var _ interface {
+ io.Closer
+ draw.Image
+} = &fb{}
+
+func Open() (*fb, error) {
+ fd, err := unix.Open("/dev/fb0", unix.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ data, err := syscall.Mmap(fd, 0, Width*Height*4, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
+ if err != nil {
+ return nil, err
+ }
+ return &fb{
+ fd: fd,
+ data: data,
+ }, nil
+}
+
+func (fb *fb) Close() error {
+ if err := syscall.Munmap(fb.data); err != nil {
+ return err
+ }
+ if err := unix.Close(fb.fd); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (fb *fb) ColorModel() color.Model { return color.RGBAModel } // draw.Image
+func (fb *fb) Bounds() image.Rectangle { return image.Rect(0, 0, Width, Height) } // draw.Image
+
+func (fb *fb) At(x, y int) color.Color { // draw.Image
+ if x < 0 || x >= Width || y < 0 || y >= Height {
+ return color.RGBA{}
+ }
+ off := (y*Width + x) * 4
+ bs := fb.data[off : off+4]
+ return color.RGBA{bs[2], bs[1], bs[0], bs[3]}
+}
+
+func (fb *fb) Set(x, y int, c color.Color) { // draw.Image
+ rgba := color.RGBAModel.Convert(c).(color.RGBA)
+ if x < 0 || x >= Width || y < 0 || y >= Height {
+ return
+ }
+ off := (y*Width + x) * 4
+ bs := fb.data[off : off+4]
+ bs[0] = rgba.B
+ bs[1] = rgba.G
+ bs[2] = rgba.R
+ bs[3] = rgba.A
+}
+
+// swizzle swizzles RGBA to BGRA, or vice versa
+func swizzle(bs []byte) {
+ for i := 0; i < len(bs); i += 4 {
+ bs[i], bs[i+2] = bs[i+2], bs[i]
+ }
+}
+
+func (fb *fb) ReadRGBA() *image.RGBA {
+ img := image.NewRGBA(fb.Bounds())
+ pix := img.Pix[:Width*Height*4]
+ copy(pix, fb.data)
+ swizzle(pix)
+ return img
+}
+
+func (fb *fb) WriteRGBA(img *image.RGBA) { // destructive: swizzles img's RGBA to BGRA in-place
+ if fb.Bounds() != img.Bounds() {
+ panic("img bounds mismatch")
+ }
+ pix := img.Pix[:Width*Height*4]
+ swizzle(pix)
+ copy(fb.data, pix)
+}
+
+// finfo: {
+// ID:[68 73 83 80 51 32 66 71 0 0 0 0 0 0 0 0]
+// SMemStart:437256192
+// SMemLen:1536000
+// Type:0
+// TypeAux:0
+// Visual:2
+// XPanStep:1
+// YPanStep:1
+// YWrapStep:1
+// LineLength:3200
+// MMIOStart:0
+// MMIOLen:0
+// Accel:0
+// Capabilities:0
+// Reserved:[0 0]
+// }
+// vinfo: {
+// XRes:800
+// YRes:480
+// XResVirtual:800
+// YResVirtual:480
+// XOffset:0
+// YOffset:0
+// BitsPerPixel:32
+// Grayscale:0
+// R:{Offset:16 Length:8 MSBRight:0}
+// G:{Offset:8 Length:8 MSBRight:0}
+// B:{Offset:0 Length:8 MSBRight:0}
+// A:{Offset:24 Length:8 MSBRight:0}
+// Nonstd:0
+// Activate:0
+// Height:4294967295
+// Width:4294967295
+// AccelFlags:0
+// Pixclock:37037
+// LeftMargin:40
+// RightMargin:60
+// UpperMargin:10
+// LowerMargin:10
+// HSyncLen:20
+// VSyncLen:10
+// Sync:1073741824
+// VMode:0
+// Rotate:0
+// Colorspace:0
+// Reserved:[0 0 0 0]
+// }
+
+// // include/uapi/linux/fb.h
+
+// type FBFInfo struct {
+// ID [16]byte
+// SMemStart uint32
+// SMemLen uint32
+// Type uint32
+// TypeAux uint32
+// Visual uint32
+// XPanStep uint16
+// YPanStep uint16
+// YWrapStep uint16
+// LineLength uint32
+// MMIOStart uint32
+// MMIOLen uint32
+// Accel uint32
+// Capabilities uint16
+// Reserved [2]uint16
+// }
+
+// type FBBitfield struct {
+// Offset uint32
+// Length uint32
+// MSBRight uint32
+// }
+
+// type FBVInfo struct {
+// XRes uint32
+// YRes uint32
+// XResVirtual uint32
+// YResVirtual uint32
+// XOffset uint32
+// YOffset uint32
+
+// BitsPerPixel uint32
+// Grayscale uint32
+// R, G, B, A FBBitfield
+
+// Nonstd uint32
+
+// Activate uint32
+
+// Height uint32
+// Width uint32
+
+// AccelFlags uint32
+
+// Pixclock uint32
+// LeftMargin uint32
+// RightMargin uint32
+// UpperMargin uint32
+// LowerMargin uint32
+// HSyncLen uint32
+// VSyncLen uint32
+// Sync uint32
+// VMode uint32
+// Rotate uint32
+// Colorspace uint32
+// Reserved [4]uint32
+// }
+
+// func FBGetFInfo(fd int) (finfo FBFInfo, err error) {
+// const FBIOGET_FSCREENINFO = 0x4602
+// _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), FBIOGET_FSCREENINFO, uintptr(unsafe.Pointer(&finfo)))
+// if errno != 0 {
+// err = errno
+// }
+// return
+// }
+
+// func FBGetVInfo(fd int) (vinfo FBVInfo, err error) {
+// const FBIOGET_VSCREENINFO = 0x4600
+// _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), FBIOGET_VSCREENINFO, uintptr(unsafe.Pointer(&vinfo)))
+// if errno != 0 {
+// err = errno
+// }
+// return
+// }
diff --git a/go.mod b/go.mod
index 2166508..7e7c8b6 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,10 @@
module pm3.dev
-go 1.24.0
+go 1.25.0
-require golang.org/x/sys v0.41.0 // indirect
+require (
+ golang.org/x/image v0.39.0
+ golang.org/x/sys v0.43.0
+)
+
+require golang.org/x/text v0.36.0 // indirect
diff --git a/go.sum b/go.sum
index cdc9b1c..b91bbf0 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,6 @@
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
+golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
+golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=