feat: Windows (CF_DIB) and macOS (AppleScript) clipboard image sync
This commit is contained in:
@@ -137,3 +137,83 @@ func lockAndWrite(h uintptr, data []uint16) bool {
|
||||
copy(dst, data)
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadImage returns the clipboard image (CF_DIB) encoded as PNG.
|
||||
func (c *windowsClipboard) ReadImage() (string, []byte, error) {
|
||||
r1, _, _ := procOpenClipboard.Call(0)
|
||||
if r1 == 0 {
|
||||
return "", nil, ErrNoImage
|
||||
}
|
||||
defer func() { _, _, _ = procCloseClipboard.Call() }()
|
||||
|
||||
h, _, _ := procGetClipboardData.Call(uintptr(cfDIB))
|
||||
if h == 0 {
|
||||
return "", nil, ErrNoImage
|
||||
}
|
||||
|
||||
size, _, _ := procGlobalSize.Call(h)
|
||||
if size == 0 {
|
||||
return "", nil, ErrNoImage
|
||||
}
|
||||
|
||||
p, _, _ := procGlobalLock.Call(h)
|
||||
if p == 0 {
|
||||
return "", nil, ErrNoImage
|
||||
}
|
||||
defer func() { _, _, _ = procGlobalUnlock.Call(h) }()
|
||||
|
||||
// Copy the DIB bytes out of the global block before parsing.
|
||||
ptr := unsafe.Add(unsafe.Pointer(nil), p)
|
||||
raw := unsafe.Slice((*byte)(ptr), size)
|
||||
dib := make([]byte, size)
|
||||
copy(dib, raw)
|
||||
|
||||
pngBytes, err := dibToPNG(dib)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return "image/png", pngBytes, nil
|
||||
}
|
||||
|
||||
// WriteImage writes a PNG image to the clipboard as a CF_DIB.
|
||||
func (c *windowsClipboard) WriteImage(mime string, data []byte) error {
|
||||
if mime != "" && mime != "image/png" {
|
||||
return fmt.Errorf("clipboard: unsupported image mime %q (only PNG)", mime)
|
||||
}
|
||||
dib, err := pngToDIB(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("clipboard: decode image: %w", err)
|
||||
}
|
||||
|
||||
r1, _, err2 := procOpenClipboard.Call(0)
|
||||
if r1 == 0 {
|
||||
return fmt.Errorf("clipboard write image: OpenClipboard: %w", err2)
|
||||
}
|
||||
defer func() { _, _, _ = procCloseClipboard.Call() }()
|
||||
|
||||
if r1, _, err2 = procEmptyClipboard.Call(); r1 == 0 {
|
||||
return fmt.Errorf("clipboard write image: EmptyClipboard: %w", err2)
|
||||
}
|
||||
|
||||
h, _, err2 := procGlobalAlloc.Call(0, uintptr(len(dib)))
|
||||
if h == 0 {
|
||||
return fmt.Errorf("clipboard write image: GlobalAlloc: %w", err2)
|
||||
}
|
||||
|
||||
p, _, _ := procGlobalLock.Call(h)
|
||||
if p == 0 {
|
||||
_, _, _ = procGlobalFree.Call(h)
|
||||
return fmt.Errorf("clipboard write image: GlobalLock failed")
|
||||
}
|
||||
ptr := unsafe.Add(unsafe.Pointer(nil), p)
|
||||
dst := unsafe.Slice((*byte)(ptr), len(dib))
|
||||
copy(dst, dib)
|
||||
_, _, _ = procGlobalUnlock.Call(h)
|
||||
|
||||
if r1, _, err2 = procSetClipboardData.Call(uintptr(cfDIB), h); r1 == 0 {
|
||||
_, _, _ = procGlobalFree.Call(h)
|
||||
return fmt.Errorf("clipboard write image: SetClipboardData: %w", err2)
|
||||
}
|
||||
// Windows now owns the handle.
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user