xa11y 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. xa11y-0.1.0/Cargo.toml +27 -0
  2. xa11y-0.1.0/PKG-INFO +21 -0
  3. xa11y-0.1.0/pyproject.toml +54 -0
  4. xa11y-0.1.0/python/xa11y/__init__.py +62 -0
  5. xa11y-0.1.0/python/xa11y/_native.pyi +278 -0
  6. xa11y-0.1.0/python/xa11y/py.typed +0 -0
  7. xa11y-0.1.0/xa11y/Cargo.toml +22 -0
  8. xa11y-0.1.0/xa11y/src/lib.rs +91 -0
  9. xa11y-0.1.0/xa11y/tests/INTEG_COVERAGE.md +175 -0
  10. xa11y-0.1.0/xa11y/tests/integ/mod.rs +100 -0
  11. xa11y-0.1.0/xa11y/tests/integ_test.rs +1812 -0
  12. xa11y-0.1.0/xa11y/tests/unit_test.rs +1075 -0
  13. xa11y-0.1.0/xa11y-core/Cargo.toml +15 -0
  14. xa11y-0.1.0/xa11y-core/src/action.rs +144 -0
  15. xa11y-0.1.0/xa11y-core/src/error.rs +50 -0
  16. xa11y-0.1.0/xa11y-core/src/event.rs +160 -0
  17. xa11y-0.1.0/xa11y-core/src/event_provider.rs +105 -0
  18. xa11y-0.1.0/xa11y-core/src/lib.rs +23 -0
  19. xa11y-0.1.0/xa11y-core/src/locator.rs +435 -0
  20. xa11y-0.1.0/xa11y-core/src/node.rs +181 -0
  21. xa11y-0.1.0/xa11y-core/src/provider.rs +91 -0
  22. xa11y-0.1.0/xa11y-core/src/role.rs +157 -0
  23. xa11y-0.1.0/xa11y-core/src/selector.rs +503 -0
  24. xa11y-0.1.0/xa11y-core/src/tree.rs +185 -0
  25. xa11y-0.1.0/xa11y-linux/Cargo.toml +13 -0
  26. xa11y-0.1.0/xa11y-linux/src/atspi.rs +1485 -0
  27. xa11y-0.1.0/xa11y-linux/src/lib.rs +16 -0
  28. xa11y-0.1.0/xa11y-linux/src/stub.rs +57 -0
  29. xa11y-0.1.0/xa11y-macos/Cargo.toml +16 -0
  30. xa11y-0.1.0/xa11y-macos/build.rs +13 -0
  31. xa11y-0.1.0/xa11y-macos/src/ax.rs +1863 -0
  32. xa11y-0.1.0/xa11y-macos/src/exception_safe.m +309 -0
  33. xa11y-0.1.0/xa11y-macos/src/lib.rs +71 -0
  34. xa11y-0.1.0/xa11y-python/.gitignore +7 -0
  35. xa11y-0.1.0/xa11y-python/Cargo.lock +1381 -0
  36. xa11y-0.1.0/xa11y-python/Cargo.toml +14 -0
  37. xa11y-0.1.0/xa11y-python/src/lib.rs +1740 -0
  38. xa11y-0.1.0/xa11y-python/tests/conftest.py +14 -0
  39. xa11y-0.1.0/xa11y-python/tests/test_actions.py +169 -0
  40. xa11y-0.1.0/xa11y-python/tests/test_exceptions.py +69 -0
  41. xa11y-0.1.0/xa11y-python/tests/test_locator.py +247 -0
  42. xa11y-0.1.0/xa11y-python/tests/test_module.py +76 -0
  43. xa11y-0.1.0/xa11y-python/tests/test_node.py +303 -0
  44. xa11y-0.1.0/xa11y-python/tests/test_provider.py +103 -0
  45. xa11y-0.1.0/xa11y-python/tests/test_tree.py +179 -0
  46. xa11y-0.1.0/xa11y-python/tests/test_typing.py +33 -0
  47. xa11y-0.1.0/xa11y-windows/Cargo.toml +20 -0
  48. xa11y-0.1.0/xa11y-windows/src/lib.rs +32 -0
  49. xa11y-0.1.0/xa11y-windows/src/stub.rs +36 -0
  50. xa11y-0.1.0/xa11y-windows/src/uia.rs +1620 -0
