diff --git a/Cargo.lock b/Cargo.lock index 9e37d83345..6ba56efb45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,8 +53,6 @@ dependencies = [ [[package]] name = "accesskit_unix" version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202f24df034a7476d07b7f74284de84f6d62aabd858dbe7ee9cad3b7ad6f8f9d" dependencies = [ "accesskit", "accesskit_atspi_common", diff --git a/Cargo.toml b/Cargo.toml index fa9ff29dd0..6403863b5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,9 @@ members = [ "./crates/gpui_path/", "./tooling/perf/", ] +# A patched copy of a crates.io crate, not part of this workspace; see +# vendor/accesskit_unix/README.md. +exclude = ["./vendor/accesskit_unix/"] default-members = ["./crates/gpui/"] resolver = "3" @@ -42,7 +45,7 @@ readme = "README.md" accesskit = { version = "0.25.1", features = ["enumn"] } accesskit_consumer = "0.39.1" accesskit_macos = "0.27.1" -accesskit_unix = "0.24.0" +accesskit_unix = { version = "0.24.0", path = "./vendor/accesskit_unix" } accesskit_windows = "0.35.1" anyhow = "1.0.86" async-task = "4.7" diff --git a/typos.toml b/typos.toml index 1ff0f70be5..f613550acf 100644 --- a/typos.toml +++ b/typos.toml @@ -4,6 +4,9 @@ ignore-hidden = false extend-exclude = [ ".git/", + # A vendored copy of a crates.io crate, kept byte for byte. + "vendor/", + # Windows likes its abbreviations. "crates/gpui/src/platform/windows/directx_renderer.rs", "crates/gpui/src/platform/windows/events.rs", diff --git a/vendor/accesskit_unix/CHANGELOG.md b/vendor/accesskit_unix/CHANGELOG.md new file mode 100644 index 0000000000..168b323921 --- /dev/null +++ b/vendor/accesskit_unix/CHANGELOG.md @@ -0,0 +1,665 @@ +# Changelog + +* The following workspace dependencies were updated + * dependencies + * accesskit_consumer bumped from 0.12.0 to 0.12.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.10.0 to 0.10.1 + * accesskit_consumer bumped from 0.14.0 to 0.14.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.10.1 to 0.11.0 + * accesskit_consumer bumped from 0.14.1 to 0.14.2 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.11.0 to 0.11.1 + * accesskit_consumer bumped from 0.15.0 to 0.15.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.11.1 to 0.11.2 + * accesskit_consumer bumped from 0.15.1 to 0.15.2 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.12.0 to 0.12.1 + * accesskit_consumer bumped from 0.16.0 to 0.16.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.1.0 to 0.1.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.12.2 to 0.12.3 + * accesskit_atspi_common bumped from 0.1.1 to 0.1.2 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.1.2 to 0.2.0 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.4.0 to 0.4.1 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.4.1 to 0.4.2 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.5.0 to 0.6.0 + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.7.0 to 0.8.0 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.16.2 to 0.16.3 + * accesskit_atspi_common bumped from 0.9.2 to 0.9.3 + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.17.0 to 0.17.1 + * accesskit_atspi_common bumped from 0.10.0 to 0.10.1 + +## [0.24.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.23.0...accesskit_unix-v0.24.0) (2026-09-25) + + +### Features + +* Support the Image interface on Unix ([#791](https://github.com/AccessKit/accesskit/issues/791)) ([93a6984](https://github.com/AccessKit/accesskit/commit/93a6984b8c3578dce1e319767b82c7652612d521)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.25.0 to 0.25.1 + * accesskit_atspi_common bumped from 0.20.0 to 0.21.0 + +## [0.23.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.22.1...accesskit_unix-v0.23.0) (2026-08-29) + + +### Features + +* Add html_id node property ([#776](https://github.com/AccessKit/accesskit/issues/776)) ([5332ac0](https://github.com/AccessKit/accesskit/commit/5332ac0927b7fb78eff6b8b68c21ef4c801aae7c)) +* Support the Document interface on Unix ([#780](https://github.com/AccessKit/accesskit/issues/780)) ([58412b8](https://github.com/AccessKit/accesskit/commit/58412b89d64a00b2f79563c2ae8dc6b9e06cb68e)) +* Support the EditableText interface on Unix ([#779](https://github.com/AccessKit/accesskit/issues/779)) ([df0a19b](https://github.com/AccessKit/accesskit/commit/df0a19bab9125026ce2c2f6ac0f464909626563d)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.24.1 to 0.25.0 + * accesskit_atspi_common bumped from 0.19.1 to 0.20.0 + +## [0.22.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.22.0...accesskit_unix-v0.22.1) (2026-07-14) + + +### Bug Fixes + +* Don't panic on recoverable D-Bus errors on Unix ([#741](https://github.com/AccessKit/accesskit/issues/741)) ([a7d6545](https://github.com/AccessKit/accesskit/commit/a7d65458bd8d81ded693c7b8eae451e1e16aaa01)) +* Set the desktop as the app's parent on Unix ([#740](https://github.com/AccessKit/accesskit/issues/740)) ([e1f63ac](https://github.com/AccessKit/accesskit/commit/e1f63acbb2c36e3cae741871300aeb121c9e6274)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.19.0 to 0.19.1 + +## [0.22.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.21.1...accesskit_unix-v0.22.0) (2026-06-12) + + +### Features + +* Implement the cache object on Unix ([#719](https://github.com/AccessKit/accesskit/issues/719)) ([61bdabd](https://github.com/AccessKit/accesskit/commit/61bdabdb70016068c21e31bf42b36bc4209232cd)) + + +### Bug Fixes + +* Watch the IsEnabled property to lazily activate on Unix ([#715](https://github.com/AccessKit/accesskit/issues/715)) ([e7299a7](https://github.com/AccessKit/accesskit/commit/e7299a753d78e8b00dd75e1d2182abb517648f98)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.24.0 to 0.24.1 + * accesskit_atspi_common bumped from 0.18.1 to 0.19.0 + +## [0.21.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.21.0...accesskit_unix-v0.21.1) (2026-05-11) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.18.0 to 0.18.1 + +## [0.21.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.20.0...accesskit_unix-v0.21.0) (2026-03-04) + + +### Features + +* Implement text attributes for AT-SPI ([#695](https://github.com/AccessKit/accesskit/issues/695)) ([33fc4f0](https://github.com/AccessKit/accesskit/commit/33fc4f06699da335f9b2de3ab381cc940314e9c1)) + + +### Bug Fixes + +* Correctly register interfaces on Unix after Hyperlink was added ([#694](https://github.com/AccessKit/accesskit/issues/694)) ([b5979a4](https://github.com/AccessKit/accesskit/commit/b5979a4c88f5d9084f70525cf24f3a37c0988f57)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.17.0 to 0.18.0 + +## [0.20.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.19.1...accesskit_unix-v0.20.0) (2026-02-01) + + +### ⚠ BREAKING CHANGES + +* Update minimum supported Rust version to 1.85 ([#683](https://github.com/AccessKit/accesskit/issues/683)) + +### Bug Fixes + +* Update minimum supported Rust version to 1.85 ([#683](https://github.com/AccessKit/accesskit/issues/683)) ([f8c22cb](https://github.com/AccessKit/accesskit/commit/f8c22cbb53ce6b1859adeff3c0b7154ad9e1f25e)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.23.0 to 0.24.0 + * accesskit_atspi_common bumped from 0.16.1 to 0.17.0 + +## [0.19.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.19.0...accesskit_unix-v0.19.1) (2026-01-18) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.16.0 to 0.16.1 + +## [0.19.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.18.0...accesskit_unix-v0.19.0) (2026-01-15) + + +### ⚠ BREAKING CHANGES + +* Multiple tree support ([#655](https://github.com/AccessKit/accesskit/issues/655)) + +### Features + +* Implement the `url` property ([#669](https://github.com/AccessKit/accesskit/issues/669)) ([6202e63](https://github.com/AccessKit/accesskit/commit/6202e63c667e73216a306b3dd45ff25c364a4f45)) +* Multiple tree support ([#655](https://github.com/AccessKit/accesskit/issues/655)) ([c16eb80](https://github.com/AccessKit/accesskit/commit/c16eb80c49d14f39e8a7defb1694bf22b8ceed6f)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.22.0 to 0.23.0 + * accesskit_atspi_common bumped from 0.15.0 to 0.16.0 + +## [0.18.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.17.2...accesskit_unix-v0.18.0) (2025-12-23) + + +### ⚠ BREAKING CHANGES + +* Drop deprecated roles ([#642](https://github.com/AccessKit/accesskit/issues/642)) +* Infrastructure for supporting text formatting changes ([#626](https://github.com/AccessKit/accesskit/issues/626)) + +### Features + +* Add GridCell role ([#643](https://github.com/AccessKit/accesskit/issues/643)) ([1e5abca](https://github.com/AccessKit/accesskit/commit/1e5abca737d1ee942c0804fec2c06d3cb08faa94)) +* Implement BrailleLabel and BrailleRoleDescription roles ([#638](https://github.com/AccessKit/accesskit/issues/638)) ([0fdcebb](https://github.com/AccessKit/accesskit/commit/0fdcebb55e308e039ec99fbc31e94e8087a69f2d)) + + +### Bug Fixes + +* Activate when an assistive technology is actually running on Unix ([#628](https://github.com/AccessKit/accesskit/issues/628)) ([ec67fdf](https://github.com/AccessKit/accesskit/commit/ec67fdf1ec549913437a601fcd1fa142ec52c47d)) +* Bump atspi crates ([dbf81c9](https://github.com/AccessKit/accesskit/commit/dbf81c968739187a9f4c3c105d0618c0f9db3135)) + + +### Code Refactoring + +* Drop deprecated roles ([#642](https://github.com/AccessKit/accesskit/issues/642)) ([4d46c27](https://github.com/AccessKit/accesskit/commit/4d46c2740631c5fe4f057707b949d12b26931d0b)) +* Infrastructure for supporting text formatting changes ([#626](https://github.com/AccessKit/accesskit/issues/626)) ([ea23ec4](https://github.com/AccessKit/accesskit/commit/ea23ec424c7dbb8841e03d71b6a15b74264850a9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.21.1 to 0.22.0 + * accesskit_atspi_common bumped from 0.14.2 to 0.15.0 + +## [0.17.2](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.17.1...accesskit_unix-v0.17.2) (2025-10-20) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.14.1 to 0.14.2 + +## [0.17.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.17.0...accesskit_unix-v0.17.1) (2025-10-02) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.21.0 to 0.21.1 + * accesskit_atspi_common bumped from 0.14.0 to 0.14.1 + +## [0.17.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.16.0...accesskit_unix-v0.17.0) (2025-07-16) + + +### ⚠ BREAKING CHANGES + +* Implement refactored `ScrollIntoView` action across desktop platforms ([#594](https://github.com/AccessKit/accesskit/issues/594)) + +### Features + +* Implement refactored `ScrollIntoView` action across desktop platforms ([#594](https://github.com/AccessKit/accesskit/issues/594)) ([1d9b74c](https://github.com/AccessKit/accesskit/commit/1d9b74c057051509b3ffbf63afeacfb16f544ff8)) +* Let parents declare actions supported on their children ([#593](https://github.com/AccessKit/accesskit/issues/593)) ([70b534b](https://github.com/AccessKit/accesskit/commit/70b534bed168a84b84cc35199588aa8ab784fb43)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.20.0 to 0.21.0 + * accesskit_atspi_common bumped from 0.13.0 to 0.14.0 + +## [0.16.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.15.0...accesskit_unix-v0.16.0) (2025-06-26) + + +### ⚠ BREAKING CHANGES + +* Force a semver-breaking release ([#589](https://github.com/AccessKit/accesskit/issues/589)) + +### Bug Fixes + +* Force a semver-breaking release ([#589](https://github.com/AccessKit/accesskit/issues/589)) ([2887cdd](https://github.com/AccessKit/accesskit/commit/2887cddde817ba3851688068d8d10de5cef7c624)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.19.0 to 0.20.0 + * accesskit_atspi_common bumped from 0.12.0 to 0.13.0 + +## [0.15.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.14.0...accesskit_unix-v0.15.0) (2025-05-06) + + +### ⚠ BREAKING CHANGES + +* Drop redundant `HasPopup::True` ([#550](https://github.com/AccessKit/accesskit/issues/550)) + +### Bug Fixes + +* Expose tabs in the platform adapters ([341a11b](https://github.com/AccessKit/accesskit/commit/341a11bca2c8a29682c11ddcfe91fa58776ea11d)) +* Mention caveats with window bounds under Wayland ([#559](https://github.com/AccessKit/accesskit/issues/559)) ([b0cf01a](https://github.com/AccessKit/accesskit/commit/b0cf01a26ded03d722818a193fa6902f69bbc102)) + + +### Code Refactoring + +* Drop redundant `HasPopup::True` ([#550](https://github.com/AccessKit/accesskit/issues/550)) ([56abf17](https://github.com/AccessKit/accesskit/commit/56abf17356e4c7f13f64aaeaca6a63c8f7ede553)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.18.0 to 0.19.0 + * accesskit_atspi_common bumped from 0.11.0 to 0.12.0 + +## [0.14.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.13.1...accesskit_unix-v0.14.0) (2025-03-06) + + +### ⚠ BREAKING CHANGES + +* Drop `Tree::app_name` ([#492](https://github.com/AccessKit/accesskit/issues/492)) + +### Bug Fixes + +* Add list box support to the platform adapters ([6c622cf](https://github.com/AccessKit/accesskit/commit/6c622cff4c0c989d9a5f16b775adff9cdacf534c)) +* Derive `Debug` for adapters ([#513](https://github.com/AccessKit/accesskit/issues/513)) ([753d904](https://github.com/AccessKit/accesskit/commit/753d90473cf57682568c7a17c82474c8e5d00b25)) +* Don't enable the `zbus/tokio` feature on Unix ([#521](https://github.com/AccessKit/accesskit/issues/521)) ([9dfad97](https://github.com/AccessKit/accesskit/commit/9dfad9796f71563a78bc6a7d5f01b111d80e63e6)) +* Fix some clippy warnings ([#509](https://github.com/AccessKit/accesskit/issues/509)) ([579b9c1](https://github.com/AccessKit/accesskit/commit/579b9c12dd8abc44ecab41fa3c326a1d8999871d)) +* Update zbus to 5.0 ([#519](https://github.com/AccessKit/accesskit/issues/519)) ([4c10b80](https://github.com/AccessKit/accesskit/commit/4c10b801b6924c7010b83f4eb44c8c350c860cf6)) + + +### Code Refactoring + +* Drop `Tree::app_name` ([#492](https://github.com/AccessKit/accesskit/issues/492)) ([089794c](https://github.com/AccessKit/accesskit/commit/089794c8f74957e91a19ae3df508e2a892f39ebc)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.17.1 to 0.18.0 + * accesskit_atspi_common bumped from 0.10.1 to 0.11.0 + +## [0.13.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.12.3...accesskit_unix-v0.13.0) (2024-10-31) + + +### ⚠ BREAKING CHANGES + +* Make the core crate no-std ([#468](https://github.com/AccessKit/accesskit/issues/468)) + +### Features + +* Make the core crate no-std ([#468](https://github.com/AccessKit/accesskit/issues/468)) ([2fa0d3f](https://github.com/AccessKit/accesskit/commit/2fa0d3f5b2b7ac11ef1751c133706f29e548bd6d)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.16.3 to 0.17.0 + * accesskit_atspi_common bumped from 0.9.3 to 0.10.0 + +## [0.12.2](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.12.1...accesskit_unix-v0.12.2) (2024-10-07) + + +### Bug Fixes + +* Update minimum supported Rust version to 1.75 ([#457](https://github.com/AccessKit/accesskit/issues/457)) ([fc622fe](https://github.com/AccessKit/accesskit/commit/fc622fe7657c80a4eedad6f6cded11d2538b54d5)) +* Update to zbus v4 ([#456](https://github.com/AccessKit/accesskit/issues/456)) ([95db8f1](https://github.com/AccessKit/accesskit/commit/95db8f1c5f7f56598eab6910a990ccbf9d864dda)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.16.1 to 0.16.2 + * accesskit_atspi_common bumped from 0.9.1 to 0.9.2 + +## [0.12.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.12.0...accesskit_unix-v0.12.1) (2024-09-24) + + +### Bug Fixes + +* Return a null object for AT-SPI application's parent ([#454](https://github.com/AccessKit/accesskit/issues/454)) ([8a84abf](https://github.com/AccessKit/accesskit/commit/8a84abf81eaf22dd3672813ca36f1a422b5c0f1e)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.16.0 to 0.16.1 + * accesskit_atspi_common bumped from 0.9.0 to 0.9.1 + +## [0.12.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.11.1...accesskit_unix-v0.12.0) (2024-06-29) + + +### ⚠ BREAKING CHANGES + +* Rename the `StaticText` role to `Label` ([#434](https://github.com/AccessKit/accesskit/issues/434)) + +### Code Refactoring + +* Rename the `StaticText` role to `Label` ([#434](https://github.com/AccessKit/accesskit/issues/434)) ([7086bc0](https://github.com/AccessKit/accesskit/commit/7086bc0fad446d3ed4a0fd5eff641a1e75f6c599)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.15.0 to 0.16.0 + * accesskit_atspi_common bumped from 0.8.0 to 0.9.0 + +## [0.11.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.10.1...accesskit_unix-v0.11.0) (2024-06-09) + + +### Features + +* Add `author_id` property ([#424](https://github.com/AccessKit/accesskit/issues/424)) ([0d1c56f](https://github.com/AccessKit/accesskit/commit/0d1c56f0bdde58715e1c69f6015df600cb7cb8c1)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.14.0 to 0.15.0 + * accesskit_atspi_common bumped from 0.6.0 to 0.7.0 + +## [0.10.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.9.2...accesskit_unix-v0.10.0) (2024-05-26) + + +### Features + +* Add basic text support on Unix ([#362](https://github.com/AccessKit/accesskit/issues/362)) ([52540f8](https://github.com/AccessKit/accesskit/commit/52540f82cf9fc148358351ed486bab3e7e91f1d6)) +* Expose the `placeholder` property ([#417](https://github.com/AccessKit/accesskit/issues/417)) ([8f4a0a1](https://github.com/AccessKit/accesskit/commit/8f4a0a1c10f83fcc8580a37d8013fec2d110865b)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_atspi_common bumped from 0.4.2 to 0.5.0 + +## [0.9.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.8.0...accesskit_unix-v0.9.0) (2024-04-30) + + +### ⚠ BREAKING CHANGES + +* Force a semver-breaking release ([#398](https://github.com/AccessKit/accesskit/issues/398)) + +### Bug Fixes + +* Force a semver-breaking release ([#398](https://github.com/AccessKit/accesskit/issues/398)) ([87b8b92](https://github.com/AccessKit/accesskit/commit/87b8b92b74a102c7cae48e013d2c2ae1cc2f9598)) +* Increase minimum supported Rust version to `1.70` ([#396](https://github.com/AccessKit/accesskit/issues/396)) ([a8398b8](https://github.com/AccessKit/accesskit/commit/a8398b847aa003de91042ac45e33126fc2cae053)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.13.0 to 0.14.0 + * accesskit_atspi_common bumped from 0.3.0 to 0.4.0 + +## [0.8.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.7.5...accesskit_unix-v0.8.0) (2024-04-14) + + +### ⚠ BREAKING CHANGES + +* New approach to lazy initialization ([#375](https://github.com/AccessKit/accesskit/issues/375)) + +### Code Refactoring + +* New approach to lazy initialization ([#375](https://github.com/AccessKit/accesskit/issues/375)) ([9baebdc](https://github.com/AccessKit/accesskit/commit/9baebdceed7300389b6768815d7ae48f1ce401e4)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.12.3 to 0.13.0 + * accesskit_atspi_common bumped from 0.2.0 to 0.3.0 + +## [0.7.2](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.7.1...accesskit_unix-v0.7.2) (2024-02-24) + + +### Bug Fixes + +* Don't emit focus event twice on Unix ([#354](https://github.com/AccessKit/accesskit/issues/354)) ([b39216c](https://github.com/AccessKit/accesskit/commit/b39216cb31df692fef377f9b3c3c718fd225cc3c)) +* Use the new accesskit_atspi_common crate in the Unix adapter ([#356](https://github.com/AccessKit/accesskit/issues/356)) ([b2a468c](https://github.com/AccessKit/accesskit/commit/b2a468ccb91ee4e6d3435e73eb00c65cbe75060a)) + +## [0.7.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.7.0...accesskit_unix-v0.7.1) (2024-01-11) + + +### Bug Fixes + +* Make full use of tokio ecosystem if the tokio feature is enabled on Unix ([#336](https://github.com/AccessKit/accesskit/issues/336)) ([c034802](https://github.com/AccessKit/accesskit/commit/c0348024665a615a30fd8fe2f02e8c93cf9c6332)) +* Run our own async executor on Unix ([#337](https://github.com/AccessKit/accesskit/issues/337)) ([8f937ba](https://github.com/AccessKit/accesskit/commit/8f937baaa510dd96da196501822b82f75f05b595)) + +## [0.7.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.6.2...accesskit_unix-v0.7.0) (2024-01-03) + + +### ⚠ BREAKING CHANGES + +* Lazily activate Unix adapters ([#324](https://github.com/AccessKit/accesskit/issues/324)) + +### Features + +* Support custom role descriptions ([#316](https://github.com/AccessKit/accesskit/issues/316)) ([c8d1a56](https://github.com/AccessKit/accesskit/commit/c8d1a5638fa6c33adfa059815c04f7e043c56026)) + + +### Bug Fixes + +* Lazily activate Unix adapters ([#324](https://github.com/AccessKit/accesskit/issues/324)) ([54ed036](https://github.com/AccessKit/accesskit/commit/54ed036c99d87428a8eb5bb03fd77e9e31562d4c)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.12.1 to 0.12.2 + * accesskit_consumer bumped from 0.16.1 to 0.17.0 + +## [0.6.2](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.6.1...accesskit_unix-v0.6.2) (2023-12-14) + + +### Bug Fixes + +* Bump async-channel dependency to `2.1.1` ([#321](https://github.com/AccessKit/accesskit/issues/321)) ([99120b8](https://github.com/AccessKit/accesskit/commit/99120b828d65306ab71d41f71979dc67e8b0bf6b)) + +## [0.6.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.5.2...accesskit_unix-v0.6.0) (2023-09-27) + + +### ⚠ BREAKING CHANGES + +* Allow providing app_name, toolkit_name and toolkit_version in Tree, remove parameters from unix adapter constructor ([#291](https://github.com/AccessKit/accesskit/issues/291)) +* Make `ActionHandler::do_action` take `&mut self` ([#296](https://github.com/AccessKit/accesskit/issues/296)) +* Clean up roles and properties ([#289](https://github.com/AccessKit/accesskit/issues/289)) +* Decouple in-tree focus from host window/view focus ([#278](https://github.com/AccessKit/accesskit/issues/278)) +* Switch to simple unsigned 64-bit integer for node IDs ([#276](https://github.com/AccessKit/accesskit/issues/276)) + +### Features + +* Add role for terminals ([#282](https://github.com/AccessKit/accesskit/issues/282)) ([ddbef37](https://github.com/AccessKit/accesskit/commit/ddbef37158b57f56217317b480e40d58f83a9c24)) +* Allow providing app_name, toolkit_name and toolkit_version in Tree, remove parameters from unix adapter constructor ([#291](https://github.com/AccessKit/accesskit/issues/291)) ([5313860](https://github.com/AccessKit/accesskit/commit/531386023257150f49b5e4be942f359855fb7cb6)) +* Support live regions on Unix ([#299](https://github.com/AccessKit/accesskit/issues/299)) ([8d52a5f](https://github.com/AccessKit/accesskit/commit/8d52a5fc4271a3b5edcc602b23fd7b920446eab0)) +* Support multiple top-level windows on Unix ([#292](https://github.com/AccessKit/accesskit/issues/292)) ([43ecf4b](https://github.com/AccessKit/accesskit/commit/43ecf4b3ab96d9e8f7d2c2222c7e664c4f4f4abf)) + + +### Bug Fixes + +* Don't require tokio rt-multi-thread feature ([#290](https://github.com/AccessKit/accesskit/issues/290)) ([cf61e47](https://github.com/AccessKit/accesskit/commit/cf61e477adff26b032fa0b24502c0ae0a96c1987)) +* Use common filters across platform adapters ([#287](https://github.com/AccessKit/accesskit/issues/287)) ([09c1204](https://github.com/AccessKit/accesskit/commit/09c12045ff4ccdb22f0cf643077a27465013572d)) + + +### Code Refactoring + +* Clean up roles and properties ([#289](https://github.com/AccessKit/accesskit/issues/289)) ([4fc9c55](https://github.com/AccessKit/accesskit/commit/4fc9c55c91812472593923d93ff89d75ff305ee4)) +* Decouple in-tree focus from host window/view focus ([#278](https://github.com/AccessKit/accesskit/issues/278)) ([d360d20](https://github.com/AccessKit/accesskit/commit/d360d20cf951e7643b81a5303006c9f7daa5bd56)) +* Make `ActionHandler::do_action` take `&mut self` ([#296](https://github.com/AccessKit/accesskit/issues/296)) ([4fc7846](https://github.com/AccessKit/accesskit/commit/4fc7846d732d61fb45c023060ebab96801a0053e)) +* Switch to simple unsigned 64-bit integer for node IDs ([#276](https://github.com/AccessKit/accesskit/issues/276)) ([3eadd48](https://github.com/AccessKit/accesskit/commit/3eadd48ec47854faa94a94ebf910ec08f514642f)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.11.2 to 0.12.0 + * accesskit_consumer bumped from 0.15.2 to 0.16.0 + +## [0.5.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.4.0...accesskit_unix-v0.5.0) (2023-05-21) + + +### Features + +* Add features for async runtimes on Unix ([#248](https://github.com/AccessKit/accesskit/issues/248)) ([b56b4ea](https://github.com/AccessKit/accesskit/commit/b56b4ea7c967ee5a1dae21a2fa0dcd385346031e)) + +## [0.4.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.3.3...accesskit_unix-v0.4.0) (2023-03-30) + + +### ⚠ BREAKING CHANGES + +* Force a semver-breaking version bump in downstream crates ([#234](https://github.com/AccessKit/accesskit/issues/234)) + +### Bug Fixes + +* Force a semver-breaking version bump in downstream crates ([#234](https://github.com/AccessKit/accesskit/issues/234)) ([773389b](https://github.com/AccessKit/accesskit/commit/773389bff857fa18edf15de426e029251fc34591)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_consumer bumped from 0.14.2 to 0.15.0 + +## [0.3.1](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.3.0...accesskit_unix-v0.3.1) (2023-02-20) + + +### Bug Fixes + +* Update atspi dependency ([#217](https://github.com/AccessKit/accesskit/issues/217)) ([93f2dc9](https://github.com/AccessKit/accesskit/commit/93f2dc9bf0a57a8b7592c3a4cf4aa3885a3356f2)) + +## [0.3.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.2.0...accesskit_unix-v0.3.0) (2023-02-12) + + +### ⚠ BREAKING CHANGES + +* Move thread synchronization into platform adapters; drop parking_lot ([#212](https://github.com/AccessKit/accesskit/issues/212)) + +### Code Refactoring + +* Move thread synchronization into platform adapters; drop parking_lot ([#212](https://github.com/AccessKit/accesskit/issues/212)) ([5df52e5](https://github.com/AccessKit/accesskit/commit/5df52e5545faddf6a51905409013c2f5be23981e)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.9.0 to 0.10.0 + * accesskit_consumer bumped from 0.13.0 to 0.14.0 + +## [0.2.0](https://github.com/AccessKit/accesskit/compare/accesskit_unix-v0.1.1...accesskit_unix-v0.2.0) (2023-02-05) + + +### ⚠ BREAKING CHANGES + +* Make `Node` opaque and optimize it for size ([#205](https://github.com/AccessKit/accesskit/issues/205)) + +### Code Refactoring + +* Make `Node` opaque and optimize it for size ([#205](https://github.com/AccessKit/accesskit/issues/205)) ([4811152](https://github.com/AccessKit/accesskit/commit/48111521439b76c1a8687418a4b20f9b705eac6d)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit bumped from 0.8.1 to 0.9.0 + * accesskit_consumer bumped from 0.12.1 to 0.13.0 + +## 0.1.0 (2023-01-05) + + +### Features + +* Basic Unix platform adapter ([#198](https://github.com/AccessKit/accesskit/issues/198)) ([1cea32e](https://github.com/AccessKit/accesskit/commit/1cea32e44ee743b778ac941ceff9087ae745cb37)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * accesskit_consumer bumped from 0.11.0 to 0.12.0 diff --git a/vendor/accesskit_unix/Cargo.toml b/vendor/accesskit_unix/Cargo.toml new file mode 100644 index 0000000000..eec543ad21 --- /dev/null +++ b/vendor/accesskit_unix/Cargo.toml @@ -0,0 +1,103 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.87" +name = "accesskit_unix" +version = "0.24.0" +authors = ["The AccessKit contributors"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "AccessKit UI accessibility infrastructure: Linux adapter" +readme = "README.md" +keywords = [ + "gui", + "ui", + "accessibility", +] +categories = ["gui"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/AccessKit/accesskit" + +[features] +async-io = [ + "dep:async-channel", + "dep:async-executor", + "dep:async-task", + "dep:futures-util", +] +default = ["async-io"] +tokio = [ + "dep:tokio", + "dep:tokio-stream", +] + +[lib] +name = "accesskit_unix" +path = "src/lib.rs" + +[dependencies.accesskit] +version = "0.25.1" + +[dependencies.accesskit_atspi_common] +version = "0.21.0" + +[dependencies.async-channel] +version = "2.1.1" +optional = true + +[dependencies.async-executor] +version = "1.5.0" +optional = true + +[dependencies.async-task] +version = "4.3.0" +optional = true + +[dependencies.atspi] +version = "0.29" +features = ["proxies"] +default-features = false + +[dependencies.futures-lite] +version = "2.3" + +[dependencies.futures-util] +version = "0.3.27" +optional = true + +[dependencies.serde] +version = "1.0" + +[dependencies.tokio] +version = "1.32.0" +features = [ + "macros", + "net", + "rt", + "sync", + "time", +] +optional = true + +[dependencies.tokio-stream] +version = "0.1.14" +optional = true + +[dependencies.zbus] +version = "5.19" +features = ["async-io"] +default-features = false diff --git a/vendor/accesskit_unix/LICENSE-APACHE b/vendor/accesskit_unix/LICENSE-APACHE new file mode 100644 index 0000000000..1b5ec8b78e --- /dev/null +++ b/vendor/accesskit_unix/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/vendor/accesskit_unix/LICENSE-MIT b/vendor/accesskit_unix/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/vendor/accesskit_unix/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/accesskit_unix/README.md b/vendor/accesskit_unix/README.md new file mode 100644 index 0000000000..5663fc43da --- /dev/null +++ b/vendor/accesskit_unix/README.md @@ -0,0 +1,10 @@ +# AccessKit Unix adapter + +This is the Unix adapter for [AccessKit](https://accesskit.dev/). It exposes an AccessKit accessibility tree through the AT-SPI protocol. + +## Compatibility with async runtimes + +While this crate's API is purely blocking, it internally spawns asynchronous tasks on an executor. + +- If you use tokio, make sure to enable the `tokio` feature of this crate. +- If you use another async runtime or if you don't use one at all, the default feature will suit your needs. diff --git a/vendor/accesskit_unix/UNITY.md b/vendor/accesskit_unix/UNITY.md new file mode 100644 index 0000000000..b85b9f25d2 --- /dev/null +++ b/vendor/accesskit_unix/UNITY.md @@ -0,0 +1,18 @@ +# Why this copy exists + +This is `accesskit_unix` 0.24.0 from crates.io, vendored so `gpui_linux` can +carry one fix that no released AccessKit has yet. The first commit that adds +this directory is the published crate unchanged (plus the licence files the +published crate leaves out); every later change is a separate commit. + +**Accessibility inside a Flatpak sandbox.** AccessKit only connects to the +AT-SPI bus once `org.a11y.Status.IsEnabled`, read from `org.a11y.Bus` on the +session bus, turns true. Flatpak hides `org.a11y.Bus` from the sandbox and +hands the app the accessibility bus directly in `AT_SPI_BUS_ADDRESS` +(`unix:path=/run/flatpak/at-spi-bus`) instead, so the property is never read, +the adapter never activates, and screen readers never see the app. The patch +in `src/context.rs` treats accessibility as enabled when that bus is provided +but the toggle can't be read, as GTK and Qt do. + +Drop this directory, and point the workspace's `accesskit_unix` back at +crates.io, once an AccessKit release handles that case itself. diff --git a/vendor/accesskit_unix/src/adapter.rs b/vendor/accesskit_unix/src/adapter.rs new file mode 100644 index 0000000000..fee7101dbd --- /dev/null +++ b/vendor/accesskit_unix/src/adapter.rs @@ -0,0 +1,276 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit::{ActionHandler, ActivationHandler, DeactivationHandler, Rect, TreeUpdate}; +use accesskit_atspi_common::{ + ActionHandlerNoMut, ActionHandlerWrapper, Adapter as AdapterImpl, AdapterCallback, CacheEvent, + Event, FullNodeId, PlatformNode, WindowBounds, next_adapter_id, +}; +#[cfg(not(feature = "tokio"))] +use async_channel::Sender; +use atspi::InterfaceSet; +use std::fmt::{Debug, Formatter}; +use std::sync::{Arc, Mutex}; +#[cfg(feature = "tokio")] +use tokio::sync::mpsc::UnboundedSender as Sender; + +use crate::context::{get_or_init_app_context, get_or_init_messages}; + +pub(crate) struct Callback { + messages: Sender, +} + +impl Callback { + pub(crate) fn new() -> Self { + let messages = get_or_init_messages(); + Self { messages } + } + + fn send_message(&self, message: Message) { + #[cfg(not(feature = "tokio"))] + let _ = self.messages.try_send(message); + #[cfg(feature = "tokio")] + let _ = self.messages.send(message); + } +} + +impl AdapterCallback for Callback { + fn register_interfaces(&self, adapter: &AdapterImpl, id: FullNodeId, interfaces: InterfaceSet) { + let node = adapter.platform_node(id); + self.send_message(Message::RegisterInterfaces { node, interfaces }); + } + + fn unregister_interfaces( + &self, + adapter: &AdapterImpl, + id: FullNodeId, + interfaces: InterfaceSet, + ) { + self.send_message(Message::UnregisterInterfaces { + adapter_id: adapter.id(), + node_id: id, + interfaces, + }) + } + + fn emit_event(&self, adapter: &AdapterImpl, event: Event) { + match event { + Event::Cache(CacheEvent::Added(id)) => { + let node = adapter.platform_node(id); + self.send_message(Message::EmitCacheAdd { node }); + } + Event::Cache(CacheEvent::Removed(id)) => { + self.send_message(Message::EmitCacheRemove { + adapter_id: adapter.id(), + node_id: id, + }); + } + event => { + self.send_message(Message::EmitEvent { + adapter_id: adapter.id(), + event, + }); + } + } + } +} + +pub(crate) enum AdapterState { + Inactive { + is_window_focused: bool, + root_window_bounds: WindowBounds, + action_handler: Arc, + }, + Pending { + is_window_focused: bool, + root_window_bounds: WindowBounds, + action_handler: Arc, + }, + Active(AdapterImpl), +} + +impl Debug for AdapterState { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AdapterState::Inactive { + is_window_focused, + root_window_bounds, + action_handler: _, + } => f + .debug_struct("Inactive") + .field("is_window_focused", is_window_focused) + .field("root_window_bounds", root_window_bounds) + .field("action_handler", &"ActionHandler") + .finish(), + AdapterState::Pending { + is_window_focused, + root_window_bounds, + action_handler: _, + } => f + .debug_struct("Pending") + .field("is_window_focused", is_window_focused) + .field("root_window_bounds", root_window_bounds) + .field("action_handler", &"ActionHandler") + .finish(), + AdapterState::Active(r#impl) => f.debug_tuple("Active").field(r#impl).finish(), + } + } +} + +#[derive(Debug)] +pub struct Adapter { + messages: Sender, + id: usize, + state: Arc>, +} + +impl Adapter { + /// Create a new Unix adapter. + /// + /// All of the handlers will always be called from another thread. + pub fn new( + activation_handler: impl 'static + ActivationHandler + Send, + action_handler: impl 'static + ActionHandler + Send, + deactivation_handler: impl 'static + DeactivationHandler + Send, + ) -> Self { + let id = next_adapter_id(); + let messages = get_or_init_messages(); + let state = Arc::new(Mutex::new(AdapterState::Inactive { + is_window_focused: false, + root_window_bounds: Default::default(), + action_handler: Arc::new(ActionHandlerWrapper::new(action_handler)), + })); + let adapter = Self { + id, + messages, + state: Arc::clone(&state), + }; + adapter.send_message(Message::AddAdapter { + id, + activation_handler: Box::new(activation_handler), + deactivation_handler: Box::new(deactivation_handler), + state, + }); + adapter + } + + pub(crate) fn send_message(&self, message: Message) { + #[cfg(not(feature = "tokio"))] + let _ = self.messages.try_send(message); + #[cfg(feature = "tokio")] + let _ = self.messages.send(message); + } + + /// Set the bounds of the top-level window. The outer bounds contain any + /// window decoration and borders. + /// + /// # Caveats + /// + /// Since an application can not get the position of its window under + /// Wayland, calling this method only makes sense under X11. + pub fn set_root_window_bounds(&mut self, outer: Rect, inner: Rect) { + let new_bounds = WindowBounds::new(outer, inner); + let mut state = self.state.lock().unwrap(); + match &mut *state { + AdapterState::Inactive { + root_window_bounds, .. + } => { + *root_window_bounds = new_bounds; + } + AdapterState::Pending { + root_window_bounds, .. + } => { + *root_window_bounds = new_bounds; + } + AdapterState::Active(r#impl) => r#impl.set_root_window_bounds(new_bounds), + } + } + + /// If and only if the tree has been initialized, call the provided function + /// and apply the resulting update. Note: If the caller's implementation of + /// [`ActivationHandler::request_initial_tree`] initially returned `None`, + /// the [`TreeUpdate`] returned by the provided function must contain + /// a full tree. + pub fn update_if_active(&mut self, update_factory: impl FnOnce() -> TreeUpdate) { + let mut state = self.state.lock().unwrap(); + match &mut *state { + AdapterState::Inactive { .. } => (), + AdapterState::Pending { + is_window_focused, + root_window_bounds, + action_handler, + } => { + let initial_state = update_factory(); + let r#impl = AdapterImpl::with_wrapped_action_handler( + self.id, + get_or_init_app_context(), + Callback::new(), + initial_state, + *is_window_focused, + *root_window_bounds, + Arc::clone(action_handler), + ); + *state = AdapterState::Active(r#impl); + } + AdapterState::Active(r#impl) => r#impl.update(update_factory()), + } + } + + /// Update the tree state based on whether the window is focused. + pub fn update_window_focus_state(&mut self, is_focused: bool) { + let mut state = self.state.lock().unwrap(); + match &mut *state { + AdapterState::Inactive { + is_window_focused, .. + } => { + *is_window_focused = is_focused; + } + AdapterState::Pending { + is_window_focused, .. + } => { + *is_window_focused = is_focused; + } + AdapterState::Active(r#impl) => r#impl.update_window_focus_state(is_focused), + } + } +} + +impl Drop for Adapter { + fn drop(&mut self) { + self.send_message(Message::RemoveAdapter { id: self.id }); + } +} + +pub(crate) enum Message { + AddAdapter { + id: usize, + activation_handler: Box, + deactivation_handler: Box, + state: Arc>, + }, + RemoveAdapter { + id: usize, + }, + RegisterInterfaces { + node: PlatformNode, + interfaces: InterfaceSet, + }, + UnregisterInterfaces { + adapter_id: usize, + node_id: FullNodeId, + interfaces: InterfaceSet, + }, + EmitEvent { + adapter_id: usize, + event: Event, + }, + EmitCacheAdd { + node: PlatformNode, + }, + EmitCacheRemove { + adapter_id: usize, + node_id: FullNodeId, + }, +} diff --git a/vendor/accesskit_unix/src/atspi/bus.rs b/vendor/accesskit_unix/src/atspi/bus.rs new file mode 100644 index 0000000000..3368185344 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/bus.rs @@ -0,0 +1,525 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use crate::{ + atspi::{ObjectId, cache_path, interfaces::*}, + context::get_or_init_app_context, + executor::{Executor, Task}, +}; +use accesskit_atspi_common::{ + DocumentEvent, FullNodeId, NodeIdOrRoot, ObjectEvent, PlatformNode, PlatformRoot, Property, + WindowEvent, +}; +use atspi::{ + Interface, InterfaceSet, ObjectRefOwned, + events::EventBodyBorrowed, + proxy::{bus::BusProxy, socket::SocketProxy}, +}; +use std::{ + env::var, + sync::{Arc, OnceLock}, +}; +use zbus::{ + Address, Connection, Result, + connection::Builder, + names::{BusName, InterfaceName, MemberName, OwnedUniqueName}, + zvariant::{Str, Value}, +}; + +pub(crate) struct Bus { + conn: Connection, + _task: Task<()>, + socket_proxy: SocketProxy<'static>, + desktop: Arc>, +} + +impl Bus { + pub(crate) async fn new( + session_bus: &Connection, + executor: &Executor<'_>, + ) -> zbus::Result { + let address = match var("AT_SPI_BUS_ADDRESS") { + Ok(address) if !address.is_empty() => address, + _ => BusProxy::new(session_bus).await?.get_address().await?, + }; + let address: Address = address.as_str().try_into()?; + let conn = Builder::address(address)? + .internal_executor(false) + .build() + .await?; + let conn_copy = conn.clone(); + let _task = executor.spawn( + async move { + loop { + conn_copy.executor().tick().await; + } + }, + "accesskit_atspi_bus_task", + ); + let socket_proxy = SocketProxy::new(&conn).await?; + let mut bus = Bus { + conn, + _task, + socket_proxy, + desktop: Arc::new(OnceLock::new()), + }; + bus.register_root_node().await?; + Ok(bus) + } + + fn unique_name(&self) -> &OwnedUniqueName { + self.conn.unique_name().unwrap() + } + + async fn register_root_node(&mut self) -> Result<()> { + let node = PlatformRoot::new(get_or_init_app_context()); + let path = ObjectId::Root.path(); + + if self + .conn + .object_server() + .at(path.clone(), ApplicationInterface(node.clone())) + .await? + { + let desktop = self + .socket_proxy + .embed(&(self.unique_name().as_str(), ObjectId::Root.path().into())) + .await?; + let _ = self.desktop.set(desktop); + + self.conn + .object_server() + .at( + path, + RootAccessibleInterface::new( + self.unique_name().to_owned(), + node.clone(), + Arc::clone(&self.desktop), + ), + ) + .await?; + + self.conn + .object_server() + .at( + cache_path(), + CacheInterface::new( + self.unique_name().to_owned(), + node, + Arc::clone(&self.desktop), + ), + ) + .await?; + } + + Ok(()) + } + + pub(crate) async fn register_interfaces( + &self, + node: PlatformNode, + new_interfaces: InterfaceSet, + ) -> zbus::Result<()> { + let path = ObjectId::from(&node).path(); + let bus_name = self.unique_name().to_owned(); + if new_interfaces.contains(Interface::Accessible) { + self.register_interface( + &path, + NodeAccessibleInterface::new(bus_name.clone(), node.clone()), + ) + .await?; + } + if new_interfaces.contains(Interface::Action) { + self.register_interface(&path, ActionInterface::new(node.clone())) + .await?; + } + if new_interfaces.contains(Interface::Component) { + self.register_interface( + &path, + ComponentInterface::new(bus_name.clone(), node.clone()), + ) + .await?; + } + if new_interfaces.contains(Interface::Document) { + self.register_interface(&path, DocumentInterface::new(node.clone())) + .await?; + } + if new_interfaces.contains(Interface::EditableText) { + self.register_interface(&path, EditableTextInterface::new(node.clone())) + .await?; + } + if new_interfaces.contains(Interface::Hyperlink) { + self.register_interface( + &path, + HyperlinkInterface::new(bus_name.clone(), node.clone()), + ) + .await?; + } + if new_interfaces.contains(Interface::Image) { + self.register_interface(&path, ImageInterface::new(node.clone())) + .await?; + } + if new_interfaces.contains(Interface::Selection) { + self.register_interface( + &path, + SelectionInterface::new(bus_name.clone(), node.clone()), + ) + .await?; + } + if new_interfaces.contains(Interface::Text) { + self.register_interface(&path, TextInterface::new(node.clone())) + .await?; + } + if new_interfaces.contains(Interface::Value) { + self.register_interface(&path, ValueInterface::new(node.clone())) + .await?; + } + + Ok(()) + } + + async fn register_interface(&self, path: &str, interface: T) -> Result + where + T: zbus::object_server::Interface, + { + map_or_ignoring_recoverable_error( + self.conn.object_server().at(path, interface).await, + false, + |result| result, + ) + } + + pub(crate) async fn unregister_interfaces( + &self, + adapter_id: usize, + node_id: FullNodeId, + old_interfaces: InterfaceSet, + ) -> zbus::Result<()> { + let path = ObjectId::Node { + adapter: adapter_id, + node: node_id, + } + .path(); + if old_interfaces.contains(Interface::Accessible) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Action) { + self.unregister_interface::(&path).await?; + } + if old_interfaces.contains(Interface::Component) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Document) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::EditableText) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Hyperlink) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Image) { + self.unregister_interface::(&path).await?; + } + if old_interfaces.contains(Interface::Selection) { + self.unregister_interface::(&path) + .await?; + } + if old_interfaces.contains(Interface::Text) { + self.unregister_interface::(&path).await?; + } + if old_interfaces.contains(Interface::Value) { + self.unregister_interface::(&path).await?; + } + + Ok(()) + } + + async fn unregister_interface(&self, path: &str) -> Result + where + T: zbus::object_server::Interface, + { + map_or_ignoring_recoverable_error( + self.conn.object_server().remove::(path).await, + false, + |result| result, + ) + } + + pub(crate) async fn emit_object_event( + &self, + adapter_id: usize, + target: NodeIdOrRoot, + event: ObjectEvent, + ) -> Result<()> { + let target = match target { + NodeIdOrRoot::Node(node) => ObjectId::Node { + adapter: adapter_id, + node, + }, + NodeIdOrRoot::Root => ObjectId::Root, + }; + let interface = "org.a11y.atspi.Event.Object"; + let signal = match event { + ObjectEvent::ActiveDescendantChanged(_) => "ActiveDescendantChanged", + ObjectEvent::Announcement(_, _) => "Announcement", + ObjectEvent::BoundsChanged(_) => "BoundsChanged", + ObjectEvent::CaretMoved(_) => "TextCaretMoved", + ObjectEvent::ChildAdded(_, _) | ObjectEvent::ChildRemoved(_) => "ChildrenChanged", + ObjectEvent::PropertyChanged(_) => "PropertyChange", + ObjectEvent::SelectionChanged => "SelectionChanged", + ObjectEvent::StateChanged(_, _) => "StateChanged", + ObjectEvent::TextInserted { .. } | ObjectEvent::TextRemoved { .. } => "TextChanged", + ObjectEvent::TextSelectionChanged => "TextSelectionChanged", + }; + match event { + ObjectEvent::ActiveDescendantChanged(child) => { + let child = ObjectId::Node { + adapter: adapter_id, + node: child, + }; + let mut body = EventBodyBorrowed::default(); + body.any_data = child.to_address(self.unique_name().inner()).into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::Announcement(message, politeness) => { + let mut body = EventBodyBorrowed::default(); + body.detail1 = politeness as i32; + body.any_data = message.into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::BoundsChanged(bounds) => { + let mut body = EventBodyBorrowed::default(); + body.any_data = Value::from(bounds); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::CaretMoved(offset) => { + let mut body = EventBodyBorrowed::default(); + body.detail1 = offset; + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::ChildAdded(index, child) => { + let child = ObjectId::Node { + adapter: adapter_id, + node: child, + }; + let mut body = EventBodyBorrowed::default(); + body.kind = "add"; + body.detail1 = index as i32; + body.any_data = child.to_address(self.unique_name().inner()).into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::ChildRemoved(child) => { + let child = ObjectId::Node { + adapter: adapter_id, + node: child, + }; + let mut body = EventBodyBorrowed::default(); + body.kind = "remove"; + body.detail1 = -1; + body.any_data = child.to_address(self.unique_name().inner()).into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::PropertyChanged(property) => { + let mut body = EventBodyBorrowed::default(); + body.kind = match property { + Property::Name(_) => "accessible-name", + Property::Description(_) => "accessible-description", + Property::Parent(_) => "accessible-parent", + Property::Role(_) => "accessible-role", + Property::Value(_) => "accessible-value", + }; + body.any_data = match property { + Property::Name(value) => Str::from(value).into(), + Property::Description(value) => Str::from(value).into(), + Property::Parent(parent) => { + let parent = match parent { + NodeIdOrRoot::Node(node) => ObjectId::Node { + adapter: adapter_id, + node, + }, + NodeIdOrRoot::Root => ObjectId::Root, + }; + parent.to_address(self.unique_name().inner()).into() + } + Property::Role(value) => Value::U32(value as u32), + Property::Value(value) => Value::F64(value), + }; + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::SelectionChanged => { + self.emit_event(target, interface, signal, EventBodyBorrowed::default()) + .await + } + ObjectEvent::StateChanged(state, value) => { + let mut body = EventBodyBorrowed::default(); + body.kind = state.to_static_str(); + body.detail1 = value as i32; + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::TextInserted { + start_index, + length, + content, + } => { + let mut body = EventBodyBorrowed::default(); + body.kind = "insert"; + body.detail1 = start_index; + body.detail2 = length; + body.any_data = content.into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::TextRemoved { + start_index, + length, + content, + } => { + let mut body = EventBodyBorrowed::default(); + body.kind = "delete"; + body.detail1 = start_index; + body.detail2 = length; + body.any_data = content.into(); + self.emit_event(target, interface, signal, body).await + } + ObjectEvent::TextSelectionChanged => { + self.emit_event(target, interface, signal, EventBodyBorrowed::default()) + .await + } + } + } + + pub(crate) async fn emit_window_event( + &self, + adapter_id: usize, + target: FullNodeId, + window_name: String, + event: WindowEvent, + ) -> Result<()> { + let target = ObjectId::Node { + adapter: adapter_id, + node: target, + }; + let signal = match event { + WindowEvent::Activated => "Activate", + WindowEvent::Deactivated => "Deactivate", + }; + let mut body = EventBodyBorrowed::default(); + body.any_data = window_name.into(); + self.emit_event(target, "org.a11y.atspi.Event.Window", signal, body) + .await + } + + pub(crate) async fn emit_document_event( + &self, + adapter_id: usize, + target: FullNodeId, + event: DocumentEvent, + ) -> Result<()> { + let target = ObjectId::Node { + adapter: adapter_id, + node: target, + }; + let signal = match event { + DocumentEvent::LoadComplete => "LoadComplete", + }; + self.emit_event( + target, + "org.a11y.atspi.Event.Document", + signal, + EventBodyBorrowed::default(), + ) + .await + } + + pub(crate) async fn emit_cache_add(&self, node: PlatformNode) -> Result<()> { + let Ok(item) = cache_item_for_node(self.unique_name().inner(), &node) else { + return Ok(()); + }; + self.emit_cache_signal("AddAccessible", &item).await + } + + pub(crate) async fn emit_cache_remove( + &self, + adapter_id: usize, + node_id: FullNodeId, + ) -> Result<()> { + let reference = object_ref( + self.unique_name().inner(), + ObjectId::Node { + adapter: adapter_id, + node: node_id, + }, + ); + self.emit_cache_signal("RemoveAccessible", &reference).await + } + + async fn emit_cache_signal(&self, signal_name: &str, body: &B) -> Result<()> + where + B: serde::Serialize + zbus::zvariant::DynamicType, + { + map_or_ignoring_recoverable_error( + self.conn + .emit_signal( + Option::::None, + cache_path(), + InterfaceName::from_str_unchecked("org.a11y.atspi.Cache"), + MemberName::from_str_unchecked(signal_name), + body, + ) + .await, + (), + |_| (), + ) + } + + async fn emit_event( + &self, + target: ObjectId, + interface: &str, + signal_name: &str, + body: EventBodyBorrowed<'_>, + ) -> Result<()> { + map_or_ignoring_recoverable_error( + self.conn + .emit_signal( + Option::::None, + target.path(), + InterfaceName::from_str_unchecked(interface), + MemberName::from_str_unchecked(signal_name), + &body, + ) + .await, + (), + |_| (), + ) + } +} + +pub(crate) fn zbus_error_is_unrecoverable(error: &zbus::Error) -> bool { + matches!( + error, + zbus::Error::InterfaceExists(..) | zbus::Error::MissingParameter(..) + ) +} + +pub(crate) fn map_or_ignoring_recoverable_error( + result: zbus::Result, + default: U, + f: F, +) -> zbus::Result +where + F: FnOnce(T) -> U, +{ + match result { + Ok(result) => Ok(f(result)), + Err(error) if zbus_error_is_unrecoverable(&error) => Err(error), + Err(_) => Ok(default), + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/accessible.rs b/vendor/accesskit_unix/src/atspi/interfaces/accessible.rs new file mode 100644 index 0000000000..ad61060cd8 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/accessible.rs @@ -0,0 +1,246 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use std::{ + collections::HashMap, + sync::{Arc, OnceLock}, +}; + +use accesskit_atspi_common::{NodeIdOrRoot, PlatformNode, PlatformRoot}; +use atspi::{InterfaceSet, ObjectRefOwned, RelationType, Role, StateSet}; +use zbus::{fdo, interface, names::OwnedUniqueName}; + +use super::map_root_error; +use crate::atspi::{ObjectId, OwnedObjectAddress}; + +pub(crate) struct NodeAccessibleInterface { + bus_name: OwnedUniqueName, + node: PlatformNode, +} + +impl NodeAccessibleInterface { + pub fn new(bus_name: OwnedUniqueName, node: PlatformNode) -> Self { + Self { bus_name, node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Accessible")] +impl NodeAccessibleInterface { + #[zbus(property)] + fn name(&self) -> fdo::Result { + self.node.name().map_err(self.map_error()) + } + + #[zbus(property)] + fn description(&self) -> fdo::Result { + self.node.description().map_err(self.map_error()) + } + + #[zbus(property)] + fn parent(&self) -> fdo::Result { + self.node.parent().map_err(self.map_error()).map(|parent| { + match parent { + NodeIdOrRoot::Node(node) => ObjectId::Node { + adapter: self.node.adapter_id(), + node, + }, + NodeIdOrRoot::Root => ObjectId::Root, + } + .to_address(self.bus_name.inner()) + }) + } + + #[zbus(property)] + fn child_count(&self) -> fdo::Result { + self.node.child_count().map_err(self.map_error()) + } + + #[zbus(property)] + fn locale(&self) -> &str { + "" + } + + #[zbus(property)] + fn accessible_id(&self) -> fdo::Result { + self.node.accessible_id().map_err(self.map_error()) + } + + fn get_child_at_index(&self, index: i32) -> fdo::Result<(OwnedObjectAddress,)> { + let index = index + .try_into() + .map_err(|_| fdo::Error::InvalidArgs("Index can't be negative.".into()))?; + let child = self + .node + .child_at_index(index) + .map_err(self.map_error())? + .map(|child| ObjectId::Node { + adapter: self.node.adapter_id(), + node: child, + }); + Ok(super::optional_object_address(&self.bus_name, child)) + } + + fn get_children(&self) -> fdo::Result> { + self.node + .map_children(|child| { + ObjectId::Node { + adapter: self.node.adapter_id(), + node: child, + } + .to_address(self.bus_name.inner()) + }) + .map_err(self.map_error()) + } + + fn get_index_in_parent(&self) -> fdo::Result { + self.node.index_in_parent().map_err(self.map_error()) + } + + fn get_relation_set(&self) -> fdo::Result)>> { + self.node + .relation_set(|relation| { + ObjectId::Node { + adapter: self.node.adapter_id(), + node: relation, + } + .to_address(self.bus_name.inner()) + }) + .map(|set| { + set.into_iter() + .collect::)>>() + }) + .map_err(self.map_error()) + } + + fn get_role(&self) -> fdo::Result { + self.node.role().map_err(self.map_error()) + } + + fn get_localized_role_name(&self) -> fdo::Result { + self.node.localized_role_name().map_err(self.map_error()) + } + + fn get_state(&self) -> StateSet { + self.node.state() + } + + fn get_attributes(&self) -> fdo::Result> { + self.node.attributes().map_err(self.map_error()) + } + + fn get_application(&self) -> (OwnedObjectAddress,) { + (ObjectId::Root.to_address(self.bus_name.inner()),) + } + + fn get_interfaces(&self) -> fdo::Result { + self.node.interfaces().map_err(self.map_error()) + } +} + +pub(crate) struct RootAccessibleInterface { + bus_name: OwnedUniqueName, + root: PlatformRoot, + desktop: Arc>, +} + +impl RootAccessibleInterface { + pub fn new( + bus_name: OwnedUniqueName, + root: PlatformRoot, + desktop: Arc>, + ) -> Self { + Self { + bus_name, + root, + desktop, + } + } +} + +#[interface(name = "org.a11y.atspi.Accessible")] +impl RootAccessibleInterface { + #[zbus(property)] + fn name(&self) -> fdo::Result { + self.root.name().map_err(map_root_error) + } + + #[zbus(property)] + fn description(&self) -> fdo::Result { + self.root.description().map_err(map_root_error) + } + + #[zbus(property)] + fn parent(&self) -> OwnedObjectAddress { + self.desktop + .get() + .cloned() + .unwrap_or_default() + .into_inner() + .into() + } + + #[zbus(property)] + fn child_count(&self) -> fdo::Result { + self.root.child_count().map_err(map_root_error) + } + + #[zbus(property)] + fn locale(&self) -> &str { + "" + } + + #[zbus(property)] + fn accessible_id(&self) -> &str { + "" + } + + fn get_child_at_index(&self, index: i32) -> fdo::Result<(OwnedObjectAddress,)> { + let index = index + .try_into() + .map_err(|_| fdo::Error::InvalidArgs("Index can't be negative.".into()))?; + let child = self + .root + .child_id_at_index(index) + .map_err(map_root_error)? + .map(|(adapter, node)| ObjectId::Node { adapter, node }); + Ok(super::optional_object_address(&self.bus_name, child)) + } + + fn get_children(&self) -> fdo::Result> { + self.root + .map_child_ids(|(adapter, node)| { + ObjectId::Node { adapter, node }.to_address(self.bus_name.inner()) + }) + .map_err(map_root_error) + } + + fn get_index_in_parent(&self) -> i32 { + self.root.index_in_parent() + } + + fn get_relation_set(&self) -> Vec<(RelationType, Vec)> { + Vec::new() + } + + fn get_role(&self) -> Role { + self.root.role() + } + + fn get_state(&self) -> StateSet { + self.root.state() + } + + fn get_application(&self) -> (OwnedObjectAddress,) { + (ObjectId::Root.to_address(self.bus_name.inner()),) + } + + fn get_interfaces(&self) -> InterfaceSet { + self.root.interfaces() + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/action.rs b/vendor/accesskit_unix/src/atspi/interfaces/action.rs new file mode 100644 index 0000000000..e982468e74 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/action.rs @@ -0,0 +1,51 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{Action, PlatformNode}; +use zbus::{fdo, interface}; + +pub(crate) struct ActionInterface(PlatformNode); + +impl ActionInterface { + pub fn new(node: PlatformNode) -> Self { + Self(node) + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.0, error) + } +} + +#[interface(name = "org.a11y.atspi.Action")] +impl ActionInterface { + #[zbus(property)] + fn n_actions(&self) -> fdo::Result { + self.0.n_actions().map_err(self.map_error()) + } + + fn get_description(&self, _index: i32) -> &str { + "" + } + + fn get_name(&self, index: i32) -> fdo::Result { + self.0.action_name(index).map_err(self.map_error()) + } + + fn get_localized_name(&self, index: i32) -> fdo::Result { + self.0.action_name(index).map_err(self.map_error()) + } + + fn get_key_binding(&self, _index: i32) -> &str { + "" + } + + fn get_actions(&self) -> fdo::Result> { + self.0.actions().map_err(self.map_error()) + } + + fn do_action(&self, index: i32) -> fdo::Result { + self.0.do_action(index).map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/application.rs b/vendor/accesskit_unix/src/atspi/interfaces/application.rs new file mode 100644 index 0000000000..6ad408b3b8 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/application.rs @@ -0,0 +1,39 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformRoot; +use zbus::{fdo, interface}; + +use super::map_root_error; + +pub(crate) struct ApplicationInterface(pub PlatformRoot); + +#[interface(name = "org.a11y.atspi.Application")] +impl ApplicationInterface { + #[zbus(property)] + fn toolkit_name(&self) -> fdo::Result { + self.0.toolkit_name().map_err(map_root_error) + } + + #[zbus(property)] + fn version(&self) -> fdo::Result { + self.0.toolkit_version().map_err(map_root_error) + } + + #[zbus(property)] + fn atspi_version(&self) -> &str { + "2.1" + } + + #[zbus(property)] + fn id(&self) -> fdo::Result { + self.0.id().map_err(map_root_error) + } + + #[zbus(property)] + fn set_id(&mut self, id: i32) -> fdo::Result<()> { + self.0.set_id(id).map_err(map_root_error) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/cache.rs b/vendor/accesskit_unix/src/atspi/interfaces/cache.rs new file mode 100644 index 0000000000..a85c6c98b2 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/cache.rs @@ -0,0 +1,311 @@ +// Copyright 2026 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{CacheNode, Error, NodeIdOrRoot, PlatformNode, PlatformRoot}; +use atspi::{CacheItem, ObjectRef, ObjectRefOwned}; +use std::sync::{Arc, OnceLock}; +use zbus::{ + fdo, interface, + names::{OwnedUniqueName, UniqueName}, +}; + +use super::map_root_error; +use crate::atspi::ObjectId; + +pub(crate) fn object_ref(bus_name: &UniqueName, id: ObjectId) -> ObjectRefOwned { + ObjectRef::new_owned(bus_name.to_owned(), id.path()) +} + +pub(crate) fn cache_item_for_node( + bus_name: &UniqueName, + node: &PlatformNode, +) -> Result { + Ok(cache_item(bus_name, node.cache_node()?)) +} + +fn cache_item(bus_name: &UniqueName, node: CacheNode) -> CacheItem { + let parent = match node.parent { + NodeIdOrRoot::Root => ObjectId::Root, + NodeIdOrRoot::Node(node_id) => ObjectId::Node { + adapter: node.adapter_id, + node: node_id, + }, + }; + CacheItem { + object: object_ref( + bus_name, + ObjectId::Node { + adapter: node.adapter_id, + node: node.id, + }, + ), + app: object_ref(bus_name, ObjectId::Root), + parent: object_ref(bus_name, parent), + index: node.index_in_parent, + children: node.child_count, + ifaces: node.interfaces, + short_name: node.name, + role: node.role, + name: node.description, + states: node.states, + } +} + +fn application_cache_item( + bus_name: &UniqueName, + root: &PlatformRoot, + desktop: ObjectRefOwned, +) -> Result { + Ok(CacheItem { + object: object_ref(bus_name, ObjectId::Root), + app: object_ref(bus_name, ObjectId::Root), + parent: desktop, + index: root.index_in_parent(), + children: root.child_count()?, + ifaces: root.interfaces(), + short_name: root.name()?, + role: root.role(), + name: root.description()?, + states: root.state(), + }) +} + +pub(crate) struct CacheInterface { + bus_name: OwnedUniqueName, + root: PlatformRoot, + desktop: Arc>, +} + +impl CacheInterface { + pub fn new( + bus_name: OwnedUniqueName, + root: PlatformRoot, + desktop: Arc>, + ) -> Self { + Self { + bus_name, + root, + desktop, + } + } + + fn items(&self) -> Result, Error> { + let bus_name = self.bus_name.inner(); + let descendants: Vec = self + .root + .map_descendant_cache_nodes(|node| cache_item(bus_name, node))?; + + let desktop = self.desktop.get().cloned().unwrap_or_default(); + let mut items = Vec::with_capacity(descendants.len() + 1); + items.push(application_cache_item(bus_name, &self.root, desktop)?); + items.extend(descendants); + Ok(items) + } +} + +#[interface(name = "org.a11y.atspi.Cache")] +impl CacheInterface { + fn get_items(&self) -> fdo::Result> { + self.items().map_err(map_root_error) + } +} + +#[cfg(test)] +mod tests { + use super::{CacheInterface, object_ref}; + use crate::atspi::ObjectId; + use accesskit::{ + ActionHandler, ActionRequest, Node, NodeId, Role, TreeId, TreeInfo, TreeUpdate, + }; + use accesskit_atspi_common::{ + Adapter, AdapterCallback, AppContext, Event, FullNodeId, PlatformRoot, WindowBounds, + }; + use atspi::{Interface, ObjectRefOwned, Role as AtspiRole}; + use std::sync::{Arc, OnceLock}; + use zbus::names::{OwnedUniqueName, UniqueName}; + + struct NoOpActionHandler; + impl ActionHandler for NoOpActionHandler { + fn do_action(&mut self, _request: ActionRequest) {} + } + + struct NoOpCallback; + impl AdapterCallback for NoOpCallback { + fn register_interfaces(&self, _: &Adapter, _: FullNodeId, _: atspi::InterfaceSet) {} + fn unregister_interfaces(&self, _: &Adapter, _: FullNodeId, _: atspi::InterfaceSet) {} + fn emit_event(&self, _: &Adapter, _: Event) {} + } + + fn with_children(role: Role, children: &[NodeId]) -> Node { + let mut node = Node::new(role); + node.set_children(children.to_vec()); + node + } + + const BUS_NAME: &str = ":1.0"; + + fn root_for(update: TreeUpdate) -> (Adapter, PlatformRoot) { + let app_context = AppContext::new(None); + let adapter = Adapter::new( + &app_context, + NoOpCallback, + update, + false, + WindowBounds::default(), + NoOpActionHandler, + ); + let root = adapter.platform_root(); + (adapter, root) + } + + fn bus_name() -> OwnedUniqueName { + OwnedUniqueName::try_from(BUS_NAME).unwrap() + } + + fn cache(update: TreeUpdate) -> (Adapter, CacheInterface) { + let (adapter, root) = root_for(update); + let desktop = Arc::new(OnceLock::new()); + desktop.set(desktop_ref()).unwrap(); + (adapter, CacheInterface::new(bus_name(), root, desktop)) + } + + fn window_with_button() -> TreeUpdate { + TreeUpdate { + nodes: vec![ + (NodeId(0), with_children(Role::Window, &[NodeId(1)])), + (NodeId(1), Node::new(Role::Button)), + ], + tree: Some(TreeInfo::new(NodeId(0))), + tree_id: TreeId::ROOT, + focus: NodeId(0), + } + } + + fn root_ref() -> ObjectRefOwned { + object_ref( + &UniqueName::from_static_str_unchecked(BUS_NAME), + ObjectId::Root, + ) + } + + fn desktop_ref() -> ObjectRefOwned { + ObjectRefOwned::from_static_str_unchecked(":1.1", "/org/a11y/atspi/accessible/root") + } + + #[test] + fn get_items_prepends_application_root() { + let (_adapter, iface) = cache(window_with_button()); + let items = iface.items().unwrap(); + let [app, _window, _button] = items.as_slice() else { + panic!("expected application root, window, and button"); + }; + assert_eq!(app.object, root_ref()); + assert_eq!(app.app, root_ref()); + assert_eq!(app.parent, desktop_ref()); + assert_eq!(app.index, -1); + assert_eq!(app.children, 1); + assert_eq!(app.role, AtspiRole::Application); + assert!(app.ifaces.contains(Interface::Application)); + } + + #[test] + fn top_window_parent_is_application_root() { + let (_adapter, iface) = cache(window_with_button()); + let items = iface.items().unwrap(); + let [_app, window, _button] = items.as_slice() else { + panic!("expected application root, window, and button"); + }; + assert_eq!(window.parent, root_ref()); + assert_eq!(window.index, 0); + assert!( + window + .object + .path_as_str() + .starts_with("/org/a11y/atspi/accessible/") + ); + assert_eq!(window.object.name_as_str(), Some(BUS_NAME)); + } + + #[test] + fn filtered_child_excluded_from_items_and_counts() { + let mut hidden = Node::new(Role::Button); + hidden.set_hidden(); + let update = TreeUpdate { + nodes: vec![ + ( + NodeId(0), + with_children(Role::Window, &[NodeId(1), NodeId(2)]), + ), + (NodeId(1), Node::new(Role::Button)), + (NodeId(2), hidden), + ], + tree: Some(TreeInfo::new(NodeId(0))), + tree_id: TreeId::ROOT, + focus: NodeId(0), + }; + let (_adapter, iface) = cache(update); + let items = iface.items().unwrap(); + let [app_root, window, _visible_button] = items.as_slice() else { + panic!("expected application root, window, and visible button only"); + }; + assert_eq!(app_root.children, 1); + assert_eq!(window.children, 1); + } + + #[test] + fn object_ref_encodes_bus_name_and_node_path() { + let (_adapter, root) = root_for(window_with_button()); + let (adapter_id, node_id) = root.child_id_at_index(0).unwrap().unwrap(); + let expected_path = ObjectId::Node { + adapter: adapter_id, + node: node_id, + } + .path(); + let reference = object_ref( + &UniqueName::from_static_str_unchecked(BUS_NAME), + ObjectId::Node { + adapter: adapter_id, + node: node_id, + }, + ); + assert_eq!(reference.name_as_str(), Some(BUS_NAME)); + assert_eq!(reference.path_as_str(), expected_path.as_str()); + } + + #[test] + fn multi_adapter_windows_have_distinct_indices() { + let app_context = AppContext::new(None); + let _a0 = Adapter::new( + &app_context, + NoOpCallback, + window_with_button(), + false, + WindowBounds::default(), + NoOpActionHandler, + ); + let _a1 = Adapter::new( + &app_context, + NoOpCallback, + window_with_button(), + false, + WindowBounds::default(), + NoOpActionHandler, + ); + let iface = CacheInterface::new( + bus_name(), + PlatformRoot::new(&app_context), + Arc::new(OnceLock::new()), + ); + let items = iface.items().unwrap(); + + let app_root = &items[0]; + assert_eq!(app_root.children, 2); + + let windows = items.iter().filter(|item| item.parent == root_ref()); + let mut window_indices: Vec = windows.map(|item| item.index).collect(); + window_indices.sort(); + assert_eq!(window_indices, vec![0, 1]); + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/component.rs b/vendor/accesskit_unix/src/atspi/interfaces/component.rs new file mode 100644 index 0000000000..a457b52a49 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/component.rs @@ -0,0 +1,76 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{PlatformNode, Rect}; +use atspi::{CoordType, Layer, ScrollType}; +use zbus::{fdo, interface, names::OwnedUniqueName}; + +use crate::atspi::{ObjectId, OwnedObjectAddress}; + +pub(crate) struct ComponentInterface { + bus_name: OwnedUniqueName, + node: PlatformNode, +} + +impl ComponentInterface { + pub fn new(bus_name: OwnedUniqueName, node: PlatformNode) -> Self { + Self { bus_name, node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Component")] +impl ComponentInterface { + fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> fdo::Result { + self.node + .contains(x, y, coord_type) + .map_err(self.map_error()) + } + + fn get_accessible_at_point( + &self, + x: i32, + y: i32, + coord_type: CoordType, + ) -> fdo::Result<(OwnedObjectAddress,)> { + let accessible = self + .node + .accessible_at_point(x, y, coord_type) + .map_err(self.map_error())? + .map(|node| ObjectId::Node { + adapter: self.node.adapter_id(), + node, + }); + Ok(super::optional_object_address(&self.bus_name, accessible)) + } + + fn get_extents(&self, coord_type: CoordType) -> fdo::Result<(Rect,)> { + self.node + .extents(coord_type) + .map(|rect| (rect,)) + .map_err(self.map_error()) + } + + fn get_layer(&self) -> fdo::Result { + self.node.layer().map_err(self.map_error()) + } + + fn grab_focus(&self) -> fdo::Result { + self.node.grab_focus().map_err(self.map_error()) + } + + fn scroll_to(&self, scroll_type: ScrollType) -> fdo::Result { + self.node.scroll_to(scroll_type).map_err(self.map_error()) + } + + fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> fdo::Result { + self.node + .scroll_to_point(coord_type, x, y) + .map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/document.rs b/vendor/accesskit_unix/src/atspi/interfaces/document.rs new file mode 100644 index 0000000000..84c87f4923 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/document.rs @@ -0,0 +1,60 @@ +// Copyright 2026 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformNode; +use atspi::TextSelection; +use std::collections::HashMap; +use zbus::{fdo, interface}; + +fn unsupported() -> fdo::Error { + fdo::Error::NotSupported("document operation is not supported".into()) +} + +pub(crate) struct DocumentInterface(PlatformNode); + +impl DocumentInterface { + pub fn new(node: PlatformNode) -> Self { + Self(node) + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.0, error) + } +} + +#[interface(name = "org.a11y.atspi.Document")] +impl DocumentInterface { + #[zbus(property)] + fn current_page_number(&self) -> fdo::Result { + Err(unsupported()) + } + + #[zbus(property)] + fn page_count(&self) -> fdo::Result { + Err(unsupported()) + } + + fn get_attribute_value(&self, attribute_name: &str) -> fdo::Result { + self.0 + .document_attribute_value(attribute_name) + .map_err(self.map_error()) + } + + fn get_attributes(&self) -> fdo::Result> { + self.0.document_attributes().map_err(self.map_error()) + } + + fn get_locale(&self) -> fdo::Result { + Err(unsupported()) + } + + fn get_text_selections(&self) -> fdo::Result> { + Err(unsupported()) + } + + fn set_text_selections(&self, _selections: Vec) -> fdo::Result { + Err(unsupported()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/editable_text.rs b/vendor/accesskit_unix/src/atspi/interfaces/editable_text.rs new file mode 100644 index 0000000000..098533a7f3 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/editable_text.rs @@ -0,0 +1,52 @@ +// Copyright 2026 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformNode; +use zbus::{fdo, interface}; + +fn unsupported() -> fdo::Error { + fdo::Error::NotSupported("editing operation is not supported".into()) +} + +pub(crate) struct EditableTextInterface(PlatformNode); + +impl EditableTextInterface { + pub fn new(node: PlatformNode) -> Self { + Self(node) + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.0, error) + } +} + +#[interface(name = "org.a11y.atspi.EditableText")] +impl EditableTextInterface { + fn set_text_contents(&self, new_contents: &str) -> fdo::Result { + self.0 + .set_text_contents(new_contents) + .map_err(self.map_error()) + } + + fn insert_text(&self, _position: i32, _text: &str, _length: i32) -> fdo::Result { + Err(unsupported()) + } + + fn copy_text(&self, _start_pos: i32, _end_pos: i32) -> fdo::Result<()> { + Err(unsupported()) + } + + fn cut_text(&self, _start_pos: i32, _end_pos: i32) -> fdo::Result { + Err(unsupported()) + } + + fn delete_text(&self, _start_pos: i32, _end_pos: i32) -> fdo::Result { + Err(unsupported()) + } + + fn paste_text(&self, _position: i32) -> fdo::Result { + Err(unsupported()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/hyperlink.rs b/vendor/accesskit_unix/src/atspi/interfaces/hyperlink.rs new file mode 100644 index 0000000000..fbb12f7e39 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/hyperlink.rs @@ -0,0 +1,63 @@ +// Copyright 2026 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformNode; +use zbus::{fdo, interface, names::OwnedUniqueName}; + +use crate::atspi::{ObjectId, OwnedObjectAddress}; + +pub(crate) struct HyperlinkInterface { + bus_name: OwnedUniqueName, + node: PlatformNode, +} + +impl HyperlinkInterface { + pub fn new(bus_name: OwnedUniqueName, node: PlatformNode) -> Self { + Self { bus_name, node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Hyperlink")] +impl HyperlinkInterface { + #[zbus(property)] + fn n_anchors(&self) -> fdo::Result { + self.node.n_anchors().map_err(self.map_error()) + } + + #[zbus(property)] + fn start_index(&self) -> fdo::Result { + self.node.hyperlink_start_index().map_err(self.map_error()) + } + + #[zbus(property)] + fn end_index(&self) -> fdo::Result { + self.node.hyperlink_end_index().map_err(self.map_error()) + } + + fn get_object(&self, index: i32) -> fdo::Result<(OwnedObjectAddress,)> { + let object = self + .node + .hyperlink_object(index) + .map_err(self.map_error())? + .map(|node| ObjectId::Node { + adapter: self.node.adapter_id(), + node, + }); + Ok(super::optional_object_address(&self.bus_name, object)) + } + + #[zbus(name = "GetURI")] + fn get_uri(&self, index: i32) -> fdo::Result { + self.node.uri(index).map_err(self.map_error()) + } + + fn is_valid(&self) -> fdo::Result { + self.node.hyperlink_is_valid().map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/image.rs b/vendor/accesskit_unix/src/atspi/interfaces/image.rs new file mode 100644 index 0000000000..183e9437e1 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/image.rs @@ -0,0 +1,50 @@ +// Copyright 2026 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{PlatformNode, Rect}; +use atspi::CoordType; +use zbus::{fdo, interface}; + +pub(crate) struct ImageInterface(PlatformNode); + +impl ImageInterface { + pub fn new(node: PlatformNode) -> Self { + Self(node) + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.0, error) + } +} + +#[interface(name = "org.a11y.atspi.Image")] +impl ImageInterface { + #[zbus(property)] + fn image_description(&self) -> fdo::Result { + self.0.image_description().map_err(self.map_error()) + } + + #[zbus(property)] + fn image_locale(&self) -> fdo::Result { + Err(fdo::Error::NotSupported( + "image locale is not supported".into(), + )) + } + + fn get_image_extents(&self, coord_type: CoordType) -> fdo::Result<(Rect,)> { + self.0 + .image_extents(coord_type) + .map(|rect| (rect,)) + .map_err(self.map_error()) + } + + fn get_image_position(&self, coord_type: CoordType) -> fdo::Result<(i32, i32)> { + self.0.image_position(coord_type).map_err(self.map_error()) + } + + fn get_image_size(&self) -> fdo::Result<(i32, i32)> { + self.0.image_size().map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/mod.rs b/vendor/accesskit_unix/src/atspi/interfaces/mod.rs new file mode 100644 index 0000000000..239413b406 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/mod.rs @@ -0,0 +1,47 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +mod accessible; +mod action; +mod application; +mod cache; +mod component; +mod document; +mod editable_text; +mod hyperlink; +mod image; +mod selection; +mod text; +mod value; + +use crate::atspi::{ObjectId, OwnedObjectAddress}; +use zbus::{fdo, names::UniqueName}; + +fn map_root_error(error: accesskit_atspi_common::Error) -> fdo::Error { + crate::util::map_error(ObjectId::Root, error) +} + +fn optional_object_address( + bus_name: &UniqueName, + object_id: Option, +) -> (OwnedObjectAddress,) { + match object_id { + Some(id) => (id.to_address(bus_name),), + None => (OwnedObjectAddress::null(),), + } +} + +pub(crate) use accessible::*; +pub(crate) use action::*; +pub(crate) use application::*; +pub(crate) use cache::*; +pub(crate) use component::*; +pub(crate) use document::*; +pub(crate) use editable_text::*; +pub(crate) use hyperlink::*; +pub(crate) use image::*; +pub(crate) use selection::*; +pub(crate) use text::*; +pub(crate) use value::*; diff --git a/vendor/accesskit_unix/src/atspi/interfaces/selection.rs b/vendor/accesskit_unix/src/atspi/interfaces/selection.rs new file mode 100644 index 0000000000..b311a905d8 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/selection.rs @@ -0,0 +1,82 @@ +// Copyright 2024 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformNode; +use zbus::{fdo, interface, names::OwnedUniqueName}; + +use crate::atspi::{ObjectId, OwnedObjectAddress}; + +pub(crate) struct SelectionInterface { + bus_name: OwnedUniqueName, + node: PlatformNode, +} + +impl SelectionInterface { + pub fn new(bus_name: OwnedUniqueName, node: PlatformNode) -> Self { + Self { bus_name, node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Selection")] +impl SelectionInterface { + #[zbus(property)] + fn n_selected_children(&self) -> fdo::Result { + self.node.n_selected_children().map_err(self.map_error()) + } + + fn get_selected_child(&self, selected_child_index: i32) -> fdo::Result<(OwnedObjectAddress,)> { + let child = self + .node + .selected_child(map_child_index(selected_child_index)?) + .map_err(self.map_error())? + .map(|child| ObjectId::Node { + adapter: self.node.adapter_id(), + node: child, + }); + Ok(super::optional_object_address(&self.bus_name, child)) + } + + fn select_child(&self, child_index: i32) -> fdo::Result { + self.node + .select_child(map_child_index(child_index)?) + .map_err(self.map_error()) + } + + fn deselect_selected_child(&self, selected_child_index: i32) -> fdo::Result { + self.node + .deselect_selected_child(map_child_index(selected_child_index)?) + .map_err(self.map_error()) + } + + fn is_child_selected(&self, child_index: i32) -> fdo::Result { + self.node + .is_child_selected(map_child_index(child_index)?) + .map_err(self.map_error()) + } + + fn select_all(&self) -> fdo::Result { + self.node.select_all().map_err(self.map_error()) + } + + fn clear_selection(&self) -> fdo::Result { + self.node.clear_selection().map_err(self.map_error()) + } + + fn deselect_child(&self, child_index: i32) -> fdo::Result { + self.node + .deselect_child(map_child_index(child_index)?) + .map_err(self.map_error()) + } +} + +fn map_child_index(index: i32) -> fdo::Result { + index + .try_into() + .map_err(|_| fdo::Error::InvalidArgs("Index can't be negative.".into())) +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/text.rs b/vendor/accesskit_unix/src/atspi/interfaces/text.rs new file mode 100644 index 0000000000..c861cbd1b2 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/text.rs @@ -0,0 +1,163 @@ +// Copyright 2024 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{PlatformNode, Rect}; +use atspi::{CoordType, Granularity, ScrollType}; +use std::collections::HashMap; +use zbus::{fdo, interface}; + +pub(crate) struct TextInterface { + node: PlatformNode, +} + +impl TextInterface { + pub fn new(node: PlatformNode) -> Self { + Self { node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Text")] +impl TextInterface { + #[zbus(property)] + fn character_count(&self) -> fdo::Result { + self.node.character_count().map_err(self.map_error()) + } + + #[zbus(property)] + fn caret_offset(&self) -> fdo::Result { + self.node.caret_offset().map_err(self.map_error()) + } + + fn get_string_at_offset( + &self, + offset: i32, + granularity: Granularity, + ) -> fdo::Result<(String, i32, i32)> { + self.node + .string_at_offset(offset, granularity) + .map_err(self.map_error()) + } + + fn get_text(&self, start_offset: i32, end_offset: i32) -> fdo::Result { + self.node + .text(start_offset, end_offset) + .map_err(self.map_error()) + } + + fn set_caret_offset(&self, offset: i32) -> fdo::Result { + self.node.set_caret_offset(offset).map_err(self.map_error()) + } + + fn get_attribute_value(&self, offset: i32, attribute_name: &str) -> fdo::Result { + self.node + .text_attribute_value(offset, attribute_name) + .map_err(self.map_error()) + } + + fn get_attributes( + &self, + offset: i32, + ) -> fdo::Result<(HashMap<&'static str, String>, i32, i32)> { + self.node.text_attributes(offset).map_err(self.map_error()) + } + + fn get_default_attributes(&self) -> fdo::Result> { + self.node + .default_text_attributes() + .map_err(self.map_error()) + } + + fn get_character_extents(&self, offset: i32, coord_type: CoordType) -> fdo::Result { + self.node + .character_extents(offset, coord_type) + .map_err(self.map_error()) + } + + fn get_offset_at_point(&self, x: i32, y: i32, coord_type: CoordType) -> fdo::Result { + self.node + .offset_at_point(x, y, coord_type) + .map_err(self.map_error()) + } + + fn get_n_selections(&self) -> fdo::Result { + self.node.n_selections().map_err(self.map_error()) + } + + fn get_selection(&self, selection_num: i32) -> fdo::Result<(i32, i32)> { + self.node.selection(selection_num).map_err(self.map_error()) + } + + fn add_selection(&self, start_offset: i32, end_offset: i32) -> fdo::Result { + self.node + .add_selection(start_offset, end_offset) + .map_err(self.map_error()) + } + + fn remove_selection(&self, selection_num: i32) -> fdo::Result { + self.node + .remove_selection(selection_num) + .map_err(self.map_error()) + } + + fn set_selection( + &self, + selection_num: i32, + start_offset: i32, + end_offset: i32, + ) -> fdo::Result { + self.node + .set_selection(selection_num, start_offset, end_offset) + .map_err(self.map_error()) + } + + fn get_range_extents( + &self, + start_offset: i32, + end_offset: i32, + coord_type: CoordType, + ) -> fdo::Result { + self.node + .range_extents(start_offset, end_offset, coord_type) + .map_err(self.map_error()) + } + + fn get_attribute_run( + &self, + offset: i32, + include_defaults: bool, + ) -> fdo::Result<(HashMap<&'static str, String>, i32, i32)> { + self.node + .text_attribute_run(offset, include_defaults) + .map_err(self.map_error()) + } + + fn scroll_substring_to( + &self, + start_offset: i32, + end_offset: i32, + scroll_type: ScrollType, + ) -> fdo::Result { + self.node + .scroll_substring_to(start_offset, end_offset, scroll_type) + .map_err(self.map_error()) + } + + fn scroll_substring_to_point( + &self, + start_offset: i32, + end_offset: i32, + coord_type: CoordType, + x: i32, + y: i32, + ) -> fdo::Result { + self.node + .scroll_substring_to_point(start_offset, end_offset, coord_type, x, y) + .map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/interfaces/value.rs b/vendor/accesskit_unix/src/atspi/interfaces/value.rs new file mode 100644 index 0000000000..e410acdf32 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/interfaces/value.rs @@ -0,0 +1,49 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::PlatformNode; +use zbus::{fdo, interface}; + +pub(crate) struct ValueInterface { + node: PlatformNode, +} + +impl ValueInterface { + pub fn new(node: PlatformNode) -> Self { + Self { node } + } + + fn map_error(&self) -> impl '_ + FnOnce(accesskit_atspi_common::Error) -> fdo::Error { + |error| crate::util::map_error_from_node(&self.node, error) + } +} + +#[interface(name = "org.a11y.atspi.Value")] +impl ValueInterface { + #[zbus(property)] + fn minimum_value(&self) -> fdo::Result { + self.node.minimum_value().map_err(self.map_error()) + } + + #[zbus(property)] + fn maximum_value(&self) -> fdo::Result { + self.node.maximum_value().map_err(self.map_error()) + } + + #[zbus(property)] + fn minimum_increment(&self) -> fdo::Result { + self.node.minimum_increment().map_err(self.map_error()) + } + + #[zbus(property)] + fn current_value(&self) -> fdo::Result { + self.node.current_value().map_err(self.map_error()) + } + + #[zbus(property)] + fn set_current_value(&mut self, value: f64) -> fdo::Result<()> { + self.node.set_current_value(value).map_err(self.map_error()) + } +} diff --git a/vendor/accesskit_unix/src/atspi/mod.rs b/vendor/accesskit_unix/src/atspi/mod.rs new file mode 100644 index 0000000000..799e9f3ac2 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +mod bus; +pub(crate) mod interfaces; +mod object_address; +mod object_id; + +pub(crate) use bus::*; +pub(crate) use object_address::OwnedObjectAddress; +pub(crate) use object_id::{ObjectId, cache_path}; diff --git a/vendor/accesskit_unix/src/atspi/object_address.rs b/vendor/accesskit_unix/src/atspi/object_address.rs new file mode 100644 index 0000000000..8acd0ec5db --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/object_address.rs @@ -0,0 +1,45 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use atspi::ObjectRef; +use serde::{Deserialize, Serialize}; +use zbus::{ + names::UniqueName, + zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}, +}; + +// https://gnome.pages.gitlab.gnome.org/at-spi2-core/libatspi/const.DBUS_PATH_NULL.html +const NULL_PATH: &str = "/org/a11y/atspi/null"; + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, OwnedValue, Type, Value)] +pub(crate) struct OwnedObjectAddress { + bus_name: String, + path: OwnedObjectPath, +} + +impl OwnedObjectAddress { + pub(crate) fn new(bus_name: &UniqueName, path: OwnedObjectPath) -> Self { + Self { + bus_name: bus_name.to_string(), + path, + } + } + + pub(crate) fn null() -> Self { + Self { + bus_name: String::new(), + path: ObjectPath::from_str_unchecked(NULL_PATH).into(), + } + } +} + +impl<'a> From> for OwnedObjectAddress { + fn from(object: ObjectRef<'a>) -> Self { + Self { + bus_name: object.name().map(|s| s.to_string()).unwrap_or_default(), + path: object.path().to_owned().into(), + } + } +} diff --git a/vendor/accesskit_unix/src/atspi/object_id.rs b/vendor/accesskit_unix/src/atspi/object_id.rs new file mode 100644 index 0000000000..e65ceda745 --- /dev/null +++ b/vendor/accesskit_unix/src/atspi/object_id.rs @@ -0,0 +1,79 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use crate::atspi::OwnedObjectAddress; +use accesskit_atspi_common::{FullNodeId, PlatformNode}; +use serde::{Serialize, Serializer}; +use zbus::{ + names::UniqueName, + zvariant::{ObjectPath, OwnedObjectPath, Signature, Structure, Type}, +}; + +const ACCESSIBLE_PATH_PREFIX: &str = "/org/a11y/atspi/accessible/"; +const ROOT_PATH: &str = "/org/a11y/atspi/accessible/root"; +const CACHE_PATH: &str = "/org/a11y/atspi/cache"; + +pub(crate) fn cache_path() -> OwnedObjectPath { + ObjectPath::from_str_unchecked(CACHE_PATH).into() +} + +#[derive(Debug, PartialEq)] +pub(crate) enum ObjectId { + Root, + Node { adapter: usize, node: FullNodeId }, +} + +impl ObjectId { + pub(crate) fn to_address(&self, bus_name: &UniqueName) -> OwnedObjectAddress { + OwnedObjectAddress::new(bus_name, self.path()) + } + + pub(crate) fn path(&self) -> OwnedObjectPath { + match self { + Self::Root => ObjectPath::from_str_unchecked(ROOT_PATH), + Self::Node { adapter, node } => ObjectPath::from_string_unchecked(format!( + "{}{}/{}", + ACCESSIBLE_PATH_PREFIX, + adapter, + u128::from(*node) + )), + } + .into() + } +} + +impl Serialize for ObjectId { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Root => serializer.serialize_str("root"), + Self::Node { node, .. } => serializer.serialize_str(&u128::from(*node).to_string()), + } + } +} + +impl Type for ObjectId { + const SIGNATURE: &'static Signature = <&str>::SIGNATURE; +} + +impl From for Structure<'_> { + fn from(id: ObjectId) -> Self { + Self::from((match id { + ObjectId::Root => "root".into(), + ObjectId::Node { node, .. } => u128::from(node).to_string(), + },)) + } +} + +impl From<&PlatformNode> for ObjectId { + fn from(node: &PlatformNode) -> Self { + Self::Node { + adapter: node.adapter_id(), + node: node.id(), + } + } +} diff --git a/vendor/accesskit_unix/src/context.rs b/vendor/accesskit_unix/src/context.rs new file mode 100644 index 0000000000..07451dcf42 --- /dev/null +++ b/vendor/accesskit_unix/src/context.rs @@ -0,0 +1,324 @@ +// Copyright 2023 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit::{ActivationHandler, DeactivationHandler}; +use accesskit_atspi_common::{Adapter as AdapterImpl, AppContext, Event}; +#[cfg(not(feature = "tokio"))] +use async_channel::{Receiver, Sender}; +use atspi::proxy::bus::StatusProxy; +#[cfg(not(feature = "tokio"))] +use futures_util::{StreamExt, pin_mut as pin, select}; +use std::{ + sync::{Arc, Mutex, OnceLock, RwLock}, + thread, +}; +#[cfg(feature = "tokio")] +use tokio::{ + pin, select, + sync::mpsc::{UnboundedReceiver as Receiver, UnboundedSender as Sender}, +}; +#[cfg(feature = "tokio")] +use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; +use zbus::{Connection, connection::Builder, proxy::PropertyChanged}; + +use crate::{ + adapter::{AdapterState, Callback, Message}, + atspi::{Bus, map_or_ignoring_recoverable_error, zbus_error_is_unrecoverable}, + executor::Executor, + util::block_on, +}; + +static APP_CONTEXT: OnceLock>> = OnceLock::new(); +static MESSAGES: OnceLock> = OnceLock::new(); + +fn app_name() -> Option { + std::env::current_exe().ok().and_then(|path| { + path.file_name() + .map(|name| name.to_string_lossy().to_string()) + }) +} + +pub(crate) fn get_or_init_app_context<'a>() -> &'a Arc> { + APP_CONTEXT.get_or_init(|| AppContext::new(app_name())) +} + +pub(crate) fn get_or_init_messages() -> Sender { + MESSAGES + .get_or_init(|| { + #[cfg(not(feature = "tokio"))] + let (tx, rx) = async_channel::unbounded(); + #[cfg(feature = "tokio")] + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + + thread::spawn(|| { + let executor = Executor::new(); + block_on(executor.run(async { + if let Ok(session_bus) = Builder::session() { + if let Ok(session_bus) = session_bus.internal_executor(false).build().await + { + if let Err(error) = run_event_loop(&executor, session_bus, rx).await { + if zbus_error_is_unrecoverable(&error) { + panic!("Accessibility event loop failed: {error}"); + } + } + } + } + })) + }); + + tx + }) + .clone() +} + +struct AdapterEntry { + id: usize, + activation_handler: Box, + deactivation_handler: Box, + state: Arc>, +} + +fn activate_adapter(entry: &mut AdapterEntry) { + let mut state = entry.state.lock().unwrap(); + if let AdapterState::Inactive { + is_window_focused, + root_window_bounds, + action_handler, + } = &*state + { + *state = match entry.activation_handler.request_initial_tree() { + Some(initial_state) => { + let r#impl = AdapterImpl::with_wrapped_action_handler( + entry.id, + get_or_init_app_context(), + Callback::new(), + initial_state, + *is_window_focused, + *root_window_bounds, + Arc::clone(action_handler), + ); + AdapterState::Active(r#impl) + } + None => AdapterState::Pending { + is_window_focused: *is_window_focused, + root_window_bounds: *root_window_bounds, + action_handler: Arc::clone(action_handler), + }, + }; + } +} + +fn deactivate_adapter(entry: &mut AdapterEntry) { + let mut state = entry.state.lock().unwrap(); + match &*state { + AdapterState::Inactive { .. } => (), + AdapterState::Pending { + is_window_focused, + root_window_bounds, + action_handler, + } => { + *state = AdapterState::Inactive { + is_window_focused: *is_window_focused, + root_window_bounds: *root_window_bounds, + action_handler: Arc::clone(action_handler), + }; + drop(state); + entry.deactivation_handler.deactivate_accessibility(); + } + AdapterState::Active(r#impl) => { + *state = AdapterState::Inactive { + is_window_focused: r#impl.is_window_focused(), + root_window_bounds: r#impl.root_window_bounds(), + action_handler: r#impl.wrapped_action_handler(), + }; + drop(state); + entry.deactivation_handler.deactivate_accessibility(); + } + } +} + +async fn bus_after_status_change( + change: Option>, + session_bus: &Connection, + executor: &Executor<'_>, +) -> zbus::Result> { + let enabled = match change { + Some(change) => change.get().await?, + None => false, + }; + if enabled { + map_or_ignoring_recoverable_error(Bus::new(session_bus, executor).await, None, Some) + } else { + Ok(None) + } +} + +fn sync_adapters(adapters: &mut [AdapterEntry], atspi_bus: &Option) { + let active = atspi_bus.is_some(); + for entry in adapters { + if active { + activate_adapter(entry); + } else { + deactivate_adapter(entry); + } + } +} + +/// Whether the app was handed the accessibility bus directly while the +/// `IsEnabled` toggle that normally gates connecting to it is out of reach. +/// +/// A Flatpak sandbox does exactly this: it hides `org.a11y.Bus` from the +/// session bus and sets `AT_SPI_BUS_ADDRESS` to its own proxy of the +/// accessibility bus. The toggle then can't be read or watched, so waiting for +/// it means never registering with AT-SPI. GTK and Qt treat accessibility as +/// enabled in that case, and so does this adapter. Where the toggle can be read, +/// it stays in charge, whatever the environment says. +async fn bus_provided_without_status(status: &StatusProxy<'_>) -> bool { + let provided = std::env::var("AT_SPI_BUS_ADDRESS").is_ok_and(|address| !address.is_empty()); + provided && status.is_enabled().await.is_err() +} + +async fn run_event_loop( + executor: &Executor<'_>, + session_bus: Connection, + rx: Receiver, +) -> zbus::Result<()> { + let session_bus_copy = session_bus.clone(); + let _session_bus_task = executor.spawn( + async move { + loop { + session_bus_copy.executor().tick().await; + } + }, + "accesskit_session_bus_task", + ); + + let status = StatusProxy::new(&session_bus).await?; + let changes = status.receive_is_enabled_changed().await.fuse(); + pin!(changes); + + #[cfg(not(feature = "tokio"))] + let messages = rx.fuse(); + #[cfg(feature = "tokio")] + let messages = UnboundedReceiverStream::new(rx).fuse(); + pin!(messages); + + let mut atspi_bus = if bus_provided_without_status(&status).await { + map_or_ignoring_recoverable_error(Bus::new(&session_bus, executor).await, None, Some)? + } else { + None + }; + let mut adapters: Vec = Vec::new(); + + loop { + select! { + change = changes.next() => { + atspi_bus = bus_after_status_change(change, &session_bus, executor).await?; + sync_adapters(&mut adapters, &atspi_bus); + + } + message = messages.next() => { + if let Some(message) = message { + process_adapter_message(&atspi_bus, &mut adapters, message).await?; + } + } + } + } +} + +async fn process_adapter_message( + atspi_bus: &Option, + adapters: &mut Vec, + message: Message, +) -> zbus::Result<()> { + match message { + Message::AddAdapter { + id, + activation_handler, + deactivation_handler, + state, + } => { + adapters.push(AdapterEntry { + id, + activation_handler, + deactivation_handler, + state, + }); + if atspi_bus.is_some() { + let entry = adapters.last_mut().unwrap(); + activate_adapter(entry); + } + } + Message::RemoveAdapter { id } => { + if let Ok(index) = adapters.binary_search_by(|entry| entry.id.cmp(&id)) { + adapters.remove(index); + } + } + Message::RegisterInterfaces { node, interfaces } => { + if let Some(bus) = atspi_bus { + bus.register_interfaces(node, interfaces).await? + } + } + Message::UnregisterInterfaces { + adapter_id, + node_id, + interfaces, + } => { + if let Some(bus) = atspi_bus { + bus.unregister_interfaces(adapter_id, node_id, interfaces) + .await? + } + } + Message::EmitEvent { + adapter_id, + event: Event::Object { target, event }, + } => { + if let Some(bus) = atspi_bus { + bus.emit_object_event(adapter_id, target, event).await? + } + } + Message::EmitEvent { + adapter_id, + event: + Event::Window { + target, + name, + event, + }, + } => { + if let Some(bus) = atspi_bus { + bus.emit_window_event(adapter_id, target, name, event) + .await?; + } + } + Message::EmitEvent { + adapter_id, + event: Event::Document { target, event }, + } => { + if let Some(bus) = atspi_bus { + bus.emit_document_event(adapter_id, target, event).await?; + } + } + Message::EmitEvent { + event: Event::Cache(_), + .. + } => unreachable!("cache events are sent as EmitCacheAdd/EmitCacheRemove"), + Message::EmitCacheAdd { node } => { + if let Some(bus) = atspi_bus { + bus.emit_cache_add(node).await?; + } + } + Message::EmitCacheRemove { + adapter_id, + node_id, + } => { + if let Some(bus) = atspi_bus { + bus.emit_cache_remove(adapter_id, node_id).await?; + } + } + } + + Ok(()) +} diff --git a/vendor/accesskit_unix/src/executor.rs b/vendor/accesskit_unix/src/executor.rs new file mode 100644 index 0000000000..f227af42c6 --- /dev/null +++ b/vendor/accesskit_unix/src/executor.rs @@ -0,0 +1,158 @@ +// Copyright 2024 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +// Derived from zbus. +// Copyright 2024 Zeeshan Ali Khan. +// Licensed under the MIT license (found in the LICENSE-MIT file). + +#[cfg(not(feature = "tokio"))] +use async_executor::Executor as AsyncExecutor; +#[cfg(not(feature = "tokio"))] +use async_task::Task as AsyncTask; +#[cfg(feature = "tokio")] +use std::marker::PhantomData; +#[cfg(not(feature = "tokio"))] +use std::sync::Arc; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +#[cfg(feature = "tokio")] +use tokio::task::JoinHandle; + +/// A wrapper around the underlying runtime/executor. +/// +/// This is used to run asynchronous tasks internally and allows integration with various runtimes. +/// See [`crate::Connection::executor`] for an example of integration with external runtimes. +/// +/// **Note:** You can (and should) completely ignore this type when building with `tokio` feature +/// enabled. +#[cfg(not(feature = "tokio"))] +#[derive(Debug, Clone)] +pub(crate) struct Executor<'a> { + executor: Arc>, +} +#[cfg(feature = "tokio")] +#[derive(Debug, Clone)] +pub(crate) struct Executor<'a> { + phantom: PhantomData<&'a ()>, +} + +impl Executor<'_> { + /// Spawns a task onto the executor. + pub(crate) fn spawn( + &self, + future: impl Future + Send + 'static, + #[allow(unused)] name: &str, + ) -> Task { + #[cfg(not(feature = "tokio"))] + { + Task(Some(self.executor.spawn(future))) + } + + #[cfg(feature = "tokio")] + { + Task(Some(tokio::task::spawn(future))) + } + } + + /// Create a new `Executor`. + pub(crate) fn new() -> Self { + #[cfg(not(feature = "tokio"))] + { + Self { + executor: Arc::new(AsyncExecutor::new()), + } + } + + #[cfg(feature = "tokio")] + { + Self { + phantom: PhantomData, + } + } + } + + /// Runs the executor until the given future completes. + /// + /// With `tokio` feature enabled, it just awaits on the `future`. + pub(crate) async fn run(&self, future: impl Future) -> T { + #[cfg(not(feature = "tokio"))] + { + self.executor.run(future).await + } + #[cfg(feature = "tokio")] + { + future.await + } + } +} + +/// A wrapper around the task API of the underlying runtime/executor. +/// +/// This follows the semantics of `async_task::Task` on drop: +/// +/// * it will be cancelled, rather than detached. For detaching, use the `detach` method. +/// * errors from the task cancellation will will be ignored. If you need to know about task errors, +/// convert the task to a `FallibleTask` using the `fallible` method. +#[cfg(not(feature = "tokio"))] +#[derive(Debug)] +pub(crate) struct Task(Option>); +#[cfg(feature = "tokio")] +#[derive(Debug)] +pub(crate) struct Task(Option>); + +impl Task { + /// Detaches the task to let it keep running in the background. + #[allow(unused_mut)] + #[allow(unused)] + pub(crate) fn detach(mut self) { + #[cfg(not(feature = "tokio"))] + { + self.0.take().expect("async_task::Task is none").detach() + } + + #[cfg(feature = "tokio")] + { + self.0.take().expect("tokio::task::JoinHandle is none"); + } + } +} + +impl Drop for Task { + fn drop(&mut self) { + #[cfg(feature = "tokio")] + { + if let Some(join_handle) = self.0.take() { + join_handle.abort(); + } + } + } +} + +impl Future for Task { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[cfg(not(feature = "tokio"))] + { + Pin::new(&mut self.get_mut().0.as_mut().expect("async_task::Task is none")).poll(cx) + } + + #[cfg(feature = "tokio")] + { + Pin::new( + &mut self + .get_mut() + .0 + .as_mut() + .expect("tokio::task::JoinHandle is none"), + ) + .poll(cx) + .map(|r| r.expect("tokio::task::JoinHandle error")) + } + } +} diff --git a/vendor/accesskit_unix/src/lib.rs b/vendor/accesskit_unix/src/lib.rs new file mode 100644 index 0000000000..d7c558c57c --- /dev/null +++ b/vendor/accesskit_unix/src/lib.rs @@ -0,0 +1,27 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +/// ## Compatibility with async runtimes +/// +/// While this crate's API is purely blocking, it internally spawns asynchronous tasks on an executor. +/// +/// - If you use tokio, make sure to enable the `tokio` feature of this crate. +/// - If you use another async runtime or if you don't use one at all, the default feature will suit your needs. + +#[cfg(all(not(feature = "async-io"), not(feature = "tokio")))] +compile_error!("Either \"async-io\" (default) or \"tokio\" feature must be enabled."); + +#[cfg(all(feature = "async-io", feature = "tokio"))] +compile_error!( + "Both \"async-io\" (default) and \"tokio\" features cannot be enabled at the same time." +); + +mod adapter; +mod atspi; +mod context; +mod executor; +mod util; + +pub use adapter::Adapter; diff --git a/vendor/accesskit_unix/src/util.rs b/vendor/accesskit_unix/src/util.rs new file mode 100644 index 0000000000..15073021b3 --- /dev/null +++ b/vendor/accesskit_unix/src/util.rs @@ -0,0 +1,42 @@ +// Copyright 2022 The AccessKit Authors. All rights reserved. +// Licensed under the Apache License, Version 2.0 (found in +// the LICENSE-APACHE file) or the MIT license (found in +// the LICENSE-MIT file), at your option. + +use accesskit_atspi_common::{Error as InternalError, PlatformNode}; +use zbus::fdo::Error as FdoError; + +use crate::atspi::ObjectId; + +#[cfg(not(feature = "tokio"))] +pub(crate) fn block_on(future: F) -> F::Output { + futures_lite::future::block_on(future) +} + +#[cfg(feature = "tokio")] +pub(crate) fn block_on(future: F) -> F::Output { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() + .expect("launch of single-threaded tokio runtime"); + runtime.block_on(future) +} + +pub(crate) fn map_error(source: ObjectId, error: InternalError) -> FdoError { + match error { + InternalError::Defunct | InternalError::UnsupportedInterface => { + FdoError::UnknownObject(source.path().to_string()) + } + InternalError::TooManyChildren => FdoError::Failed("Too many children.".into()), + InternalError::IndexOutOfRange => FdoError::Failed("Index is too big.".into()), + InternalError::TooManyCharacters => FdoError::Failed("Too many characters.".into()), + InternalError::UnsupportedTextGranularity => { + FdoError::Failed("Unsupported text granularity.".into()) + } + } +} + +pub(crate) fn map_error_from_node(source: &PlatformNode, error: InternalError) -> FdoError { + map_error(ObjectId::from(source), error) +}