Skip to content

feat: AT-SPI focus backend implementation #316

@Coldaine

Description

@Coldaine

Source

PR #232 () - can be closed after this issue exists.

What it does

Real AT-SPI focus detection replacing the stub SystemFocusAdapter. Uses the atspi crate to query the currently focused UI element and determine if it's an editable text field.

Key code (~100 lines)

#[cfg(feature = "atspi")]
#[async_trait]
impl FocusBackend for SystemFocusAdapter {
    async fn query_focus(&self) -> Result<FocusStatus, InjectionError> {
        use atspi::{
            connection::AccessibilityConnection,
            proxy::accessible::AccessibleProxy,
            proxy::collection::CollectionProxy,
            Interface, MatchType, ObjectMatchRule, SortOrder, State,
        };

        let conn = match AccessibilityConnection::new().await {
            Ok(c) => c,
            Err(e) => {
                log_atspi_connection_failure(&e.to_string());
                return Ok(FocusStatus::Unknown);
            }
        };

        let zbus_conn = conn.connection();
        let collection = CollectionProxy::builder(zbus_conn)
            .destination("org.a11y.atspi.Registry")?
            .path("/org/a11y/atspi/accessible/root")?
            .build().await?;

        let mut rule = ObjectMatchRule::default();
        rule.states = State::Focused.into();
        rule.states_mt = MatchType::All;

        let matches = collection.get_matches(rule, SortOrder::Canonical, 1, false).await?;

        if let Some(obj_ref) = matches.first() {
            let accessible = AccessibleProxy::builder(zbus_conn)
                .destination(obj_ref.name.clone())?
                .path(obj_ref.path.clone())?
                .build().await?;

            let ifaces = accessible.get_interfaces().await?;
            if ifaces.contains(Interface::EditableText) {
                Ok(FocusStatus::EditableText)
            } else {
                Ok(FocusStatus::NonEditable)
            }
        } else {
            Ok(FocusStatus::Unknown)
        }
    }
}

Implementation notes

  • Feature-gated behind atspi feature flag
  • Graceful fallback to FocusStatus::Unknown on errors
  • Needs runtime testing on GNOME/GTK desktop
  • Test file included: focus_backend_test.rs (gated by live-hardware-tests)

Status

Ready to implement fresh or cherry-pick from PR #232 after rebase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions