Files
clip-sync/internal/clipboard/clipboard_windows.go
T
bruno d8ee48445b
CI / Lint (push) Successful in 18m7s
CI / Test (push) Successful in 9m2s
CI / Build (darwin/amd64) (push) Failing after 4m4s
CI / Build (linux/amd64) (push) Failing after 4m7s
CI / Build (windows/amd64) (push) Failing after 4m5s
CI / Build (darwin/arm64) (push) Failing after 4m4s
CI / Build (linux/arm64) (push) Failing after 4m7s
CI / Build (windows/arm64) (push) Failing after 4m6s
CI / Release (push) Skipped
fix: correction de toutes les erreurs CI — vet, lint, race detector et config
- clipboard_windows.go: remplacement unsafe.Pointer par unsafe.Add pour go vet 1.26
- clipboard_windows.go: correction errcheck (defer syscall) et ineffassign
- daemon_test.go: mockClipboard thread-safe avec sync.RWMutex (data race)
- daemon_test.go: correction errcheck sur go da.Run()
- server_test.go: vérification des erreurs json.Marshal
- config_test.go: vérification des erreurs MkdirAll/WriteFile
- peers.example.toml: mise à jour au format TOML actuel
2026-08-08 16:28:45 -04:00

131 lines
3.7 KiB
Go

//go:build windows
package clipboard
import (
"fmt"
"syscall"
"unsafe"
)
const cfUnicodeText = 13
var (
user32 = syscall.NewLazyDLL("user32.dll")
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procOpenClipboard = user32.NewProc("OpenClipboard")
procCloseClipboard = user32.NewProc("CloseClipboard")
procEmptyClipboard = user32.NewProc("EmptyClipboard")
procGetClipboardData = user32.NewProc("GetClipboardData")
procSetClipboardData = user32.NewProc("SetClipboardData")
procGlobalAlloc = kernel32.NewProc("GlobalAlloc")
procGlobalLock = kernel32.NewProc("GlobalLock")
procGlobalUnlock = kernel32.NewProc("GlobalUnlock")
procGlobalFree = kernel32.NewProc("GlobalFree")
)
type windowsClipboard struct{}
// New creates a Windows clipboard using Win32 API (syscall, no CGo).
func New() (Clipboard, error) {
return newWindowsClipboard()
}
func newWindowsClipboard() (*windowsClipboard, error) {
return &windowsClipboard{}, nil
}
func (c *windowsClipboard) Read() (string, error) {
r1, _, err := procOpenClipboard.Call(0)
if r1 == 0 {
return "", fmt.Errorf("clipboard read: OpenClipboard: %w", err)
}
defer func() { _, _, _ = procCloseClipboard.Call() }()
h, _, _ := procGetClipboardData.Call(uintptr(cfUnicodeText))
if h == 0 {
// No text in clipboard — not an error, just empty.
return "", nil
}
defer func() { _, _, _ = procGlobalUnlock.Call(h) }()
// Lock and read the global memory block.
// Use a helper to keep the uintptr→unsafe.Pointer conversion close to the syscall.
mem := lockAndRead(h)
if mem == nil {
return "", fmt.Errorf("clipboard read: GlobalLock failed")
}
return syscall.UTF16ToString(mem), nil
}
func (c *windowsClipboard) Write(text string) error {
r1, _, err := procOpenClipboard.Call(0)
if r1 == 0 {
return fmt.Errorf("clipboard write: OpenClipboard: %w", err)
}
defer func() { _, _, _ = procCloseClipboard.Call() }()
r1, _, err = procEmptyClipboard.Call()
if r1 == 0 {
return fmt.Errorf("clipboard write: EmptyClipboard: %w", err)
}
utf16, err := syscall.UTF16FromString(text)
if err != nil {
return fmt.Errorf("clipboard write: UTF16FromString: %w", err)
}
// Allocate global memory: size in bytes (2 bytes per UTF-16 unit, including null terminator)
byteSize := uintptr(len(utf16) * 2)
h, _, err := procGlobalAlloc.Call(0, byteSize)
if h == 0 {
return fmt.Errorf("clipboard write: GlobalAlloc: %w", err)
}
// Lock and write data using a helper (keeps uintptr→unsafe.Pointer close to syscall).
if !lockAndWrite(h, utf16) {
_, _, _ = procGlobalFree.Call(h)
return fmt.Errorf("clipboard write: GlobalLock failed")
}
_, _, _ = procGlobalUnlock.Call(h)
r1, _, err = procSetClipboardData.Call(uintptr(cfUnicodeText), h)
if r1 == 0 {
_, _, _ = procGlobalFree.Call(h)
return fmt.Errorf("clipboard write: SetClipboardData: %w", err)
}
// Windows now owns the handle — do not free it.
return nil
}
// lockAndRead locks a global memory handle and returns its UTF-16 data.
// Returns nil on failure. Caller must call GlobalUnlock after use.
func lockAndRead(h uintptr) []uint16 {
p, _, _ := procGlobalLock.Call(h)
if p == 0 {
return nil
}
// Use unsafe.Add to derive a pointer from nil; the uintptr came from
// GlobalLock (GMEM_FIXED) and is a valid virtual address, not a Go pointer.
ptr := unsafe.Add(unsafe.Pointer(nil), p)
return unsafe.Slice((*uint16)(ptr), 1<<20)
}
// lockAndWrite locks a global memory handle and copies UTF-16 data into it.
// Returns false on failure. Caller must call GlobalUnlock after use.
func lockAndWrite(h uintptr, data []uint16) bool {
p, _, _ := procGlobalLock.Call(h)
if p == 0 {
return false
}
ptr := unsafe.Add(unsafe.Pointer(nil), p)
dst := unsafe.Slice((*uint16)(ptr), len(data))
copy(dst, data)
return true
}