xa11y-0.1.0/Cargo.toml ADDED
@@ -0,0 +1,27 @@
1
+ [workspace]
2
+ members = [
3
+ "xa11y-core",
4
+ "xa11y-macos",
5
+ "xa11y-windows",
6
+ "xa11y-linux",
7
+ "xa11y",
8
+ "xa11y-test-app",
9
+ "xa11y-fuzz",
10
+ ]
11
+ exclude = ["xa11y-python"]
12
+ resolver = "2"
13
+
14
+ [workspace.package]
15
+ version = "0.1.1"
16
+ edition = "2021"
17
+ license = "MIT"
18
+ repository = "https://github.com/xa11y/xa11y"
19
+
20
+ [workspace.dependencies]
21
+ xa11y-core = { path = "xa11y-core", version = "0.1.1" }
22
+ xa11y-macos = { path = "xa11y-macos", version = "0.1.1" }
23
+ xa11y-windows = { path = "xa11y-windows", version = "0.1.1" }
24
+ xa11y-linux = { path = "xa11y-linux", version = "0.1.1" }
25
+ serde = { version = "1", features = ["derive"] }
26
+ serde_json = "1"
27
+ thiserror = "2"
xa11y-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: xa11y
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Rust
8
+ Classifier: Programming Language :: Python :: Implementation :: CPython
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Summary: Cross-platform accessibility client library
17
+ Keywords: accessibility,a11y,automation,screen-reader
18
+ License-Expression: MIT
19
+ Requires-Python: >=3.9
20
+ Project-URL: Homepage, https://github.com/xa11y/xa11y
21
+ Project-URL: Repository, https://github.com/xa11y/xa11y
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.0,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "xa11y"
7
+ requires-python = ">=3.9"
8
+ dynamic = ["version"]
9
+ description = "Cross-platform accessibility client library"
10
+ license = "MIT"
11
+ keywords = ["accessibility", "a11y", "automation", "screen-reader"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Rust",
17
+ "Programming Language :: Python :: Implementation :: CPython",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Software Development :: Libraries",
24
+ "Topic :: Software Development :: Testing",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/xa11y/xa11y"
29
+ Repository = "https://github.com/xa11y/xa11y"
30
+
31
+ [tool.maturin]
32
+ features = ["pyo3/extension-module", "pyo3/abi3-py39"]
33
+ module-name = "xa11y._native"
34
+ manifest-path = "xa11y-python/Cargo.toml"
35
+ python-source = "python"
36
+
37
+ [tool.ruff]
38
+ target-version = "py39"
39
+ line-length = 100
40
+
41
+ [tool.ruff.lint]
42
+ select = [
43
+ "E", # pycodestyle errors
44
+ "W", # pycodestyle warnings
45
+ "F", # pyflakes
46
+ "I", # isort
47
+ "UP", # pyupgrade
48
+ "B", # flake8-bugbear
49
+ "SIM", # flake8-simplify
50
+ "RUF", # ruff-specific
51
+ ]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
@@ -0,0 +1,62 @@
1
+ """xa11y — Cross-Platform Accessibility Client Library for Python.
2
+
3
+ Quick start:
4
+ >>> import xa11y
5
+ >>> tree = xa11y.app("Safari")
6
+ >>> for button in tree.query("button"):
7
+ ... print(button.name)
8
+ >>> tree.press("button[name='OK']")
9
+
10
+ With explicit provider:
11
+ >>> with xa11y.connect() as provider:
12
+ ... tree = provider.app("Safari")
13
+ ... loc = tree.locator("button[name='Submit']")
14
+ ... loc.press()
15
+ """
16
+
17
+ from xa11y._native import (
18
+ ActionNotSupportedError,
19
+ AppInfo,
20
+ AppNotFoundError,
21
+ InvalidSelectorError,
22
+ Locator,
23
+ Node,
24
+ NormalizedRect,
25
+ PermissionDeniedError,
26
+ PlatformError,
27
+ # Classes
28
+ Provider,
29
+ Rect,
30
+ SelectorNotMatchedError,
31
+ TimeoutError,
32
+ Tree,
33
+ # Exceptions
34
+ XA11yError,
35
+ app,
36
+ check_permissions,
37
+ # Convenience functions
38
+ connect,
39
+ list_apps,
40
+ )
41
+
42
+ __all__ = [
43
+ "ActionNotSupportedError",
44
+ "AppInfo",
45
+ "AppNotFoundError",
46
+ "InvalidSelectorError",
47
+ "Locator",
48
+ "Node",
49
+ "NormalizedRect",
50
+ "PermissionDeniedError",
51
+ "PlatformError",
52
+ "Provider",
53
+ "Rect",
54
+ "SelectorNotMatchedError",
55
+ "TimeoutError",
56
+ "Tree",
57
+ "XA11yError",
58
+ "app",
59
+ "check_permissions",
60
+ "connect",
61
+ "list_apps",
62
+ ]
@@ -0,0 +1,278 @@
1
+ """Type stubs for the xa11y native extension module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from types import TracebackType
7
+
8
+ # ── Exceptions ───────────────────────────────────────────────────────────────
9
+
10
+ class XA11yError(Exception): ...
11
+ class PermissionDeniedError(XA11yError): ...
12
+ class AppNotFoundError(XA11yError): ...
13
+ class SelectorNotMatchedError(XA11yError): ...
14
+ class ActionNotSupportedError(XA11yError): ...
15
+ class TimeoutError(XA11yError): ...
16
+ class InvalidSelectorError(XA11yError): ...
17
+ class PlatformError(XA11yError): ...
18
+
19
+ # ── Data Classes ─────────────────────────────────────────────────────────────
20
+
21
+ class Rect:
22
+ @property
23
+ def x(self) -> int: ...
24
+ @property
25
+ def y(self) -> int: ...
26
+ @property
27
+ def width(self) -> int: ...
28
+ @property
29
+ def height(self) -> int: ...
30
+ def __repr__(self) -> str: ...
31
+ def __eq__(self, other: object) -> bool: ...
32
+
33
+ class NormalizedRect:
34
+ @property
35
+ def left(self) -> float: ...
36
+ @property
37
+ def top(self) -> float: ...
38
+ @property
39
+ def right(self) -> float: ...
40
+ @property
41
+ def bottom(self) -> float: ...
42
+ def __repr__(self) -> str: ...
43
+
44
+ class AppInfo:
45
+ @property
46
+ def name(self) -> str: ...
47
+ @property
48
+ def pid(self) -> int: ...
49
+ @property
50
+ def bundle_id(self) -> str | None: ...
51
+ def __repr__(self) -> str: ...
52
+
53
+ # ── Node ─────────────────────────────────────────────────────────────────────
54
+
55
+ class Node:
56
+ @property
57
+ def role(self) -> str: ...
58
+ @property
59
+ def name(self) -> str | None: ...
60
+ @property
61
+ def value(self) -> str | None: ...
62
+ @property
63
+ def description(self) -> str | None: ...
64
+ @property
65
+ def depth(self) -> int: ...
66
+ @property
67
+ def numeric_value(self) -> float | None: ...
68
+ @property
69
+ def min_value(self) -> float | None: ...
70
+ @property
71
+ def max_value(self) -> float | None: ...
72
+ @property
73
+ def stable_id(self) -> str | None: ...
74
+ @property
75
+ def actions(self) -> list[str]: ...
76
+ @property
77
+ def bounds(self) -> Rect | None: ...
78
+ @property
79
+ def bounds_normalized(self) -> NormalizedRect | None: ...
80
+ @property
81
+ def enabled(self) -> bool: ...
82
+ @property
83
+ def visible(self) -> bool: ...
84
+ @property
85
+ def focused(self) -> bool: ...
86
+ @property
87
+ def checked(self) -> str | None: ...
88
+ @property
89
+ def selected(self) -> bool: ...
90
+ @property
91
+ def expanded(self) -> bool | None: ...
92
+ @property
93
+ def editable(self) -> bool: ...
94
+ @property
95
+ def focusable(self) -> bool: ...
96
+ @property
97
+ def modal(self) -> bool: ...
98
+ @property
99
+ def required(self) -> bool: ...
100
+ @property
101
+ def busy(self) -> bool: ...
102
+ def __repr__(self) -> str: ...
103
+ def __str__(self) -> str: ...
104
+ def __len__(self) -> int: ...
105
+
106
+ # ── Target type for action methods ───────────────────────────────────────────
107
+
108
+ _Target = Node | str
109
+
110
+ # ── Tree ─────────────────────────────────────────────────────────────────────
111
+
112
+ class Tree:
113
+ @property
114
+ def app_name(self) -> str: ...
115
+ @property
116
+ def pid(self) -> int | None: ...
117
+ @property
118
+ def screen_size(self) -> tuple[int, int]: ...
119
+ @property
120
+ def root(self) -> Node: ...
121
+ def children(self, node: Node) -> list[Node]: ...
122
+ def parent(self, node: Node) -> Node | None: ...
123
+ def query(self, selector: str) -> list[Node]: ...
124
+ def find_by_role(self, role: str) -> list[Node]: ...
125
+ def find_by_name(self, pattern: str) -> list[Node]: ...
126
+ def perform(
127
+ self,
128
+ target: _Target,
129
+ action: str,
130
+ *,
131
+ value: str | None = None,
132
+ numeric_value: float | None = None,
133
+ direction: str | None = None,
134
+ amount: float | None = None,
135
+ start: int | None = None,
136
+ end: int | None = None,
137
+ ) -> None: ...
138
+ def press(self, target: _Target) -> None: ...
139
+ def focus(self, target: _Target) -> None: ...
140
+ def blur(self, target: _Target) -> None: ...
141
+ def toggle(self, target: _Target) -> None: ...
142
+ def expand(self, target: _Target) -> None: ...
143
+ def collapse(self, target: _Target) -> None: ...
144
+ def select(self, target: _Target) -> None: ...
145
+ def increment(self, target: _Target) -> None: ...
146
+ def decrement(self, target: _Target) -> None: ...
147
+ def show_menu(self, target: _Target) -> None: ...
148
+ def scroll_into_view(self, target: _Target) -> None: ...
149
+ def set_value(self, target: _Target, value: str) -> None: ...
150
+ def set_numeric_value(self, target: _Target, value: float) -> None: ...
151
+ def type_text(self, target: _Target, text: str) -> None: ...
152
+ def scroll(self, target: _Target, direction: str, amount: float = 1.0) -> None: ...
153
+ def select_text(self, target: _Target, start: int, end: int) -> None: ...
154
+ def locator(
155
+ self,
156
+ selector: str,
157
+ *,
158
+ max_depth: int | None = None,
159
+ max_elements: int | None = None,
160
+ visible_only: bool = False,
161
+ roles: list[str] | None = None,
162
+ include_raw: bool = False,
163
+ ) -> Locator: ...
164
+ def dump(self) -> str: ...
165
+ def __len__(self) -> int: ...
166
+ def __iter__(self) -> Iterator[Node]: ...
167
+ def __repr__(self) -> str: ...
168
+ def __str__(self) -> str: ...
169
+
170
+ # ── Provider ─────────────────────────────────────────────────────────────────
171
+
172
+ class Provider:
173
+ def __init__(self) -> None: ...
174
+ def app(
175
+ self,
176
+ name: str | None = None,
177
+ *,
178
+ pid: int | None = None,
179
+ max_depth: int | None = None,
180
+ max_elements: int | None = None,
181
+ visible_only: bool = False,
182
+ roles: list[str] | None = None,
183
+ include_raw: bool = False,
184
+ ) -> Tree: ...
185
+ def all_apps(
186
+ self,
187
+ *,
188
+ max_depth: int | None = None,
189
+ max_elements: int | None = None,
190
+ visible_only: bool = False,
191
+ roles: list[str] | None = None,
192
+ include_raw: bool = False,
193
+ ) -> Tree: ...
194
+ def list_apps(self) -> list[AppInfo]: ...
195
+ def check_permissions(self) -> str: ...
196
+ def locator(
197
+ self,
198
+ name: str | None = None,
199
+ *,
200
+ pid: int | None = None,
201
+ selector: str,
202
+ max_depth: int | None = None,
203
+ max_elements: int | None = None,
204
+ visible_only: bool = False,
205
+ roles: list[str] | None = None,
206
+ include_raw: bool = False,
207
+ ) -> Locator: ...
208
+ def __enter__(self) -> Provider: ...
209
+ def __exit__(
210
+ self,
211
+ exc_type: type[BaseException] | None = None,
212
+ exc_val: BaseException | None = None,
213
+ exc_tb: TracebackType | None = None,
214
+ ) -> bool: ...
215
+ def __repr__(self) -> str: ...
216
+
217
+ # ── Locator ──────────────────────────────────────────────────────────────────
218
+
219
+ class Locator:
220
+ @property
221
+ def selector(self) -> str: ...
222
+ def nth(self, n: int) -> Locator: ...
223
+ def first(self) -> Locator: ...
224
+ def child(self, selector: str) -> Locator: ...
225
+ def descendant(self, selector: str) -> Locator: ...
226
+ def role(self) -> str: ...
227
+ def name(self) -> str | None: ...
228
+ def value(self) -> str | None: ...
229
+ def description(self) -> str | None: ...
230
+ def is_visible(self) -> bool: ...
231
+ def is_enabled(self) -> bool: ...
232
+ def is_focused(self) -> bool: ...
233
+ def exists(self) -> bool: ...
234
+ def count(self) -> int: ...
235
+ def get(self) -> Node: ...
236
+ def press(self) -> None: ...
237
+ def focus(self) -> None: ...
238
+ def blur(self) -> None: ...
239
+ def toggle(self) -> None: ...
240
+ def expand(self) -> None: ...
241
+ def collapse(self) -> None: ...
242
+ def select_item(self) -> None: ...
243
+ def show_menu(self) -> None: ...
244
+ def scroll_into_view(self) -> None: ...
245
+ def increment(self) -> None: ...
246
+ def decrement(self) -> None: ...
247
+ def set_value(self, value: str) -> None: ...
248
+ def set_numeric_value(self, value: float) -> None: ...
249
+ def type_text(self, text: str) -> None: ...
250
+ def select_text(self, start: int, end: int) -> None: ...
251
+ def scroll(self, direction: str, amount: float = 1.0) -> None: ...
252
+ def wait_visible(self, timeout: float = 5.0) -> None: ...
253
+ def wait_attached(self, timeout: float = 5.0) -> None: ...
254
+ def wait_detached(self, timeout: float = 5.0) -> None: ...
255
+ def wait_enabled(self, timeout: float = 5.0) -> None: ...
256
+ def wait_hidden(self, timeout: float = 5.0) -> None: ...
257
+ def __repr__(self) -> str: ...
258
+
259
+ # ── Module-level functions ───────────────────────────────────────────────────
260
+
261
+ def connect() -> Provider: ...
262
+ def app(
263
+ name: str | None = None,
264
+ *,
265
+ pid: int | None = None,
266
+ max_depth: int | None = None,
267
+ max_elements: int | None = None,
268
+ visible_only: bool = False,
269
+ roles: list[str] | None = None,
270
+ include_raw: bool = False,
271
+ ) -> Tree: ...
272
+ def list_apps() -> list[AppInfo]: ...
273
+ def check_permissions() -> str: ...
274
+
275
+ # ── Test helpers ─────────────────────────────────────────────────────────────
276
+
277
+ def _make_test_tree() -> Tree: ...
278
+ def _make_test_provider() -> Provider: ...
File without changes
@@ -0,0 +1,22 @@
1
+ [package]
2
+ name = "xa11y"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ repository.workspace = true
7
+ description = "Cross-platform accessibility client library — unified API for reading and interacting with accessibility trees"
8
+
9
+ [dependencies]
10
+ xa11y-core = { workspace = true }
11
+
12
+ [target.'cfg(target_os = "macos")'.dependencies]
13
+ xa11y-macos = { workspace = true }
14
+
15
+ [target.'cfg(target_os = "windows")'.dependencies]
16
+ xa11y-windows = { workspace = true }
17
+
18
+ [target.'cfg(target_os = "linux")'.dependencies]
19
+ xa11y-linux = { workspace = true }
20
+
21
+ [dev-dependencies]
22
+ serde_json = { workspace = true }
@@ -0,0 +1,91 @@
1
+ //! xa11y — Cross-Platform Accessibility Client Library
2
+ //!
3
+ //! Provides a unified API for reading and interacting with accessibility trees
4
+ //! across desktop platforms (macOS, Windows, Linux).
5
+ //!
6
+ //! # Quick Start
7
+ //!
8
+ //! ```no_run
9
+ //! use xa11y::*;
10
+ //!
11
+ //! let provider = create_provider().expect("Failed to create provider");
12
+ //! let status = provider.check_permissions().expect("Permission check failed");
13
+ //!
14
+ //! match status {
15
+ //! PermissionStatus::Granted => {
16
+ //! let tree = provider.get_app_tree(
17
+ //! &AppTarget::ByName("Safari".to_string()),
18
+ //! &QueryOptions::default(),
19
+ //! ).expect("Failed to get tree");
20
+ //!
21
+ //! let buttons = tree.query("button").expect("Query failed");
22
+ //! println!("Found {} buttons", buttons.len());
23
+ //! }
24
+ //! PermissionStatus::Denied { instructions } => {
25
+ //! eprintln!("Accessibility not enabled: {}", instructions);
26
+ //! }
27
+ //! }
28
+ //! ```
29
+
30
+ // Re-export all core types
31
+ pub use xa11y_core::*;
32
+
33
+ // Platform-specific provider creation
34
+
35
+ /// Create a platform-appropriate accessibility provider.
36
+ ///
37
+ /// Returns a boxed `Provider` trait object for the current platform.
38
+ /// On unsupported platforms, returns a `Platform` error.
39
+ pub fn create_provider() -> Result<Box<dyn Provider>> {
40
+ #[cfg(target_os = "macos")]
41
+ {
42
+ Ok(Box::new(xa11y_macos::MacOSProvider::new()?))
43
+ }
44
+
45
+ #[cfg(target_os = "windows")]
46
+ {
47
+ Ok(Box::new(xa11y_windows::WindowsProvider::new()?))
48
+ }
49
+
50
+ #[cfg(target_os = "linux")]
51
+ {
52
+ Ok(Box::new(xa11y_linux::LinuxProvider::new()?))
53
+ }
54
+
55
+ #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
56
+ {
57
+ Err(Error::Platform {
58
+ code: -1,
59
+ message: format!("Unsupported platform: {}", std::env::consts::OS),
60
+ })
61
+ }
62
+ }
63
+
64
+ /// Create a platform-appropriate event provider (supports subscribe/wait).
65
+ ///
66
+ /// Returns a boxed `EventProvider` trait object for the current platform.
67
+ /// EventProvider extends Provider with event subscription capabilities.
68
+ pub fn create_event_provider() -> Result<Box<dyn EventProvider>> {
69
+ #[cfg(target_os = "macos")]
70
+ {
71
+ Ok(Box::new(xa11y_macos::MacOSProvider::new()?))
72
+ }
73
+
74
+ #[cfg(target_os = "windows")]
75
+ {
76
+ Ok(Box::new(xa11y_windows::WindowsProvider::new()?))
77
+ }
78
+
79
+ #[cfg(target_os = "linux")]
80
+ {
81
+ Ok(Box::new(xa11y_linux::LinuxProvider::new()?))
82
+ }
83
+
84
+ #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
85
+ {
86
+ Err(Error::Platform {
87
+ code: -1,
88
+ message: format!("Unsupported platform: {}", std::env::consts::OS),
89
+ })
90
+ }
91
+ }