This file documents everything an AI agent needs to know to work effectively in this codebase.
go-nativeclipboard is a Go library that provides native clipboard functionality using purego instead of cgo. This enables clipboard access without requiring a C compiler.
- Language: Go 1.25.4
- Module:
github.com/aymanbagabas/go-nativeclipboard - Key Dependencies:
github.com/ebitengine/purego- For calling native APIs without cgo
- Status: macOS, Linux, FreeBSD, and Windows implementations complete
.
├── clipboard.go # Main API and public interfaces
├── clipboard_darwin.go # macOS implementation (NSPasteboard via purego)
├── clipboard_x11.go # X11 implementation for Linux and FreeBSD (via purego)
├── clipboard_windows.go # Windows implementation (Win32 API via syscall)
├── clipboard_test.go # Platform-agnostic tests
├── doc.go # Package documentation for pkg.go.dev
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── README.md # User-facing documentation
├── AGENTS.md # This file
└── examples/ # Example programs
The project uses purego to call native operating system APIs without cgo:
- github.com/ebitengine/purego - Pure Go library for calling C functions and native APIs
- Used to call Objective-C runtime on macOS
- Will be used for X11/Wayland on Linux and Win32 on Windows
Previous dependencies (from golang.design/x/clipboard) have been removed as they used cgo.
go build ./...# Run all tests on Linux/BSD (requires X11 and Xvfb for headless)
go test ./...
# Run specific test
go test -v -run TestWriteReadText
# Run with coverage
go test -cover ./...
# Run with race detector
go test -race ./...
# For headless Linux/BSD testing
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
export DISPLAY=:99.0
go test -v ./...# Download dependencies
go mod download
# Update purego to latest
go get -u github.com/ebitengine/purego
go mod tidy- Package name:
nativeclipboard - Single package: No subpackages
- Platform-specific code: Build tags separate implementations
The public API is in clipboard.go:
Formattype withTextandImageconstantsFormat.Read()- Read clipboard dataFormat.Write([]byte)- Write clipboard dataFormat.Watch(context.Context)- Monitor clipboard changes
Usage: nativeclipboard.Text.Read(), nativeclipboard.Image.Write(data), etc.
All methods return errors. The package initializes automatically via func init().
Platform-specific files use build constraints:
//go:build darwin && !ios
//go:build (linux || freebsd) && !android
//go:build windowsUses purego to call X11 library functions directly:
Note: Only FreeBSD is supported among BSD systems, as purego officially supports FreeBSD only.
-
Dynamic library loading:
- Dynamically loads libX11.so using
purego.Dlopen - Searches common paths: libX11.so.6, libX11.so
- FreeBSD-specific paths: /usr/local/lib, /usr/X11R6/lib
- Dynamically loads libX11.so using
-
X11 API:
XOpenDisplay- Connect to X serverXInternAtom- Get atom identifiersXSetSelectionOwner- Claim clipboard ownershipXConvertSelection- Request clipboard dataXGetWindowProperty- Read clipboard dataXChangeProperty- Provide clipboard data
-
Supported formats:
- Text: UTF8_STRING
- Images: image/png
-
Requirements:
- libX11 must be installed
- X11 display must be available (DISPLAY environment variable)
- For headless: use Xvfb virtual framebuffer
- CGO_ENABLED=0 - No cgo required (pure Go)
-
Thread safety:
- Use
runtime.LockOSThread()for thread-sensitive operations
- Use
Uses purego's objc package to call Objective-C runtime and AppKit framework:
-
Objective-C integration via purego/objc package:
objc.GetClass()- Get class by nameobjc.RegisterName()- Register method selectorobjc.ID.Send()- Call Objective-C methodsobjc.Send[T]()- Call methods with typed return values
-
Dynamic library loading:
- AppKit framework for NSPasteboard
- objc package auto-loads Objective-C runtime
-
NSPasteboard API:
[NSPasteboard generalPasteboard]- Get clipboard[pasteboard dataForType:]- Read data[pasteboard setData:forType:]- Write data[pasteboard changeCount]- Monitor changes
-
Key patterns:
// Get class class := objc.GetClass("NSPasteboard") // Register selector sel := objc.RegisterName("generalPasteboard") // Call method (returns ID) result := objc.ID(class).Send(sel) // Call method with typed return count := objc.Send[int64](obj, sel) // Call method with arguments data := obj.Send(sel, arg1, arg2)
-
Thread safety:
- Use
runtime.LockOSThread()for thread-sensitive operations - Objective-C runtime is thread-safe but pasteboard operations benefit from thread locking
- Use
Clean imports, no blank imports:
import (
"context"
"github.com/ebitengine/purego"
)- Required: Go 1.25.4
- This is a very recent Go version
- Dependencies are managed via
go.modandgo.sum - Use
go getto add new dependencies, thengo mod tidy - Keep
go.modandgo.sumin sync and committed to version control
- Main API in
clipboard.go - Platform-specific implementations in
clipboard_<os>.go - Tests in
clipboard_test.go(platform-agnostic where possible) - Use build tags for platform-specific files
- Follow standard Go conventions
- Use
gofmtorgo fmt ./... - Keep public API minimal and clear
- Document all exported functions and types
clipboard_test.go- Main test file- Platform-agnostic tests when possible
- Uses standard Go testing package
- TestInit - Verifies initialization
- TestWriteReadText - Tests text clipboard operations
- TestWriteEmptyText - Tests empty clipboard
- TestWatch - Tests change monitoring
- TestMultipleReads - Tests concurrent reads
// Always initialize first
if err := Init(); err != nil {
t.Fatalf("Init failed: %v", err)
}
// Add delays for system processing
time.Sleep(100 * time.Millisecond)
// Test with timeouts for async operations
select {
case data := <-ch:
// process data
case <-time.After(2 * time.Second):
t.Fatal("Timeout")
}- macOS, Linux, and BSD tests all work with appropriate setup
- Windows tests work natively
- X11-based systems (Linux/BSD) require Xvfb for headless testing
- Add build tags for platform-specific tests if needed
Implementation: Uses Objective-C runtime via purego
- NSPasteboard for clipboard access
- Supports text (NSPasteboardTypeString) and images (NSPasteboardTypePNG)
- Change monitoring via changeCount
- Thread-safe with LockOSThread
Testing: Fully tested and working
Implementation: Unified X11 implementation via purego in clipboard_x11.go
Supported systems:
- Linux (all distributions with X11)
- FreeBSD (only BSD officially supported by purego)
Approach:
- Dynamically loads libX11.so using
purego.Dlopen - Searches multiple library paths (Linux and FreeBSD-specific)
- Calls X11 clipboard functions directly
- Supports CLIPBOARD selection (not PRIMARY)
- Handles UTF8_STRING and image/png types
Requirements:
- libX11 must be installed
- Linux:
apt install libx11-devordnf install libX11-devel - FreeBSD:
pkg install xorg-libraries - OpenBSD:
pkg_add libX11 - NetBSD:
pkgin install libX11
- Linux:
- X11 display must be available (DISPLAY environment variable)
- For headless: use Xvfb virtual framebuffer
- CGO_ENABLED=0 - No cgo required (pure Go)
Key X11 functions used:
XOpenDisplay- Connect to X serverXInternAtom- Get atom identifiersXSetSelectionOwner- Claim clipboard ownershipXConvertSelection- Request clipboard dataXGetWindowProperty- Read clipboard dataXChangeProperty- Provide clipboard data to requesters
Implementation notes:
- Write operation runs in goroutine with event loop
- Clipboard ownership maintained until overwritten
- Uses SelectionRequest/SelectionNotify protocol
- Supports TARGETS atom for format negotiation
Current state: Fully implemented, compiles successfully
Implementation: Uses Win32 API via syscall
Approach:
- Uses Go's built-in syscall package
- Loads user32.dll and kernel32.dll dynamically
- Supports CF_UNICODETEXT and CF_DIBV5/CF_DIB formats
- Handles UTF-16 encoding for text
Key Win32 functions used:
OpenClipboard- Open clipboard for reading/writingCloseClipboard- Close clipboardEmptyClipboard- Clear clipboard contentsGetClipboardData- Read clipboard dataSetClipboardData- Write clipboard dataGetClipboardSequenceNumber- Monitor changesGlobalAlloc/GlobalLock/GlobalUnlock- Memory management
Format handling:
- Text: CF_UNICODETEXT with UTF-16 encoding
- Images: CF_DIBV5 (Device Independent Bitmap V5) format
- Converts PNG ↔ BITMAPV5HEADER + bitmap data
- Handles fallback to CF_DIB for compatibility
Implementation notes:
- Thread safety via
runtime.LockOSThread() - Retry loop for OpenClipboard (handles contention)
- Global memory management for clipboard data
- Sequence number monitoring for change detection
Current state: Fully implemented, compiles successfully
Get class and selector:
class := objc.GetClass("NSPasteboard")
sel := objc.RegisterName("generalPasteboard")Call method (returns ID):
result := objc.ID(class).Send(sel)Call method with typed return:
count := objc.Send[int64](obj, sel)
length := objc.Send[uint64](data, sel_length)Call method with arguments:
data := obj.Send(sel, arg1, arg2)Load framework and get constants:
appkit, _ := purego.Dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", purego.RTLD_NOW|purego.RTLD_GLOBAL)
constPtr, _ := purego.Dlsym(appkit, "NSPasteboardTypeString")
constant := objc.ID(*(*uintptr)(unsafe.Pointer(constPtr)))runtime.LockOSThread()
defer runtime.UnlockOSThread()
// ... perform thread-sensitive operations// Check for unavailable clipboard
if data == 0 {
return nil, ErrUnavailable
}
// Check for unsupported format
switch t {
case FmtText, FmtImage:
// supported
default:
return nil, ErrUnsupported
}The current blank imports (_ "package") are unusual. This typically means:
- The packages have
init()functions that register handlers - Or they're being used to ensure certain code is linked into the binary
- Future work should clarify why these are blank imports vs. actual API usage
- Make changes to Go files
- Build with
go build ./...to check for compile errors - Add tests in
*_test.gofiles - Run tests with
go test ./... - Update dependencies if needed:
go get -u <package>thengo mod tidy - Format code with
gofmtorgo fmt ./...(standard Go practice)
As this codebase grows, consider adding:
- CI/CD: GitHub Actions for automated testing
- Documentation: README.md with usage examples
- Examples:
examples/directory orexample_test.gofiles - Benchmarks:
*_test.gofiles withBenchmark*functions - Linting: golangci-lint or similar for code quality
- Platform tests: Ensure clipboard operations work on all supported platforms