desktop

package
v2.22.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package desktop captures the screen and sends pointer and keyboard input to the desktop of the current user session.

A Driver builds clicks, drags, key combinations and typing from the few primitives a Backend implements for each operating system. Positions are pixels of the screenshots the driver captures, on the primary display.

Index

Constants

View Source
const (
	RoleButton    = "button"
	RoleTextField = "text_field"
	RoleText      = "text"
	RoleCheckbox  = "checkbox"
	RoleRadio     = "radio"
	RoleComboBox  = "combo_box"
	RoleListItem  = "list_item"
	RoleMenuItem  = "menu_item"
	RoleTab       = "tab"
	RoleLink      = "link"
	RoleCell      = "cell"
	RoleList      = "list"
	RoleGroup     = "group"
	RoleWindow    = "window"
	RoleOther     = "other"
)

Roles an element can have. Each system maps its control types onto them.

View Source
const (
	SideRight = "right"
	SideBelow = "below"
	SideLeft  = "left"
	SideAbove = "above"
)

Sides a label can be on, relative to the element it names.

Variables

View Source
var ErrAmbiguous = errors.New("several elements match")

ErrAmbiguous reports a selector that matches several elements. The error returned is an *AmbiguousError that wraps it.

View Source
var ErrDisabled = errors.New("the element is disabled")

ErrDisabled reports an element that takes no input until the application enables it.

View Source
var ErrElementsUnsupported = errors.New("accessible elements are supported on 64-bit Windows only")

ErrElementsUnsupported reports a system that cannot read elements.

View Source
var ErrNoElements = errors.New("the application exposes no accessible elements")

ErrNoElements reports an application that exposes no accessible elements, such as one run as administrator or one that draws its own controls.

View Source
var ErrNoPattern = errors.New("the element offers no such pattern")

ErrNoPattern reports an element that cannot perform a pattern itself, so the action falls back to the pointer and keys.

View Source
var ErrNotFound = errors.New("no element matches")

ErrNotFound reports a selector that matches no element.

View Source
var ErrUnsupported = errors.New("desktop automation is supported on macOS and Windows only")

ErrUnsupported reports an operating system without a desktop backend.

Roles lists every role a selector may name.

Functions

func Crop

func Crop(src *image.RGBA, rect image.Rectangle) *image.RGBA

Crop returns the part of an image inside a rectangle, clipped to the image.

func EncodePNG

func EncodePNG(img image.Image) ([]byte, error)

EncodePNG encodes an image as PNG, favoring speed over size.

func FitSize

func FitSize(width, height, longEdge, maxPixels int) (int, int)

FitSize returns the largest size with the same aspect ratio that fits a long edge and a pixel count; a zero limit is ignored. The size never grows.

func Launch

func Launch(dir, command string, args []string) error

Launch starts an application in a working directory without waiting for it. The application keeps running after the calling process exits.

func NormalizeText

func NormalizeText(s string) string

NormalizeText returns text as it is compared: full-width letters, digits, and punctuation folded to their half-width forms, half-width katakana to full-width, and runs of whitespace, including the ideographic space, collapsed to one space with none at the ends. A name an application pads, or writes in the other width, still matches what a person typed.

func OnConsoleSession

func OnConsoleSession() bool

OnConsoleSession reports whether a person at the keyboard could collide with a step's input. Off Windows the system does not distinguish a remote session, so the console is assumed, which keeps the guard.

func RequestPermissions

func RequestPermissions()

RequestPermissions does nothing where desktop automation is unsupported.

func Resize

func Resize(src *image.RGBA, width, height int) *image.RGBA

Resize scales an image down to a size by averaging the source pixels each target pixel covers.

func WindowMatches

func WindowMatches(title, pattern string) bool

WindowMatches reports whether a window title contains pattern, where * matches any run of characters, comparing both as NormalizeText does.

Types

type AmbiguousError

type AmbiguousError struct {
	Selector Selector
	Count    int
}

