Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"0041b4977724b159f2881822289458d62fce06963c40d16cb3cc1df62789c1bd","Cargo.lock":"f22006defac528b51eac5a7c7b0b01a078a6c96fabe468dd0fd37d1ba0b8d885","Cargo.toml":"c7abed6dc05dc4c1ed37448bc36ee9804993aa6823398038c8a605a6e5e95259","README.md":"622db8e92d711b64279a91e25ae9345959c82bb4d47157dbf5c3a6a53f80f6d6","examples/hello_world.rs":"590657e2d6ad3c8d96a5a258651bcb1402346ecc13c7ebf3c95b9306f49546b8","src/adapter.rs":"88879ce53c38d60597b648aa7ba2d717eeea51591e6bb4aef15d75ddb08215e8","src/context.rs":"8a45005a8c1449e4ed234bb6f041f026f2dbc6d2935e76407637adb1c127601c","src/filters.rs":"744374b547065b153123c80028f129f5c6bb53052660205e1a2f231c3e169f64","src/lib.rs":"8a6f20ed5354c9834d88a59091c764f65aa31359f81c57386fe06e9d806e9396","src/node.rs":"6f80380f4b347ea068cebc2272772ae32d8dc2cef384676c95848867ecdc907e","src/subclass.rs":"bc103a11203177cfe64f98a3ea64e2d80e34fc2da3d24344863fa6c09d80a435","src/tests/mod.rs":"72229fa5e2d94ee7ac09719e3fc4d4cd9e6d9456cc10ef5fe628e1b804815674","src/tests/simple.rs":"9ad0c8b85921620d5ccb7e83b1d90a6c9d565925dda6d9d4fcae2afdfc17b252","src/tests/subclassed.rs":"bcf410cd35d058908fe37d9d84588bfe1eec699dffbcaf5a9db86be3861de400","src/text.rs":"da038079c4912b22333c3abcf606e4dafb6ae150d2f0e1554ff2f8e4b326c2eb","src/util.rs":"5652f87597342ddf898a967a1759d460650c25bff0015908f2f57c18db4adbfd","src/window_handle.rs":"dec1d78c81784a18c50f066e89d57d0fe02a7c9199a11e47a0833f8b9113d8c2"},"package":"65178f3df98a51e4238e584fcb255cb1a4f9111820848eeddd37663be40a625f"}

