winput is a lightweight, high-performance Go library for Windows background input automation.
It provides a unified, window-centric API that abstracts the underlying input mechanism, allowing seamless switching between standard Window Messages (PostMessage) and kernel-level injection (Interception driver).
- Pure Go (No CGO): Uses dynamic DLL loading. No GCC required for compilation.
- Window-Centric API: Operations are performed on
Windowobjects, not raw HWNDs. - Background Input:
- Message Backend: Sends inputs directly to window message queues. Works without window focus or mouse cursor movement.
- HID Backend: Uses the Interception driver to simulate hardware input at the kernel level.
- Coordinate Management:
- Unified Client Coordinate system for all APIs.
- Built-in
ScreenToClient/ClientToScreenconversion. - DPI Awareness: Helpers for Per-Monitor DPI scaling.
- Safety & Reliability:
- Thread-Safe: Global input serialization ensures atomic operations (
inputMutex). - Explicit error returns (no silent failures).
- Type-safe Key definitions.
- Thread-Safe: Global input serialization ensures atomic operations (
- Keyboard Layout:
- The
KeyFromRuneandTypefunctions currently assume a US QWERTY keyboard layout.
- The
For applications like Electron (VS Code, Discord) or games where HWND is unreliable or standard messages are ignored:
- Do not use
FindBy...: Window handles are often just containers. - Use Global Coordinates: Locate elements visually (e.g., using OpenCV) to get screen coordinates.
- Use Global Input:
// Move to absolute screen position (Virtual Desktop) winput.MoveMouseTo(1920, 500) winput.ClickMouseAt(1920, 500) // Type globally (no specific window target) winput.Type("Hello Electron!") winput.Press(winput.KeyEnter)
- Use
winput/screen: Helper package to query monitor bounds.import "github.com/rpdg/winput/screen" bounds := screen.VirtualBounds() fmt.Printf("Desktop: %d, %d", bounds.Right, bounds.Bottom)
- Mechanism: Uses
PostMessageW. - Pros: No focus required, no mouse movement, works in background.
- Cons:
- Modifier Keys:
PostMessagedoes not update global keyboard state. Apps checkingGetKeyState(e.g. for Ctrl+C) might fail. - UIPI: Cannot send messages to apps running as Administrator if your app is not.
- Coordinates: Limited to 16-bit signed integer range ([-32768, 32767]). Larger coordinates will be clipped.
- Compatibility: Some games (DirectX/OpenGL/RawInput) and frameworks (Qt/WPF) ignore these messages.
- Modifier Keys:
- Mechanism: Uses Interception driver (kernel-level).
- Context: Uses a global driver context (singleton). Safe for automation scripts, but be aware if integrating into larger apps.
- Pros: Works with almost everything (games, anti-cheat), undetectable as software input.
- Cons:
- Driver Required: Must install Interception driver.
- Blocking:
Moveoperations are synchronous and blocking (to simulate human speed). - Mouse Movement: Physically moves the cursor.
- Focus: Usually requires the window to be active/foreground.
go get github.com/rpdg/winputThis library is Pure Go and does not require CGO. To use the HID backend:
- Install the Interception driver.
- Place
interception.dllin your app directory, or specify its path:winput.SetHIDLibraryPath("libs/interception.dll") winput.SetBackend(winput.BackendHID)
Ideal for standard windows (Notepad, etc.). Works in background. Tip: For some apps (like Notepad), you may need to find the child window (e.g., "Edit") to send text.
go run cmd/example/basic_message/main.goFor apps where HWND is unreliable (VS Code, Discord, Games). Uses absolute screen coordinates.
Tip: Use screen.ImageToVirtual(x, y) to convert OpenCV screenshot coordinates to winput coordinates.
go run cmd/example/global_vision/main.goSimulates physical hardware input. Requires driver.
go run cmd/example/advanced_hid/main.gopackage main
import (
"log"
"github.com/rpdg/winput"
)
func main() {
// 1. Find target window
w, err := winput.FindByTitle("Untitled - Notepad")
if err != nil {
log.Fatal(err)
}
// 2. Click (Left Button)
if err := w.Click(100, 100); err != nil {
log.Fatal(err)
}
// 3. Type text
w.Type("Hello World")
w.Press(winput.KeyEnter)
}winput avoids silent failures. Common errors you should handle:
| Error Variable | Description | Handling |
|---|---|---|
ErrWindowNotFound |
Window not found by Title/Class/PID. | Check if the app is running or use FindByClass as fallback. |
ErrDriverNotInstalled |
Interception driver missing (HID mode only). | Prompt user to install the driver or fallback to Message backend. |
ErrDLLLoadFailed |
interception.dll not found or invalid. |
Check DLL path (SetHIDLibraryPath) or installation. |
ErrUnsupportedKey |
Character cannot be mapped to a key. | Check input string encoding or use raw KeyDown for special keys. |
ErrPermissionDenied |
Operation blocked (e.g., UIPI). | Run your application as Administrator. |
Example of robust error handling:
if err := winput.SetBackend(winput.BackendHID); err != nil {
// This won't fail immediately, but checkBackend will fail on first action
}
err := w.Click(100, 100)
if errors.Is(err, winput.ErrDriverNotInstalled) {
log.Println("HID driver missing, falling back to Message backend...")
winput.SetBackend(winput.BackendMessage)
w.Click(100, 100) // Retry
}Modern Windows scales applications. To ensure your (100, 100) click lands on the correct pixel:
// Call this at program start
if err := winput.EnablePerMonitorDPI(); err != nil {
log.Printf("DPI Awareness failed: %v", err)
}
// Check window specific DPI (96 is standard 100%)
dpi, _ := w.DPI()
fmt.Printf("Target Window DPI: %d (Scale: %.2f%%)
", dpi, float64(dpi)/96.0*100)Use HID for games/anti-cheat, fallback to Message for standard apps.
winput.SetBackend(winput.BackendHID)
err := w.Type("password")
if err != nil {
// If HID fails (e.g. driver not installed), switch back
winput.SetBackend(winput.BackendMessage)
w.Type("password")
}winput maps runes to Scan Codes (Set 1).
- Supported: A-Z, 0-9, Common Symbols (
!,@,#...), Space, Enter, Tab. - Auto-Shift:
Type("A")automatically sendsShift Down->a Down->a Up->Shift Up.
| Feature | winput (Go) | C# Interceptor Wrappers | Python winput (ctypes) |
|---|---|---|---|
| Backends | Dual (HID + Message) | HID (Interception) Only | Message (User32) Only |
| API Style | Object-Oriented (w.Click) |
Low-level (SendInput) |
Function-based |
| Dependency | None (Default) / Driver (HID) | Driver Required | None |
| Safety | Explicit Errors | Exceptions / Silent | Silent / Return Codes |
| DPI Aware | ✅ Yes | ❌ Manual calc needed | ❌ Manual calc needed |
- vs Python winput: Python's version is great for simple automation but lacks the kernel-level injection capability required for games or stubborn applications.
- vs C# Interceptor: Most C# wrappers expose the raw driver API.
winputabstracts this into high-level actions (Click, Type) and adds coordinate translation logic.
MIT