AmbiguousError says how many elements a selector matched.

func (*AmbiguousError) Error

func (e *AmbiguousError) Error() string

func (*AmbiguousError) Is

func (e *AmbiguousError) Is(target error) bool

type Backend

type Backend interface {
	// Capture returns the primary display in physical pixels.
	Capture() (*image.RGBA, error)
	// MoveTo moves the pointer to a physical pixel position.
	MoveTo(x, y int) error
	// Position reports the pointer's physical pixel position.
	Position() (x, y int, err error)
	// Button presses or releases a button at the pointer. clicks counts
	// the clicks of a multi-click so far, starting at 1.
	Button(button Button, down bool, clicks int) error
	// Wheel scrolls by wheel notches; positive values scroll right and down.
	Wheel(dx, dy int) error
	// Key presses or releases a key.
	Key(key Key, down bool) error
	// Type types text at the keyboard focus.
	Type(text string) error
	// FocusedWindow reports the window that has the keyboard focus, or a
	// zero WindowID where the system does not say.
	FocusedWindow() WindowID
	// WindowAt reports the top-level window under a physical pixel
	// position, or a zero WindowID where the system does not say.
	WindowAt(x, y int) WindowID
	// Windows lists the visible top-level windows, the front of the
	// Z-order first. It is empty where the system does not enumerate them.
	Windows() []WindowID
	// Raise brings a window to the foreground. It is a best effort: the
	// system may refuse a foreground change asked by a background process.
	Raise(w WindowID) error
	// LastInput reports when the desktop last received pointer or keyboard
	// input from any source, the driver included, or the zero time when it
	// cannot tell.
	LastInput() time.Time
	// Close releases the backend.
	Close() error
}

Backend is the operating system's side of a Driver.

type Button

type Button string

Button is a pointer button.

const (
	ButtonLeft   Button = "left"
	ButtonRight  Button = "right"
	ButtonMiddle Button = "middle"
)

Pointer buttons.

func ParseButton

func ParseButton(name string) (Button, error)

ParseButton accepts a button name, defaulting to the left button.

type Diagnostics

type Diagnostics struct {
	OS     string
	Width  int
	Height int
	// Problems are the reasons automation cannot work, such as a missing
	// permission; empty when the desktop is usable.
	Problems []Problem
}

Diagnostics describes whether the current session can be automated.

func Check

func Check() Diagnostics

Check reports whether the desktop of the current session can be automated.

func (Diagnostics) AwaitsPerson

func (d Diagnostics) AwaitsPerson() bool

AwaitsPerson reports whether the desktop is unusable only until a person unlocks the screen, logs in, or switches back to this user, so a caller can wait for it rather than fail. It is false for a usable desktop and whenever a problem needs a permission or the setup changed.

func (Diagnostics) Err

func (d Diagnostics) Err() error

Err summarizes the problems as one error, or returns nil.

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver performs desktop actions on a Backend.

func New

func New(backend Backend) *Driver

New returns a driver for a backend.

func Open

func Open() (*Driver, error)

Open returns a driver for the desktop of the current session.

func (*Driver) AssumeInputSent

func (d *Driver) AssumeInputSent(at time.Time)

AssumeInputSent records that input was sent at, such as by an earlier driver on the same desktop, so the desktop reporting it is not taken for a person's.

func (*Driver) Click

func (d *Driver) Click(ctx context.Context, at *image.Point, button Button, count int, modifiers []Key) error

Click clicks a button count times at a position, or at the pointer when at is nil, while holding modifiers.

func (*Driver) Close

func (d *Driver) Close() error

Close releases the driver.

func (*Driver) CursorPosition

func (d *Driver) CursorPosition() (image.Point, error)

CursorPosition reports the pointer position.

func (*Driver) Drag

func (d *Driver) Drag(ctx context.Context, path []image.Point, modifiers []Key) error

Drag holds the left button from the first point of a path to the last, moving in small steps so applications see the drag.

func (*Driver) FocusedWindow