751
vendor/accesskit_windows/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,751 @@
# Changelog
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.8.0 to 0.8.1
* accesskit_consumer bumped from 0.9.1 to 0.10.0
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.11.0 to 0.12.0
* 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.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.2 to 0.12.3
* accesskit_consumer bumped from 0.17.0 to 0.17.1
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.16.2 to 0.16.3
* accesskit_consumer bumped from 0.24.2 to 0.24.3
## [0.25.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.24.1...accesskit_windows-v0.25.0) (2025-03-06)
### ⚠ BREAKING CHANGES
* Optimize simple string getters ([#493](https://github.com/AccessKit/accesskit/issues/493))
* Drop `Tree::app_name` ([#492](https://github.com/AccessKit/accesskit/issues/492))
### Features
* Expose the `is_required` property ([#497](https://github.com/AccessKit/accesskit/issues/497)) ([46ed99b](https://github.com/AccessKit/accesskit/commit/46ed99bb958ddb32cbf1bee2fcfb7b328bcbe0ab))
### 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))
* Expose password input fields on Windows ([#516](https://github.com/AccessKit/accesskit/issues/516)) ([19514da](https://github.com/AccessKit/accesskit/commit/19514dabc40bcfc01bee1b1efa77355ec5b0822b))
* Fix some clippy warnings ([#509](https://github.com/AccessKit/accesskit/issues/509)) ([579b9c1](https://github.com/AccessKit/accesskit/commit/579b9c12dd8abc44ecab41fa3c326a1d8999871d))
* Optimize dynamic string building ([#491](https://github.com/AccessKit/accesskit/issues/491)) ([a86901d](https://github.com/AccessKit/accesskit/commit/a86901ddea5d5ba72ab237e98b53d6adcc6087bb))
* Remove unnecessary explicit lifetimes ([#488](https://github.com/AccessKit/accesskit/issues/488)) ([d2bcd6d](https://github.com/AccessKit/accesskit/commit/d2bcd6d3048d23df4e132bee6171eb247b2dc2c8))
### Code Refactoring
* Drop `Tree::app_name` ([#492](https://github.com/AccessKit/accesskit/issues/492)) ([089794c](https://github.com/AccessKit/accesskit/commit/089794c8f74957e91a19ae3df508e2a892f39ebc))
* Optimize simple string getters ([#493](https://github.com/AccessKit/accesskit/issues/493)) ([484fd7c](https://github.com/AccessKit/accesskit/commit/484fd7cbfb778222369d3f57d31dd998f6fa80d8))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.17.1 to 0.18.0
* accesskit_consumer bumped from 0.26.0 to 0.27.0
## [0.24.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.24.0...accesskit_windows-v0.24.1) (2024-11-23)
### Bug Fixes
* Optimize use of hash tables in platform adapters ([#485](https://github.com/AccessKit/accesskit/issues/485)) ([f4f0bfb](https://github.com/AccessKit/accesskit/commit/f4f0bfbf21b8e22e80ab07deb432f9e7e16469ab))
* Tell docs.rs to only build accesskit_windows on Windows ([#483](https://github.com/AccessKit/accesskit/issues/483)) ([1fead29](https://github.com/AccessKit/accesskit/commit/1fead296ed012f83afb482021051ccc1d4946167))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.17.0 to 0.17.1
* accesskit_consumer bumped from 0.25.0 to 0.26.0
## [0.24.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.23.2...accesskit_windows-v0.24.0) (2024-10-31)
### ⚠ BREAKING CHANGES
* Rename `name` to `label` and use `value` for label content ([#475](https://github.com/AccessKit/accesskit/issues/475))
* Rename `NodeBuilder` to `Node` and the old `Node` to `FrozenNode` ([#476](https://github.com/AccessKit/accesskit/issues/476))
* Rename `Role::InlineTextBox` to `TextRun` ([#473](https://github.com/AccessKit/accesskit/issues/473))
* Drop `DefaultActionVerb` ([#472](https://github.com/AccessKit/accesskit/issues/472))
* 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))
### Code Refactoring
* Drop `DefaultActionVerb` ([#472](https://github.com/AccessKit/accesskit/issues/472)) ([ef3b003](https://github.com/AccessKit/accesskit/commit/ef3b0038224459094f650368412650bc3b69526b))
* Rename `name` to `label` and use `value` for label content ([#475](https://github.com/AccessKit/accesskit/issues/475)) ([e0053a5](https://github.com/AccessKit/accesskit/commit/e0053a5399929e8e0d4f07aa18de604ed8766ace))
* Rename `NodeBuilder` to `Node` and the old `Node` to `FrozenNode` ([#476](https://github.com/AccessKit/accesskit/issues/476)) ([7d8910e](https://github.com/AccessKit/accesskit/commit/7d8910e35f7bc0543724cc124941a3bd0304bcc0))
* Rename `Role::InlineTextBox` to `TextRun` ([#473](https://github.com/AccessKit/accesskit/issues/473)) ([29fa341](https://github.com/AccessKit/accesskit/commit/29fa34125a811bd3a0f9da579a9f35c9da90bf29))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.16.3 to 0.17.0
* accesskit_consumer bumped from 0.24.3 to 0.25.0
## [0.23.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.23.0...accesskit_windows-v0.23.1) (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))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.16.1 to 0.16.2
* accesskit_consumer bumped from 0.24.1 to 0.24.2
## [0.23.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.22.0...accesskit_windows-v0.23.0) (2024-09-24)
### ⚠ BREAKING CHANGES
* Update windows to 0.58 on accesskit_windows ([#453](https://github.com/AccessKit/accesskit/issues/453))
### Bug Fixes
* Update windows to 0.58 on accesskit_windows ([#453](https://github.com/AccessKit/accesskit/issues/453)) ([cda35e7](https://github.com/AccessKit/accesskit/commit/cda35e77a78f72386c2bfd88e9fd29000106f7e6))
* Use the new HWND type on accesskit_winit ([#453](https://github.com/AccessKit/accesskit/issues/453)) ([68a2462](https://github.com/AccessKit/accesskit/commit/68a24629381f0b18f6ed1ee008fe72ce9330092e))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.16.0 to 0.16.1
* accesskit_consumer bumped from 0.24.0 to 0.24.1
## [0.22.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.21.0...accesskit_windows-v0.22.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_consumer bumped from 0.23.0 to 0.24.0
## [0.21.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.20.0...accesskit_windows-v0.21.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_consumer bumped from 0.22.0 to 0.23.0
## [0.20.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.19.0...accesskit_windows-v0.20.0) (2024-05-27)
### Features
* Expose the `orientation` property ([#421](https://github.com/AccessKit/accesskit/issues/421)) ([590aada](https://github.com/AccessKit/accesskit/commit/590aada070dc812f9b8f171fb9e43ac984fad2a1))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.21.0 to 0.22.0
## [0.19.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.18.2...accesskit_windows-v0.19.0) (2024-05-26)
### Features
* 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_consumer bumped from 0.20.0 to 0.21.0
## [0.18.2](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.18.1...accesskit_windows-v0.18.2) (2024-05-13)
### Bug Fixes
* Fix platform adapters to support copy-on-write tree snapshots again ([#411](https://github.com/AccessKit/accesskit/issues/411)) ([d3a130a](https://github.com/AccessKit/accesskit/commit/d3a130a5ec8ae1d9edf0bf85a44f35f0e365242c))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.19.1 to 0.20.0
## [0.18.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.18.0...accesskit_windows-v0.18.1) (2024-05-11)
### Bug Fixes
* Make the transition from placeholder to real tree more robust ([#405](https://github.com/AccessKit/accesskit/issues/405)) ([928e11d](https://github.com/AccessKit/accesskit/commit/928e11d00e7c60b3cafcc0ac623f6377b36f7ea7))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.19.0 to 0.19.1
## [0.18.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.17.0...accesskit_windows-v0.18.0) (2024-04-30)
### ⚠ BREAKING CHANGES
* Update winit to 0.30 ([#397](https://github.com/AccessKit/accesskit/issues/397))
* Clean up table roles and properties ([#393](https://github.com/AccessKit/accesskit/issues/393))
* Drop `NodeClassSet` ([#389](https://github.com/AccessKit/accesskit/issues/389))
* Rename `Checked` to `Toggled`; drop `ToggleButton` role ([#388](https://github.com/AccessKit/accesskit/issues/388))
### Features
* Expose the class name property ([#385](https://github.com/AccessKit/accesskit/issues/385)) ([53dcf2a](https://github.com/AccessKit/accesskit/commit/53dcf2ae47546273590c46a9b31b708aa1409837))
* Implement the `description` property ([#382](https://github.com/AccessKit/accesskit/issues/382)) ([d49f406](https://github.com/AccessKit/accesskit/commit/d49f40660b5dc23ed074cd72a91e511b130756ae))
### Bug Fixes
* Increase minimum supported Rust version to `1.70` ([#396](https://github.com/AccessKit/accesskit/issues/396)) ([a8398b8](https://github.com/AccessKit/accesskit/commit/a8398b847aa003de91042ac45e33126fc2cae053))
* Update winit to 0.30 ([#397](https://github.com/AccessKit/accesskit/issues/397)) ([de93be3](https://github.com/AccessKit/accesskit/commit/de93be387c03a438fbf598670207e578686e6bcf))
### Code Refactoring
* Clean up table roles and properties ([#393](https://github.com/AccessKit/accesskit/issues/393)) ([e34dad9](https://github.com/AccessKit/accesskit/commit/e34dad94448a5321ece9def3f2e054aa5f62dd79))
* Drop `NodeClassSet` ([#389](https://github.com/AccessKit/accesskit/issues/389)) ([1b153ed](https://github.com/AccessKit/accesskit/commit/1b153ed51f8421cdba2dc98beca2e8f5f8c781bc))
* Rename `Checked` to `Toggled`; drop `ToggleButton` role ([#388](https://github.com/AccessKit/accesskit/issues/388)) ([6bc040b](https://github.com/AccessKit/accesskit/commit/6bc040b7cf75cdbd6a019cc380d8dbce804b3c81))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.13.0 to 0.14.0
* accesskit_consumer bumped from 0.18.0 to 0.19.0
## [0.17.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.16.4...accesskit_windows-v0.17.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_consumer bumped from 0.17.1 to 0.18.0
## [0.16.4](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.16.3...accesskit_windows-v0.16.4) (2024-03-18)
### Bug Fixes
* Update `windows` to `0.54` ([#373](https://github.com/AccessKit/accesskit/issues/373)) ([50f112f](https://github.com/AccessKit/accesskit/commit/50f112f0085a03f0180f86915d2ac4e5845f6b63))
## [0.16.2](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.16.1...accesskit_windows-v0.16.2) (2024-02-25)
### Bug Fixes
* Bump windows-rs dependency to 0.52 ([#359](https://github.com/AccessKit/accesskit/issues/359)) ([69d74f6](https://github.com/AccessKit/accesskit/commit/69d74f6947922b76d4aee683eb53d8fbdd2259f2))
## [0.16.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.16.0...accesskit_windows-v0.16.1) (2024-02-24)
### Bug Fixes
* Avoid `unnecessary_literal_unwrap` clippy lint ([#346](https://github.com/AccessKit/accesskit/issues/346)) ([59a6eae](https://github.com/AccessKit/accesskit/commit/59a6eae40ba35069b90d4cc0b765d838f8477c66))
## [0.16.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.15.1...accesskit_windows-v0.16.0) (2024-01-03)
### Features
* Support custom role descriptions ([#316](https://github.com/AccessKit/accesskit/issues/316)) ([c8d1a56](https://github.com/AccessKit/accesskit/commit/c8d1a5638fa6c33adfa059815c04f7e043c56026))
### 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.15.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.15.0...accesskit_windows-v0.15.1) (2023-11-04)
### Bug Fixes
* Make the test focus event handler thread-safe again ([#306](https://github.com/AccessKit/accesskit/issues/306)) ([bb479c6](https://github.com/AccessKit/accesskit/commit/bb479c69bddff77a1102549a7ff6ea5c7155d90d))
* Make the Windows subclassing test more robust ([#308](https://github.com/AccessKit/accesskit/issues/308)) ([0078f79](https://github.com/AccessKit/accesskit/commit/0078f7997f11a3bb1f3c33dc7a91f427b1a60db8))
### Dependencies
* 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
## [0.15.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.14.3...accesskit_windows-v0.15.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))
### Bug Fixes
* Support text fields without a value property ([#274](https://github.com/AccessKit/accesskit/issues/274)) ([5ae557b](https://github.com/AccessKit/accesskit/commit/5ae557b40d395b4a9966a90a2d80e7d97ad50bf9))
* 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.14.3](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.14.2...accesskit_windows-v0.14.3) (2023-08-08)
### Bug Fixes
* Update windows crate to v0.48 ([#257](https://github.com/AccessKit/accesskit/issues/257)) ([cc703ed](https://github.com/AccessKit/accesskit/commit/cc703ed33d535aa1803e423a53beff9354b5b0df))
## [0.14.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.13.3...accesskit_windows-v0.14.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.13.2](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.13.1...accesskit_windows-v0.13.2) (2023-02-23)
### Bug Fixes
* Fix Windows 32-bit build errors ([#223](https://github.com/AccessKit/accesskit/issues/223)) ([41f28b6](https://github.com/AccessKit/accesskit/commit/41f28b670ac457b2d067bbc4ba40aa0fc8842e4d))
## [0.13.1](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.13.0...accesskit_windows-v0.13.1) (2023-02-20)
### Bug Fixes
* Update windows-rs to 0.44 ([#220](https://github.com/AccessKit/accesskit/issues/220)) ([a6b0a12](https://github.com/AccessKit/accesskit/commit/a6b0a124e7511e37760d769b517fd5fc9050160b))
### Dependencies
* 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
## [0.13.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.12.0...accesskit_windows-v0.13.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.12.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.11.0...accesskit_windows-v0.12.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.11.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.10.4...accesskit_windows-v0.11.0) (2023-02-02)
### ⚠ BREAKING CHANGES
* Update winit to 0.28 ([#207](https://github.com/AccessKit/accesskit/issues/207))
### Miscellaneous Chores
* Update winit to 0.28 ([#207](https://github.com/AccessKit/accesskit/issues/207)) ([3ff0cf5](https://github.com/AccessKit/accesskit/commit/3ff0cf59f982af504499142a3804f7aeeb4defe0))
## [0.10.2](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.10.1...accesskit_windows-v0.10.2) (2022-12-17)
### Bug Fixes
* Correct broken UIA method implementation that was incompatible with Windows 11 ATs ([#193](https://github.com/AccessKit/accesskit/issues/193)) ([3c527c7](https://github.com/AccessKit/accesskit/commit/3c527c76cb4139402d2b5550d2eb1ad12e07ebe5))
* More reliable handling of the edge case for wrapped lines ([#192](https://github.com/AccessKit/accesskit/issues/192)) ([c626d2c](https://github.com/AccessKit/accesskit/commit/c626d2c3028085b076ada7dd31242cf3ca3c0f08))
* Provide fallback property implementations for the window root ([#194](https://github.com/AccessKit/accesskit/issues/194)) ([f3d30b9](https://github.com/AccessKit/accesskit/commit/f3d30b9ba2f66e08fb7f78c304ab8a9e83e1aeca))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.10.0 to 0.11.0
## [0.10.0](https://github.com/AccessKit/accesskit/compare/accesskit_windows-v0.9.3...accesskit_windows-v0.10.0) (2022-11-29)
### ⚠ BREAKING CHANGES
* Move lazy initialization from the core platform adapter to the caller ([#179](https://github.com/AccessKit/accesskit/issues/179))
### Code Refactoring
* Move lazy initialization from the core platform adapter to the caller ([#179](https://github.com/AccessKit/accesskit/issues/179)) ([f35c941](https://github.com/AccessKit/accesskit/commit/f35c941f395f3162db376a69cfaaaf770d376267))
### [0.9.3](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.9.2...accesskit_windows-v0.9.3) (2022-11-25)
### Bug Fixes
* Gracefully handle nodes that only support text ranges some of the time ([#169](https://www.github.com/AccessKit/accesskit/issues/169)) ([1f50df6](https://www.github.com/AccessKit/accesskit/commit/1f50df6820b9d23fe2e579f043f4981acf285de2))
* **platforms/windows:** Change default minimum and maximum for range value pattern ([#166](https://www.github.com/AccessKit/accesskit/issues/166)) ([176ec58](https://www.github.com/AccessKit/accesskit/commit/176ec5853ca127b1e12f9a992b75478177a4acce))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.9.0 to 0.9.1
### [0.9.2](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.9.1...accesskit_windows-v0.9.2) (2022-11-24)
### Bug Fixes
* **platforms/windows:** Re-export more types from windows-rs ([#162](https://www.github.com/AccessKit/accesskit/issues/162)) ([eed692b](https://www.github.com/AccessKit/accesskit/commit/eed692b27407e1ddd5f200464f0e2d52a272b958))
### [0.9.1](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.9.0...accesskit_windows-v0.9.1) (2022-11-23)
### Bug Fixes
* **platforms/windows:** Re-export the windows-rs HWND type ([#159](https://www.github.com/AccessKit/accesskit/issues/159)) ([389187a](https://www.github.com/AccessKit/accesskit/commit/389187ac5e96895ed1763d14d315d2f8f4256460))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.8.0 to 0.9.0
## [0.9.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.8.0...accesskit_windows-v0.9.0) (2022-11-17)
### ⚠ BREAKING CHANGES
* **consumer:** Eliminate the dependency on `im` due to licensing (#153)
### Code Refactoring
* **consumer:** Eliminate the dependency on `im` due to licensing ([#153](https://www.github.com/AccessKit/accesskit/issues/153)) ([b4c4cb5](https://www.github.com/AccessKit/accesskit/commit/b4c4cb5713d4833d8ee7979e4f4e39c7e96a3ed4))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.7.0 to 0.8.0
* accesskit_consumer bumped from 0.7.1 to 0.8.0
## [0.8.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.7.0...accesskit_windows-v0.8.0) (2022-11-12)
### ⚠ BREAKING CHANGES
* **platforms/windows:** Update to windows-rs 0.42.0 (#148)
### Bug Fixes
* **consumer, platforms/windows, platforms/winit:** Update to parking_lot 0.12.1 ([#146](https://www.github.com/AccessKit/accesskit/issues/146)) ([6772855](https://www.github.com/AccessKit/accesskit/commit/6772855a7b540fd728faad15d8d208b05c1bbd8a))
* **platforms/windows:** Update to windows-rs 0.42.0 ([#148](https://www.github.com/AccessKit/accesskit/issues/148)) ([70d1a89](https://www.github.com/AccessKit/accesskit/commit/70d1a89f51fd6c3a32b7192d9d7f3937db09d196))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit_consumer bumped from 0.7.0 to 0.7.1
## [0.7.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.6.1...accesskit_windows-v0.7.0) (2022-11-11)
### ⚠ BREAKING CHANGES
* Text range support (#145)
* Drop the `ignored` field and implement generic filtered tree traversal (#143)
### Features
* Text range support ([#145](https://www.github.com/AccessKit/accesskit/issues/145)) ([455e6f7](https://www.github.com/AccessKit/accesskit/commit/455e6f73bc058644d299c06eeeda9cc4cbe8844f))
### Code Refactoring
* Drop the `ignored` field and implement generic filtered tree traversal ([#143](https://www.github.com/AccessKit/accesskit/issues/143)) ([a4befe6](https://www.github.com/AccessKit/accesskit/commit/a4befe6e8a5afbe4a52dfd09eb87fdf2078d6c1d))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.6.1 to 0.7.0
* accesskit_consumer bumped from 0.6.1 to 0.7.0
### [0.6.1](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.6.0...accesskit_windows-v0.6.1) (2022-10-10)
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.6.0 to 0.6.1
* accesskit_consumer bumped from 0.6.0 to 0.6.1
## [0.6.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.5.1...accesskit_windows-v0.6.0) (2022-10-09)
### ⚠ BREAKING CHANGES
* **consumer:** Optimize tree access and change handling (#134)
* Wrap `TreeUpdate` nodes in `Arc` (#135)
* **consumer:** Make `Node::data` private to the crate (#137)
* Store node ID in `TreeUpdate`, not `accesskit::Node` (#132)
### Code Refactoring
* **consumer:** Make `Node::data` private to the crate ([#137](https://www.github.com/AccessKit/accesskit/issues/137)) ([adb372d](https://www.github.com/AccessKit/accesskit/commit/adb372dda78d183c7189966e3bbc2d3780070513))
* **consumer:** Optimize tree access and change handling ([#134](https://www.github.com/AccessKit/accesskit/issues/134)) ([765ab74](https://www.github.com/AccessKit/accesskit/commit/765ab74efcf10a3b3871dc901d28f3cf1ff6020c))
* Store node ID in `TreeUpdate`, not `accesskit::Node` ([#132](https://www.github.com/AccessKit/accesskit/issues/132)) ([0bb86dd](https://www.github.com/AccessKit/accesskit/commit/0bb86ddb298cb5a253a91f07be0bad8b84b2fda3))
* Wrap `TreeUpdate` nodes in `Arc` ([#135](https://www.github.com/AccessKit/accesskit/issues/135)) ([907bc18](https://www.github.com/AccessKit/accesskit/commit/907bc1820b80d95833b6c5c3acaa2a8a4e93a6c2))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.5.1 to 0.6.0
* accesskit_consumer bumped from 0.5.1 to 0.6.0
### [0.5.1](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.5.0...accesskit_windows-v0.5.1) (2022-10-03)
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.5.0 to 0.5.1
* accesskit_consumer bumped from 0.5.0 to 0.5.1
## [0.5.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.4.0...accesskit_windows-v0.5.0) (2022-09-23)
### ⚠ BREAKING CHANGES
* Basic live regions (#128)
* **platforms/windows:** Bump windows-rs dependency (#126)
### Features
* Basic live regions ([#128](https://www.github.com/AccessKit/accesskit/issues/128)) ([03d745b](https://www.github.com/AccessKit/accesskit/commit/03d745b891147175bde2693cc10b96a2f6e31f39))
### Miscellaneous Chores
* **platforms/windows:** Bump windows-rs dependency ([#126](https://www.github.com/AccessKit/accesskit/issues/126)) ([472a75e](https://www.github.com/AccessKit/accesskit/commit/472a75e4214b90396f3282f247df08100ed8362d))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.4.0 to 0.5.0
* accesskit_consumer bumped from 0.4.0 to 0.5.0
## [0.4.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.3.0...accesskit_windows-v0.4.0) (2022-07-22)
### ⚠ BREAKING CHANGES
* String indices are always in UTF-8 code units (#114)
* **platforms/windows:** Refactor window subclassing to avoid lifetime issue (#120)
* **platforms/windows:** Simplify the adapter API by always boxing the tree source (#119)
* Drop unused tree IDs (#113)
* **platforms/windows:** Migrate to windows-rs 0.37 (#109)
* Switch to NonZeroU128 for NodeIDs (#99)
### Features
* **platforms/windows:** Win32 subclassing support ([#118](https://www.github.com/AccessKit/accesskit/issues/118)) ([60c69b7](https://www.github.com/AccessKit/accesskit/commit/60c69b7b8a18ca8db62a84495b9e71a6e8140204))
* **platforms/winit:** New winit adapter ([#121](https://www.github.com/AccessKit/accesskit/issues/121)) ([fdc274e](https://www.github.com/AccessKit/accesskit/commit/fdc274e7d3a901873d2ad0c7a4824a19111787ef))
### Bug Fixes
* **consumer, platforms/windows:** Resolve new clippy warning ([#100](https://www.github.com/AccessKit/accesskit/issues/100)) ([e8cd95c](https://www.github.com/AccessKit/accesskit/commit/e8cd95c3741b39b77e4ddc8ce82efdc20f93f096))
* Migrate to 2021 edition ([#115](https://www.github.com/AccessKit/accesskit/issues/115)) ([f2333c8](https://www.github.com/AccessKit/accesskit/commit/f2333c8ce17d46aab6fc190338ab4cfcf8569f9e))
* **platforms/windows:** Print usage text to the terminal from the Windows example ([#103](https://www.github.com/AccessKit/accesskit/issues/103)) ([7fba3ce](https://www.github.com/AccessKit/accesskit/commit/7fba3ce55345d7787f08d2ae60d841dd13b27693))
* **platforms/windows:** Restore the optimization of the FragmentRoot method ([#116](https://www.github.com/AccessKit/accesskit/issues/116)) ([d48c31b](https://www.github.com/AccessKit/accesskit/commit/d48c31b41f35baebe59bb654b38dd48265062b14))
* Switch to NonZeroU128 for NodeIDs ([#99](https://www.github.com/AccessKit/accesskit/issues/99)) ([25a1a52](https://www.github.com/AccessKit/accesskit/commit/25a1a52c4562b163bfcc8c625a233c00a41aacf2))
### Miscellaneous Chores
* **platforms/windows:** Migrate to windows-rs 0.37 ([#109](https://www.github.com/AccessKit/accesskit/issues/109)) ([1065e11](https://www.github.com/AccessKit/accesskit/commit/1065e11421176a8abc37ef579cb47d973c968462))
### Code Refactoring
* Drop unused tree IDs ([#113](https://www.github.com/AccessKit/accesskit/issues/113)) ([ca60770](https://www.github.com/AccessKit/accesskit/commit/ca607702cee13c93fe538d2faec88e474261f7ab))
* **platforms/windows:** Refactor window subclassing to avoid lifetime issue ([#120](https://www.github.com/AccessKit/accesskit/issues/120)) ([37579aa](https://www.github.com/AccessKit/accesskit/commit/37579aa8dd0c019ffaf4eac1b0bf1f7a8c719323))
* **platforms/windows:** Simplify the adapter API by always boxing the tree source ([#119](https://www.github.com/AccessKit/accesskit/issues/119)) ([27d5c78](https://www.github.com/AccessKit/accesskit/commit/27d5c78afa0f8d1ae3b626265da8bccd3e5b09d1))
* String indices are always in UTF-8 code units ([#114](https://www.github.com/AccessKit/accesskit/issues/114)) ([386ca0a](https://www.github.com/AccessKit/accesskit/commit/386ca0a89c42fd201843f617b2fd6b6d1de77f59))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.3.0 to 0.4.0
* accesskit_consumer bumped from 0.3.0 to 0.4.0
## [0.3.0](https://www.github.com/AccessKit/accesskit/compare/accesskit_windows-v0.2.0...accesskit_windows-v0.3.0) (2021-12-29)
### ⚠ BREAKING CHANGES
* Drop `TreeUpdate::clear` (#96)
### Code Refactoring
* Drop `TreeUpdate::clear` ([#96](https://www.github.com/AccessKit/accesskit/issues/96)) ([38f520b](https://www.github.com/AccessKit/accesskit/commit/38f520b960c6db7b3927b369aee206ee6bc5e8aa))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* accesskit bumped from 0.2.0 to 0.3.0
* accesskit_consumer bumped from 0.2.0 to 0.3.0

1843
vendor/accesskit_windows/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

89
vendor/accesskit_windows/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,89 @@
# 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 = "2021"
rust-version = "1.77.2"
name = "accesskit_windows"
version = "0.25.0"
authors = ["The AccessKit contributors"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "AccessKit UI accessibility infrastructure: Windows adapter"
readme = "README.md"
keywords = [
"gui",
"ui",
"accessibility",
]
categories = ["gui"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/AccessKit/accesskit"
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
targets = []
[lib]
name = "accesskit_windows"
path = "src/lib.rs"
[[example]]
name = "hello_world"
path = "examples/hello_world.rs"
[dependencies.accesskit]
version = "0.18.0"
[dependencies.accesskit_consumer]
version = "0.27.0"
[dependencies.hashbrown]
version = "0.15"
features = ["default-hasher"]
default-features = false
[dependencies.paste]
version = "1.0"
[dependencies.static_assertions]
version = "1.1.0"
[dependencies.windows]
version = "0.58.0"
features = [
"implement",
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_Variant",
"Win32_UI_Accessibility",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_WindowsAndMessaging",
]
[dependencies.windows-core]
version = "0.58.0"
[dev-dependencies.once_cell]
version = "1.13.0"
[dev-dependencies.scopeguard]
version = "1.1.0"
[dev-dependencies.winit]
version = "0.30"

3
vendor/accesskit_windows/README.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# AccessKit Windows adapter
This is the Windows adapter for [AccessKit](https://accesskit.dev/). It exposes an AccessKit accessibility tree through the UI Automation API.

View File

@@ -0,0 +1,364 @@
// Based on the create_window sample in windows-samples-rs.
use accesskit::{
Action, ActionHandler, ActionRequest, ActivationHandler, Live, Node, NodeId, Rect, Role, Tree,
TreeUpdate,
};
use accesskit_windows::Adapter;
use once_cell::sync::Lazy;
use std::cell::RefCell;
use windows::{
core::*,
Win32::{
Foundation::*,
Graphics::Gdi::ValidateRect,
System::LibraryLoader::GetModuleHandleW,
UI::{Input::KeyboardAndMouse::*, WindowsAndMessaging::*},
},
};
static WINDOW_CLASS_ATOM: Lazy<u16> = Lazy::new(|| {
let class_name = w!("AccessKitTest");
let wc = WNDCLASSW {
hCursor: unsafe { LoadCursorW(None, IDC_ARROW) }.unwrap(),
hInstance: unsafe { GetModuleHandleW(None) }.unwrap().into(),
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc),
..Default::default()
};
let atom = unsafe { RegisterClassW(&wc) };
if atom == 0 {
panic!("{}", Error::from_win32());
}
atom
});
const WINDOW_TITLE: &str = "Hello world";
const WINDOW_ID: NodeId = NodeId(0);
const BUTTON_1_ID: NodeId = NodeId(1);
const BUTTON_2_ID: NodeId = NodeId(2);
const ANNOUNCEMENT_ID: NodeId = NodeId(3);
const INITIAL_FOCUS: NodeId = BUTTON_1_ID;
const BUTTON_1_RECT: Rect = Rect {
x0: 20.0,
y0: 20.0,
x1: 100.0,
y1: 60.0,
};
const BUTTON_2_RECT: Rect = Rect {
x0: 20.0,
y0: 60.0,
x1: 100.0,
y1: 100.0,
};
const SET_FOCUS_MSG: u32 = WM_USER;
const CLICK_MSG: u32 = WM_USER + 1;
fn build_button(id: NodeId, label: &str) -> Node {
let rect = match id {
BUTTON_1_ID => BUTTON_1_RECT,
BUTTON_2_ID => BUTTON_2_RECT,
_ => unreachable!(),
};
let mut node = Node::new(Role::Button);
node.set_bounds(rect);
node.set_label(label);
node.add_action(Action::Focus);
node.add_action(Action::Click);
node
}
fn build_announcement(text: &str) -> Node {
let mut node = Node::new(Role::Label);
node.set_value(text);
node.set_live(Live::Polite);
node
}
struct InnerWindowState {
focus: NodeId,
announcement: Option<String>,
}
impl InnerWindowState {
fn build_root(&mut self) -> Node {
let mut node = Node::new(Role::Window);
node.set_children(vec![BUTTON_1_ID, BUTTON_2_ID]);
if self.announcement.is_some() {
node.push_child(ANNOUNCEMENT_ID);
}
node
}
}
impl ActivationHandler for InnerWindowState {
fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
println!("Initial tree requested");
let root = self.build_root();
let button_1 = build_button(BUTTON_1_ID, "Button 1");
let button_2 = build_button(BUTTON_2_ID, "Button 2");
let tree = Tree::new(WINDOW_ID);
let mut result = TreeUpdate {
nodes: vec![
(WINDOW_ID, root),
(BUTTON_1_ID, button_1),
(BUTTON_2_ID, button_2),
],
tree: Some(tree),
focus: self.focus,
};
if let Some(announcement) = &self.announcement {
result
.nodes
.push((ANNOUNCEMENT_ID, build_announcement(announcement)));
}
Some(result)
}
}
struct WindowState {
adapter: RefCell<Adapter>,
inner_state: RefCell<InnerWindowState>,
}
impl WindowState {
fn set_focus(&self, focus: NodeId) {
self.inner_state.borrow_mut().focus = focus;
let mut adapter = self.adapter.borrow_mut();
if let Some(events) = adapter.update_if_active(|| TreeUpdate {
nodes: vec![],
tree: None,
focus,
}) {
drop(adapter);
events.raise();
}
}
fn press_button(&self, id: NodeId) {
let mut inner_state = self.inner_state.borrow_mut();
let text = if id == BUTTON_1_ID {
"You pressed button 1"
} else {
"You pressed button 2"
};
inner_state.announcement = Some(text.into());
let mut adapter = self.adapter.borrow_mut();
if let Some(events) = adapter.update_if_active(|| {
let announcement = build_announcement(text);
let root = inner_state.build_root();
TreeUpdate {
nodes: vec![(ANNOUNCEMENT_ID, announcement), (WINDOW_ID, root)],
tree: None,
focus: inner_state.focus,
}
}) {
drop(adapter);
drop(inner_state);
events.raise();
}
}
}
unsafe fn get_window_state(window: HWND) -> *const WindowState {
GetWindowLongPtrW(window, GWLP_USERDATA) as _
}
fn update_window_focus_state(window: HWND, is_focused: bool) {
let state = unsafe { &*get_window_state(window) };
let mut adapter = state.adapter.borrow_mut();
if let Some(events) = adapter.update_window_focus_state(is_focused) {
drop(adapter);
events.raise();
}
}
struct WindowCreateParams(NodeId);
struct SimpleActionHandler {
window: HWND,
}
unsafe impl Send for SimpleActionHandler {}
unsafe impl Sync for SimpleActionHandler {}
impl ActionHandler for SimpleActionHandler {
fn do_action(&mut self, request: ActionRequest) {
match request.action {
Action::Focus => {
unsafe {
PostMessageW(
self.window,
SET_FOCUS_MSG,
WPARAM(0),
LPARAM(request.target.0 as _),
)
}
.unwrap();
}
Action::Click => {
unsafe {
PostMessageW(
self.window,
CLICK_MSG,
WPARAM(0),
LPARAM(request.target.0 as _),
)
}
.unwrap();
}
_ => (),
}
}
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match message {
WM_NCCREATE => {
let create_struct: &CREATESTRUCTW = unsafe { &mut *(lparam.0 as *mut _) };
let create_params: Box<WindowCreateParams> =
unsafe { Box::from_raw(create_struct.lpCreateParams as _) };
let WindowCreateParams(initial_focus) = *create_params;
let inner_state = RefCell::new(InnerWindowState {
focus: initial_focus,
announcement: None,
});
let adapter = Adapter::new(window, false, SimpleActionHandler { window });
let state = Box::new(WindowState {
adapter: RefCell::new(adapter),
inner_state,
});
unsafe { SetWindowLongPtrW(window, GWLP_USERDATA, Box::into_raw(state) as _) };
unsafe { DefWindowProcW(window, message, wparam, lparam) }
}
WM_PAINT => {
unsafe { ValidateRect(window, None) }.unwrap();
LRESULT(0)
}
WM_DESTROY => {
let ptr = unsafe { SetWindowLongPtrW(window, GWLP_USERDATA, 0) };
if ptr != 0 {
drop(unsafe { Box::<WindowState>::from_raw(ptr as _) });
}
unsafe { PostQuitMessage(0) };
LRESULT(0)
}
WM_GETOBJECT => {
let state_ptr = unsafe { get_window_state(window) };
if state_ptr.is_null() {
// We need to be prepared to gracefully handle WM_GETOBJECT
// while the window is being destroyed; this can happen if
// the thread is using a COM STA.
return unsafe { DefWindowProcW(window, message, wparam, lparam) };
}
let state = unsafe { &*state_ptr };
let mut adapter = state.adapter.borrow_mut();
let mut inner_state = state.inner_state.borrow_mut();
let result = adapter.handle_wm_getobject(wparam, lparam, &mut *inner_state);
drop(inner_state);
drop(adapter);
result.map_or_else(
|| unsafe { DefWindowProcW(window, message, wparam, lparam) },
|result| result.into(),
)
}
WM_SETFOCUS | WM_EXITMENULOOP | WM_EXITSIZEMOVE => {
update_window_focus_state(window, true);
LRESULT(0)
}
WM_KILLFOCUS | WM_ENTERMENULOOP | WM_ENTERSIZEMOVE => {
update_window_focus_state(window, false);
LRESULT(0)
}
WM_KEYDOWN => match VIRTUAL_KEY(wparam.0 as u16) {
VK_TAB => {
let state = unsafe { &*get_window_state(window) };
let old_focus = state.inner_state.borrow().focus;
let new_focus = if old_focus == BUTTON_1_ID {
BUTTON_2_ID
} else {
BUTTON_1_ID
};
state.set_focus(new_focus);
LRESULT(0)
}
VK_SPACE => {
let state = unsafe { &*get_window_state(window) };
let id = state.inner_state.borrow().focus;
state.press_button(id);
LRESULT(0)
}
_ => unsafe { DefWindowProcW(window, message, wparam, lparam) },
},
SET_FOCUS_MSG => {
let id = NodeId(lparam.0 as _);
if id == BUTTON_1_ID || id == BUTTON_2_ID {
let state = unsafe { &*get_window_state(window) };
state.set_focus(id);
}
LRESULT(0)
}
CLICK_MSG => {
let id = NodeId(lparam.0 as _);
if id == BUTTON_1_ID || id == BUTTON_2_ID {
let state = unsafe { &*get_window_state(window) };
state.press_button(id);
}
LRESULT(0)
}
_ => unsafe { DefWindowProcW(window, message, wparam, lparam) },
}
}
fn create_window(title: &str, initial_focus: NodeId) -> Result<HWND> {
let create_params = Box::new(WindowCreateParams(initial_focus));
let window = unsafe {
CreateWindowExW(
Default::default(),
PCWSTR(*WINDOW_CLASS_ATOM as usize as _),
&HSTRING::from(title),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
None,
None,
GetModuleHandleW(None).unwrap(),
Some(Box::into_raw(create_params) as _),
)?
};
if window.is_invalid() {
return Err(Error::from_win32());
}
Ok(window)
}
fn main() -> Result<()> {
println!("This example has no visible GUI, and a keyboard interface:");
println!("- [Tab] switches focus between two logical buttons.");
println!("- [Space] 'presses' the button, adding static text in a live region announcing that it was pressed.");
println!("Enable Narrator with [Win]+[Ctrl]+[Enter] (or [Win]+[Enter] on older versions of Windows).");
let window = create_window(WINDOW_TITLE, INITIAL_FOCUS)?;
let _ = unsafe { ShowWindow(window, SW_SHOW) };
let mut message = MSG::default();
while unsafe { GetMessageW(&mut message, HWND::default(), 0, 0) }.into() {
let _ = unsafe { TranslateMessage(&message) };
unsafe { DispatchMessageW(&message) };
}
Ok(())
}

587
vendor/accesskit_windows/src/adapter.rs vendored Normal file
View File

@@ -0,0 +1,587 @@
// 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, Live, Node as NodeProvider, NodeId, Role, Tree as TreeData,
TreeUpdate,
};
use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler};
use hashbrown::{HashMap, HashSet};
use std::fmt::{Debug, Formatter};
use std::sync::{atomic::Ordering, Arc};
use windows::Win32::{
Foundation::*,
UI::{Accessibility::*, WindowsAndMessaging::*},
};
use crate::{
context::{ActionHandlerNoMut, ActionHandlerWrapper, Context},
filters::filter,
node::{NodeWrapper, PlatformNode},
util::QueuedEvent,
window_handle::WindowHandle,
};
fn focus_event(context: &Arc<Context>, node_id: NodeId) -> QueuedEvent {
let platform_node = PlatformNode::new(context, node_id);
let element: IRawElementProviderSimple = platform_node.into();
QueuedEvent::Simple {
element,
event_id: UIA_AutomationFocusChangedEventId,
}
}
struct AdapterChangeHandler<'a> {
context: &'a Arc<Context>,
queue: Vec<QueuedEvent>,
text_changed: HashSet<NodeId>,
selection_changed: HashMap<NodeId, SelectionChanges>,
}
impl<'a> AdapterChangeHandler<'a> {
fn new(context: &'a Arc<Context>) -> Self {
Self {
context,
queue: Vec::new(),
text_changed: HashSet::new(),
selection_changed: HashMap::new(),
}
}
}
impl AdapterChangeHandler<'_> {
fn insert_text_change_if_needed_parent(&mut self, node: Node) {
if !node.supports_text_ranges() {
return;
}
let id = node.id();
if self.text_changed.contains(&id) {
return;
}
let platform_node = PlatformNode::new(self.context, node.id());
let element: IRawElementProviderSimple = platform_node.into();
// Text change events must come before selection change
// events. It doesn't matter if text change events come
// before other events.
self.queue.insert(
0,
QueuedEvent::Simple {
element,
event_id: UIA_Text_TextChangedEventId,
},
);
self.text_changed.insert(id);
}
fn insert_text_change_if_needed(&mut self, node: &Node) {
if node.role() != Role::TextRun {
return;
}
if let Some(node) = node.filtered_parent(&filter) {
self.insert_text_change_if_needed_parent(node);
}
}
fn handle_selection_state_change(&mut self, node: &Node, is_selected: bool) {
// If `node` belongs to a selection container, then map the events with the
// selection container as the key because |FinalizeSelectionEvents| needs to
// determine whether or not there is only one element selected in order to
// optimize what platform events are sent.
let key = if let Some(container) = node.selection_container(&filter) {
container.id()
} else {
node.id()
};
let changes = self
.selection_changed
.entry(key)
.or_insert_with(|| SelectionChanges {
added_items: HashSet::new(),
removed_items: HashSet::new(),
});
if is_selected {
changes.added_items.insert(node.id());
} else {
changes.removed_items.insert(node.id());
}
}
fn enqueue_selection_changes(&mut self, tree: &Tree) {
let tree_state = tree.state();
for (id, changes) in self.selection_changed.iter() {
let Some(node) = tree_state.node_by_id(*id) else {
continue;
};
// Determine if `node` is a selection container with one selected child in
// order to optimize what platform events are sent.
let mut container = None;
let mut only_selected_child = None;
if node.is_container_with_selectable_children() {
container = Some(node);
for child in node.filtered_children(filter) {
if let Some(true) = child.is_selected() {
if only_selected_child.is_none() {
only_selected_child = Some(child);
} else {
only_selected_child = None;
break;
}
}
}
}
if let Some(only_selected_child) = only_selected_child {
self.queue.push(QueuedEvent::Simple {
element: PlatformNode::new(self.context, only_selected_child.id()).into(),
event_id: UIA_SelectionItem_ElementSelectedEventId,
});
self.queue.push(QueuedEvent::PropertyChanged {
element: PlatformNode::new(self.context, only_selected_child.id()).into(),
property_id: UIA_SelectionItemIsSelectedPropertyId,
old_value: false.into(),
new_value: true.into(),
});
for child_id in changes.removed_items.iter() {
let platform_node = PlatformNode::new(self.context, *child_id);
self.queue.push(QueuedEvent::PropertyChanged {
element: platform_node.into(),
property_id: UIA_SelectionItemIsSelectedPropertyId,
old_value: true.into(),
new_value: false.into(),
});
}
} else {
// Per UIA documentation, beyond the "invalidate limit" we're supposed to
// fire a 'SelectionInvalidated' event. The exact value isn't specified,
// but System.Windows.Automation.Provider uses a value of 20.
const INVALIDATE_LIMIT: usize = 20;
if let Some(container) = container.filter(|_| {
changes.added_items.len() + changes.removed_items.len() > INVALIDATE_LIMIT
}) {
let platform_node = PlatformNode::new(self.context, container.id());
self.queue.push(QueuedEvent::Simple {
element: platform_node.into(),
event_id: UIA_Selection_InvalidatedEventId,
});
} else {
let container_is_multiselectable =
container.is_some_and(|c| c.is_multiselectable());
for added_id in changes.added_items.iter() {
self.queue.push(QueuedEvent::Simple {
element: PlatformNode::new(self.context, *added_id).into(),
event_id: match container_is_multiselectable {
true => UIA_SelectionItem_ElementAddedToSelectionEventId,
false => UIA_SelectionItem_ElementSelectedEventId,
},
});
self.queue.push(QueuedEvent::PropertyChanged {
element: PlatformNode::new(self.context, *added_id).into(),
property_id: UIA_SelectionItemIsSelectedPropertyId,
old_value: false.into(),
new_value: true.into(),
});
}
for removed_id in changes.removed_items.iter() {
self.queue.push(QueuedEvent::Simple {
element: PlatformNode::new(self.context, *removed_id).into(),
event_id: UIA_SelectionItem_ElementRemovedFromSelectionEventId,
});
self.queue.push(QueuedEvent::PropertyChanged {
element: PlatformNode::new(self.context, *removed_id).into(),
property_id: UIA_SelectionItemIsSelectedPropertyId,
old_value: true.into(),
new_value: false.into(),
});
}
}
}
}
}
}
struct SelectionChanges {
added_items: HashSet<NodeId>,
removed_items: HashSet<NodeId>,
}
impl TreeChangeHandler for AdapterChangeHandler<'_> {
fn node_added(&mut self, node: &Node) {
self.insert_text_change_if_needed(node);
if filter(node) != FilterResult::Include {
return;
}
let wrapper = NodeWrapper(node);
if wrapper.name().is_some() && node.live() != Live::Off {
let platform_node = PlatformNode::new(self.context, node.id());
let element: IRawElementProviderSimple = platform_node.into();
self.queue.push(QueuedEvent::Simple {
element,
event_id: UIA_LiveRegionChangedEventId,
});
}
if wrapper.is_selection_item_pattern_supported() && wrapper.is_selected() {
self.handle_selection_state_change(node, true);
}
}
fn node_updated(&mut self, old_node: &Node, new_node: &Node) {
if old_node.raw_value() != new_node.raw_value() {
self.insert_text_change_if_needed(new_node);
}
let old_node_was_filtered_out = filter(old_node) != FilterResult::Include;
if filter(new_node) != FilterResult::Include {
if !old_node_was_filtered_out {
let old_wrapper = NodeWrapper(old_node);
if old_wrapper.is_selection_item_pattern_supported() && old_wrapper.is_selected() {
self.handle_selection_state_change(old_node, false);
}
}
return;
}
let platform_node = PlatformNode::new(self.context, new_node.id());
let element: IRawElementProviderSimple = platform_node.into();
let old_wrapper = NodeWrapper(old_node);
let new_wrapper = NodeWrapper(new_node);
new_wrapper.enqueue_property_changes(&mut self.queue, &element, &old_wrapper);
let new_name = new_wrapper.name();
if new_name.is_some()
&& new_node.live() != Live::Off
&& (new_node.live() != old_node.live()
|| old_node_was_filtered_out
|| new_name != old_wrapper.name())
{
self.queue.push(QueuedEvent::Simple {
element,
event_id: UIA_LiveRegionChangedEventId,
});
}
if new_wrapper.is_selection_item_pattern_supported()
&& (new_wrapper.is_selected() != old_wrapper.is_selected()
|| (old_node_was_filtered_out && new_wrapper.is_selected()))
{
self.handle_selection_state_change(new_node, new_wrapper.is_selected());
}
}
fn focus_moved(&mut self, _old_node: Option<&Node>, new_node: Option<&Node>) {
if let Some(new_node) = new_node {
self.queue.push(focus_event(self.context, new_node.id()));
}
}
fn node_removed(&mut self, node: &Node) {
self.insert_text_change_if_needed(node);
if filter(node) != FilterResult::Include {
return;
}
let wrapper = NodeWrapper(node);
if wrapper.is_selection_item_pattern_supported() {
self.handle_selection_state_change(node, false);
}
}
// TODO: handle other events (#20)
}
const PLACEHOLDER_ROOT_ID: NodeId = NodeId(0);
enum State {
Inactive {
hwnd: WindowHandle,
is_window_focused: bool,
action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
},
Placeholder(Arc<Context>),
Active(Arc<Context>),
}
impl Debug for State {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
State::Inactive {
hwnd,
is_window_focused,
action_handler: _,
} => f
.debug_struct("Inactive")
.field("hwnd", hwnd)
.field("is_window_focused", is_window_focused)
.field("action_handler", &"ActionHandler")
.finish(),
State::Placeholder(context) => f.debug_tuple("Placeholder").field(context).finish(),
State::Active(context) => f.debug_tuple("Active").field(context).finish(),
}
}
}
#[derive(Debug)]
pub struct Adapter {
state: State,
}
impl Adapter {
/// Creates a new Windows platform adapter.
///
/// The action handler may or may not be called on the thread that owns
/// the window.
///
/// This must not be called while handling the `WM_GETOBJECT` message,
/// because this function must initialize UI Automation before
/// that message is handled. This is necessary to prevent a race condition
/// that leads to nested `WM_GETOBJECT` messages and, in some cases,
/// assistive technologies not realizing that the window natively implements.
/// UIA. See [AccessKit issue #37](https://github.com/AccessKit/accesskit/issues/37)
/// for more details.
pub fn new(
hwnd: HWND,
is_window_focused: bool,
action_handler: impl 'static + ActionHandler + Send,
) -> Self {
Self::with_wrapped_action_handler(
hwnd,
is_window_focused,
Arc::new(ActionHandlerWrapper::new(action_handler)),
)
}
// Currently required by the test infrastructure
pub(crate) fn with_wrapped_action_handler(
hwnd: HWND,
is_window_focused: bool,
action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
) -> Self {
init_uia();
let state = State::Inactive {
hwnd: hwnd.into(),
is_window_focused,
action_handler,
};
Self { state }
}
/// 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.
///
/// If a [`QueuedEvents`] instance is returned, the caller must call
/// [`QueuedEvents::raise`] on it.
///
/// This method may be safely called on any thread, but refer to
/// [`QueuedEvents::raise`] for restrictions on the context in which
/// it should be called.
pub fn update_if_active(
&mut self,
update_factory: impl FnOnce() -> TreeUpdate,
) -> Option<QueuedEvents> {
match &self.state {
State::Inactive { .. } => None,
State::Placeholder(context) => {
let is_window_focused = context.read_tree().state().is_host_focused();
let tree = Tree::new(update_factory(), is_window_focused);
*context.tree.write().unwrap() = tree;
context.is_placeholder.store(false, Ordering::SeqCst);
let result = context
.read_tree()
.state()
.focus_id()
.map(|id| QueuedEvents(vec![focus_event(context, id)]));
self.state = State::Active(Arc::clone(context));
result
}
State::Active(context) => {
let mut handler = AdapterChangeHandler::new(context);
let mut tree = context.tree.write().unwrap();
tree.update_and_process_changes(update_factory(), &mut handler);
handler.enqueue_selection_changes(&tree);
Some(QueuedEvents(handler.queue))
}
}
}
/// Update the tree state based on whether the window is focused.
///
/// If a [`QueuedEvents`] instance is returned, the caller must call
/// [`QueuedEvents::raise`] on it.
///
/// This method may be safely called on any thread, but refer to
/// [`QueuedEvents::raise`] for restrictions on the context in which
/// it should be called.
pub fn update_window_focus_state(&mut self, is_focused: bool) -> Option<QueuedEvents> {
match &mut self.state {
State::Inactive {
is_window_focused, ..
} => {
*is_window_focused = is_focused;
None
}
State::Placeholder(context) => {
let mut handler = AdapterChangeHandler::new(context);
let mut tree = context.tree.write().unwrap();
tree.update_host_focus_state_and_process_changes(is_focused, &mut handler);
Some(QueuedEvents(handler.queue))
}
State::Active(context) => {
let mut handler = AdapterChangeHandler::new(context);
let mut tree = context.tree.write().unwrap();
tree.update_host_focus_state_and_process_changes(is_focused, &mut handler);
Some(QueuedEvents(handler.queue))
}
}
}
/// Handle the `WM_GETOBJECT` window message. The accessibility tree
/// is lazily initialized if necessary using the provided
/// [`ActivationHandler`] implementation.
///
/// This returns an `Option` so the caller can pass the message
/// to `DefWindowProc` if AccessKit decides not to handle it.
/// The optional value is an `Into<LRESULT>` rather than simply an `LRESULT`
/// so the necessary call to UIA, which may lead to a nested `WM_GETOBJECT`
/// message, can be done outside of any lock that the caller might hold
/// on the `Adapter` or window state, while still abstracting away
/// the details of that call to UIA.
pub fn handle_wm_getobject<H: ActivationHandler + ?Sized>(
&mut self,
wparam: WPARAM,
lparam: LPARAM,
activation_handler: &mut H,
) -> Option<impl Into<LRESULT>> {
// Don't bother with MSAA object IDs that are asking for something other
// than the client area of the window. DefWindowProc can handle those.
// First, cast the lparam to i32, to handle inconsistent conversion
// behavior in senders.
let objid = normalize_objid(lparam);
if objid < 0 && objid != UiaRootObjectId && objid != OBJID_CLIENT.0 {
return None;
}
let (hwnd, platform_node) = match &self.state {
State::Inactive {
hwnd,
is_window_focused,
action_handler,
} => match activation_handler.request_initial_tree() {
Some(initial_state) => {
let hwnd = *hwnd;
let tree = Tree::new(initial_state, *is_window_focused);
let context = Context::new(hwnd, tree, Arc::clone(action_handler), false);
let node_id = context.read_tree().state().root_id();
let platform_node = PlatformNode::new(&context, node_id);
self.state = State::Active(context);
(hwnd, platform_node)
}
None => {
let hwnd = *hwnd;
let placeholder_update = TreeUpdate {
nodes: vec![(PLACEHOLDER_ROOT_ID, NodeProvider::new(Role::Window))],
tree: Some(TreeData::new(PLACEHOLDER_ROOT_ID)),
focus: PLACEHOLDER_ROOT_ID,
};
let placeholder_tree = Tree::new(placeholder_update, *is_window_focused);
let context =
Context::new(hwnd, placeholder_tree, Arc::clone(action_handler), true);
let platform_node = PlatformNode::unspecified_root(&context);
self.state = State::Placeholder(context);
(hwnd, platform_node)
}
},
State::Placeholder(context) => (context.hwnd, PlatformNode::unspecified_root(context)),
State::Active(context) => {
let node_id = context.read_tree().state().root_id();
(context.hwnd, PlatformNode::new(context, node_id))
}
};
let el: IRawElementProviderSimple = platform_node.into();
Some(WmGetObjectResult {
hwnd,
wparam,
lparam,
el,
})
}
}
fn init_uia() {
// `UiaLookupId` is a cheap way of forcing UIA to initialize itself.
unsafe {
UiaLookupId(
AutomationIdentifierType_Property,
&ControlType_Property_GUID,
)
};
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn normalize_objid(lparam: LPARAM) -> i32 {
(lparam.0 & 0xFFFFFFFF) as _
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn normalize_objid(lparam: LPARAM) -> i32 {
lparam.0 as _
}
struct WmGetObjectResult {
hwnd: WindowHandle,
wparam: WPARAM,
lparam: LPARAM,
el: IRawElementProviderSimple,
}
impl From<WmGetObjectResult> for LRESULT {
fn from(this: WmGetObjectResult) -> Self {
unsafe { UiaReturnRawElementProvider(this.hwnd.0, this.wparam, this.lparam, &this.el) }
}
}
/// Events generated by a tree update.
#[must_use = "events must be explicitly raised"]
pub struct QueuedEvents(Vec<QueuedEvent>);
impl QueuedEvents {
/// Raise all queued events synchronously.
///
/// The window may receive `WM_GETOBJECT` messages during this call.
/// This means that any locks required by the `WM_GETOBJECT` handler
/// must not be held when this method is called.
///
/// This method should be called on the thread that owns the window.
/// It's not clear whether this is a strict requirement of UIA itself,
/// but based on the known behavior of UIA, MSAA, and some ATs,
/// it's strongly recommended.
pub fn raise(self) {
for event in self.0 {
match event {
QueuedEvent::Simple { element, event_id } => {
unsafe { UiaRaiseAutomationEvent(&element, event_id) }.unwrap();
}
QueuedEvent::PropertyChanged {
element,
property_id,
old_value,
new_value,
} => {
unsafe {
UiaRaiseAutomationPropertyChangedEvent(
&element,
property_id,
&old_value,
&new_value,
)
}
.unwrap();
}
}
}
}
}
// We explicitly want to allow the queued events to be sent to the UI thread,
// so implement Send even though windows-rs doesn't implement it for all
// contained types. This is safe because we're not using COM threading.
unsafe impl Send for QueuedEvents {}

75
vendor/accesskit_windows/src/context.rs vendored Normal file
View File

@@ -0,0 +1,75 @@
// 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::{ActionHandler, ActionRequest, Point};
use accesskit_consumer::Tree;
use std::fmt::{Debug, Formatter};
use std::sync::{atomic::AtomicBool, Arc, Mutex, RwLock, RwLockReadGuard};
use crate::{util::*, window_handle::WindowHandle};
pub(crate) trait ActionHandlerNoMut {
fn do_action(&self, request: ActionRequest);
}
pub(crate) struct ActionHandlerWrapper<H: ActionHandler + Send>(Mutex<H>);
impl<H: 'static + ActionHandler + Send> ActionHandlerWrapper<H> {
pub(crate) fn new(inner: H) -> Self {
Self(Mutex::new(inner))
}
}
impl<H: ActionHandler + Send> ActionHandlerNoMut for ActionHandlerWrapper<H> {
fn do_action(&self, request: ActionRequest) {
self.0.lock().unwrap().do_action(request)
}
}
pub(crate) struct Context {
pub(crate) hwnd: WindowHandle,
pub(crate) tree: RwLock<Tree>,
pub(crate) action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
pub(crate) is_placeholder: AtomicBool,
}
impl Debug for Context {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Context")
.field("hwnd", &self.hwnd)
.field("tree", &self.tree)
.field("action_handler", &"ActionHandler")
.field("is_placeholder", &self.is_placeholder)
.finish()
}
}
impl Context {
pub(crate) fn new(
hwnd: WindowHandle,
tree: Tree,
action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
is_placeholder: bool,
) -> Arc<Self> {
Arc::new(Self {
hwnd,
tree: RwLock::new(tree),
action_handler,
is_placeholder: AtomicBool::new(is_placeholder),
})
}
pub(crate) fn read_tree(&self) -> RwLockReadGuard<'_, Tree> {
self.tree.read().unwrap()
}
pub(crate) fn client_top_left(&self) -> Point {
client_top_left(self.hwnd)
}
pub(crate) fn do_action(&self, request: ActionRequest) {
self.action_handler.do_action(request);
}
}

View File

@@ -0,0 +1,8 @@
// 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.
pub(crate) use accesskit_consumer::{
common_filter as filter, common_filter_with_root_exception as filter_with_root_exception,
};

22
vendor/accesskit_windows/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2021 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 context;
mod filters;
mod node;
mod text;
mod util;
mod window_handle;
mod adapter;
pub use adapter::{Adapter, QueuedEvents};
mod subclass;
pub use subclass::SubclassingAdapter;
pub use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
#[cfg(test)]
mod tests;

1079
vendor/accesskit_windows/src/node.rs vendored Normal file

File diff suppressed because it is too large Load Diff

188
vendor/accesskit_windows/src/subclass.rs vendored Normal file
View File

@@ -0,0 +1,188 @@
// 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, TreeUpdate};
use std::{
cell::{Cell, RefCell},
ffi::c_void,
mem::transmute,
};
use windows::{
core::*,
Win32::{Foundation::*, UI::WindowsAndMessaging::*},
};
use crate::{Adapter, QueuedEvents};
fn win32_error() -> ! {
panic!("{}", Error::from_win32())
}
// Work around a difference between the SetWindowLongPtrW API definition
// in windows-rs on 32-bit and 64-bit Windows.
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
type LongPtr = isize;
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
type LongPtr = i32;
const PROP_NAME: PCWSTR = w!("AccessKitAdapter");
struct SubclassState {
adapter: Adapter,
activation_handler: Box<dyn ActivationHandler>,
}
struct SubclassImpl {
hwnd: HWND,
state: RefCell<SubclassState>,
prev_wnd_proc: WNDPROC,
window_destroyed: Cell<bool>,
}
extern "system" fn wnd_proc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
let handle = unsafe { GetPropW(window, PROP_NAME) };
let impl_ptr = handle.0 as *const SubclassImpl;
assert!(!impl_ptr.is_null());
let r#impl = unsafe { &*impl_ptr };
match message {
WM_GETOBJECT => {
let mut state = r#impl.state.borrow_mut();
let state_mut = &mut *state;
if let Some(result) = state_mut.adapter.handle_wm_getobject(
wparam,
lparam,
&mut *state_mut.activation_handler,
) {
drop(state);
return result.into();
}
}
WM_SETFOCUS | WM_EXITMENULOOP | WM_EXITSIZEMOVE => {
r#impl.update_window_focus_state(true);
}
WM_KILLFOCUS | WM_ENTERMENULOOP | WM_ENTERSIZEMOVE => {
r#impl.update_window_focus_state(false);
}
WM_NCDESTROY => {
r#impl.window_destroyed.set(true);
}
_ => (),
}
unsafe { CallWindowProcW(r#impl.prev_wnd_proc, window, message, wparam, lparam) }
}
impl SubclassImpl {
fn new(
hwnd: HWND,
activation_handler: impl 'static + ActivationHandler,
action_handler: impl 'static + ActionHandler + Send,
) -> Box<Self> {
let adapter = Adapter::new(hwnd, false, action_handler);
let state = RefCell::new(SubclassState {
adapter,
activation_handler: Box::new(activation_handler),
});
Box::new(Self {
hwnd,
state,
prev_wnd_proc: None,
window_destroyed: Cell::new(false),
})
}
fn install(&mut self) {
unsafe {
SetPropW(
self.hwnd,
PROP_NAME,
HANDLE(self as *const SubclassImpl as _),
)
}
.unwrap();
let result =
unsafe { SetWindowLongPtrW(self.hwnd, GWLP_WNDPROC, wnd_proc as *const c_void as _) };
if result == 0 {
win32_error();
}
self.prev_wnd_proc = unsafe { transmute::<LongPtr, WNDPROC>(result) };
}
fn update_window_focus_state(&self, is_focused: bool) {
let mut state = self.state.borrow_mut();
if let Some(events) = state.adapter.update_window_focus_state(is_focused) {
drop(state);
events.raise();
}
}
fn uninstall(&self) {
if self.window_destroyed.get() {
return;
}
let result = unsafe {
SetWindowLongPtrW(
self.hwnd,
GWLP_WNDPROC,
transmute::<WNDPROC, LongPtr>(self.prev_wnd_proc),
)
};
if result == 0 {
win32_error();
}
unsafe { RemovePropW(self.hwnd, PROP_NAME) }.unwrap();
}
}
/// Uses [Win32 subclassing] to handle `WM_GETOBJECT` messages on a window
/// that provides no other way of adding custom message handlers.
///
/// [Win32 subclassing]: https://docs.microsoft.com/en-us/windows/win32/controls/subclassing-overview
pub struct SubclassingAdapter(Box<SubclassImpl>);
impl SubclassingAdapter {
/// Creates a new Windows platform adapter using window subclassing.
/// This must be done before the window is shown or focused
/// for the first time.
///
/// This must be called on the thread that owns the window. The activation
/// handler will always be called on that thread. The action handler
/// may or may not be called on that thread.
pub fn new(
hwnd: HWND,
activation_handler: impl 'static + ActivationHandler,
action_handler: impl 'static + ActionHandler + Send,
) -> Self {
let mut r#impl = SubclassImpl::new(hwnd, activation_handler, action_handler);
r#impl.install();
Self(r#impl)
}
/// 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.
///
/// If a [`QueuedEvents`] instance is returned, the caller must call
/// [`QueuedEvents::raise`] on it.
pub fn update_if_active(
&mut self,
update_factory: impl FnOnce() -> TreeUpdate,
) -> Option<QueuedEvents> {
// SAFETY: We use `RefCell::borrow_mut` here, even though
// `RefCell::get_mut` is allowed (because this method takes
// a mutable self reference), just in case there's some way
// this method can be called from within the subclassed window
// procedure, e.g. via `ActivationHandler`.
let mut state = self.0.state.borrow_mut();
state.adapter.update_if_active(update_factory)
}
}
impl Drop for SubclassingAdapter {
fn drop(&mut self) {
self.0.uninstall();
}
}

View File

@@ -0,0 +1,336 @@
// 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};
use once_cell::sync::Lazy;
use std::{
cell::RefCell,
sync::{Arc, Condvar, Mutex},
thread,
time::Duration,
};
use windows as Windows;
use windows::{
core::*,
Win32::{
Foundation::*,
Graphics::Gdi::ValidateRect,
System::{Com::*, LibraryLoader::GetModuleHandleW},
UI::{Accessibility::*, WindowsAndMessaging::*},
},
};
use crate::window_handle::WindowHandle;
use super::{
context::{ActionHandlerNoMut, ActionHandlerWrapper},
Adapter,
};
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
static WINDOW_CLASS_ATOM: Lazy<u16> = Lazy::new(|| {
let class_name = w!("AccessKitTest");
let wc = WNDCLASSW {
hCursor: unsafe { LoadCursorW(None, IDC_ARROW) }.unwrap(),
hInstance: unsafe { GetModuleHandleW(None) }.unwrap().into(),
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc),
..Default::default()
};
let atom = unsafe { RegisterClassW(&wc) };
if atom == 0 {
panic!("{}", Error::from_win32());
}
atom
});
struct WindowState {
activation_handler: RefCell<Box<dyn ActivationHandler>>,
adapter: RefCell<Adapter>,
}
unsafe fn get_window_state(window: HWND) -> *const WindowState {
GetWindowLongPtrW(window, GWLP_USERDATA) as _
}
fn update_window_focus_state(window: HWND, is_focused: bool) {
let state = unsafe { &*get_window_state(window) };
let mut adapter = state.adapter.borrow_mut();
if let Some(events) = adapter.update_window_focus_state(is_focused) {
events.raise();
}
}
struct WindowCreateParams {
activation_handler: Box<dyn ActivationHandler>,
action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match message {
WM_NCCREATE => {
let create_struct: &CREATESTRUCTW = unsafe { &mut *(lparam.0 as *mut _) };
let create_params: Box<WindowCreateParams> =
unsafe { Box::from_raw(create_struct.lpCreateParams as _) };
let WindowCreateParams {
activation_handler,
action_handler,
} = *create_params;
let adapter = Adapter::with_wrapped_action_handler(window, false, action_handler);
let state = Box::new(WindowState {
activation_handler: RefCell::new(activation_handler),
adapter: RefCell::new(adapter),
});
unsafe { SetWindowLongPtrW(window, GWLP_USERDATA, Box::into_raw(state) as _) };
unsafe { DefWindowProcW(window, message, wparam, lparam) }
}
WM_PAINT => {
unsafe { ValidateRect(window, None) }.unwrap();
LRESULT(0)
}
WM_DESTROY => {
let ptr = unsafe { SetWindowLongPtrW(window, GWLP_USERDATA, 0) };
if ptr != 0 {
drop(unsafe { Box::<WindowState>::from_raw(ptr as _) });
}
unsafe { PostQuitMessage(0) };
LRESULT(0)
}
WM_GETOBJECT => {
let state_ptr = unsafe { get_window_state(window) };
if state_ptr.is_null() {
// We need to be prepared to gracefully handle WM_GETOBJECT
// while the window is being destroyed; this can happen if
// the thread is using a COM STA.
return unsafe { DefWindowProcW(window, message, wparam, lparam) };
}
let state = unsafe { &*state_ptr };
let mut adapter = state.adapter.borrow_mut();
let mut activation_handler = state.activation_handler.borrow_mut();
let result = adapter.handle_wm_getobject(wparam, lparam, &mut **activation_handler);
drop(activation_handler);
drop(adapter);
result.map_or_else(
|| unsafe { DefWindowProcW(window, message, wparam, lparam) },
|result| result.into(),
)
}
WM_SETFOCUS | WM_EXITMENULOOP | WM_EXITSIZEMOVE => {
update_window_focus_state(window, true);
LRESULT(0)
}
WM_KILLFOCUS | WM_ENTERMENULOOP | WM_ENTERSIZEMOVE => {
update_window_focus_state(window, false);
LRESULT(0)
}
_ => unsafe { DefWindowProcW(window, message, wparam, lparam) },
}
}
fn create_window(
title: &str,
activation_handler: impl 'static + ActivationHandler,
action_handler: impl 'static + ActionHandler + Send,
) -> Result<HWND> {
let create_params = Box::new(WindowCreateParams {
activation_handler: Box::new(activation_handler),
action_handler: Arc::new(ActionHandlerWrapper::new(action_handler)),
});
let window = unsafe {
CreateWindowExW(
Default::default(),
PCWSTR(*WINDOW_CLASS_ATOM as usize as _),
&HSTRING::from(title),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
None,
None,
GetModuleHandleW(None).unwrap(),
Some(Box::into_raw(create_params) as _),
)?
};
if window.is_invalid() {
return Err(Error::from_win32());
}
Ok(window)
}
pub(crate) struct Scope {
pub(crate) uia: IUIAutomation,
pub(crate) window: WindowHandle,
}
impl Scope {
pub(crate) fn show_and_focus_window(&self) {
let _ = unsafe { ShowWindow(self.window.0, SW_SHOW) };
let _ = unsafe { SetForegroundWindow(self.window.0) };
}
}
// It's not safe to run these UI-related tests concurrently.
pub(crate) static MUTEX: Mutex<()> = Mutex::new(());
pub(crate) fn scope<F>(
window_title: &str,
activation_handler: impl 'static + ActivationHandler + Send,
action_handler: impl 'static + ActionHandler + Send,
f: F,
) -> Result<()>
where
F: FnOnce(&Scope) -> Result<()>,
{
let _lock_guard = MUTEX.lock().unwrap();
let window_mutex: Mutex<Option<WindowHandle>> = Mutex::new(None);
let window_cv = Condvar::new();
thread::scope(|thread_scope| {
thread_scope.spawn(|| {
// We explicitly don't want to initialize COM on the provider thread,
// because we want to make sure that the provider side of UIA works
// even if COM is never initialized on the provider thread
// (as is the case if the window is never shown), or if COM is
// initialized after the window is shown (as is the case,
// at least on some Windows 10 machines, due to IME support).
let window = create_window(window_title, activation_handler, action_handler).unwrap();
{
let mut state = window_mutex.lock().unwrap();
*state = Some(window.into());
window_cv.notify_one();
}
let mut message = MSG::default();
while unsafe { GetMessageW(&mut message, HWND::default(), 0, 0) }.into() {
let _ = unsafe { TranslateMessage(&message) };
unsafe { DispatchMessageW(&message) };
}
});
let window = {
let state = window_mutex.lock().unwrap();
let mut state = if state.is_none() {
window_cv.wait(state).unwrap()
} else {
state
};
state.take().unwrap()
};
let _window_guard = scopeguard::guard((), |_| {
unsafe { PostMessageW(window.0, WM_CLOSE, WPARAM(0), LPARAM(0)) }.unwrap()
});
// We must initialize COM before creating the UIA client. The MTA option
// is cleaner by far, especially when we want to wait for a UIA event
// handler to be called, and there's no reason not to use it here.
// Note that we don't initialize COM this way on the provider thread,
// as explained above. It's also important that we let the provider
// thread do its forced initialization of UIA, in an environment
// where COM has not been initialized, before we create the UIA client,
// which also triggers UIA initialization, in a thread where COM
// _has_ been initialized. This way, we ensure that the provider side
// of UIA works even if it is set up in an environment where COM
// has not been initialized, and that this sequence of events
// doesn't prevent the UIA client from working.
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }.unwrap();
let _com_guard = scopeguard::guard((), |_| unsafe { CoUninitialize() });
let uia: IUIAutomation =
unsafe { CoCreateInstance(&CUIAutomation8, None, CLSCTX_INPROC_SERVER) }?;
let s = Scope { uia, window };
f(&s)
})
}
/// This must only be used to wrap UIA elements returned by a UIA client
/// that was created in the MTA. Those are safe to send between threads.
struct SendableUiaElement(IUIAutomationElement);
unsafe impl Send for SendableUiaElement {}
pub(crate) struct ReceivedFocusEvent {
mutex: Mutex<Option<SendableUiaElement>>,
cv: Condvar,
}
impl ReceivedFocusEvent {
fn new() -> Arc<Self> {
Arc::new(Self {
mutex: Mutex::new(None),
cv: Condvar::new(),
})
}
pub(crate) fn wait<F>(&self, f: F) -> IUIAutomationElement
where
F: Fn(&IUIAutomationElement) -> bool,
{
let mut received = self.mutex.lock().unwrap();
loop {
if let Some(SendableUiaElement(element)) = received.take() {
if f(&element) {
return element;
}
}
let (lock, result) = self.cv.wait_timeout(received, DEFAULT_TIMEOUT).unwrap();
assert!(!result.timed_out());
received = lock;
}
}
fn put(&self, element: IUIAutomationElement) {
let mut received = self.mutex.lock().unwrap();
*received = Some(SendableUiaElement(element));
self.cv.notify_one();
}
}
#[implement(Windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler)]
pub(crate) struct FocusEventHandler {
received: Arc<ReceivedFocusEvent>,
}
// Because we create a UIA client in the COM MTA, this event handler
// _will_ be called from a different thread, and possibly multiple threads
// at once.
static_assertions::assert_impl_all!(FocusEventHandler: Send, Sync);
impl FocusEventHandler {
#[allow(clippy::new_ret_no_self)] // it does return self, but wrapped
pub(crate) fn new() -> (
IUIAutomationFocusChangedEventHandler,
Arc<ReceivedFocusEvent>,
) {
let received = ReceivedFocusEvent::new();
(
Self {
received: Arc::clone(&received),
}
.into(),
received,
)
}
}
#[allow(non_snake_case)]
impl IUIAutomationFocusChangedEventHandler_Impl for FocusEventHandler_Impl {
fn HandleFocusChangedEvent(&self, sender: Option<&IUIAutomationElement>) -> Result<()> {
self.received.put(sender.unwrap().clone());
Ok(())
}
}
mod simple;
mod subclassed;

View File

@@ -0,0 +1,196 @@
// 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::{
Action, ActionHandler, ActionRequest, ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
};
use windows::{core::*, Win32::UI::Accessibility::*};
use super::*;
const WINDOW_TITLE: &str = "Simple test";
const WINDOW_ID: NodeId = NodeId(0);
const BUTTON_1_ID: NodeId = NodeId(1);
const BUTTON_2_ID: NodeId = NodeId(2);
fn make_button(label: &str) -> Node {
let mut node = Node::new(Role::Button);
node.set_label(label);
node.add_action(Action::Focus);
node
}
fn get_initial_state() -> TreeUpdate {
let mut root = Node::new(Role::Window);
root.set_children(vec![BUTTON_1_ID, BUTTON_2_ID]);
let button_1 = make_button("Button 1");
let button_2 = make_button("Button 2");
TreeUpdate {
nodes: vec![
(WINDOW_ID, root),
(BUTTON_1_ID, button_1),
(BUTTON_2_ID, button_2),
],
tree: Some(Tree::new(WINDOW_ID)),
focus: BUTTON_1_ID,
}
}
pub struct NullActionHandler;
impl ActionHandler for NullActionHandler {
fn do_action(&mut self, _request: ActionRequest) {}
}
struct SimpleActivationHandler;
impl ActivationHandler for SimpleActivationHandler {
fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
Some(get_initial_state())
}
}
fn scope<F>(f: F) -> Result<()>
where
F: FnOnce(&Scope) -> Result<()>,
{
super::scope(
WINDOW_TITLE,
SimpleActivationHandler {},
NullActionHandler {},
f,
)
}
#[test]
fn has_native_uia() -> Result<()> {
scope(|s| {
let has_native_uia: bool = unsafe { UiaHasServerSideProvider(s.window.0) }.into();
assert!(has_native_uia);
Ok(())
})
}
fn is_button_named(element: &IUIAutomationElement, expected_name: &str) -> bool {
let control_type = unsafe { element.CurrentControlType() }.unwrap();
let name = unsafe { element.CurrentName() }.unwrap();
let name: String = name.try_into().unwrap();
control_type == UIA_ButtonControlTypeId && name == expected_name
}
fn is_button_1(element: &IUIAutomationElement) -> bool {
is_button_named(element, "Button 1")
}
fn is_button_2(element: &IUIAutomationElement) -> bool {
is_button_named(element, "Button 2")
}
#[test]
fn navigation() -> Result<()> {
scope(|s| {
let root = unsafe { s.uia.ElementFromHandle(s.window.0) }?;
let walker = unsafe { s.uia.ControlViewWalker() }?;
// The children of the window include the children that we provide,
// but also the title bar provided by the OS. We know that our own
// children are in the order we specified, but we don't know
// their position relative to the title bar. In fact, Windows
// has changed this in the past.
//
// Note that a UIA client would normally use the UIA condition feature
// to traverse the tree looking for an element that meets
// some condition. But we want to be explicit about navigating
// forward (and backward below) through only the immediate children.
// We'll accept the performance hit of multiple cross-thread calls
// (insignificant in this case) to achieve that.
let mut button_1_forward: Option<IUIAutomationElement> = None;
let mut wrapped_child = unsafe { walker.GetFirstChildElement(&root) };
while let Ok(child) = wrapped_child {
if is_button_1(&child) {
button_1_forward = Some(child);
break;
}
wrapped_child = unsafe { walker.GetNextSiblingElement(&child) };
}
let button_1_forward = button_1_forward.unwrap();
let mut button_2_forward: Option<IUIAutomationElement> = None;
let wrapped_child = unsafe { walker.GetNextSiblingElement(&button_1_forward) };
if let Ok(child) = wrapped_child {
if is_button_2(&child) {
button_2_forward = Some(child);
}
}
let _button_2_forward = button_2_forward.unwrap();
let mut button_2_backward: Option<IUIAutomationElement> = None;
let mut wrapped_child = unsafe { walker.GetLastChildElement(&root) };
while let Ok(child) = wrapped_child {
if is_button_2(&child) {
button_2_backward = Some(child);
break;
}
wrapped_child = unsafe { walker.GetPreviousSiblingElement(&child) };
}
let button_2_backward = button_2_backward.unwrap();
let mut button_1_backward: Option<IUIAutomationElement> = None;
let wrapped_child = unsafe { walker.GetPreviousSiblingElement(&button_2_backward) };
if let Ok(child) = wrapped_child {
if is_button_1(&child) {
button_1_backward = Some(child);
}
}
let button_1_backward = button_1_backward.unwrap();
let equal: bool =
unsafe { s.uia.CompareElements(&button_1_forward, &button_1_backward) }?.into();
assert!(equal);
let parent = unsafe { walker.GetParentElement(&button_1_forward) }?;
let equal: bool = unsafe { s.uia.CompareElements(&parent, &root) }?.into();
assert!(equal);
let desktop_root = unsafe { s.uia.GetRootElement() }?;
let parent = unsafe { walker.GetParentElement(&root) }?;
let equal: bool = unsafe { s.uia.CompareElements(&parent, &desktop_root) }?.into();
assert!(equal);
let wrapped_child = unsafe { walker.GetFirstChildElement(&button_1_forward) };
assert_eq!(Err(Error::empty()), wrapped_child);
let wrapped_child = unsafe { walker.GetLastChildElement(&button_1_forward) };
assert_eq!(Err(Error::empty()), wrapped_child);
Ok(())
})
}
#[test]
fn focus() -> Result<()> {
scope(|s| {
let (focus_event_handler, received_focus_event) = FocusEventHandler::new();
unsafe {
s.uia
.AddFocusChangedEventHandler(None, &focus_event_handler)
}?;
s.show_and_focus_window();
let focus_from_event = received_focus_event.wait(is_button_1);
let has_focus: bool = unsafe { focus_from_event.CurrentHasKeyboardFocus() }?.into();
assert!(has_focus);
let is_focusable: bool = unsafe { focus_from_event.CurrentIsKeyboardFocusable() }?.into();
assert!(is_focusable);
let focus_on_demand = unsafe { s.uia.GetFocusedElement() }?;
let equal: bool =
unsafe { s.uia.CompareElements(&focus_from_event, &focus_on_demand) }?.into();
assert!(equal);
Ok(())
})
}

View File

@@ -0,0 +1,103 @@
// 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::{
Action, ActionHandler, ActionRequest, ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
};
use windows::Win32::{Foundation::*, UI::Accessibility::*};
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
platform::windows::EventLoopBuilderExtWindows,
raw_window_handle::{HasWindowHandle, RawWindowHandle},
window::{Window, WindowId},
};
use super::MUTEX;
use crate::SubclassingAdapter;
const WINDOW_TITLE: &str = "Simple test";
const WINDOW_ID: NodeId = NodeId(0);
const BUTTON_1_ID: NodeId = NodeId(1);
const BUTTON_2_ID: NodeId = NodeId(2);
fn make_button(label: &str) -> Node {
let mut node = Node::new(Role::Button);
node.set_label(label);
node.add_action(Action::Focus);
node
}
fn get_initial_state() -> TreeUpdate {
let mut root = Node::new(Role::Window);
root.set_children(vec![BUTTON_1_ID, BUTTON_2_ID]);
let button_1 = make_button("Button 1");
let button_2 = make_button("Button 2");
TreeUpdate {
nodes: vec![
(WINDOW_ID, root),
(BUTTON_1_ID, button_1),
(BUTTON_2_ID, button_2),
],
tree: Some(Tree::new(WINDOW_ID)),
focus: BUTTON_1_ID,
}
}
pub struct NullActionHandler;
impl ActionHandler for NullActionHandler {
fn do_action(&mut self, _request: ActionRequest) {}
}
struct SimpleActivationHandler;
impl ActivationHandler for SimpleActivationHandler {
fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
Some(get_initial_state())
}
}
// This module uses winit for the purpose of testing with a real third-party
// window implementation that we don't control.
struct TestApplication;
impl ApplicationHandler<()> for TestApplication {
fn window_event(&mut self, _: &ActiveEventLoop, _: WindowId, _: WindowEvent) {}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title(WINDOW_TITLE)
.with_visible(false);
let window = event_loop.create_window(window_attributes).unwrap();
let hwnd = match window.window_handle().unwrap().as_raw() {
RawWindowHandle::Win32(handle) => HWND(handle.hwnd.get() as *mut core::ffi::c_void),
RawWindowHandle::WinRt(_) => unimplemented!(),
_ => unreachable!(),
};
let adapter =
SubclassingAdapter::new(hwnd, SimpleActivationHandler {}, NullActionHandler {});
assert!(unsafe { UiaHasServerSideProvider(hwnd) }.as_bool());
drop(window);
drop(adapter);
event_loop.exit();
}
}
#[test]
fn has_native_uia() {
// This test is simple enough that we know it's fine to run entirely
// on one thread, so we don't need a full multithreaded test harness.
// Still, we must prevent this test from running concurrently with other
// tests, especially the focus test.
let _lock_guard = MUTEX.lock().unwrap();
let event_loop = EventLoop::builder().with_any_thread(true).build().unwrap();
let mut state = TestApplication {};
event_loop.run_app(&mut state).unwrap();
}

601
vendor/accesskit_windows/src/text.rs vendored Normal file
View File

@@ -0,0 +1,601 @@
// 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.
#![allow(non_upper_case_globals)]
use accesskit::{Action, ActionData, ActionRequest};
use accesskit_consumer::{
Node, TextPosition as Position, TextRange as Range, TreeState, WeakTextRange as WeakRange,
};
use std::sync::{Arc, RwLock, Weak};
use windows::{
core::*,
Win32::{Foundation::*, System::Com::*, UI::Accessibility::*},
};
use crate::{context::Context, node::PlatformNode, util::*};
fn upgrade_range<'a>(weak: &WeakRange, tree_state: &'a TreeState) -> Result<Range<'a>> {
if let Some(range) = weak.upgrade(tree_state) {
Ok(range)
} else {
Err(element_not_available())
}
}
fn upgrade_range_node<'a>(weak: &WeakRange, tree_state: &'a TreeState) -> Result<Node<'a>> {
if let Some(node) = weak.upgrade_node(tree_state) {
Ok(node)
} else {
Err(element_not_available())
}
}
fn weak_comparable_position_from_endpoint(
range: &WeakRange,
endpoint: TextPatternRangeEndpoint,
) -> Result<&(Vec<usize>, usize)> {
match endpoint {
TextPatternRangeEndpoint_Start => Ok(range.start_comparable()),
TextPatternRangeEndpoint_End => Ok(range.end_comparable()),
_ => Err(invalid_arg()),
}
}
fn position_from_endpoint<'a>(
range: &Range<'a>,
endpoint: TextPatternRangeEndpoint,
) -> Result<Position<'a>> {
match endpoint {
TextPatternRangeEndpoint_Start => Ok(range.start()),
TextPatternRangeEndpoint_End => Ok(range.end()),
_ => Err(invalid_arg()),
}
}
fn set_endpoint_position<'a>(
range: &mut Range<'a>,
endpoint: TextPatternRangeEndpoint,
pos: Position<'a>,
) -> Result<()> {
match endpoint {
TextPatternRangeEndpoint_Start => {
range.set_start(pos);
}
TextPatternRangeEndpoint_End => {
range.set_end(pos);
}
_ => {
return Err(invalid_arg());
}
}
Ok(())
}
fn back_to_unit_start(start: Position, unit: TextUnit) -> Result<Position> {
match unit {
TextUnit_Character => {
// If we get here, this position is at the start of a non-degenerate
// range, so it's always at the start of a character.
debug_assert!(!start.is_document_end());
Ok(start)
}
TextUnit_Format => {
if start.is_format_start() {
Ok(start)
} else {
Ok(start.backward_to_format_start())
}
}
TextUnit_Word => {
if start.is_word_start() {
Ok(start)
} else {
Ok(start.backward_to_word_start())
}
}
TextUnit_Line => {
if start.is_line_start() {
Ok(start)
} else {
Ok(start.backward_to_line_start())
}
}
TextUnit_Paragraph => {
if start.is_paragraph_start() {
Ok(start)
} else {
Ok(start.backward_to_paragraph_start())
}
}
TextUnit_Page => {
if start.is_page_start() {
Ok(start)
} else {
Ok(start.backward_to_page_start())
}
}
TextUnit_Document => {
if start.is_document_start() {
Ok(start)
} else {
Ok(start.document_start())
}
}
_ => Err(invalid_arg()),
}
}
fn move_forward_to_start(pos: Position, unit: TextUnit) -> Result<Position> {
match unit {
TextUnit_Character => Ok(pos.forward_to_character_start()),
TextUnit_Format => Ok(pos.forward_to_format_start()),
TextUnit_Word => Ok(pos.forward_to_word_start()),
TextUnit_Line => Ok(pos.forward_to_line_start()),
TextUnit_Paragraph => Ok(pos.forward_to_paragraph_start()),
TextUnit_Page => Ok(pos.forward_to_page_start()),
TextUnit_Document => Ok(pos.document_end()),
_ => Err(invalid_arg()),
}
}
fn move_forward_to_end(pos: Position, unit: TextUnit) -> Result<Position> {
match unit {
TextUnit_Character => Ok(pos.forward_to_character_end()),
TextUnit_Format => Ok(pos.forward_to_format_end()),
TextUnit_Word => Ok(pos.forward_to_word_end()),
TextUnit_Line => Ok(pos.forward_to_line_end()),
TextUnit_Paragraph => Ok(pos.forward_to_paragraph_end()),
TextUnit_Page => Ok(pos.forward_to_page_end()),
TextUnit_Document => Ok(pos.document_end()),
_ => Err(invalid_arg()),
}
}
fn move_backward(pos: Position, unit: TextUnit) -> Result<Position> {
match unit {
TextUnit_Character => Ok(pos.backward_to_character_start()),
TextUnit_Format => Ok(pos.backward_to_format_start()),
TextUnit_Word => Ok(pos.backward_to_word_start()),
TextUnit_Line => Ok(pos.backward_to_line_start()),
TextUnit_Paragraph => Ok(pos.backward_to_paragraph_start()),
TextUnit_Page => Ok(pos.backward_to_page_start()),
TextUnit_Document => Ok(pos.document_start()),
_ => Err(invalid_arg()),
}
}
fn move_position(
mut pos: Position,
unit: TextUnit,
to_end: bool,
count: i32,
) -> Result<(Position, i32)> {
let forward = count > 0;
let count = count.abs();
let mut moved = 0i32;
for _ in 0..count {
let at_end = if forward {
pos.is_document_end()
} else {
pos.is_document_start()
};
if at_end {
break;
}
pos = if forward {
if to_end {
move_forward_to_end(pos, unit)
} else {
move_forward_to_start(pos, unit)
}
} else {
move_backward(pos, unit)
}?;
moved += 1;
}
if !forward {
moved = -moved;
}
Ok((pos, moved))
}
#[implement(ITextRangeProvider)]
pub(crate) struct PlatformRange {
context: Weak<Context>,
state: RwLock<WeakRange>,
}
impl PlatformRange {
pub(crate) fn new(context: &Weak<Context>, range: Range) -> Self {
Self {
context: context.clone(),
state: RwLock::new(range.downgrade()),
}
}
fn upgrade_context(&self) -> Result<Arc<Context>> {
upgrade(&self.context)
}
fn with_tree_state_and_context<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&TreeState, &Context) -> Result<T>,
{
let context = self.upgrade_context()?;
let tree = context.read_tree();
f(tree.state(), &context)
}
fn with_tree_state<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&TreeState) -> Result<T>,
{
self.with_tree_state_and_context(|state, _| f(state))
}
fn upgrade_node<'a>(&self, tree_state: &'a TreeState) -> Result<Node<'a>> {
let state = self.state.read().unwrap();
upgrade_range_node(&state, tree_state)
}
fn with_node<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(Node) -> Result<T>,
{
self.with_tree_state(|tree_state| {
let node = self.upgrade_node(tree_state)?;
f(node)
})
}
fn upgrade_for_read<'a>(&self, tree_state: &'a TreeState) -> Result<Range<'a>> {
let state = self.state.read().unwrap();
upgrade_range(&state, tree_state)
}
fn read_with_context<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(Range, &Context) -> Result<T>,
{
self.with_tree_state_and_context(|tree_state, context| {
let range = self.upgrade_for_read(tree_state)?;
f(range, context)
})
}
fn read<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(Range) -> Result<T>,
{
self.read_with_context(|range, _| f(range))
}
fn write<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&mut Range) -> Result<T>,
{
self.with_tree_state(|tree_state| {
let mut state = self.state.write().unwrap();
let mut range = upgrade_range(&state, tree_state)?;
let result = f(&mut range);
*state = range.downgrade();
result
})
}
fn do_action<F>(&self, f: F) -> Result<()>
where
for<'a> F: FnOnce(Range<'a>) -> ActionRequest,
{
let context = self.upgrade_context()?;
let tree = context.read_tree();
let range = self.upgrade_for_read(tree.state())?;
let request = f(range);
drop(tree);
context.do_action(request);
Ok(())
}
fn require_same_context(&self, other: &PlatformRange) -> Result<()> {
if self.context.ptr_eq(&other.context) {
Ok(())
} else {
Err(invalid_arg())
}
}
}
impl Clone for PlatformRange {
fn clone(&self) -> Self {
PlatformRange {
context: self.context.clone(),
state: RwLock::new(self.state.read().unwrap().clone()),
}
}
}
// Some text range methods take another text range interface pointer as a
// parameter. We need to cast these interface pointers to their underlying
// implementations. We assume that AccessKit is the only UIA provider
// within this process. This seems a safe assumption for most AccessKit users.
#[allow(non_snake_case)]
impl ITextRangeProvider_Impl for PlatformRange_Impl {
fn Clone(&self) -> Result<ITextRangeProvider> {
Ok(self.this.clone().into())
}
fn Compare(&self, other: Option<&ITextRangeProvider>) -> Result<BOOL> {
let other = unsafe { required_param(other)?.as_impl() };
Ok((self.context.ptr_eq(&other.context)
&& *self.state.read().unwrap() == *other.state.read().unwrap())
.into())
}
fn CompareEndpoints(
&self,
endpoint: TextPatternRangeEndpoint,
other: Option<&ITextRangeProvider>,
other_endpoint: TextPatternRangeEndpoint,
) -> Result<i32> {
let other = unsafe { required_param(other)?.as_impl() };
if std::ptr::eq(other as *const _, &self.this as *const _) {
// Comparing endpoints within the same range can be done
// safely without upgrading the range. This allows ATs
// to determine whether an old range is degenerate even if
// that range is no longer valid.
let state = self.state.read().unwrap();
let other_state = other.state.read().unwrap();
let pos = weak_comparable_position_from_endpoint(&state, endpoint)?;
let other_pos = weak_comparable_position_from_endpoint(&other_state, other_endpoint)?;
let result = pos.cmp(other_pos);
return Ok(result as i32);
}
self.require_same_context(other)?;
self.with_tree_state(|tree_state| {
let range = self.upgrade_for_read(tree_state)?;
let other_range = other.upgrade_for_read(tree_state)?;
if range.node().id() != other_range.node().id() {
return Err(invalid_arg());
}
let pos = position_from_endpoint(&range, endpoint)?;
let other_pos = position_from_endpoint(&other_range, other_endpoint)?;
let result = pos.partial_cmp(&other_pos).unwrap();
Ok(result as i32)
})
}
fn ExpandToEnclosingUnit(&self, unit: TextUnit) -> Result<()> {
if unit == TextUnit_Document {
// Handle document as a special case so we can get to a document
// range even if the current endpoints are now invalid.
// Based on observed behavior, Narrator needs this ability.
return self.with_tree_state(|tree_state| {
let mut state = self.state.write().unwrap();
let node = upgrade_range_node(&state, tree_state)?;
*state = node.document_range().downgrade();
Ok(())
});
}
self.write(|range| {
let start = range.start();
if unit == TextUnit_Character && start.is_document_end() {
// We know from experimentation that some Windows ATs
// expect ExpandToEnclosingUnit(TextUnit_Character)
// to do nothing if the range is degenerate at the end
// of the document.
return Ok(());
}
let start = back_to_unit_start(start, unit)?;
range.set_start(start);
if !start.is_document_end() {
let end = move_forward_to_end(start, unit)?;
range.set_end(end);
}
Ok(())
})
}
fn FindAttribute(
&self,
_id: UIA_TEXTATTRIBUTE_ID,
_value: &VARIANT,
_backward: BOOL,
) -> Result<ITextRangeProvider> {
// TODO: implement when we support variable formatting (part of rich text)
// Justification: JUCE doesn't implement this.
Err(Error::empty())
}
fn FindText(
&self,
_text: &BSTR,
_backward: BOOL,
_ignore_case: BOOL,
) -> Result<ITextRangeProvider> {
// TODO: implement when there's a real-world use case that requires it
// Justification: Quorum doesn't implement this and is being used
// by blind students.
Err(Error::empty())
}
fn GetAttributeValue(&self, id: UIA_TEXTATTRIBUTE_ID) -> Result<VARIANT> {
match id {
UIA_IsReadOnlyAttributeId => {
// TBD: do we ever want to support mixed read-only/editable text?
self.with_node(|node| {
let value = node.is_read_only();
Ok(value.into())
})
}
UIA_CaretPositionAttributeId => self.read(|range| {
let mut value = CaretPosition_Unknown;
if range.is_degenerate() {
let pos = range.start();
if pos.is_line_start() {
value = CaretPosition_BeginningOfLine;
} else if pos.is_line_end() {
value = CaretPosition_EndOfLine;
}
}
Ok(value.0.into())
}),
// TODO: implement more attributes
_ => {
let value = unsafe { UiaGetReservedNotSupportedValue() }.unwrap();
Ok(value.into())
}
}
}
fn GetBoundingRectangles(&self) -> Result<*mut SAFEARRAY> {
self.read_with_context(|range, context| {
let rects = range.bounding_boxes();
if rects.is_empty() {
return Ok(std::ptr::null_mut());
}
let client_top_left = context.client_top_left();
let mut result = Vec::<f64>::with_capacity(rects.len() * 4);
for rect in rects {
result.push(rect.x0 + client_top_left.x);
result.push(rect.y0 + client_top_left.y);
result.push(rect.width());
result.push(rect.height());
}
Ok(safe_array_from_f64_slice(&result))
})
}
fn GetEnclosingElement(&self) -> Result<IRawElementProviderSimple> {
self.with_node(|node| {
// Revisit this if we eventually support embedded objects.
Ok(PlatformNode {
context: self.context.clone(),
node_id: Some(node.id()),
}
.into())
})
}
fn GetText(&self, _max_length: i32) -> Result<BSTR> {
// The Microsoft docs imply that the provider isn't _required_
// to truncate text at the max length, so we just ignore it.
self.read(|range| {
let mut result = WideString::default();
range.write_text(&mut result).unwrap();
Ok(result.into())
})
}
fn Move(&self, unit: TextUnit, count: i32) -> Result<i32> {
self.write(|range| {
let degenerate = range.is_degenerate();
let start = range.start();
let start = if degenerate {
start
} else {
back_to_unit_start(start, unit)?
};
let (start, moved) = move_position(start, unit, false, count)?;
if moved != 0 {
range.set_start(start);
let end = if degenerate || start.is_document_end() {
start
} else {
move_forward_to_end(start, unit)?
};
range.set_end(end);
}
Ok(moved)
})
}
fn MoveEndpointByUnit(
&self,
endpoint: TextPatternRangeEndpoint,
unit: TextUnit,
count: i32,
) -> Result<i32> {
self.write(|range| {
let pos = position_from_endpoint(range, endpoint)?;
let (pos, moved) =
move_position(pos, unit, endpoint == TextPatternRangeEndpoint_End, count)?;
set_endpoint_position(range, endpoint, pos)?;
Ok(moved)
})
}
fn MoveEndpointByRange(
&self,
endpoint: TextPatternRangeEndpoint,
other: Option<&ITextRangeProvider>,
other_endpoint: TextPatternRangeEndpoint,
) -> Result<()> {
let other = unsafe { required_param(other)?.as_impl() };
self.require_same_context(other)?;
// We have to obtain the tree state and ranges manually to avoid
// lifetime issues, and work with the two locks in a specific order
// to avoid deadlock.
self.with_tree_state(|tree_state| {
let other_range = other.upgrade_for_read(tree_state)?;
let mut state = self.state.write().unwrap();
let mut range = upgrade_range(&state, tree_state)?;
if range.node().id() != other_range.node().id() {
return Err(invalid_arg());
}
let pos = position_from_endpoint(&other_range, other_endpoint)?;
set_endpoint_position(&mut range, endpoint, pos)?;
*state = range.downgrade();
Ok(())
})
}
fn Select(&self) -> Result<()> {
self.do_action(|range| ActionRequest {
action: Action::SetTextSelection,
target: range.node().id(),
data: Some(ActionData::SetTextSelection(range.to_text_selection())),
})
}
fn AddToSelection(&self) -> Result<()> {
// AccessKit doesn't support multiple text selections.
Err(invalid_operation())
}
fn RemoveFromSelection(&self) -> Result<()> {
// AccessKit doesn't support multiple text selections.
Err(invalid_operation())
}
fn ScrollIntoView(&self, align_to_top: BOOL) -> Result<()> {
self.do_action(|range| {
let position = if align_to_top.into() {
range.start()
} else {
range.end()
};
ActionRequest {
action: Action::ScrollIntoView,
target: position.inner_node().id(),
data: None,
}
})
}
fn GetChildren(&self) -> Result<*mut SAFEARRAY> {
// We don't support embedded objects in text.
Ok(safe_array_from_com_slice(&[]))
}
}
// Ensures that `PlatformRange` is actually safe to use in the free-threaded
// manner that we advertise via `ProviderOptions`.
#[test]
fn platform_range_impl_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<PlatformRange>();
}

272
vendor/accesskit_windows/src/util.rs vendored Normal file
View File

@@ -0,0 +1,272 @@
// 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::Point;
use accesskit_consumer::TreeState;
use std::{
fmt::{self, Write},
sync::{Arc, Weak},
};
use windows::{
core::*,
Win32::{
Foundation::*,
Graphics::Gdi::*,
System::{Com::*, Ole::*, Variant::*},
UI::{Accessibility::*, WindowsAndMessaging::*},
},
};
use crate::window_handle::WindowHandle;
#[derive(Clone, Default, PartialEq, Eq)]
pub(crate) struct WideString(Vec<u16>);
impl Write for WideString {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0.extend(s.encode_utf16());
Ok(())
}
fn write_char(&mut self, c: char) -> fmt::Result {
self.0.extend_from_slice(c.encode_utf16(&mut [0; 2]));
Ok(())
}
}
impl From<WideString> for BSTR {
fn from(value: WideString) -> Self {
Self::from_wide(&value.0).unwrap()
}
}
pub(crate) struct Variant(VARIANT);
impl From<Variant> for VARIANT {
fn from(variant: Variant) -> Self {
variant.0
}
}
impl Variant {
pub(crate) fn empty() -> Self {
Self(VARIANT::default())
}
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<BSTR> for Variant {
fn from(value: BSTR) -> Self {
Self(value.into())
}
}
impl From<WideString> for Variant {
fn from(value: WideString) -> Self {
BSTR::from(value).into()
}
}
impl From<&str> for Variant {
fn from(value: &str) -> Self {
let mut result = WideString::default();
result.write_str(value).unwrap();
result.into()
}
}
impl From<String> for Variant {
fn from(value: String) -> Self {
value.as_str().into()
}
}
impl From<IUnknown> for Variant {
fn from(value: IUnknown) -> Self {
Self(value.into())
}
}
impl From<i32> for Variant {
fn from(value: i32) -> Self {
Self(value.into())
}
}
impl From<f64> for Variant {
fn from(value: f64) -> Self {
Self(value.into())
}
}
impl From<ToggleState> for Variant {
fn from(value: ToggleState) -> Self {
Self(value.0.into())
}
}
impl From<LiveSetting> for Variant {
fn from(value: LiveSetting) -> Self {
Self(value.0.into())
}
}
impl From<CaretPosition> for Variant {
fn from(value: CaretPosition) -> Self {
Self(value.0.into())
}
}
impl From<UIA_CONTROLTYPE_ID> for Variant {
fn from(value: UIA_CONTROLTYPE_ID) -> Self {
Self(value.0.into())
}
}
impl From<OrientationType> for Variant {
fn from(value: OrientationType) -> Self {
Self(value.0.into())
}
}
impl From<bool> for Variant {
fn from(value: bool) -> Self {
Self(value.into())
}
}
impl<T: Into<Variant>> From<Option<T>> for Variant {
fn from(value: Option<T>) -> Self {
value.map_or_else(Self::empty, T::into)
}
}
fn safe_array_from_primitive_slice<T>(vt: VARENUM, slice: &[T]) -> *mut SAFEARRAY {
let sa = unsafe { SafeArrayCreateVector(VARENUM(vt.0), 0, slice.len().try_into().unwrap()) };
if sa.is_null() {
panic!("SAFEARRAY allocation failed");
}
for (i, item) in slice.iter().enumerate() {
let i: i32 = i.try_into().unwrap();
unsafe { SafeArrayPutElement(&*sa, &i, (item as *const T) as *const _) }.unwrap();
}
sa
}
pub(crate) fn safe_array_from_i32_slice(slice: &[i32]) -> *mut SAFEARRAY {
safe_array_from_primitive_slice(VT_I4, slice)
}
pub(crate) fn safe_array_from_f64_slice(slice: &[f64]) -> *mut SAFEARRAY {
safe_array_from_primitive_slice(VT_R8, slice)
}
pub(crate) fn safe_array_from_com_slice(slice: &[IUnknown]) -> *mut SAFEARRAY {
let sa = unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, slice.len().try_into().unwrap()) };
if sa.is_null() {
panic!("SAFEARRAY allocation failed");
}
for (i, item) in slice.iter().enumerate() {
let i: i32 = i.try_into().unwrap();
unsafe { SafeArrayPutElement(&*sa, &i, std::mem::transmute_copy(item)) }.unwrap();
}
sa
}
pub(crate) enum QueuedEvent {
Simple {
element: IRawElementProviderSimple,
event_id: UIA_EVENT_ID,
},
PropertyChanged {
element: IRawElementProviderSimple,
property_id: UIA_PROPERTY_ID,
old_value: VARIANT,
new_value: VARIANT,
},
}
pub(crate) fn not_implemented() -> Error {
E_NOTIMPL.into()
}
pub(crate) fn invalid_arg() -> Error {
E_INVALIDARG.into()
}
pub(crate) fn required_param<T>(param: Option<&T>) -> Result<&T> {
param.map_or_else(|| Err(invalid_arg()), Ok)
}
pub(crate) fn element_not_available() -> Error {
HRESULT(UIA_E_ELEMENTNOTAVAILABLE as _).into()
}
pub(crate) fn element_not_enabled() -> Error {
HRESULT(UIA_E_ELEMENTNOTENABLED as _).into()
}
pub(crate) fn invalid_operation() -> Error {
HRESULT(UIA_E_INVALIDOPERATION as _).into()
}
pub(crate) fn client_top_left(hwnd: WindowHandle) -> Point {
let mut result = POINT::default();
// If ClientToScreen fails, that means the window is gone.
// That's an unexpected condition, so we should fail loudly.
unsafe { ClientToScreen(hwnd.0, &mut result) }.unwrap();
Point::new(result.x.into(), result.y.into())
}
pub(crate) fn window_title(hwnd: WindowHandle) -> Option<BSTR> {
// The following is an old hack to get the window caption without ever
// sending messages to the window itself, even if the window is in
// the same process but possibly a separate thread. This prevents
// possible hangs and sluggishness. This hack has been proven to work
// over nearly 20 years on every version of Windows back to XP.
let result = unsafe { DefWindowProcW(hwnd.0, WM_GETTEXTLENGTH, WPARAM(0), LPARAM(0)) };
if result.0 <= 0 {
return None;
}
let capacity = (result.0 as usize) + 1; // make room for the null
let mut buffer = Vec::<u16>::with_capacity(capacity);
let result = unsafe {
DefWindowProcW(
hwnd.0,
WM_GETTEXT,
WPARAM(capacity),
LPARAM(buffer.as_mut_ptr() as _),
)
};
if result.0 <= 0 {
return None;
}
let len = result.0 as usize;
unsafe { buffer.set_len(len) };
Some(BSTR::from_wide(&buffer).unwrap())
}
pub(crate) fn toolkit_description(state: &TreeState) -> Option<WideString> {
state.toolkit_name().map(|name| {
let mut result = WideString::default();
result.write_str(name).unwrap();
if let Some(version) = state.toolkit_version() {
result.write_char(' ').unwrap();
result.write_str(version).unwrap();
}
result
})
}
pub(crate) fn upgrade<T>(weak: &Weak<T>) -> Result<Arc<T>> {
if let Some(strong) = weak.upgrade() {
Ok(strong)
} else {
Err(element_not_available())
}
}

View File

@@ -0,0 +1,32 @@
// 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 windows::Win32::Foundation::HWND;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WindowHandle(pub HWND);
unsafe impl Send for WindowHandle {}
unsafe impl Sync for WindowHandle {}
impl From<HWND> for WindowHandle {
fn from(value: HWND) -> Self {
Self(value)
}
}
impl From<WindowHandle> for HWND {
fn from(value: WindowHandle) -> Self {
value.0
}
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::assert_impl_all!(WindowHandle: Send, Sync);
}