Documentation
¶
Overview ¶
Package kinax is a pure-Go binding to the macOS Accessibility (AX) API.
kinax lets Go programs read and manipulate the system-wide UI tree — inspecting running applications, finding buttons, reading text field contents, clicking on elements by semantic identity rather than pixel coordinates. It's the foundation for UI automation agents, screen readers, and accessibility-aware tools.
Quick start ¶
ctx := context.Background()
if err := kinax.Load(); err != nil { log.Fatal(err) }
if err := kinax.RequireTrust(); err != nil { log.Fatal(err) }
app, _ := kinax.FocusedApplication()
defer app.Close()
// Walk windows
wins, _ := app.Windows()
for _, w := range wins {
title, _ := w.Title()
fmt.Println(title)
w.Close()
}
// Click a button by title
if btn, ok := app.FindFirst(kinax.MatchTitle("OK")); ok {
btn.Perform(kinax.ActionPress)
btn.Close()
}
Permission ¶
macOS requires the invoking binary to be listed in System Settings → Privacy & Security → Accessibility for AX calls to succeed. Without permission, every call returns an error (unlike `input-go`, where events silently no-op). Use Trusted / PromptTrust / RequireTrust.
Memory ownership ¶
Every Element returned by kinax wraps a retained CFTypeRef. Callers MUST call (*Element).Close to release it. Forgetting to Close leaks a handle for the lifetime of the process — on the order of tens of bytes per element, but it adds up if you're walking the UI tree on a timer.
Dylib placement ¶
kinax-go ships a universal (arm64+x86_64) companion dylib via //go:embed. On the first call into the package, the embedded bytes are extracted to ~/Library/Caches/kinax-go/<hash>/libkinax_sync.dylib and Dlopened. Set DylibPath to a non-empty value before the first call if you ship a custom-built or patched dylib.
Index ¶
- Constants
- Variables
- func FrontmostPID() int
- func Load() error
- func PromptTrust() bool
- func RequireTrust() error
- func ResolvedDylibPath() string
- func Trusted() bool
- type Element
- func (e *Element) ActionNames() ([]string, error)
- func (e *Element) Attribute(name string) (string, error)
- func (e *Element) AttributeBool(name string) (bool, error)
- func (e *Element) AttributeElement(name string) (*Element, error)
- func (e *Element) AttributeElements(name string) ([]*Element, error)
- func (e *Element) AttributeInt(name string) (int64, error)
- func (e *Element) AttributeNames() ([]string, error)
- func (e *Element) AttributePoint(name string) (image.Point, error)
- func (e *Element) AttributeSize(name string) (image.Point, error)
- func (e *Element) Children() ([]*Element, error)
- func (e *Element) Close()
- func (e *Element) Description() (string, error)
- func (e *Element) Enabled() (bool, error)
- func (e *Element) FindAll(m Matcher, maxDepth int) []*Element
- func (e *Element) FindFirst(m Matcher, maxDepth int) (*Element, bool)
- func (e *Element) Focused() (bool, error)
- func (e *Element) FocusedElement() (*Element, error)
- func (e *Element) FocusedWindow() (*Element, error)
- func (e *Element) GetMany(attrs ...string) (map[string]any, error)
- func (e *Element) Handle() uintptr
- func (e *Element) Identifier() (string, error)
- func (e *Element) MainWindow() (*Element, error)
- func (e *Element) Parent() (*Element, error)
- func (e *Element) Perform(action string) error
- func (e *Element) Position() (image.Point, error)
- func (e *Element) Role() (string, error)
- func (e *Element) SetBool(attr string, value bool) error
- func (e *Element) SetString(attr, value string) error
- func (e *Element) Size() (image.Point, error)
- func (e *Element) Subrole() (string, error)
- func (e *Element) Title() (string, error)
- func (e *Element) Value() (string, error)
- func (e *Element) Windows() ([]*Element, error)
- type Matcher
Constants ¶
const ( AttrRole = "AXRole" AttrSubrole = "AXSubrole" AttrRoleDescription = "AXRoleDescription" AttrTitle = "AXTitle" AttrDescription = "AXDescription" AttrHelp = "AXHelp" AttrValue = "AXValue" AttrValueDescription = "AXValueDescription" AttrPlaceholder = "AXPlaceholderValue" AttrIdentifier = "AXIdentifier" AttrEnabled = "AXEnabled" AttrFocused = "AXFocused" AttrSelected = "AXSelected" AttrVisible = "AXVisible" AttrExpanded = "AXExpanded" AttrMain = "AXMain" // primary window AttrMinimized = "AXMinimized" AttrFullscreen = "AXFullscreen" // Geometry AttrPosition = "AXPosition" // CGPoint AttrSize = "AXSize" // CGSize AttrFrame = "AXFrame" // CGRect (not all elements expose this) // Tree navigation AttrParent = "AXParent" AttrChildren = "AXChildren" AttrChildrenInOrder = "AXChildrenInNavigationOrder" AttrWindows = "AXWindows" AttrMainWindow = "AXMainWindow" AttrFocusedWindow = "AXFocusedWindow" AttrFocusedElement = "AXFocusedUIElement" AttrMenuBar = "AXMenuBar" AttrTopLevelElement = "AXTopLevelUIElement" // Containers AttrRows = "AXRows" AttrColumns = "AXColumns" AttrCell = "AXCell" AttrTabs = "AXTabs" AttrSelectedText = "AXSelectedText" AttrNumberOfCharacters = "AXNumberOfCharacters" )
Standard AXUIElement attribute names. These are stable strings from <HIServices/AXAttributeConstants.h>. Use them rather than string literals so typos fail at compile time.
const ( RoleApplication = "AXApplication" RoleWindow = "AXWindow" RoleButton = "AXButton" RoleCheckBox = "AXCheckBox" RoleRadioButton = "AXRadioButton" RoleTextField = "AXTextField" RoleTextArea = "AXTextArea" RoleStaticText = "AXStaticText" RolePopUpButton = "AXPopUpButton" RoleMenu = "AXMenu" RoleMenuItem = "AXMenuItem" RoleMenuBar = "AXMenuBar" RoleMenuBarItem = "AXMenuBarItem" RoleGroup = "AXGroup" RoleList = "AXList" RoleTable = "AXTable" RoleRow = "AXRow" RoleCell = "AXCell" RoleColumn = "AXColumn" RoleScrollArea = "AXScrollArea" RoleScrollBar = "AXScrollBar" RoleSlider = "AXSlider" RoleToolbar = "AXToolbar" RoleImage = "AXImage" RoleLink = "AXLink" RoleTabGroup = "AXTabGroup" RoleSheet = "AXSheet" RoleSplitGroup = "AXSplitGroup" RoleOutline = "AXOutline" RoleWebArea = "AXWebArea" )
Standard AX role values (stable constants from AXRoleConstants.h). Use with Element.Role comparisons.
const ( ActionPress = "AXPress" ActionIncrement = "AXIncrement" ActionDecrement = "AXDecrement" ActionConfirm = "AXConfirm" ActionCancel = "AXCancel" ActionShowMenu = "AXShowMenu" ActionRaise = "AXRaise" ActionShowAlternateUI = "AXShowAlternateUI" ActionShowDefaultUI = "AXShowDefaultUI" ActionPick = "AXPick" )
Standard AX actions (stable constants from AXActionConstants.h).
const Version = "0.2.0"
Version is the semantic-version tag of this package.
Variables ¶
var DylibPath = ""
DylibPath is an optional override for the location of libkinax_sync.dylib. Default (empty): extract embedded copy to cache directory.
var ErrClosed = errors.New("kinax: element closed")
ErrClosed is returned when a method is called on an Element after Close.
var ErrInvalidType = errors.New("kinax: attribute has wrong type")
ErrInvalidType is returned when an attribute exists but has the wrong type for the requested accessor (e.g. calling AttributeInt on a string attribute).
var ErrNotFound = errors.New("kinax: not found")
ErrNotFound is returned when a requested element, attribute, or app isn't present. Callers typically want to distinguish this from a real error (e.g. permission denied).
var ErrNotTrusted = errors.New("kinax: accessibility permission not granted")
ErrNotTrusted is returned when the current process lacks Accessibility permission. This is the dominant error shape for kinax — virtually every call fails without permission.
Functions ¶
func FrontmostPID ¶
func FrontmostPID() int
FrontmostPID returns the PID of the currently-active application. Returns 0 if none (rare — happens during login and app transitions).
func PromptTrust ¶
func PromptTrust() bool
PromptTrust triggers the system "wants to control your computer" dialog if permission has not been granted. Returns the resulting trust state (true if already granted, false otherwise — including the common case where the user hasn't yet responded to the dialog).
func RequireTrust ¶
func RequireTrust() error
RequireTrust returns nil if Accessibility permission is granted, or ErrNotTrusted otherwise. Convenience for callers that want a hard fail at startup rather than opaque failures deep in a traversal.
func ResolvedDylibPath ¶
func ResolvedDylibPath() string
ResolvedDylibPath returns the path Load used (or would use) to Dlopen the dylib. Intended for diagnostics.
Types ¶
type Element ¶
type Element struct {
// contains filtered or unexported fields
}
Element is a reference to a single AXUIElement in the system UI tree. Elements are opaque handles onto a retained Core Foundation object — callers MUST call Element.Close when done to avoid leaking.
Element is safe for concurrent use by multiple goroutines; all AX API calls are thread-safe per Apple docs.
func ApplicationByBundleID ¶
ApplicationByBundleID returns the Element for the first running app with the given bundle identifier (e.g. "com.apple.Safari"). Returns ErrNotFound if no such app is running. Caller must Close.
func ApplicationByPID ¶
ApplicationByPID returns the AX Element for the running application with the given Unix PID. Returns a valid Element even if no such process exists — subsequent AX calls on it will simply fail. Caller must Close.
func ElementAtPoint ¶
ElementAtPoint returns the AX element at the given global screen coordinates. This is how you implement "inspect element under cursor" (hold ⌥ hover over anything → show its AX info). Returns ErrNotFound if no element is at the point (e.g. on the desktop wallpaper in some configurations). Caller must Close.
func FocusedApplication ¶
FocusedApplication returns the Element for the currently active (frontmost) application. Returns ErrNotFound if there is none — this can happen briefly during login screens and app transitions. Caller must Close.
func SystemWide ¶
SystemWide returns an Element representing the system-wide AX root. Useful primarily to query AXFocusedApplication or to hit-test points. Caller must Close.
func (*Element) ActionNames ¶
ActionNames returns all action names this element responds to.
func (*Element) Attribute ¶
Attribute reads a string-valued attribute. The return convention:
- empty string + nil error → attribute absent OR value is empty string
- non-empty string + nil error → attribute present with value
- "" + non-nil error → real failure (permission, etc.)
This collapses "absent" and "empty" into one case; if you need to distinguish, use Element.AttributeNames first.
func (*Element) AttributeBool ¶
AttributeBool reads a bool-valued attribute.
func (*Element) AttributeElement ¶
AttributeElement reads an element-valued attribute (e.g. AXFocusedWindow, AXParent) and returns a new *Element that the caller must Close. Returns (nil, nil) if the attribute is absent — not an error, because "no focused window" is a perfectly normal state.
func (*Element) AttributeElements ¶
AttributeElements reads an array-of-elements attribute (e.g. AXChildren, AXWindows). Each returned Element must be closed by the caller.
func (*Element) AttributeInt ¶
AttributeInt reads a number-valued attribute as int64.
func (*Element) AttributeNames ¶
AttributeNames returns all attribute names this element exposes. Useful for dumping the full AX state of an element during debugging.
func (*Element) AttributePoint ¶
AttributePoint reads a CGPoint-valued attribute (e.g. AXPosition).
func (*Element) AttributeSize ¶
AttributeSize reads a CGSize-valued attribute (e.g. AXSize).
func (*Element) Close ¶
func (e *Element) Close()
Close releases the underlying CFTypeRef. Safe to call multiple times; subsequent calls are no-ops. After Close, all other methods return ErrClosed.
func (*Element) Description ¶
Description returns the element's AXDescription.
func (*Element) FindAll ¶
FindAll collects every descendant of e matching `m` (excluding e). Caller must Close every returned element.
func (*Element) FindFirst ¶
FindFirst walks the descendants of e (depth-first, excluding e itself) and returns the first match. Traversal is bounded by `maxDepth` to prevent runaway on pathological trees.
The returned Element is a fresh handle owned by the caller — Close it when done. Returns (nil, false) if no match.
Note: e itself is NOT considered a match candidate. This keeps ownership semantics clean — the caller's existing handle is never returned, so there's no risk of a double-Close.
func (*Element) FocusedElement ¶
FocusedElement returns the element within an app (or window) that currently has keyboard focus. For the system-wide element, this is the globally-focused UI element across all apps.
func (*Element) FocusedWindow ¶
FocusedWindow returns the app's currently-focused window.
func (*Element) GetMany ¶ added in v0.2.0
GetMany fetches multiple scalar attribute values in a single AX IPC round-trip via AXUIElementCopyMultipleAttributeValues. The returned map's values are the same JSON-decoded shapes the Apple AX API produces — string, float64 (json.Number), bool, or json.RawMessage for nested structures the caller chose to keep raw. Missing or unsupported attributes are simply absent from the result map.
Why use this:
- One IPC instead of N. A tree dump that previously paid an IPC round-trip per (node × attribute) pair now pays one per node. Measured 2-5× speedup on dense Electron / iWork apps.
- Atomicity within the batch. The fetched values are a coherent snapshot of the element at one point in time, not a sequence of N reads with arbitrary state changes between them.
What's *not* returned by GetMany:
- Element-valued attributes (AXChildren, AXMainWindow, AXFocusedWindow, AXParent). The C-level multi-fetch can't return handle-typed values usefully — they'd lose ownership semantics. Use Element.AttributeElement / Element.AttributeElements instead for those names.
- AXValue point/size/rect values come back as descriptions ("CGPoint {x=0,y=0}"). Use Element.AttributePoint / Element.AttributeSize for structured access.
Example:
attrs, err := el.GetMany(kinax.AttrRole, kinax.AttrTitle, kinax.AttrEnabled)
if err != nil { return err }
role := attrs[kinax.AttrRole].(string)
title := attrs[kinax.AttrTitle].(string)
enabled := attrs[kinax.AttrEnabled].(bool)
func (*Element) Handle ¶
Handle returns the opaque pointer value — useful for identity comparisons (though two handles may point to the same element in some cases — prefer comparing semantic identity via Title/Role).
func (*Element) Identifier ¶
Identifier returns the element's AXIdentifier — the stable string ID set by the app developer for automation.
func (*Element) MainWindow ¶
MainWindow returns the app's main window (usually the frontmost). Returns (nil, nil) if the app has no main window right now.
func (*Element) Perform ¶
Perform fires the named action (e.g. AXPress). Returns nil on success or an error with the underlying AXError code.
func (*Element) Position ¶
Position returns the element's top-left corner in global screen coordinates.
func (*Element) SetBool ¶
SetBool sets a bool-valued attribute (e.g. AXFocused=true to request keyboard focus).
func (*Element) SetString ¶
SetString sets a string-valued attribute. Typically used to set AXValue on a text field:
field.SetString(kinax.AttrValue, "new text")
type Matcher ¶
Matcher is a predicate over an Element, used by Element.FindFirst / Element.FindAll to select elements during tree traversal.
A Matcher may freely call accessors (Role, Title, attributes). It MUST NOT Close the element — ownership stays with the traversal.
func MatchIdentifier ¶
MatchIdentifier selects elements with AXIdentifier == `id`. App developers set this for automation-stable lookup — prefer it over titles when available.
func MatchRole ¶
MatchRole returns a Matcher that selects elements whose AXRole equals `role`. Use the Role* constants:
app.FindFirst(kinax.MatchRole(kinax.RoleButton), 20)
func MatchRoleAndTitle ¶
MatchRoleAndTitle selects elements matching both role and exact title.
func MatchTitle ¶
MatchTitle selects elements whose AXTitle equals `title` exactly.
func MatchTitleContains ¶
MatchTitleContains selects elements whose title contains `sub` as a substring (case-insensitive).
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
kinax
command
Command kinax inspects and manipulates the macOS UI tree via the Accessibility API.
|
Command kinax inspects and manipulates the macOS UI tree via the Accessibility API. |
|
internal
|
|
|
dylib
Package dylib embeds the ObjC companion library so downstream users can simply `go get` kinax-go without building C code on their machine.
|
Package dylib embeds the ObjC companion library so downstream users can simply `go get` kinax-go without building C code on their machine. |