Go Apache-2.0

robotgo

RobotGo, Go Native cross-platform RPA, GUI automation, Auto test and Computer use @vcaesar

G

go-vgo

Dernière activité 22 sept. 2026
go-vgo/robotgo

10,8 k

étoiles

961

forks

5

issues ouvertes

aiauto-testautomationccomputer-usegogolanghookimagemouseopencvrobotrobotgorpawindow

Ce README est souvent en anglais.

Robotgo

RobotGo logo — a robot with a mouse pointer

Build Status CircleCI Status codecov golangci-lint GoDoc GitHub release Join the Discord chat at https://discord.gg/npPb3NzE4A

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

Codg Demo

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

Donors and sponsors, or others, Contact: vzvway@gmail.com

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 (experimental): 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"

Or MinGW-w64 (WinLibs):

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): xclip or xsel on X11; wl-clipboard on 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

Experimental 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; MaxWindow toggles native full screen and GetClient equals GetBounds.
  • wayland, libei: Location() returns the last position injected by RobotGo, not the physical cursor. Clipboard needs wl-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.

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")
  }
}
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)
    }
  }
}
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")
  }
}
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")
}
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)
}
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")
  }
}
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

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.

Projets similaires

Python package for doing RPA

Pythoncross-platformopencvpython
Ttebelorg
5,5 k étoiles723

Collection of open-source libraries and tools for Robotic Process Automation (RPA), designed to be used with both Robot Framework and Python

Pythonaiautomationdocumentai
Rrobocorp
1,6 k étoiles276

Generic automation framework for acceptance testing and RPA

Pythonattdautomationbdd
Rrobotframework
11,9 k étoiles2,6 k