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
- Variables
- func Crop(src *image.RGBA, rect image.Rectangle) *image.RGBA
- func EncodePNG(img image.Image) ([]byte, error)
- func FitSize(width, height, longEdge, maxPixels int) (int, int)
- func Launch(dir, command string, args []string) error
- func NormalizeText(s string) string
- func OnConsoleSession() bool
- func RequestPermissions()
- func Resize(src *image.RGBA, width, height int) *image.RGBA
- func WindowMatches(title, pattern string) bool
- type AmbiguousError
- type Backend
- type Button
- type Diagnostics
- type Driver
- func (d *Driver) AssumeInputSent(at time.Time)
- func (d *Driver) Click(ctx context.Context, at *image.Point, button Button, count int, ...) error
- func (d *Driver) Close() error
- func (d *Driver) CursorPosition() (image.Point, error)
- func (d *Driver) Drag(ctx context.Context, path []image.Point, modifiers []Key) error
- func (d *Driver) FocusedWindow() WindowID
- func (d *Driver) HoldKeys(ctx context.Context, keys []Key, duration time.Duration) error
- func (d *Driver) InputSentAt() time.Time
- func (d *Driver) Move(ctx context.Context, at image.Point) error
- func (d *Driver) MoveHolding(ctx context.Context, at image.Point, modifiers []Key) error
- func (d *Driver) Owned(w WindowID) bool
- func (d *Driver) PersonInputSince(since time.Time) bool
- func (d *Driver) PressButton(button Button) error
- func (d *Driver) PressKeys(ctx context.Context, keys []Key, repeat int) error
- func (d *Driver) Protect(pids ...uint32)
- func (d *Driver) Raise(w WindowID) error
- func (d *Driver) ReleaseButton(button Button) error
- func (d *Driver) Screenshot() (*image.RGBA, error)
- func (d *Driver) Scroll(ctx context.Context, at *image.Point, dx, dy int, modifiers []Key) error
- func (d *Driver) Type(ctx context.Context, text string) error
- func (d *Driver) WaitForIdle(ctx context.Context, idle, poll time.Duration, ...) error
- func (d *Driver) WindowAt(at image.Point) WindowID
- func (d *Driver) Windows() []WindowID
- type Element
- type Elements
- type Fingerprint
- type Key
- type Near
- type PathStep
- type Pattern
- type Problem
- type Selector
- type WindowID
Constants ¶
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.
const ( SideRight = "right" SideBelow = "below" SideLeft = "left" SideAbove = "above" )
Sides a label can be on, relative to the element it names.
Variables ¶
var ErrAmbiguous = errors.New("several elements match")
ErrAmbiguous reports a selector that matches several elements. The error returned is an *AmbiguousError that wraps it.
var ErrDisabled = errors.New("the element is disabled")
ErrDisabled reports an element that takes no input until the application enables it.
var ErrElementsUnsupported = errors.New("accessible elements are supported on 64-bit Windows only")
ErrElementsUnsupported reports a system that cannot read elements.
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.
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.
var ErrNotFound = errors.New("no element matches")
ErrNotFound reports a selector that matches no element.
var ErrUnsupported = errors.New("desktop automation is supported on macOS and Windows only")
ErrUnsupported reports an operating system without a desktop backend.
var Roles = []string{ RoleButton, RoleTextField, RoleText, RoleCheckbox, RoleRadio, RoleComboBox, RoleListItem, RoleMenuItem, RoleTab, RoleLink, RoleCell, RoleList, RoleGroup, RoleWindow, RoleOther, }
Roles lists every role a selector may name.
Functions ¶
func FitSize ¶
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 ¶
Launch starts an application in a working directory without waiting for it. The application keeps running after the calling process exits.
func NormalizeText ¶
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 ¶
Resize scales an image down to a size by averaging the source pixels each target pixel covers.
func WindowMatches ¶
WindowMatches reports whether a window title contains pattern, where * matches any run of characters, comparing both as NormalizeText does.
Types ¶
type AmbiguousError ¶
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.
Pointer buttons.
func ParseButton ¶
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 (*Driver) AssumeInputSent ¶
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) CursorPosition ¶
CursorPosition reports the pointer position.
func (*Driver) Drag ¶
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 ¶
FocusedWindow reports the window that has the keyboard focus.
func (*Driver) InputSentAt ¶
InputSentAt reports when the driver last sent input.
func (*Driver) MoveHolding ¶
MoveHolding moves the pointer while holding modifiers, as when hovering with a key pressed.
func (*Driver) Owned ¶
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 ¶
PersonInputSince reports whether input the driver did not send, such as a person's, reached the desktop after since.
func (*Driver) PressButton ¶
PressButton presses a button at the pointer.
func (*Driver) ReleaseButton ¶
ReleaseButton releases a button at the pointer.
func (*Driver) Screenshot ¶
Screenshot captures the primary display.
func (*Driver) Scroll ¶
Scroll scrolls wheel notches at a position, or at the pointer when at is nil, while holding modifiers.
func (*Driver) Type ¶
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.
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 ¶
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 ¶
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 (Element) MarshalJSON ¶
MarshalJSON writes the element in its wire form.
func (*Element) UnmarshalJSON ¶
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 ¶
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 ¶
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 (Key) FunctionNumber ¶
FunctionNumber returns n for the function key "fn".
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 ¶
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.
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.