From 960743650370838f06acae9e2dd13c5d866715cc Mon Sep 17 00:00:00 2001 From: Carlos Gutierrez Date: Mon, 29 Sep 2025 13:39:40 -0700 Subject: [PATCH] fix: validate DIBv5 format before fallback to DIB in Windows clipboard Previously, the clipboard reader would return errUnsupported when DIBv5 data existed but had an unsupported BitCount. This change validates the DIBv5 format (BitCount == 32) before processing, and falls back to readImageDib() for any DIBv5 validation failures or when DIBv5 data is unavailable, ensuring more reliable clipboard image reading. --- .../internal/clipboard/clipboard_windows.go | 80 +++++++++---------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/packages/tui/internal/clipboard/clipboard_windows.go b/packages/tui/internal/clipboard/clipboard_windows.go index 09fc1416913..e5c7892de28 100644 --- a/packages/tui/internal/clipboard/clipboard_windows.go +++ b/packages/tui/internal/clipboard/clipboard_windows.go @@ -108,49 +108,47 @@ func writeText(buf []byte) error { // if presents. The caller is responsible for opening/closing the // clipboard before calling this function. func readImage() ([]byte, error) { - hMem, _, err := getClipboardData.Call(cFmtDIBV5) - if hMem == 0 { - // second chance to try FmtDIB - return readImageDib() - } - p, _, err := gLock.Call(hMem) - if p == 0 { - return nil, err - } - defer gUnlock.Call(hMem) - - // inspect header information - info := (*bitmapV5Header)(unsafe.Pointer(p)) - - // maybe deal with other formats? - if info.BitCount != 32 { - return nil, errUnsupported - } - - var data []byte - sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - sh.Data = uintptr(p) - sh.Cap = int(info.Size + 4*uint32(info.Width)*uint32(info.Height)) - sh.Len = int(info.Size + 4*uint32(info.Width)*uint32(info.Height)) - img := image.NewRGBA(image.Rect(0, 0, int(info.Width), int(info.Height))) - offset := int(info.Size) - stride := int(info.Width) - for y := 0; y < int(info.Height); y++ { - for x := 0; x < int(info.Width); x++ { - idx := offset + 4*(y*stride+x) - xhat := (x + int(info.Width)) % int(info.Width) - yhat := int(info.Height) - 1 - y - r := data[idx+2] - g := data[idx+1] - b := data[idx+0] - a := data[idx+3] - img.SetRGBA(xhat, yhat, color.RGBA{r, g, b, a}) + hMem, _, _ := getClipboardData.Call(cFmtDIBV5) + if hMem != 0 { + p, _, _ := gLock.Call(hMem) + if p != 0 { + defer gUnlock.Call(hMem) + + // inspect header information + info := (*bitmapV5Header)(unsafe.Pointer(p)) + + // validate format + if info.BitCount == 32 { + var data []byte + sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + sh.Data = uintptr(p) + sh.Cap = int(info.Size + 4*uint32(info.Width)*uint32(info.Height)) + sh.Len = int(info.Size + 4*uint32(info.Width)*uint32(info.Height)) + img := image.NewRGBA(image.Rect(0, 0, int(info.Width), int(info.Height))) + offset := int(info.Size) + stride := int(info.Width) + for y := 0; y < int(info.Height); y++ { + for x := 0; x < int(info.Width); x++ { + idx := offset + 4*(y*stride+x) + xhat := (x + int(info.Width)) % int(info.Width) + yhat := int(info.Height) - 1 - y + r := data[idx+2] + g := data[idx+1] + b := data[idx+0] + a := data[idx+3] + img.SetRGBA(xhat, yhat, color.RGBA{r, g, b, a}) + } + } + // always use PNG encoding. + var buf bytes.Buffer + png.Encode(&buf, img) + return buf.Bytes(), nil + } } } - // always use PNG encoding. - var buf bytes.Buffer - png.Encode(&buf, img) - return buf.Bytes(), nil + + // Fallback to DIB format if DIBv5 is unavailable or unsupported + return readImageDib() } func readImageDib() ([]byte, error) {