perfuncted is a Go library and CLI for automating Linux desktop applications. It detects your session type at runtime and selects the right backend automatically — no configuration needed.
pf, _ := perfuncted.New(perfuncted.Options{})
defer pf.Close()
pf.Window.Activate("Firefox")
pf.Input.Type("hello world")
pf.Input.KeyTap("ctrl+s")| Session | Screen capture | Input | Window management |
|---|---|---|---|
| X11 | XGetImage ✅ | XTEST / uinput ✅ | EWMH ✅ |
| wlroots Wayland (Sway, Hyprland) | wlr-screencopy ✅ | wl-virtual / uinput ✅ | Sway IPC / wlr-foreign-toplevel ✅ |
| KDE Plasma Wayland | KWin.ScreenShot2 / ext-image-copy-capture / xdg-desktop-portal ✅ | uinput ✅ | KWin D-Bus scripting ✅ |
| GNOME Wayland | gnome-shell screenshot / xdg-desktop-portal |
uinput ✅ | Shell.Eval |
KDE: wl-virtual input protocols are wlroots-only; KDE uses uinput.
GNOME (Mutter): The compositor intentionally restricts some window-management protocols. There are two paths:
GnomeManager (org.gnome.Shell.Eval): when available it runs JavaScript inside gnome-shell and supports List, Activate, Move, Resize, ActiveTitle, Close, Minimize, Maximize. On many distributions org.gnome.Shell.Eval is disabled by default (GNOME 41+); enabling it requires unsafe-mode and is a security risk.
GnomeShellScreenshotBackend (org.gnome.Shell.Screenshot): when GNOME Shell is running in unsafe mode it can capture full-screen or rectangular PNGs directly, avoiding portal consent prompts. In safe mode perfuncted falls back to xdg-desktop-portal Screenshot.
foreign-toplevel protocol:
zwlr_foreign_toplevel_manager_v1supports list plus basic actions (activate, close, minimize, maximize), whileext_foreign_toplevel_list_v1is list-only. Mutter typically does not advertise either one.Therefore, on GNOME you will usually rely on org.gnome.Shell.Eval (if enabled) or use a nested wlroots compositor (e.g., nested sway) for full automation.
Run pf info to see exactly which backends are active on your system.
CLI:
go install github.com/nskaggs/perfuncted/cmd/pf@latestLibrary:
go get github.com/nskaggs/perfunctedRuntime dependencies (only install what your session needs):
| Dependency | Required for |
|---|---|
udev rule or input group |
/dev/uinput access (see Setup below) |
Four top-level bundles are available after perfuncted.New(...):
pf.Screen— capture regions, compute pixel hashes, locate images, wait for visual changespf.Input— type text, tap keys, click and drag, scrollpf.Window— list, activate, resize, move, and wait for windowspf.Clipboard— get and set clipboard contents in the selected target session
Full API reference: pkg.go.dev/github.com/nskaggs/perfuncted
uinput permission (required for input on all Wayland sessions and as X11 fallback):
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' | \
sudo tee /etc/udev/rules.d/99-uinput.rules
sudo udevadm control --reload && sudo udevadm trigger
sudo usermod -aG input $USER # log out and back inThe integration suite runs in isolated nested Wayland/X11 sessions and never touches your real desktop:
just test-integration-headless-x11
just test-integration-headless-wayland
just test-integration-nested-x11
just test-integration-nested-wayland
just test-integration # CI headless matrixOptional: install wl-clipboard for Wayland clipboard round-trip verification and xclip for X11 clipboard round-trip verification.
Requires: just, staticcheck.
just check # fmt + vet + staticcheck
just precommit # full pre-commit gate (format, vet, lint, tidy, docs)
just pf info # probe backend availability on the current sessionApache 2.0 — see LICENSE.