Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions drm/drm_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ func nullTermString(b []byte) string {
// "rotation" property to DRM_MODE_ROTATE_180 or DRM_MODE_ROTATE_0.
// crtcIDs is the ordered list from DRM_IOCTL_MODE_GETRESOURCES; the index
// of crtcID in that list determines which bit to match in PossibleCrtcs.
// If rotate is true but rotation cannot be set, logs a warning.
func setPlaneRotation(fd uintptr, crtcID uint32, crtcIDs []uint32, rotate bool) {
crtcBit := uint32(0)
for i, id := range crtcIDs {
Expand Down Expand Up @@ -526,19 +527,19 @@ func Open(dev string, rotate bool) (*Device, error) {
cap := drmSetClientCap{Capability: drmClientCapUniversalPlanes, Value: 1}
err = drmIoctl(fd, ioctlSetClientCap, &cap)
if err != nil {
f.Close()
cleanup(&buffers)
return nil, fmt.Errorf("DRM_CLIENT_CAP_UNIVERSAL_PLANES: %w", err)
}

// First call: discover how many connectors exist.
var res drmModeRes
err = drmIoctl(fd, ioctlModeGetResources, &res)
if err != nil {
f.Close()
cleanup(&buffers)
return nil, fmt.Errorf("DRM_IOCTL_MODE_GETRESOURCES: %w", err)
}
if res.CountConnectors == 0 {
f.Close()
cleanup(&buffers)
return nil, fmt.Errorf("no DRM connectors found")
}

Expand All @@ -554,7 +555,7 @@ func Open(dev string, rotate bool) (*Device, error) {
res.EncoderIDPtr = 0
err = drmIoctl(fd, ioctlModeGetResources, &res)
if err != nil {
f.Close()
cleanup(&buffers)
return nil, fmt.Errorf("DRM_IOCTL_MODE_GETRESOURCES (ids): %w", err)
}

Expand Down Expand Up @@ -602,7 +603,7 @@ func Open(dev string, rotate bool) (*Device, error) {
enc.EncoderID = encoderID
err = drmIoctl(fd, ioctlModeGetEncoder, &enc)
if err != nil {
f.Close()
cleanup(&buffers)
return nil, fmt.Errorf("DRM_IOCTL_MODE_GETENCODER: %w", err)
}
crtcID := enc.CrtcID
Expand Down Expand Up @@ -751,6 +752,9 @@ func (d *Device) Flip() {
SetConnectorsPtr: uint64(uintptr(unsafe.Pointer(&d.connID))),
CountConnectors: 1,
}
drmIoctl(d.fd, ioctlModeSetCRTC, &crtc)
if err := drmIoctl(d.fd, ioctlModeSetCRTC, &crtc); err != nil {
slog.Error("DRM flip failed; display not updated", "err", err)
return
}
d.active = next
}
18 changes: 16 additions & 2 deletions fb/fb_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,15 @@ func Open(dev string, rotate bool) (*Device, error) {
}
}

fbSize := stride * height
fbSize := int64(stride) * int64(height)
if fbSize <= 0 || fbSize > 256*1024*1024 {
f.Close()
return nil, fmt.Errorf("invalid framebuffer size: %d bytes (stride=%d, height=%d)", fbSize, stride, height)
}
data, err := syscall.Mmap(
int(f.Fd()),
0,
fbSize,
int(fbSize),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED,
)
Expand Down Expand Up @@ -282,6 +286,7 @@ func (fb *Device) blitToHardware(img *image.RGBA) {
}

// Cache the bayer row for this y — it is constant across the x loop.
// The 4×4 Bayer matrix repeats every 4 pixels, so y&3 selects which row.
bayerRow := bayer4x4[y&3]

// Pack the pixel into the hardware format. The vinfo bitfields
Expand Down Expand Up @@ -310,6 +315,7 @@ func (fb *Device) blitToHardware(img *image.RGBA) {
// steps of 8 (R/B) or 4 (G), producing a visible staircase.
// Adding a position-dependent threshold before the right-shift
// spreads the quantisation error across neighbouring pixels.
// x&3 selects the column in the 4-wide Bayer row for this pixel.
d := bayerRow[x&3]
r5 := uint16(clamp8(int(r)+int(d>>3))) >> 3
g6 := uint16(clamp8(int(g)+int(d>>2))) >> 2
Expand All @@ -329,10 +335,18 @@ func (fb *Device) BackBuffer() *image.RGBA {

// Flip copies the back buffer to the hardware framebuffer.
func (fb *Device) Flip() {
if fb.back.Bounds().Dx() != fb.width || fb.back.Bounds().Dy() != fb.height {
slog.Error("back buffer size mismatch; this should not happen", "got", fb.back.Bounds(), "want", image.Rect(0, 0, fb.width, fb.height))
return
}
fb.blitToHardware(fb.back)
}

// Blit copies img to the framebuffer. Deprecated: use BackBuffer and Flip instead.
func (fb *Device) Blit(img *image.RGBA) {
if img.Bounds().Dx() != fb.width || img.Bounds().Dy() != fb.height {
slog.Error("Blit image size mismatch", "got", img.Bounds(), "want", image.Rect(0, 0, fb.width, fb.height))
return
}
fb.blitToHardware(img)
}