English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Русский | Português
Golang 데스크톱 자동화, 자동 테스트 및 AI 컴퓨터 사용(Computer Use).
마우스와 키보드 제어, 화면 읽기, 프로세스, 윈도우 핸들, 이미지와 비트맵, 그리고 전역 이벤트 리스너.
RobotGo는 Mac, Windows, Linux를 지원하며, arm64와 x86-amd64 아키텍처도 지원합니다.
저는 지금 Codg를 만들고 있습니다. 간편하게 코딩하고 작업할 수 있는 AI 에이전트(Agent) 시스템으로, 자동화, 비동기, 동시성, 고효율 그리고 높은 정확도를 갖추고 있습니다.
RobotGo-Pro는 JavaScript, Python, Lua 등 다른 언어 버전과 기술 지원, 새로운 기능, 그리고 최신 robotgo 버전("현재 오픈소스 버전 없음")을 제공합니다.
기부자, 후원자 및 기타 문의: vzvway@gmail.com
ADB, Android adb와 iOS WDA를 래핑하고 robotgo 호환 API를 제공하는 패키지.
Go 1.26+(go.mod 참조)와 백엔드 하나가 필요합니다:
- 기본(Cgo):
CGO_ENABLED=1, C 컴파일러, 그리고 아래의 플랫폼 라이브러리. - 순수 Go(테스트됨): C 툴체인 불필요. Cgo 없는 빌드 참조.
두 방식 모두 사용하는 기능에 맞는 기능별 의존성이 필요합니다.
Go와 Xcode 명령줄 도구(Clang):
brew install go
xcode-select --install권한(Cgo와 순수 Go 공통): 시스템 설정 > 개인정보 보호 및 보안에서 앱 또는 터미널에 손쉬운 사용(입력)과 화면 기록(캡처) 권한을 부여하세요.
winget install GoLang.Go추가로 C 툴체인 하나가 필요합니다. LLVM-MinGW:
winget install MartinStorsjo.LLVM-MinGW.UCRT
$env:CC = "clang"winget install BrechtSanders.WinLibs.POSIX.UCRT
$env:CC = "gcc"툴체인의 bin 경로(예: C:\mingw64\bin)가 PATH에 있어야 하고 GOARCH와 일치해야
합니다. 수동으로 설치한 MinGW-w64
처럼 Cgo와 호환되는 다른 컴파일러도 사용할 수 있습니다.
Bitmap은 MinGW-w64용 libpng만 포함합니다. 다른 툴체인을 쓸 때는 libpng를 직접 빌드하세요.
빌드: GCC, libc 헤더, X11/XTest 헤더. 런타임: XTEST를 지원하는 X 서버와 DISPLAY
설정. 네이티브 Wayland는 Cgo 없는 빌드를 참조하세요.
Ubuntu / Debian:
# Go (배포판 패키지가 너무 오래되었으면 Snap 사용)
sudo snap install go --classic
# 또는: sudo apt install golang
# 핵심(Cgo): 컴파일러, libc, X11, XTest
sudo apt install gcc libc6-dev libx11-dev xorg-dev libxtst-dev
# 클립보드(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-devFedora:
# 핵심(Cgo): 컴파일러, libc, X11, XTest
sudo dnf install gcc glibc-devel libX11-devel libXtst-devel
# 클립보드(X11)
sudo dnf install xsel xclip
# Bitmap: libpng
sudo dnf install libpng-devel
# GoHook: XKB (Fedora 34 미만: xkbcomp-devel 대신 xorg-x11-xkb-utils-devel)
sudo dnf install libxkbcommon-devel libxkbcommon-x11-devel xkbcomp-devel핵심 설정에 더해, 사용하는 기능에만 필요합니다:
-
Linux 클립보드(Cgo와 순수 Go 공통): X11에서는
xclip또는xsel, Wayland에서는wl-clipboard:sudo apt install wl-clipboard # Ubuntu / Debian sudo dnf install wl-clipboard # Fedora
-
Bitmap: libpng 헤더.
-
GoHook: Linux에서는 XCB/XKB 헤더.
Bitmap과 GoHook은 별도의 Cgo 패키지입니다. 순수 Go RobotGo 백엔드를 써도 이들의 C 의존성은 없어지지 않습니다.
테스트된 순수 Go 백엔드는 CGO_ENABLED=0으로 빌드 및 크로스 컴파일됩니다
(GCC, MinGW, Xcode, X11 헤더 불필요). import 경로와 API는 동일하며 빌드 태그로
백엔드를 선택합니다 — CGO_ENABLED=0만으로는 아무것도 선택되지 않습니다.
대상 GOOS |
빌드 태그 | 패키지 | 런타임 요구 사항 |
|---|---|---|---|
windows |
win |
win | Win32만 사용, 추가 런타임 없음 |
darwin |
mac |
darwin | 시스템 프레임워크와 개인정보 권한 |
linux |
x11 |
x11 | XTEST를 지원하는 X 서버, DISPLAY 설정 |
linux |
wayland |
wayland | wlroots 컴포지터, Wayland 참조 |
linux |
libei |
libei | RemoteDesktop portal(GNOME/KDE), libei 참조 |
**purego**는 단축 태그입니다: macOS에서는 mac, Windows에서는 win, Linux에서는
wayland. Linux에서 재정의하려면 x11 또는 libei를 추가하세요(-tags "purego,x11").
GOOS에 맞는 백엔드 태그 하나만 사용하세요. x11,libei 같은 조합은 컴파일에
실패합니다(purego와 Linux 재정의 하나를 함께 쓰는 경우만 예외).
# 현재 OS
CGO_ENABLED=0 go build -tags purego .
# 크로스 컴파일
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 .
./...가 아니라 모듈 루트(.) 또는 직접 만든 패키지를 빌드하세요. examples/와
일부 하위 패키지는 Cgo 전용 API가 필요합니다.
위의 런타임 요구 사항은 그대로 적용됩니다. CaptureScreen / FreeBitmap 같은 Cgo
전용 API는 존재하지 않으므로 CaptureImg를 사용하세요. 지원되지 않는 동작은
ErrNotSupported를 반환합니다(또는 빈 결과).
mac: 창 관리 없음 —ActiveName은ErrNotSupported를 반환하고,GetTitle은""를 반환하며, 최소화/최대화/닫기는 아무 동작도 하지 않습니다.wayland,libei: Wayland에는 전역 커서 위치를 위한 프로토콜이 없습니다.Location()은 Hyprland(IPC 소켓)와 KDE Plasma(D-Bus를 통한 KWin 스크립트)에서 해당 조회가 성공하면 실제 커서를 읽습니다.wayland는 추가로 모든 출력의 배율 비율이 같아야 합니다(배율이 다르거나 회전된 출력은 대체 동작). 그렇지 않거나 그 외 환경(GNOME, Sway, 기타 wlroots 컴포지터)에서는 실제 커서가 아니라 RobotGo가 마지막으로 주입한 위치를 반환합니다. 클립보드에는wl-clipboard가 필요합니다.
WAYLAND_DISPLAY와 XDG_RUNTIME_DIR가 설정되어 있고 다음을 노출하는 wlroots
컴포지터(Sway, Hyprland, Wayfire 등):
| 기능 | 프로토콜 global |
|---|---|
| 마우스 제어 | zwlr_virtual_pointer_manager_v1 |
| 키보드 제어 | zwp_virtual_keyboard_manager_v1 |
| 화면 캡처 | zwlr_screencopy_manager_v1 |
| 창 관리 | zwlr_foreign_toplevel_manager_v1 |
GNOME과 KDE는 이러한 프로토콜을 제공하지 않으므로, 그곳에서는 libei를 사용하세요.
입력은 xdg-desktop-portal의 RemoteDesktop D-Bus 인터페이스를 통해 전달됩니다.
세션 버스, xdg-desktop-portal, 그리고 이를 구현하는 백엔드
(xdg-desktop-portal-gnome, -kde, -wlr)가 필요합니다. 첫 실행 시 동의 대화상자가
표시되며, 복원 토큰은 $XDG_STATE_HOME/robotgo에 캐시됩니다.
절대 좌표 이동은 연결된 ScreenCast 스트림을 사용합니다(기본값). 상대 이동만 쓰려면
libei.LinkScreenCast = false로 설정하세요. 화면 캡처와 창 관리는
ErrNotSupported를 반환합니다.
go get github.com/go-vgo/robotgoimport "github.com/go-vgo/robotgo"Bitmap에서 png.h: No such file or directory가 발생하면 해당 프로젝트의 libpng 요구
사항과 issues/47을 참조하세요.
go get -u github.com/go-vgo/robotgogo1.10.x의 C 파일 컴파일 캐시 문제에 주의하세요, golang #24355.
go mod vendor 문제, 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")
// 반환된 오류를 확인합니다. 잘못된 인자는 오류를 반환합니다
if err := robotgo.ScrollDir(10, "forward"); err != nil {
fmt.Println("robotgo.ScrollDir error:", err)
}
// 백엔드마다 다릅니다: Cgo는 오류를 반환하고, 순수 Go는 왼쪽 단일 클릭 후 nil을 반환합니다
if err := robotgo.Click("left", "double"); err != nil {
fmt.Println("robotgo.Click error:", err)
}
// MoveSmoothRelative는 부드러운 이동 실패를 ErrSmoothMove로 보고합니다
err := robotgo.MoveSmoothRelative(10, -10)
if errors.Is(err, robotgo.ErrSmoothMove) {
fmt.Println("smooth move failed:", err)
}
// 순수 Go 백엔드는 지원하지 않는 작업에 ErrNotSupported를 반환합니다
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)
}
// 알 수 없는 키 이름은 잘못된 키를 누르지 않고 오류를 반환합니다
if err := robotgo.KeyTap("notakey"); err != nil {
fmt.Println("robotgo.KeyTap error:", err)
}
// TypeStr는 첫 번째 입력 오류를 반환하고, Type은 입력한 문자 수만 반환합니다
if err := robotgo.TypeStr("Hello, 世界"); err != nil {
fmt.Println("robotgo.TypeStr error:", err)
}
// 수정 키를 누른 뒤 항상 해제하고 두 오류를 모두 확인합니다
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)
}- 더 나은 멀티 스크린 지원
- 윈도우 핸들 업데이트
Robotgo는 주로 "the Apache License (Version 2.0)" 조건에 따라 배포되며, 일부 내용은 다양한 BSD 계열 라이선스의 적용을 받습니다.
LICENSE-APACHE, LICENSE를 참조하세요.