Capture the panel by reading the displayed drm buffer instead of fbdev
This commit is contained in:
+4
-74
@@ -1,5 +1,5 @@
|
||||
// Runs cabletest for a fixed time and dumps the panel to png, since the machine
|
||||
// with the framebuffer on it is not the machine looking at it.
|
||||
// with the display on it is not the machine looking at it.
|
||||
//
|
||||
// sudo go run ./harness -for 20s -at 3s,15s -- -a enp1s0f0np0 -b enp1s0f1np1
|
||||
package main
|
||||
@@ -7,41 +7,21 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
fbPath = "/dev/fb0"
|
||||
shotsDir = "shots"
|
||||
grace = 3 * time.Second
|
||||
)
|
||||
|
||||
// Matching fb.go, which reads the variable screen info as a flat run of u32s
|
||||
// rather than a struct to sidestep any question of padding.
|
||||
const (
|
||||
fbioGetVScreenInfo = 0x4600
|
||||
viXres = 0
|
||||
viYres = 1
|
||||
viBitsPerPixel = 6
|
||||
viRedOffset = 8
|
||||
viGreenOffset = 11
|
||||
viBlueOffset = 14
|
||||
viScreenInfoLen = 40
|
||||
)
|
||||
|
||||
func main() {
|
||||
runFor := flag.Duration("for", 15*time.Second, "how long to let cabletest run")
|
||||
at := flag.String("at", "", "offsets to capture the panel at, comma separated, e.g. 3s,10s")
|
||||
@@ -165,55 +145,17 @@ func groupGone(pgid int) bool {
|
||||
}
|
||||
|
||||
func shoot(name string) error {
|
||||
f, err := os.Open(fbPath)
|
||||
g, err := openGrabber()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
defer g.close()
|
||||
|
||||
var vi [viScreenInfoLen]uint32
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
|
||||
fbioGetVScreenInfo, uintptr(unsafe.Pointer(&vi[0]))); errno != 0 {
|
||||
return fmt.Errorf("get screen info: %w", errno)
|
||||
}
|
||||
if vi[viBitsPerPixel] != 32 {
|
||||
return fmt.Errorf("only 32bpp supported, got %d", vi[viBitsPerPixel])
|
||||
}
|
||||
stride, err := readUint("/sys/class/graphics/fb0/stride")
|
||||
img, err := g.frame()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pw, ph := int(vi[viXres]), int(vi[viYres])
|
||||
mem, err := unix.Mmap(int(f.Fd()), 0, int(stride)*ph, unix.PROT_READ, unix.MAP_SHARED)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mmap: %w", err)
|
||||
}
|
||||
defer unix.Munmap(mem)
|
||||
|
||||
// cabletest flushes a whole frame into this mapping every 16ms, so the pixel
|
||||
// loop below is far too slow to read it directly: consecutive scanlines come
|
||||
// from different frames and every changing digit ends up drawn twice. One
|
||||
// memmove out first narrows the window to about the length of its own copy.
|
||||
buf := make([]byte, len(mem))
|
||||
copy(buf, mem)
|
||||
|
||||
rs, gs, bs := vi[viRedOffset], vi[viGreenOffset], vi[viBlueOffset]
|
||||
// The panel is landscape and every draw is turned a quarter turn on its way
|
||||
// into it, so the turn is undone here to get back what a person standing in
|
||||
// front of it sees.
|
||||
img := image.NewNRGBA(image.Rect(0, 0, ph, pw))
|
||||
for x := 0; x < ph; x++ {
|
||||
for y := 0; y < pw; y++ {
|
||||
o := x*int(stride) + (pw-1-y)*4
|
||||
v := uint32(buf[o]) | uint32(buf[o+1])<<8 |
|
||||
uint32(buf[o+2])<<16 | uint32(buf[o+3])<<24
|
||||
img.SetNRGBA(x, y, color.NRGBA{
|
||||
R: uint8(v >> rs), G: uint8(v >> gs), B: uint8(v >> bs), A: 255,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
out, err := os.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -224,15 +166,3 @@ func shoot(name string) error {
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
func readUint(path string) (uint64, error) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v, err := strconv.ParseUint(strings.TrimSpace(string(b)), 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user