1- //! AT-SPI2 UiTree provider for Unix desktops (stub) .
1+ //! AT-SPI2 UiTree provider for Unix desktops.
22//!
3- //! This crate currently exposes a minimal provider factory so tests and
4- //! consumers can construct a `Runtime` with an AT-SPI2 provider via
5- //! `Runtime::new_with_factories(&[&ATSPI_FACTORY])`. The actual D-Bus backed
6- //! implementation will be added incrementally.
3+ //! Provides a blocking D-Bus integration to query the accessibility tree on
4+ //! Linux/X11 systems. Event streaming and full WindowSurface integration will
5+ //! follow in later phases.
76
8- use once_cell:: sync:: Lazy ;
9- use platynui_core:: provider:: { ProviderDescriptor , ProviderError , ProviderKind , UiTreeProvider , UiTreeProviderFactory } ;
7+ mod connection;
8+ mod node;
9+
10+ use crate :: connection:: connect_a11y_bus;
11+ use crate :: node:: AtspiNode ;
12+ use atspi_connection:: AccessibilityConnection ;
13+ use atspi_proxies:: accessible:: AccessibleProxy ;
14+ use futures_lite:: future:: block_on;
15+ use once_cell:: sync:: { Lazy , OnceCell } ;
16+ use platynui_core:: provider:: {
17+ ProviderDescriptor , ProviderError , ProviderErrorKind , ProviderKind , UiTreeProvider , UiTreeProviderFactory ,
18+ } ;
1019use platynui_core:: ui:: { TechnologyId , UiNode } ;
1120use std:: sync:: Arc ;
21+ use zbus:: proxy:: CacheProperties ;
1222
1323pub const PROVIDER_ID : & str = "atspi" ;
1424pub const PROVIDER_NAME : & str = "AT-SPI2" ;
1525pub static TECHNOLOGY : Lazy < TechnologyId > = Lazy :: new ( || TechnologyId :: from ( "AT-SPI2" ) ) ;
1626
17- // Bus discovery and the actual AT-SPI integration will be added in Phase 2.
18- // No feature gate is used because the functionality is part of the core Linux
19- // path; the module will land once we introduce the required dependencies.
27+ const REGISTRY_BUS : & str = "org.a11y.atspi.Registry" ;
28+ const ROOT_PATH : & str = "/org/a11y/atspi/accessible/root" ;
2029
2130pub struct AtspiFactory ;
2231
@@ -33,32 +42,69 @@ impl UiTreeProviderFactory for AtspiFactory {
3342 }
3443}
3544
36- struct AtspiProvider {
45+ pub struct AtspiProvider {
3746 descriptor : & ' static ProviderDescriptor ,
47+ conn : OnceCell < Arc < AccessibilityConnection > > ,
3848}
3949
4050impl AtspiProvider {
4151 fn new ( ) -> Self {
4252 static DESCRIPTOR : Lazy < ProviderDescriptor > = Lazy :: new ( || {
4353 ProviderDescriptor :: new ( PROVIDER_ID , PROVIDER_NAME , TechnologyId :: from ( "AT-SPI2" ) , ProviderKind :: Native )
4454 } ) ;
45- Self { descriptor : & DESCRIPTOR }
55+ Self { descriptor : & DESCRIPTOR , conn : OnceCell :: new ( ) }
56+ }
57+
58+ fn connection ( & self ) -> Result < Arc < AccessibilityConnection > , ProviderError > {
59+ self . conn
60+ . get_or_try_init ( || connect_a11y_bus ( ) . map ( Arc :: new) )
61+ . map ( Arc :: clone)
62+ . map_err ( |err| ProviderError :: new ( ProviderErrorKind :: TreeUnavailable , err. to_string ( ) ) )
4663 }
4764}
4865
4966impl UiTreeProvider for AtspiProvider {
5067 fn descriptor ( & self ) -> & ProviderDescriptor {
5168 self . descriptor
5269 }
70+
5371 fn get_nodes (
5472 & self ,
55- _parent : Arc < dyn UiNode > ,
73+ parent : Arc < dyn UiNode > ,
5674 ) -> Result < Box < dyn Iterator < Item = Arc < dyn UiNode > > + Send > , ProviderError > {
57- Ok ( Box :: new ( std:: iter:: empty ( ) ) )
75+ let conn = self . connection ( ) ?;
76+ let proxy = block_on (
77+ AccessibleProxy :: builder ( conn. connection ( ) )
78+ . cache_properties ( CacheProperties :: No )
79+ . destination ( REGISTRY_BUS )
80+ . map_err ( |err| {
81+ ProviderError :: new ( ProviderErrorKind :: CommunicationFailure , format ! ( "registry destination: {err}" ) )
82+ } ) ?
83+ . path ( ROOT_PATH )
84+ . map_err ( |err| {
85+ ProviderError :: new ( ProviderErrorKind :: CommunicationFailure , format ! ( "registry path: {err}" ) )
86+ } ) ?
87+ . build ( ) ,
88+ )
89+ . map_err ( |err| ProviderError :: new ( ProviderErrorKind :: CommunicationFailure , format ! ( "registry proxy: {err}" ) ) ) ?;
90+
91+ let children = block_on ( proxy. get_children ( ) ) . map_err ( |err| {
92+ ProviderError :: new ( ProviderErrorKind :: CommunicationFailure , format ! ( "registry children: {err}" ) )
93+ } ) ?;
94+
95+ let parent = Arc :: clone ( & parent) ;
96+ let conn = conn. clone ( ) ;
97+ Ok ( Box :: new ( children. into_iter ( ) . filter_map ( move |child| {
98+ if AtspiNode :: is_null_object ( & child) {
99+ return None ;
100+ }
101+ let node = AtspiNode :: new ( conn. clone ( ) , child, Some ( & parent) ) ;
102+ Some ( node as Arc < dyn UiNode > )
103+ } ) ) )
58104 }
59105}
60106
61107pub static ATSPI_FACTORY : AtspiFactory = AtspiFactory ;
62108
63- // Auto-register the AT-SPI provider when linked
109+ // Auto-register the AT-SPI provider when linked.
64110platynui_core:: register_provider!( & ATSPI_FACTORY ) ;
0 commit comments