diff --git a/drm/drm_linux.go b/drm/drm_linux.go index 3ca9a4a..56acb71 100644 --- a/drm/drm_linux.go +++ b/drm/drm_linux.go @@ -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 { @@ -526,7 +527,7 @@ 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) } @@ -534,11 +535,11 @@ func Open(dev string, rotate bool) (*Device, error) { 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") } @@ -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) } @@ -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 @@ -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 } diff --git a/fb/fb_linux.go b/fb/fb_linux.go index 7aa94b3..e9253bc 100644 --- a/fb/fb_linux.go +++ b/fb/fb_linux.go @@ -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, ) @@ -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 @@ -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 @@ -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) }