func (d *Driver) FocusedWindow() WindowID

FocusedWindow reports the window that has the keyboard focus.

func (*Driver) HoldKeys

func (d *Driver) HoldKeys(ctx context.Context, keys []Key, duration time.Duration) error

HoldKeys holds keys down for a duration.

func (*Driver) InputSentAt

func (d *Driver) InputSentAt() time.Time

InputSentAt reports when the driver last sent input.

func (*Driver) Move

func (d *Driver) Move(ctx context.Context, at image.Point) error

Move moves the pointer.

func (*Driver) MoveHolding

func (d *Driver) MoveHolding(ctx context.Context, at image.Point, modifiers []Key) error

MoveHolding moves the pointer while holding modifiers, as when hovering with a key pressed.

func (*Driver) Owned

func (d *Driver) Owned(w WindowID) bool

Owned reports a window of the automation's own application, which a step never operates: a keystroke to it could close the application that runs the step.

func (*Driver) PersonInputSince

func (d *Driver) PersonInputSince(since time.Time) bool

PersonInputSince reports whether input the driver did not send, such as a person's, reached the desktop after since.

func (*Driver) PressButton

func (d *Driver) PressButton(button Button) error

PressButton presses a button at the pointer.

func (*Driver) PressKeys

func (d *Driver) PressKeys(ctx context.Context, keys []Key, repeat int) error

PressKeys presses keys together, then releases them, repeat times.

func (*Driver) Protect

func (d *Driver) Protect(pids ...uint32)

Protect adds processes whose windows the driver never operates.

func (*Driver) Raise

func (d *Driver) Raise(w WindowID) error

Raise brings a window to the foreground, a best effort.

func (*Driver) ReleaseButton

func (d *Driver) ReleaseButton(button Button) error

ReleaseButton releases a button at the pointer.

func (*Driver) Screenshot

func (d *Driver) Screenshot() (*image.RGBA, error)

Screenshot captures the primary display.

func (*Driver) Scroll

func (d *Driver) Scroll(ctx context.Context, at *image.Point, dx, dy int, modifiers []Key) error

Scroll scrolls wheel notches at a position, or at the pointer when at is nil, while holding modifiers.

func (*Driver) Type

func (d *Driver) Type(ctx context.Context, text string) error

Type types text. Line breaks are sent as the Enter key, which applications treat more consistently than a typed newline.

func (*Driver) WaitForIdle

func (d *Driver) WaitForIdle(ctx context.Context, idle, poll time.Duration, onWait func(lastUsed time.Duration)) error

WaitForIdle returns once nobody but the driver has used the desktop for idle, checking every poll. onWait runs once if the driver has to wait, told how long ago the desktop was last used so a view can say why.

func (*Driver) WindowAt

func (d *Driver) WindowAt(at image.Point) WindowID

WindowAt reports the top-level window under a display position.

func (*Driver) Windows

func (d *Driver) Windows() []WindowID

Windows lists the visible top-level windows, the front of the Z-order first, so a step can find one to bring forward.

type Element

type Element struct {
	Role string
	Name string
	// ID is the identifier the application gives the element, when it
	// sets one: the UI Automation AutomationId on Windows.
	ID string
	// Value is what the element holds, such as the text of a field. It is
	// blank for a password field.
	Value string
	// Label is the name of the element that labels this one, when the
	// application links them.
	Label string
	// App is the process image name without its extension. It is set on
	// window elements.
	App string
	// Bounds is where the element is, in display pixels.
	Bounds image.Rectangle
	// Window is the title of the window the element belongs to.
	Window string
	// Path leads from the window down to the element. A window's path is
	// empty.
	Path []PathStep
}

Element is an accessible element of a window on the desktop.

func Match

func Match(window Element, elements []Element, sel Selector) ([]Element, error)

Match returns the elements sel selects among a window's elements, in reading order: one when the selector is good, none on a miss, several when it is ambiguous. An error reports a container (in) that misses or is ambiguous, naming it.

