Documentation
¶
Overview ¶
Package linux implements the Linux AT-SPI2 accessibility backend for the Pando Desktop Controller (internal/uiauto). It talks to the AT-SPI2 registry and applications over D-Bus using github.com/godbus/dbus/v5 — no cgo, no external process.
Design notes:
Every accessibility object is identified by a (bus name, object path) pair (accessibleRef). core.Element stores both in Native.Data so a later Children/Perform call can act on it without re-searching.
Traversal is selector-driven (see traverse.go): Find never walks the whole tree. It carries a small per-DFS-branch set of "pending selector steps" down the tree, testing only what could still complete a match, and stops as soon as it has `limit` results, a depth cap is hit, or ctx is cancelled.
Property reads are batched with org.freedesktop.DBus.Properties.GetAll instead of one round-trip per attribute, and a per-call memo cache (traverseCache) makes sure a single Find/Children/Observe call never re-fetches the same object twice.
This package purposefully depends only on a small busConn interface (conn.go) rather than *dbus.Conn directly, so the traversal and matching logic can be exercised in unit tests against a fake in-memory tree with no real accessibility bus involved.
Index ¶
- func NewBackend() (core.Backend, error)
- type AtspiBackend
- func (b *AtspiBackend) Apps(ctx context.Context) ([]core.AppInfo, error)
- func (b *AtspiBackend) Available(ctx context.Context) (core.Capabilities, error)
- func (b *AtspiBackend) Children(ctx context.Context, el *core.Element) ([]*core.Element, error)
- func (b *AtspiBackend) Close() error
- func (b *AtspiBackend) Find(ctx context.Context, scope core.Scope, sel *core.Selector, limit int) ([]*core.Element, error)
- func (b *AtspiBackend) Name() string
- func (b *AtspiBackend) Perform(ctx context.Context, el *core.Element, action core.Action) error
- func (b *AtspiBackend) Properties(ctx context.Context, el *core.Element, props []string) (map[string]any, error)
- func (b *AtspiBackend) Subscribe(ctx context.Context, scope core.Scope) (<-chan events.Event, func(), error)
- func (b *AtspiBackend) Windows(ctx context.Context, appID string) ([]core.WindowInfo, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewBackend ¶
NewBackend constructs an AtspiBackend. It never fails: connecting to the accessibility bus happens lazily on the first real operation.
Types ¶
type AtspiBackend ¶
type AtspiBackend struct {
// contains filtered or unexported fields
}
AtspiBackend is the core.Backend implementation talking to Linux AT-SPI2 over D-Bus. The bus connection is established lazily on first use (not in NewBackend, so backend construction itself can never fail) and cached; Available reports honestly instead of erroring when the accessibility bus/session is unreachable, matching the contract NullBackend and Manager document.
func (*AtspiBackend) Apps ¶
Apps implements core.Backend by listing the AT-SPI2 registry's top-level children, one per running (a11y-registered) application.
func (*AtspiBackend) Available ¶
func (b *AtspiBackend) Available(ctx context.Context) (core.Capabilities, error)
Available implements core.Backend. It never returns an error: a reachable a11y bus reports Accessibility/UIInspection/UIActions true; anything else degrades to an all-false Capabilities so callers fall back gracefully (see the Manager/NullBackend contract this mirrors).
func (*AtspiBackend) Children ¶
Children implements core.Backend: the direct children of el, built from a single GetChildren call plus one batched fetch per child.
func (*AtspiBackend) Find ¶
func (b *AtspiBackend) Find(ctx context.Context, scope core.Scope, sel *core.Selector, limit int) ([]*core.Element, error)
Find implements core.Backend with the selector-driven, depth-capped, limit-capped, ctx-aware traversal in traverse.go — it never walks the whole tree.
func (*AtspiBackend) Properties ¶
func (b *AtspiBackend) Properties(ctx context.Context, el *core.Element, props []string) (map[string]any, error)
Properties implements core.Backend. When props is empty it returns the cheap attributes already gathered by a normal node fetch (raw role, interfaces, child count, decoded state extras); "text" and "actions" are only fetched on request since they cost an extra round trip each.
func (*AtspiBackend) Subscribe ¶
func (b *AtspiBackend) Subscribe(ctx context.Context, scope core.Scope) (<-chan events.Event, func(), error)
Subscribe implements events.Subscriber for AtspiBackend: it lazily connects to the a11y bus (same as every other operation), lazily starts the shared eventSource, and hands the caller a fresh fan-out channel from its EventBus. scope is currently not used to filter server-side (AT-SPI2's Event.Object signals are not natively scopable that way); events.WaitFor always re-evaluates the actual locator/condition against the backend on every received event, so an unrelated event only costs one harmless extra Find call, never an incorrect result.
func (*AtspiBackend) Windows ¶
func (b *AtspiBackend) Windows(ctx context.Context, appID string) ([]core.WindowInfo, error)
Windows implements core.Backend by listing the direct children (frames/ windows/dialogs) of the matching application(s).