surface — targeting the desktop itself
Applications are not the whole screen. The menu bar, the taskbar/panel/dock,
tray status items, the desktop underneath, and transient flyouts (notification
centers, shell context menus) all live OUTSIDE any application’s tree.
surface makes them a first-class targeting root: pass surface instead of
name/pid to the addressing tools and their selectors resolve from the
shell’s element tree.
surface::resolve(kind: SurfaceKind, timeout: Duration) -> Result<ShellSurface, String>
surface::resolve_with(provider, kind, timeout) -> Result<ShellSurface, String> // injected
surface::surface_label(kind) -> &'static str // human label
Consumers rarely call these directly — snapshot,
act and screenshot accept surface as a
scope field and route through here.
The model: the third root
| Root | Fields | Means |
|---|---|---|
| Application | name/pid |
One app’s accessibility tree. |
| Shell surface | surface |
Part of the desktop chrome itself. |
The vocabulary — SurfaceKind, snake_case on the wire, mirroring
xa11y’s ShellSurfaceKind exactly:
| Kind | Wire | What lives there |
|---|---|---|
MenuBar |
"menu_bar" |
The frontmost app’s menu bar (macOS: File → Save As; per-window menus on Windows/Linux live in the app’s own tree). |
StatusItems |
"status_items" |
Tray icons / menu-bar extras. Windows: the overflow’s content exists only while the overflow is open. |
Taskbar |
"taskbar" |
The Windows taskbar. |
Panel |
"panel" |
GNOME top bar, KDE panel, and friends. |
Dock |
"dock" |
The macOS Dock. |
Desktop |
"desktop" |
Icons and wallpaper-level widgets. |
Flyout |
"flyout" |
Transient shell popups open RIGHT NOW. |
Unknown |
"unknown" |
A surface the platform reported without a known kind. |
Flyouts exist only while open
The one timing rule worth memorizing: a flyout (Notification Center, Quick Settings, a shell context menu) has elements only while it is open. The caller performs the press that opens it on a real element first, THEN re-enumerates — which is also why a flyout snapshot can legitimately be empty.
Edges: platform honesty
Surface enumeration is only as rich as the backend. On macOS and
GNOME-family desktops the full vocabulary is reachable. On X11 window
managers like i3 the backend classifies only AT-SPI dock frames — a
legitimately empty result, not a bug. That honesty is built into the errors:
when enumeration finds nothing, the error carries a live census of what
the platform DID report, so “0 surfaces” is diagnosable from the message
alone (see errors).
// The taskbar's tree, same outline as an app snapshot:
computer.snapshot(&ComputerSnapshot {
surface: Some(SurfaceKind::Taskbar),
..Default::default()
}).await?;
// Press a taskbar icon by selector:
computer.act(&ComputerAct {
surface: Some(SurfaceKind::Taskbar),
selector: Some("button[name='Files']".into()),
..Default::default()
}).await?;
See also
snapshot / act for the tools that consume it ·
cargo run --example computer-surface