The search runs in a fixed order: in narrows, an id wins when it is unique there, otherwise role and name select the candidates, near keeps the one labelled by the label, and nth comes last.

func MatchPath

func MatchPath(matches []Element, path []PathStep) []Element

MatchPath narrows several matches by the path an element had when it was recorded: when exactly one of them sits at that path, it is the one. A single match is kept wherever it sits, since an element may move in the tree; several that none or more than one of sit at the path stay ambiguous, never a guess.

func One

func One(matches []Element, sel Selector) (Element, error)

One returns the single match, ErrNotFound, or an *AmbiguousError.

func (Element) MarshalJSON

func (e Element) MarshalJSON() ([]byte, error)

MarshalJSON writes the element in its wire form.

func (*Element) UnmarshalJSON

func (e *Element) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form.

type Elements

type Elements interface {
	// At returns the element under a point.
	At(x, y int) (Element, error)
	// Focused returns the element with the keyboard focus.
	Focused() (Element, error)
	// Find returns the elements a selector matches in the front window, in
	// reading order. It returns no error and no elements when nothing
	// matches.
	Find(Selector) ([]Element, error)
	// FrontWindow returns the window in front.
	FrontWindow() (Element, error)
	// Outline lists the visible elements of a window in tree order, at
	// most limit of them, or a bounded number when limit is 0.
	Outline(window Element, limit int) ([]Element, error)
	// Focus gives an element the keyboard focus.
	Focus(Element) error
	// Apply has an element perform a pattern itself, finding it again as
	// Focus does; value is the text PatternSetValue sets. ErrNoPattern says
	// the element cannot, so the action falls back to the pointer and keys;
	// ErrDisabled says it takes no input yet; a read-only field or an
	// element with nothing to expand is an error saying so.
	Apply(target Element, pattern Pattern, value string) error
	// Rows reads a grid, table, or list's rows as text, one []string per
	// row, each cell the text under the column that columns names by its
	// header, up to max rows. A container with no grid, a column with no
	// header, or an unreadable cell is an error. It may take longer than
	// one read, so it is bounded by ctx.
	Rows(ctx context.Context, container Selector, columns []string, max int) ([][]string, error)
	Close() error
}

Elements reads the accessible elements of the desktop. Positions are display pixels, as the Driver uses them.

func OpenElements

func OpenElements() (Elements, error)

OpenElements fails: elements are read through UI Automation, which needs 64-bit Windows.

type Fingerprint

type Fingerprint uint64

Fingerprint is a perceptual hash of an image: similar images have fingerprints a small Distance apart.

func FingerprintAround

func FingerprintAround(img *image.RGBA, at image.Point, radius int) Fingerprint

FingerprintAround hashes the square of an image centered on a point, which tells apart screens that differ only near where an action lands.

func FingerprintOf

func FingerprintOf(img *image.RGBA) Fingerprint

FingerprintOf hashes an image by whether brightness rises between neighboring cells of a coarse grid.

func (Fingerprint) Distance

func (f Fingerprint) Distance(other Fingerprint) int

Distance counts the bits two fingerprints differ in, from 0 to 64.

type Key

type Key string

Key is a keyboard key: one of the named keys below, a function key "f1" to "f24", or a single printable ASCII character such as "a" or "/".

const (
	KeyShift       Key = "shift"
	KeyCtrl        Key = "ctrl"
	KeyAlt         Key = "alt"
	KeyMeta        Key = "meta" // Command on macOS, Windows key on Windows.
	KeyEnter       Key = "enter"
	KeyTab         Key = "tab"
	KeyEscape      Key = "escape"
	KeyBackspace   Key = "backspace"
	KeyDelete      Key = "delete"
	KeySpace       Key = "space"
	KeyInsert      Key = "insert"
	KeyHome        Key = "home"
	KeyEnd         Key = "end"
	KeyPageUp      Key = "pageup"
	KeyPageDown    Key = "pagedown"
	KeyUp          Key = "up"
	KeyDown        Key = "down"
	KeyLeft        Key = "left"
	KeyRight       Key = "right"
	KeyCapsLock    Key = "capslock"
	KeyPrintScreen Key = "printscreen"
)

