默认 Cgo 后端需 C 编译器和平台库,Linux 还要装 X11/XTest 头文件;纯 Go 构建必须加 -tags(如 purego/win/mac/x11),只设 CGO_ENABLED=0 不会选中后端;macOS 需手动授予辅助功能与屏幕录制权限,Bitmap 和 GoHook 是独立 Cgo 包仍需 libpng/XCB 头文件
robotgo
go-vgoApache-2.0活跃维护用 Go 写的跨平台 RPA 与 GUI 自动化库,能控制鼠标键盘、截屏找图、监听事件,适合做桌面自动化和自动化测试。
RobotGo, Go Native cross-platform RPA, GUI automation, Auto test and Computer use @vcaesar
go get github.com/go-vgo/robotgoREADME 未说明显存或 GPU 要求;支持 macOS、Windows、Linux,架构覆盖 arm64 与 x86-amd64。Linux 需 X 服务器并设置 DISPLAY,Wayland 需 wlroots 合成器或 xdg-desktop-portal。
Go 原生跨平台桌面自动化库,用代码控制鼠标键盘、读屏和窗口,做 RPA 与自动测试
用 Go 写的跨平台 RPA 与 GUI 自动化库,能控制鼠标键盘、截屏找图、监听事件,适合做桌面自动化和自动化测试。
核心能力
- 跨平台控制鼠标键盘,支持平滑移动与拖拽
- 读取屏幕图像与位图,支持截屏处理
- 监听全局事件,获取窗口句柄与进程信息
- 提供纯 Go 后端,CGO_ENABLED=0 可交叉编译
- 支持 Linux Wayland 与 libei 输入注入
容易踩的坑
- v1.3.7最新正式版2026-10-22 · 2026-10-22 20:53 UTC
What's Changed
- chore: update go mod and translate README.md diff into lang by @vcaesar in https://github.com/go-vgo/robotgo/pull/813
Full Changelog: https://github.com/go-vgo/robotgo/compare/v1.3.6...v1.3.7
- v1.3.62026-10-16 · 2026-10-16 20:06 UTC
查看更新说明
What's Changed
- chore: update README.md and examples add new mouse and key error demos by @vcaesar in https://github.com/go-vgo/robotgo/pull/810
- chore: update README.md demos and add it to lang/ by @vcaesar in https://github.com/go-vgo/robotgo/pull/811
- add: add purego windows hid levels and postmsg support by @vcaesar in https://github.com/go-vgo/robotgo/pull/812
Full Changelog: https://github.com/go-vgo/robotgo/compare/v1.3.5...v1.3.6
- v1.3.52026-10-07 · 2026-10-07 10:33 UTC
查看更新说明
What's Changed
- chore: move KeyPress/up/down() to key_pub.go and update AGENTS.md (key) by @vcaesar in https://github.com/go-vgo/robotgo/pull/809
Full Changelog: https://github.com/go-vgo/robotgo/compare/v1.3.4...v1.3.5
- v1.3.42026-10-07 · 2026-10-07 07:55 UTC
查看更新说明
What's Changed
- chore: move no-cgo functions to robotgo pub.go and extend purego api by @vcaesar in https://github.com/go-vgo/robotgo/pull/808
Full Changelog: https://github.com/go-vgo/robotgo/compare/v1.3.3...v1.3.4
- v1.3.32026-10-05 · 2026-10-05 19:26 UTC
查看更新说明
What's Changed
- fix: optimze failing clipboard tests on Ubuntu (clipboard/) by @vcaesar in https://github.com/go-vgo/robotgo/pull/806
- add: add window errors return support by @vcaesar in https://github.com/go-vgo/robotgo/pull/807
Full Changelog: https://github.com/go-vgo/robotgo/compare/v1.3.2...v1.3.3
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Robotgo
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Русский | Português
Golang Desktop Automation, auto test and AI Computer Use. Control the mouse, keyboard, read the screen, process, Window Handle, image and bitmap and global event listener.
RobotGo supports Mac, Windows, and Linux; and robotgo supports arm64 and x86-amd64.
I build Codg now, Easy code and work AI agent system: auto, asynchronous, concurrency, efficiently and High accuracy
RobotGo-Pro get the JavaScript, Python, Lua and others version, tech supports, new features and newest robotgo version ("no open-source version now").
Contents
- Docs
- Donate
- Binding
- Requirements
- Cgo-free Builds
- Installation
- Update
- Examples
- Type Conversion and keys
- Cross-Compiling
- Authors
- Plans
- License
Docs
Donate
Donors and sponsors, or others, Contact: [email protected]
Binding
ADB, packaging android adb and ios wda, robotgo compatible API.
Requirements
Go 1.26+ (see go.mod) and one backend:
- Default (Cgo):
CGO_ENABLED=1, a C compiler and the platform libraries below. - Pure Go (tested): no C toolchain; see Cgo-free Builds.
Both need the feature-specific dependencies for the features you use.
Default Cgo setup
macOS
Go and the Xcode Command Line Tools (Clang):
brew install go
xcode-select --install
Permissions (Cgo and pure Go): in System Settings > Privacy & Security, grant the app or terminal Accessibility (input) and Screen Recording (capture).
Windows
winget install GoLang.Go
Plus one C toolchain. LLVM-MinGW:
winget install MartinStorsjo.LLVM-MinGW.UCRT
$env:CC = "clang"
winget install BrechtSanders.WinLibs.POSIX.UCRT
$env:CC = "gcc"
The toolchain's bin (e.g. C:\mingw64\bin) must be on PATH and match
GOARCH. Any other Cgo-compatible compiler, such as a manual
MinGW-w64 install, also works.
Bitmap ships libpng for MinGW-w64 only; with another toolchain, build libpng yourself.
Linux (X11)
Build: GCC, libc headers, X11/XTest headers. Runtime: an X server with XTEST
and DISPLAY set. For native Wayland, see Cgo-free Builds.
Ubuntu / Debian:
# Go (Snap if the distro package is too old)
sudo snap install go --classic
# or: sudo apt install golang
# Core (Cgo): compiler, libc, X11, XTest
sudo apt install gcc libc6-dev libx11-dev xorg-dev libxtst-dev
# Clipboard (X11)
sudo apt install xsel xclip
# Bitmap: libpng
sudo apt install libpng++-dev
# GoHook: XCB, XKB
sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev
Fedora:
# Core (Cgo): compiler, libc, X11, XTest
sudo dnf install gcc glibc-devel libX11-devel libXtst-devel
# Clipboard (X11)
sudo dnf install xsel xclip
# Bitmap: libpng
sudo dnf install libpng-devel
# GoHook: XKB (Fedora < 34: xorg-x11-xkb-utils-devel instead of xkbcomp-devel)
sudo dnf install libxkbcommon-devel libxkbcommon-x11-devel xkbcomp-devel
Feature-specific dependencies
Needed on top of the core setup, only for the features you use:
-
Linux clipboard (Cgo and pure Go):
xcliporxselon X11;wl-clipboardon Wayland:sudo apt install wl-clipboard # Ubuntu / Debian sudo dnf install wl-clipboard # Fedora -
Bitmap: libpng headers.
-
GoHook: XCB/XKB headers on Linux.
Bitmap and GoHook are separate Cgo packages; a pure-Go RobotGo backend does not remove their C requirements.
Cgo-free Builds
Tested pure-Go backends build and cross-compile with CGO_ENABLED=0
(no GCC, MinGW, Xcode or X11 headers). Same import path and API; a build tag
selects the backend — CGO_ENABLED=0 alone selects nothing.
Target GOOS | Build tag | Package | Runtime requirements |
|---|---|---|---|
windows | win | win | Win32 only, no extra runtime |
darwin | mac | darwin | System frameworks and privacy permissions |
linux | x11 | x11 | X server with XTEST, DISPLAY set |
linux | wayland | wayland | wlroots compositor, see Wayland |
linux | libei | libei | RemoteDesktop portal (GNOME/KDE), see libei |
purego is a shortcut: mac on macOS, win on Windows, wayland on
Linux. Add x11 or libei to override on Linux (-tags "purego,x11").
Use one backend tag matching GOOS; combinations such as x11,libei fail
to compile (purego plus one Linux override is the only exception).
Build commands
# Current OS
CGO_ENABLED=0 go build -tags purego .
# Cross-compile
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -tags win .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -tags mac .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags x11 .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags wayland .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags libei .
PowerShell: $env:CGO_ENABLED = "0"; go build -tags purego .
Build the module root (.) or your own package, not ./...: examples/ and
some subpackages need Cgo-only APIs.
Limitations
The runtime requirements above still apply. Cgo-only APIs such as
CaptureScreen / FreeBitmap do not exist; use CaptureImg. Unsupported
operations return ErrNotSupported (or an empty result).
mac: window management (GetTitle,GetBounds,ActivePid,ActiveName,MinWindow,MaxWindow,CloseWindow) uses Accessibility and needs the Accessibility permission;MaxWindowtoggles native full screen andGetClientequalsGetBounds.wayland,libei: Wayland has no protocol for the global cursor position.Location()reads the real cursor from Hyprland (IPC socket) and KDE Plasma (KWin script over D-Bus) when that query succeeds;waylandalso needs every output to share one scale ratio (differently scaled or rotated outputs fall back). Otherwise, and elsewhere (GNOME, Sway, other wlroots compositors), it returns the last position injected by RobotGo, not the physical cursor. Clipboard needswl-clipboard.
Wayland
A wlroots compositor (Sway, Hyprland, Wayfire, ...) with WAYLAND_DISPLAY and
XDG_RUNTIME_DIR set, exposing:
| Feature | Protocol global |
|---|---|
| Mouse control | zwlr_virtual_pointer_manager_v1 |
| Keyboard control | zwp_virtual_keyboard_manager_v1 |
| Screen capture | zwlr_screencopy_manager_v1 |
| Window management | zwlr_foreign_toplevel_manager_v1 |
GNOME and KDE do not provide these protocols; use libei there.
libei (GNOME / KDE)
Input goes through the xdg-desktop-portal RemoteDesktop D-Bus interface:
needs a session bus, xdg-desktop-portal and a backend implementing it
(xdg-desktop-portal-gnome, -kde or -wlr). The first run shows a consent
dialog; the restore token is cached under $XDG_STATE_HOME/robotgo.
Absolute motion uses a linked ScreenCast stream (default); set
libei.LinkScreenCast = false for relative motion only. Screen capture and
window management report ErrNotSupported.
Installation
go get github.com/go-vgo/robotgo
import "github.com/go-vgo/robotgo"
png.h: No such file or directory with Bitmap: see its libpng requirements and
issues/47.
Update
go get -u github.com/go-vgo/robotgo
Note go1.10.x C file compilation cache problem, golang #24355.
go mod vendor problem, golang #26366.
Examples
Mouse
package main
import (
"errors"
"fmt"
"github.com/go-vgo/robotgo"
)
func main() {
robotgo.MouseSleep = 300
robotgo.Move(100, 100)
fmt.Println(robotgo.Location())
robotgo.Move(100, -200) // multi screen supported
robotgo.MoveSmooth(120, -150)
fmt.Println(robotgo.Location())
robotgo.ScrollDir(10, "up")
robotgo.ScrollDir(20, "right")
robotgo.Scroll(0, -10)
robotgo.Scroll(100, 0)
robotgo.MilliSleep(100)
robotgo.ScrollSmooth(-10, 6)
// robotgo.ScrollRelative(10, -100)
robotgo.Move(10, 20)
robotgo.MoveRelative(0, -10)
robotgo.DragSmooth(10, 10)
robotgo.Click("wheelRight")
robotgo.Click("left", true)
robotgo.MoveSmooth(100, 200, 1.0, 10.0)
robotgo.Toggle("left")
robotgo.Toggle("left", "up")
// Check the returned errors, invalid arguments return an error
if err := robotgo.ScrollDir(10, "forward"); err != nil {
fmt.Println("robotgo.ScrollDir error:", err)
}
// Backends differ: Cgo returns an error, pure Go does a single left click and returns nil
if err := robotgo.Click("left", "double"); err != nil {
fmt.Println("robotgo.Click error:", err)
}
// MoveSmoothRelative reports a failed smooth move as ErrSmoothMove
err := robotgo.MoveSmoothRelative(10, -10)
if errors.Is(err, robotgo.ErrSmoothMove) {
fmt.Println("smooth move failed:", err)
}
// Pure-Go backends return ErrNotSupported for unsupported operations
err = robotgo.Move(100, 200)
if errors.Is(err, robotgo.ErrNotSupported) {
fmt.Println("robotgo.Move is not supported by this backend")
}
}
Keyboard
package main
import (
"errors"
"fmt"
"github.com/go-vgo/robotgo"
)
func main() {
robotgo.Type("Hello World")
robotgo.Type("だんしゃり", 0, 1)
// robotgo.Type("テストする")
robotgo.Type("Hi, Seattle space needle, Golden gate bridge, One world trade center.")
robotgo.Type("Hi galaxy, hi stars, hi MT.Rainier, hi sea. こんにちは世界.")
robotgo.Sleep(1)
// ustr := uint32(robotgo.CharCodeAt("Test", 0))
// robotgo.UnicodeType(ustr)
robotgo.KeySleep = 100
robotgo.KeyTap("enter")
// robotgo.Type("en")
robotgo.KeyTap("i", "alt", "cmd")
arr := []string{"alt", "cmd"}
robotgo.KeyTap("i", arr)
robotgo.MilliSleep(100)
robotgo.KeyToggle("a")
robotgo.KeyToggle("a", "up")
robotgo.WriteAll("Test")
text, err := robotgo.ReadAll()
if err == nil {
fmt.Println(text)
}
// Unknown key names return an error instead of pressing a wrong key
if err := robotgo.KeyTap("notakey"); err != nil {
fmt.Println("robotgo.KeyTap error:", err)
}
// TypeStr returns the first typing error, Type only returns the count
if err := robotgo.TypeStr("Hello, 世界"); err != nil {
fmt.Println("robotgo.TypeStr error:", err)
}
// Hold a modifier, then always release it and check both errors
if err := robotgo.KeyDown("shift"); err == nil {
err = errors.Join(robotgo.KeyTap("a"), robotgo.KeyUp("shift"))
if err != nil {
fmt.Println("shift + a error:", err)
}
}
}
Screen
package main
import (
"fmt"
"strconv"
"github.com/go-vgo/robotgo"
"github.com/vcaesar/imgo"
)
func main() {
x, y := robotgo.Location()
fmt.Println("pos: ", x, y)
color := robotgo.GetPixelColor(100, 200)
fmt.Println("color---- ", color)
sx, sy := robotgo.GetScreenSize()
fmt.Println("get screen size: ", sx, sy)
bit := robotgo.CaptureScreen(10, 10, 30, 30)
defer robotgo.FreeBitmap(bit)
img := robotgo.ToImage(bit)
imgo.Save("test.png", img)
num := robotgo.DisplaysNum()
for i := 0; i < num; i++ {
robotgo.DisplayID = i
img1, _ := robotgo.CaptureImg()
path1 := "save_" + strconv.Itoa(i)
robotgo.Save(img1, path1+".png")
robotgo.SaveJpeg(img1, path1+".jpeg", 50)
img2, _ := robotgo.CaptureImg(10, 10, 20, 20)
robotgo.Save(img2, "test_"+strconv.Itoa(i)+".png")
x, y, w, h := robotgo.GetDisplayBounds(i)
img3, err := robotgo.CaptureImg(x, y, w, h)
fmt.Println("Capture error: ", err)
robotgo.Save(img3, path1+"_1.png")
}
}
Bitmap
package main
import (
"fmt"
"github.com/go-vgo/robotgo"
"github.com/vcaesar/bitmap"
)
func main() {
bit := robotgo.CaptureScreen(10, 20, 30, 40)
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
defer robotgo.FreeBitmap(bit)
fmt.Println("bitmap...", bit)
img := robotgo.ToImage(bit)
// robotgo.SavePng(img, "test_1.png")
robotgo.Save(img, "test_1.png")
bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))
fx, fy := bitmap.Find(bit2)
fmt.Println("FindBitmap------ ", fx, fy)
robotgo.Move(fx, fy)
arr := bitmap.FindAll(bit2)
fmt.Println("Find all bitmap: ", arr)
fx, fy = bitmap.Find(bit)
fmt.Println("FindBitmap------ ", fx, fy)
bitmap.Save(bit, "test.png")
}
OpenCV
package main
import (
"fmt"
"math/rand"
"github.com/go-vgo/robotgo"
"github.com/vcaesar/gcv"
"github.com/vcaesar/bitmap"
)
func main() {
opencv()
}
func opencv() {
name := "test.png"
name1 := "test_001.png"
robotgo.SaveCapture(name1, 10, 10, 30, 30)
robotgo.SaveCapture(name)
fmt.Print("gcv find image: ")
fmt.Println(gcv.FindImgFile(name1, name))
fmt.Println(gcv.FindAllImgFile(name1, name))
bit := bitmap.Open(name1)
defer robotgo.FreeBitmap(bit)
fmt.Print("find bitmap: ")
fmt.Println(bitmap.Find(bit))
// bit0 := robotgo.CaptureScreen()
// img := robotgo.ToImage(bit0)
// bit1 := robotgo.CaptureScreen(10, 10, 30, 30)
// img1 := robotgo.ToImage(bit1)
// defer robotgo.FreeBitmapArr(bit0, bit1)
img, _ := robotgo.CaptureImg()
img1, _ := robotgo.CaptureImg(10, 10, 30, 30)
fmt.Print("gcv find image: ")
fmt.Println(gcv.FindImg(img1, img))
fmt.Println()
res := gcv.FindAllImg(img1, img)
fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res)
x, y := res[0].TopLeft.X, res[0].TopLeft.Y
robotgo.Move(x, y-rand.Intn(5))
robotgo.MilliSleep(100)
robotgo.Click()
res = gcv.FindAll(img1, img) // use find template and sift
fmt.Println("find all: ", res)
res1 := gcv.Find(img1, img)
fmt.Println("find: ", res1)
img2, _, _ := robotgo.DecodeImg("test_001.png")
x, y = gcv.FindX(img2, img)
fmt.Println(x, y)
}
Event
package main
import (
"fmt"
// "github.com/go-vgo/robotgo"
hook "github.com/robotn/gohook"
)
func main() {
add()
low()
event()
}
func add() {
fmt.Println("--- Please press ctrl + shift + q to stop hook ---")
hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
fmt.Println("ctrl-shift-q")
hook.End()
})
fmt.Println("--- Please press w---")
hook.Register(hook.KeyDown, []string{"w"}, func(e hook.Event) {
fmt.Println("w")
})
s := hook.Start()
<-hook.Process(s)
}
func low() {
evChan := hook.Start()
defer hook.End()
for ev := range evChan {
fmt.Println("hook: ", ev)
}
}
func event() {
ok := hook.AddEvents("q", "ctrl", "shift")
if ok {
fmt.Println("add events...")
}
keve := hook.AddEvent("k")
if keve {
fmt.Println("you press... ", "k")
}
mleft := hook.AddEvent("mleft")
if mleft {
fmt.Println("you press... ", "mouse left button")
}
}
Window
package main
import (
"fmt"
"github.com/go-vgo/robotgo"
)
func main() {
fpid, err := robotgo.FindIds("Google")
if err == nil {
fmt.Println("pids... ", fpid)
if len(fpid) > 0 {
robotgo.Type("Hi galaxy!", fpid[0])
robotgo.KeyTap("a", fpid[0], "cmd")
robotgo.KeyToggle("a", fpid[0])
robotgo.KeyToggle("a", fpid[0], "up")
robotgo.ActivePid(fpid[0])
robotgo.Kill(fpid[0])
}
}
robotgo.ActiveName("chrome")
isExist, err := robotgo.PidExists(100)
if err == nil && isExist {
fmt.Println("pid exists is", isExist)
robotgo.Kill(100)
}
abool := robotgo.Alert("test", "robotgo")
if abool {
fmt.Println("ok@@@ ", "ok")
}
title := robotgo.GetTitle()
fmt.Println("title@@@ ", title)
}
Authors
Plans
- Better multiscreen support
- Update Window Handle
Contributors
- See contributors page for full list of contributors.
- See Contribution Guidelines.
License
Robotgo is primarily distributed under the terms of "the Apache License (Version 2.0)", with portions covered by various BSD-like licenses.
See LICENSE-APACHE, LICENSE.