Named keys.

func ParseKey

func ParseKey(name string) (Key, error)

ParseKey accepts a key name in any common spelling, ignoring case.

func ParseKeys

func ParseKeys(names []string) ([]Key, error)

ParseKeys parses each key name.

func (Key) Char

func (k Key) Char() (rune, bool)

Char returns the character of a single-character key.

func (Key) FunctionNumber

func (k Key) FunctionNumber() (int, bool)

FunctionNumber returns n for the function key "fn".

func (Key) USKey

func (k Key) USKey() (base rune, shift bool, ok bool)

USKey returns the unshifted character that produces a character key on a US layout and whether Shift is needed.

type Near

type Near struct {
	Label string `json:"label"`
	Side  string `json:"side"`
}

Near names a label an element sits next to.

type PathStep

type PathStep struct {
	Role string `json:"role"`
	Name string `json:"name"`
	// Index is the element's position among its parent's children of the
	// same role, from 0.
	Index int `json:"index"`
}

PathStep is one level of an element's path from its window down.

type Pattern

type Pattern string

Pattern is something an element does by itself when asked through the system's automation, with no pointer or key: the UI Automation control patterns on Windows.

const (
	// PatternInvoke presses a button, menu item, or link.
	PatternInvoke Pattern = "invoke"
	// PatternToggle flips a checkbox.
	PatternToggle Pattern = "toggle"
	// PatternSelect selects a radio button, list item, tab, or cell.
	PatternSelect Pattern = "select"
	// PatternExpand opens a combo box or another collapsed element.
	PatternExpand Pattern = "expand"
	// PatternSetValue replaces the text of a field.
	PatternSetValue Pattern = "set_value"
)

type Problem

type Problem struct {
	// Code identifies the kind of problem, such as screen_locked, for
	// programs that act on it.
	Code string `json:"code"`
	// Message explains the problem and how to fix it.
	Message string `json:"message"`
}

Problem is one reason the desktop cannot be automated.

type Selector

type Selector struct {
	// App is the process image name without its extension, compared
	// ignoring case.
	App string `json:"app,omitempty"`
	// Window is text the window title contains.
	Window string `json:"window,omitempty"`
	Role   string `json:"role"`
	// Name is the accessible name, exact.
	Name string `json:"name,omitempty"`
	// ID is the identifier the application gives the element.
	ID string `json:"id,omitempty"`
	// In narrows the search to the descendants of one container.
	In *Selector `json:"in,omitempty"`
	// Near keeps the element labelled by a label.
	Near *Near `json:"near,omitempty"`
	// Nth picks one of several equal matches, in reading order, from 0.
	Nth *int `json:"nth,omitempty"`
}

Selector names an element the way a condition, a pinned step, or an extract field does. Role is required, with at least one of Name, ID, or Near. Name and Window take * for any run of characters. A %name% placeholder in a name is matched as written; whoever runs a workflow substitutes it first.

func ParseSelector

func ParseSelector(data []byte) (Selector, error)

ParseSelector reads a selector from JSON and validates it. An unknown key is an error, so a misspelt field does not silently widen the match.

func (Selector) String

func (s Selector) String() string

String returns the selector as compact JSON, which is how it is written and quoted.

func (Selector) Validate

func (s Selector) Validate() error

Validate checks that the selector names an element.

type WindowID

type WindowID struct {
	Handle uint64
	Title  string
	PID    uint32
	// App is the window's process image name without its extension, the
	// stable identity a replay raises a window by, since a title often
	// shows the document's contents. Empty where the system does not say.
	App string
}

WindowID names a top-level window the way the system reports it, so a step can tell whether the focus moved and whose window an action would operate.

func (WindowID) Known

func (w WindowID) Known() bool

Known reports a window the system named.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL