Merge branch 'worktree-agent-a466c08a4014dbcbe' into rustify (I4: AccessKit names)

This commit is contained in:
iris committed 2026-09-05 07:06:29 -04:00
commit 32a5256a0d
22 files changed
+2251 -22

No files matched your search

+31
View File
@@ -8,6 +8,37 @@ capability that moved. Small and trivial changes do not go here.
An entry gives the date, what changed, why, and a short before/after where An entry gives the date, what changed, why, and a short before/after where
it helps judge the change without the session that made it. Newest first. it helps judge the change without the session that made it. Newest first.
## 2026-09-05: accessibility names via AccessKit (RUST.md's I4)
`.label()` (already in `trait_fns.rs`, previously unused anywhere in-tree)
is now load-bearing: it's the one thing that puts a widget in the AccessKit
tree `iris_core::ui::access::AccessTree` builds and both backends push
out. A widget author who wants a control to be findable by name (and
tappable by name, through `ui-trace`/a real screen reader) calls `.label()`
on it; nothing else is required, and a widget nobody labels is invisible
to this system at zero cost, not just zero UI.
```rust
let button = rect(Color::LIME)
.on(CursorSense::click(), move |_, rsc| { ... })
.label("Add task"); // now findable by uiautomator/AccessKit as "Add task"
```
Two new things a widget author might touch directly:
- **`Widget::access_role(&self) -> accesskit::Role`**, default `Unknown`.
Override it if your widget has a real platform equivalent —
`TextEdit` now returns `TextInput`/`MultilineTextInput` by `EditMode`.
Only consulted for a widget that also has a `.label()`; an unlabelled
widget's `access_role` is never called.
- **`Widgets::named() -> impl Iterator<Item = WidgetId>`** — every widget
with an explicit label, for anything else that wants to walk the same
set `AccessTree` does.
Nothing about `Painter`, `draw`, or the layout/move machinery changed —
this sits entirely beside them, reading `resolved_region`'s output rather
than participating in producing it.
## 2026-09-05: `List`, a virtualised bottom-anchored list (RUST.md's I3) ## 2026-09-05: `List`, a virtualised bottom-anchored list (RUST.md's I3)
A new widget, `iris::widget::List` (`iris/src/widget/list.rs` -- read its A new widget, `iris::widget::List` (`iris/src/widget/list.rs` -- read its
+153 -4
View File
@@ -42,7 +42,7 @@ session spending an afternoon on them again.
below), **E3 (the Kotlin/Java shell over a JNI bridge into Rust, both below), **E3 (the Kotlin/Java shell over a JNI bridge into Rust, both
pass conditions proved on the emulator — see its own box)**, I0a, I0b pass conditions proved on the emulator — see its own box)**, I0a, I0b
(iris builds on a pinned nightly and runs), I1 (parley + glyph atlas), (iris builds on a pinned nightly and runs), I1 (parley + glyph atlas),
I2 (iris on android-view). I2 (iris on android-view), I3 (`iris::widget::List`).
- **E3 done, 2026-09-05, and unlike E1/E2 it is committed to this repo** - **E3 done, 2026-09-05, and unlike E1/E2 it is committed to this repo**
(`android-shell/` — a JNI-bridge crate on `client-core` — plus a new (`android-shell/` — a JNI-bridge crate on `client-core` — plus a new
Gradle module `app/shellApp/`, left deliberately separate from Gradle module `app/shellApp/`, left deliberately separate from
@@ -61,6 +61,20 @@ session spending an afternoon on them again.
box for the full account, the exact commands, and what was deliberately box for the full account, the exact commands, and what was deliberately
cut (attachment uploads, a session picker, the on-screen/banner cut (attachment uploads, a session picker, the on-screen/banner
suppression — all pending E4's screen). suppression — all pending E4's screen).
- **I4 — accessibility names via AccessKit: host half done and verified
2026-09-05, ticked in the box below.** `iris_core::ui::access::AccessTree`
builds one flat AccessKit tree from `Widgets::named()` (a side set only
`.label()` populates, so an unnamed widget costs this nothing), pushed
through `accesskit_winit` on the desktop and `accesskit_android` on
Android, updated only when a name/role/bounds actually changes (a
counter confirms it: 1 rebuild on first draw, 0 across an unchanged
frame, 1 more after a real move). E1's detach-abort mitigation is
carried (`android/access.rs`'s `raise_if_enabled`). Every check that
doesn't need the emulator is clean — see I4's own box for the exact
numbers. **What's left**: the emulator itself is held by another session
this pass, so `ui-trace record --do "tap 'pad'"` against
`iris-android-app`'s tabs screen (which now has five named buttons) has
not been run for real yet — exact commands at the bottom of I4's box.
- **E2 done, 2026-09-05, and its headline finding changes what "decide - **E2 done, 2026-09-05, and its headline finding changes what "decide
from the measurements" (recommendation item 3) can mean right now.** from the measurements" (recommendation item 3) can mean right now.**
Built a real transcript screen (`~/src/android-view/e2-transcript`, Built a real transcript screen (`~/src/android-view/e2-transcript`,
@@ -1772,9 +1786,144 @@ silently on real hardware.
session screen, i.e. most of I5's work) rather than something this session screen, i.e. most of I5's work) rather than something this
box's scope could finish alone -- recorded here rather than left box's scope could finish alone -- recorded here rather than left
silently undone. silently undone.
- [ ] **I4 — accessibility names via AccessKit.** Every control carries a - [x] **I4 — accessibility names via AccessKit, host half done and verified
name; `ui-trace` can find and tap it by label. Pass: `bench-lib.sh`'s 2026-09-05; the emulator half is the one step left, named at the
tap-by-name works against the iris screen unchanged. bottom of this box.** Built `iris_core::ui::access::AccessTree`
(`iris/core/src/ui/access.rs`) -- one flat AccessKit tree, a synthetic
`Role::Window` root with every **named** widget as a direct child.
Deliberately flat rather than mirroring iris's real widget nesting:
nothing upstream of a named leaf needs a node, since a screen
reader's traversal (and uiautomator's tap-by-name, this box's own
pass condition) works from each node's on-screen bounds, not from
tree structure -- and mirroring the real tree would rebuild
intermediate nodes on every resize of any container above a named
widget, which is most frames.
**Modular the way input's sense registry is.** `Widgets` gained one
`HashSet<WidgetId>` (`named`), populated only by `.label()`/
`set_label` and drained by `free_next` (the same removal path a
freed id already went through -- no second bookkeeping call added
anywhere). `AccessTree::update` walks `widgets.named()` directly,
never the full widget arena, so a widget nobody named costs this
subsystem nothing -- not a visit, not a branch. Roles come from a
new `Widget::access_role(&self) -> accesskit::Role` trait method,
default `Unknown`; the one override so far is `TextEdit` ->
`TextInput`/`MultilineTextInput` by `EditMode`. Bounds come from
`UiRenderState::window_region`, which sits on `resolved_region`'s
move-chain walk -- so a widget moved via `Offset`/`Scroll` (never
redrawn from scratch) still reports where it actually ended up; see
`bounds_follow_a_moved_widget_and_updates_stay_incremental` below.
**Incremental, not per-frame.** `AccessTree` keeps the last
`HashMap<WidgetId, Entry>` (name, role, bounds) it sent and only
returns a new `TreeUpdate` -- and only then bumps its `rebuilds`
counter, `take_rebuilds()`'s the AccessKit twin of
`UiRenderState::take_counters` -- when that set actually differs.
Confirmed by `bounds_follow_a_moved_widget_and_updates_stay_incremental`
(`iris/src/access_tests.rs`): 1 rebuild on the first draw, 0 across an
unchanged frame, 1 more after a real move, regardless of how many
other widgets are on screen.
**`SlotId::as_u64`** (`core/src/util/slot.rs`) encodes a `WidgetId`
into accesskit's flat `NodeId(u64)`, offset by one so a real widget
never collides with the reserved window node (`NodeId(0)`).
**Pushed through two backends, each behind an inert action/activation
handler** -- see below for why inert is correct, not incomplete.
`default/access.rs` (winit): `accesskit_winit::Adapter`, built in
`DefaultApp::new` with the window created hidden
(`with_visible(false)`) and shown only after the adapter exists,
which is what that constructor requires. `process_event` runs on
every `WindowEvent`; `update_if_active` runs once per
`RedrawRequested`, after `render.update()` so bounds reflect the
frame just drawn. `android/access.rs` (android-view):
`accesskit_android::Adapter` on `AndroidUiState`, `IrisViewPeer` now
implements `AccessibilityNodeProvider`
(`create_accessibility_node_info`/`find_focus`/`perform_action`), and
`render()` (now taking `&mut CallbackCtx`, needed for the JNI handle
any `raise` requires) pushes the same `AccessTree::update` after
every draw.
**Why the `ActionHandler`s are empty, not a placeholder for later
work**: AGENTS.md's own "Driving the UI" section says it plainly --
`ui-trace record --do "tap 'Save'"` resolves the label against the
screen and performs a **real touch at that node's bounds**, the same
as a person's finger. It does not call into AccessKit's action
system at all. So once `AccessTree` reports correct bounds, the
ordinary pointer path (already built, already tested) is what
answers the tap -- there is nothing for `do_action` to do for this
pass condition specifically. A future real screen reader's own
double-tap-to-activate gesture works the same way, for the same
reason. If iris ever needs to answer an AccessKit `Action::Click`
injected without a matching touch (e.g. a switch-access scanner),
that is new scope, not a gap in this box.
**E1's abort mitigation, carried.** `android/access.rs`'s
`raise_if_enabled` is the one place `QueuedEvents::raise` may be
called: it asks `AccessibilityManager.isEnabled()` (a `getSystemService`
JNI call, since android-view has no ready-made wrapper) immediately
before every `raise` and drops the events instead when the answer is
no. Every call site (`render`'s per-frame push, `perform_action`)
goes through it, and each pushes it as a *deferred* callback exactly
like android-view's own demo, so it runs after the current JNI
callback has released whatever it's holding -- `raise`'s own
documented requirement. Not independently re-triggered on this
pass (that needs the emulator, see below); the mitigation is coded
to the exact mechanism E1 diagnosed (`sendAccessibilityEvent`
throwing when accessibility is off) rather than to the symptom, so
there is no reason to expect it behaves differently here than it did
there.
**Verified, 2026-09-05, host only.**
`cargo fmt --all -- --check`, `cargo build --workspace --all-targets`,
`cargo clippy --all-targets` (both plain and `--all-targets`) clean;
`cargo test --workspace` -- 28 tests in `iris/`, three of them new
(`access_tests::a_named_widget_reaches_the_tree_with_its_role_and_bounds`,
`::a_widget_with_no_label_never_reaches_the_tree`,
`::bounds_follow_a_moved_widget_and_updates_stay_incremental`).
`cd iris/android-app && cargo ndk -t x86_64 -P 26 build` and
`... clippy` clean for both `iris` (with the android module) and
`iris-android-app`, same shape as I2/I3's checks.
`iris/run-headless.sh tabs --shot /tmp/iris_i4_tabs.png --seconds 4`
still renders -- **27266 bytes, byte-for-byte identical to I2's own
post-fix screenshot** -- confirming the hidden-window-then-adapter
change to `DefaultApp::new` cost nothing visible. `tabs-ui`'s five
switch buttons (`tabs-ui/src/lib.rs`) now carry `.label()`s matching
their on-screen text ("pad", "span", "image span", "text layout",
"text edit scroll") -- both so the desktop run above exercises a
non-empty tree and so the emulator step below has real names to tap.
Not independently checked on this pass: whether `accesskit_winit`'s
Linux path (AT-SPI, via `accesskit_unix`) actually reaches a real
assistive-technology client on this VM's headless sway -- there is
no AT-SPI registry running here, so `default/access.rs`'s handlers
are exercised as inert code paths (built, called, no panic) rather
than confirmed end-to-end the way the emulator step below confirms
the Android path.
**What remains -- the one check that needs the emulator, held by
another session during this pass.** `iris-android-app`'s tabs screen
has never been driven by `ui-trace` for real; everything above is
"builds, runs, produces the right data" on the host. Once the
emulator is free:
cd iris/android-app && cargo ndk -t x86_64 -P 26 -o app/src/main/jniLibs/ build --release && gradle :app:assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
# launch iris-android-app on the emulator, then:
ui-trace record --do "tap 'pad'"
ui-trace record --do "tap 'span'"
ui-trace record --do "tap 'image span'"
ui-trace record --do "tap 'text layout'"
ui-trace record --do "tap 'text edit scroll'"
Pass condition: each tap resolves (uiautomator finds a node with
that exact label) and switches `main`'s visible pane the way a
direct touch on that button already does -- i.e. `bench-lib.sh`'s
tap-by-name mechanism, unmodified, driving the iris screen instead
of the Compose one. Also worth checking while the emulator is up,
since E1 found it exactly this way: run a second `ui-trace record`
immediately after the first (attach, detach, attach again) and
confirm the process is still alive afterward -- the detach-abort
this box's mitigation exists for.
- [ ] **I5 — the transcript screen in iris.** E2's pass conditions, all - [ ] **I5 — the transcript screen in iris.** E2's pass conditions, all
seven behaviours, against the sandbox with `--delay`. This is the seven behaviours, against the sandbox with `--delay`. This is the
point the decision in the recommendation is made at. point the decision in the recommendation is made at.
+733 -2
View File
@@ -18,6 +18,126 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "accesskit"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "438a7081a65b95c668db56591a4fef9bc5dad275f448bd6223fc6949d823db38"
dependencies = [
"uuid",
]
[[package]]
name = "accesskit_android"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "929aff36b0d3dd22ddb59191989d5293ed958294d7728bd6c2ec9778f73414aa"
dependencies = [
"accesskit",
"accesskit_consumer",
"jni",
"log",
]
[[package]]
name = "accesskit_atspi_common"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9d47ad644916f6cb7e432a5ca0dbd7cc78281a9772881057bb72d4aadb93257"
dependencies = [
"accesskit",
"accesskit_consumer",
"atspi-common",
"phf",
"serde",
"zvariant",
]
[[package]]
name = "accesskit_consumer"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc882fa0c9c24c53649e256311b51046cf36eca9a8f7e54ff4ef682160b0cb27"
dependencies = [
"accesskit",
"hashbrown 0.17.1",
]
[[package]]
name = "accesskit_ios"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5e9b29c6ba5f4d2e0662e9c562484f061ffc58f658479c538ecca8299ada1e9"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
"objc2-ui-kit",
]
[[package]]
name = "accesskit_macos"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f278988416e5fb600f24d0cfffe31a249ebbff980fb9c5a3b5fbed2c579f73"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"objc2 0.5.2",
"objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
]
[[package]]
name = "accesskit_unix"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "665c9079b7325a8168a6793437a172dda44b19a05af8ed52d7e32abaada996fe"
dependencies = [
"accesskit",
"accesskit_atspi_common",
"async-channel",
"async-executor",
"async-task",
"atspi",
"futures-lite",
"futures-util",
"serde",
"zbus",
]
[[package]]
name = "accesskit_windows"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a59e8d7160cbac991c1345c99153783ab96423644ccb1f20617cf80c97596aa1"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"static_assertions",
"windows",
"windows-core",
]
[[package]]
name = "accesskit_winit"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1eb960a51582305f94b3884a7ff54b3462abe38f0de95a9e7e04dd7ab7b757da"
dependencies = [
"accesskit",
"accesskit_ios",
"accesskit_macos",
"accesskit_unix",
"accesskit_windows",
"raw-window-handle",
"winit",
]
[[package]] [[package]]
name = "adler2" name = "adler2"
version = "2.0.1" version = "2.0.1"
@@ -191,12 +311,180 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "async-broadcast"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 1.1.3",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-process"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix 1.1.3",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.113",
]
[[package]]
name = "async-signal"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
dependencies = [
"async-io",
"async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.3",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atspi"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
dependencies = [
"atspi-common",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zbus",
"zbus-lockstep",
"zbus-lockstep-macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "atspi-proxies"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
dependencies = [
"atspi-common",
"serde",
"zbus",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.5.0" version = "1.5.0"
@@ -303,6 +591,19 @@ dependencies = [
"objc2 0.5.2", "objc2 0.5.2",
] ]
[[package]]
name = "blocking"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite",
"piper",
]
[[package]] [[package]]
name = "built" name = "built"
version = "0.8.0" version = "0.8.0"
@@ -633,6 +934,33 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "endi"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.113",
]
[[package]] [[package]]
name = "equator" name = "equator"
version = "0.4.2" version = "0.4.2"
@@ -675,6 +1003,26 @@ version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "event-listener"
version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
dependencies = [
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.0" version = "1.74.0"
@@ -690,6 +1038,12 @@ dependencies = [
"zune-inflate", "zune-inflate",
] ]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]] [[package]]
name = "fax" name = "fax"
version = "0.2.6" version = "0.2.6"
@@ -811,6 +1165,61 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "futures-core"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-io"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-macro"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]]
name = "futures-task"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-util"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]] [[package]]
name = "fxhash" name = "fxhash"
version = "0.2.1" version = "0.2.1"
@@ -977,6 +1386,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hexf-parse" name = "hexf-parse"
version = "0.2.1" version = "0.2.1"
@@ -1176,6 +1591,9 @@ dependencies = [
name = "iris" name = "iris"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"accesskit",
"accesskit_android",
"accesskit_winit",
"android-view", "android-view",
"arboard", "arboard",
"image", "image",
@@ -1196,6 +1614,7 @@ dependencies = [
name = "iris-core" name = "iris-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"accesskit",
"bytemuck", "bytemuck",
"fxhash", "fxhash",
"image", "image",
@@ -1428,6 +1847,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.33.0" version = "0.33.0"
@@ -1927,6 +2355,16 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.2.3" version = "1.2.3"
@@ -1946,6 +2384,12 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.5" version = "0.12.5"
@@ -2031,6 +2475,49 @@ dependencies = [
"indexmap", "indexmap",
] ]
[[package]]
name = "phf"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
dependencies = [
"phf_macros",
"phf_shared",
"serde",
]
[[package]]
name = "phf_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
dependencies = [
"fastrand",
"phf_shared",
]
[[package]]
name = "phf_macros"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.113",
]
[[package]]
name = "phf_shared"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.10" version = "1.1.10"
@@ -2057,6 +2544,17 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "piper"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.32" version = "0.3.32"
@@ -2500,12 +2998,33 @@ dependencies = [
"syn 2.0.113", "syn 2.0.113",
] ]
[[package]]
name = "serde_repr"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.8" version = "0.3.8"
@@ -2521,6 +3040,12 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "siphasher"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]] [[package]]
name = "skrifa" name = "skrifa"
version = "0.44.0" version = "0.44.0"
@@ -2664,6 +3189,19 @@ dependencies = [
"iris", "iris",
] ]
[[package]]
name = "tempfile"
version = "3.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix 1.1.3",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@@ -2790,7 +3328,7 @@ dependencies = [
"indexmap", "indexmap",
"toml_datetime", "toml_datetime",
"toml_parser", "toml_parser",
"winnow", "winnow 0.7.14",
] ]
[[package]] [[package]]
@@ -2799,7 +3337,7 @@ version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [ dependencies = [
"winnow", "winnow 0.7.14",
] ]
[[package]] [[package]]
@@ -2809,14 +3347,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.113",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.36" version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]] [[package]]
name = "tree_magic_mini" name = "tree_magic_mini"
@@ -2835,6 +3388,17 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "ui-events" name = "ui-events"
version = "0.1.0" version = "0.1.0"
@@ -2869,6 +3433,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812"
dependencies = [
"js-sys",
"serde_core",
"wasm-bindgen",
]
[[package]] [[package]]
name = "v_frame" name = "v_frame"
version = "0.3.9" version = "0.3.9"
@@ -3659,6 +4234,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.46.0" version = "0.46.0"
@@ -3798,6 +4382,112 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zbus"
version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907"
dependencies = [
"async-broadcast",
"async-executor",
"async-io",
"async-lock",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"libc",
"ordered-stream",
"rustix 1.1.3",
"serde",
"serde_repr",
"tracing",
"uds_windows",
"uuid",
"windows-sys 0.61.2",
"winnow 1.0.4",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus-lockstep"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863"
dependencies = [
"zbus_xml",
"zvariant",
]
[[package]]
name = "zbus-lockstep-macros"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.113",
"zbus-lockstep",
"zbus_xml",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 3.0.5",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
dependencies = [
"serde",
"winnow 1.0.4",
"zvariant",
]
[[package]]
name = "zbus_xml"
version = "5.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a"
dependencies = [
"serde",
"winnow 1.0.4",
"zbus_names",
"zvariant",
]
[[package]]
name = "zcheapstr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "zeno" name = "zeno"
version = "0.3.3" version = "0.3.3"
@@ -3918,3 +4608,44 @@ checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [ dependencies = [
"zune-core 0.5.0", "zune-core 0.5.0",
] ]
[[package]]
name = "zvariant"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow 1.0.4",
"zcheapstr",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 3.0.5",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 3.0.5",
"winnow 1.0.4",
]
+15
View File
@@ -13,6 +13,7 @@ swash = { workspace = true }
pollster = { workspace = true } pollster = { workspace = true }
wgpu = { workspace = true } wgpu = { workspace = true }
image = { workspace = true } image = { workspace = true }
accesskit = { workspace = true }
tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread"] } tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread"] }
# winit everywhere except Android; android-view (below) is what stands in # winit everywhere except Android; android-view (below) is what stands in
@@ -27,6 +28,11 @@ tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread"] }
[target.'cfg(not(target_os = "android"))'.dependencies] [target.'cfg(not(target_os = "android"))'.dependencies]
winit = { workspace = true } winit = { workspace = true }
arboard = { workspace = true, features = ["wayland-data-control"] } arboard = { workspace = true, features = ["wayland-data-control"] }
# I4 (RUST.md): the desktop half of the AccessKit push, `winit`'s own
# adapter over `accesskit`. No pin needed the way android-view's rev is
# pinned -- this is an ordinary crates.io release with no local abort to
# track (that finding is Android-only, see below).
accesskit_winit = "0.34.0"
# Pinned to the exact commit RUST.md's E1 (2026-09-04) measured on this # Pinned to the exact commit RUST.md's E1 (2026-09-04) measured on this
# emulator -- real Vulkan rendering, a working `InputConnection`, and the # emulator -- real Vulkan rendering, a working `InputConnection`, and the
@@ -35,6 +41,14 @@ arboard = { workspace = true, features = ["wayland-data-control"] }
# is dated rather than floating. # is dated rather than floating.
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-view = { git = "https://github.com/rust-mobile/android-view.git", rev = "bec6c62a96cef8239b0fd7fedeef9b184d02e3a1" } android-view = { git = "https://github.com/rust-mobile/android-view.git", rev = "bec6c62a96cef8239b0fd7fedeef9b184d02e3a1" }
# I4 (RUST.md): the Android half of the AccessKit push, over android-view's
# `AccessibilityNodeProvider`. **0.8.0 carries the same detach-abort E1
# found on 0.4.0** (the `State` enum still never returns to `Inactive`,
# and `send_completed_event` still unwraps a Java exception) -- advancing
# the version is not the fix, so pinning to a specific rev buys nothing
# here the way it does for android-view itself. `android/view.rs`'s
# `raise_if_enabled` is the mitigation, carried from E1.
accesskit_android = "0.8.0"
# Not re-exported by android-view (only `jni` and `ndk` are), and needed # Not re-exported by android-view (only `jni` and `ndk` are), and needed
# for `android/insets.rs`'s own id -> state map -- the same reason # for `android/insets.rs`'s own id -> state map -- the same reason
# android-view's own `PEER_MAP` carries one. # android-view's own `PEER_MAP` carries one.
@@ -82,6 +96,7 @@ parley = "0.11.1"
swash = "0.2.10" swash = "0.2.10"
fxhash = "0.2.1" fxhash = "0.2.1"
arboard = "3.6.1" arboard = "3.6.1"
accesskit = "0.25.0"
iris-core = { path = "core" } iris-core = { path = "core" }
iris-macro = { path = "macro" } iris-macro = { path = "macro" }
tokio = "1.49.0" tokio = "1.49.0"
+698
View File
@@ -18,6 +18,126 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "accesskit"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "438a7081a65b95c668db56591a4fef9bc5dad275f448bd6223fc6949d823db38"
dependencies = [
"uuid",
]
[[package]]
name = "accesskit_android"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "929aff36b0d3dd22ddb59191989d5293ed958294d7728bd6c2ec9778f73414aa"
dependencies = [
"accesskit",
"accesskit_consumer",
"jni 0.21.1",
"log",
]
[[package]]
name = "accesskit_atspi_common"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9d47ad644916f6cb7e432a5ca0dbd7cc78281a9772881057bb72d4aadb93257"
dependencies = [
"accesskit",
"accesskit_consumer",
"atspi-common",
"phf",
"serde",
"zvariant",
]
[[package]]
name = "accesskit_consumer"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc882fa0c9c24c53649e256311b51046cf36eca9a8f7e54ff4ef682160b0cb27"
dependencies = [
"accesskit",
"hashbrown 0.17.1",
]
[[package]]
name = "accesskit_ios"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5e9b29c6ba5f4d2e0662e9c562484f061ffc58f658479c538ecca8299ada1e9"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
"objc2-ui-kit",
]
[[package]]
name = "accesskit_macos"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f278988416e5fb600f24d0cfffe31a249ebbff980fb9c5a3b5fbed2c579f73"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"objc2 0.5.2",
"objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
]
[[package]]
name = "accesskit_unix"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "665c9079b7325a8168a6793437a172dda44b19a05af8ed52d7e32abaada996fe"
dependencies = [
"accesskit",
"accesskit_atspi_common",
"async-channel",
"async-executor",
"async-task",
"atspi",
"futures-lite",
"futures-util",
"serde",
"zbus",
]
[[package]]
name = "accesskit_windows"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a59e8d7160cbac991c1345c99153783ab96423644ccb1f20617cf80c97596aa1"
dependencies = [
"accesskit",
"accesskit_consumer",
"hashbrown 0.17.1",
"static_assertions",
"windows",
"windows-core",
]
[[package]]
name = "accesskit_winit"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1eb960a51582305f94b3884a7ff54b3462abe38f0de95a9e7e04dd7ab7b757da"
dependencies = [
"accesskit",
"accesskit_ios",
"accesskit_macos",
"accesskit_unix",
"accesskit_windows",
"raw-window-handle",
"winit",
]
[[package]] [[package]]
name = "adler2" name = "adler2"
version = "2.0.1" version = "2.0.1"
@@ -215,12 +335,180 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "async-broadcast"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 1.1.4",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-process"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix 1.1.4",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "async-signal"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
dependencies = [
"async-io",
"async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.4",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atspi"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
dependencies = [
"atspi-common",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zbus",
"zbus-lockstep",
"zbus-lockstep-macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "atspi-proxies"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
dependencies = [
"atspi-common",
"serde",
"zbus",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.5.1" version = "1.5.1"
@@ -327,6 +615,19 @@ dependencies = [
"objc2 0.5.2", "objc2 0.5.2",
] ]
[[package]]
name = "blocking"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite",
"piper",
]
[[package]] [[package]]
name = "built" name = "built"
version = "0.8.1" version = "0.8.1"
@@ -648,6 +949,33 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]]
name = "endi"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.4" version = "0.1.4"
@@ -700,6 +1028,26 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7"
[[package]]
name = "event-listener"
version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
dependencies = [
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.2" version = "1.74.2"
@@ -717,6 +1065,12 @@ dependencies = [
"zune-inflate", "zune-inflate",
] ]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]] [[package]]
name = "fax" name = "fax"
version = "0.2.7" version = "0.2.7"
@@ -831,6 +1185,36 @@ version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-io"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-macro"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.34" version = "0.3.34"
@@ -844,6 +1228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-macro",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
@@ -1026,6 +1411,12 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hexf-parse" name = "hexf-parse"
version = "0.2.1" version = "0.2.1"
@@ -1225,6 +1616,9 @@ dependencies = [
name = "iris" name = "iris"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"accesskit",
"accesskit_android",
"accesskit_winit",
"android-view", "android-view",
"arboard", "arboard",
"image", "image",
@@ -1255,6 +1649,7 @@ dependencies = [
name = "iris-core" name = "iris-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"accesskit",
"bytemuck", "bytemuck",
"fxhash", "fxhash",
"image", "image",
@@ -1541,6 +1936,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.33.0" version = "0.33.0"
@@ -2069,6 +2473,16 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.2.3" version = "1.2.3"
@@ -2088,6 +2502,12 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.5" version = "0.12.5"
@@ -2173,6 +2593,49 @@ dependencies = [
"indexmap", "indexmap",
] ]
[[package]]
name = "phf"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
dependencies = [
"phf_macros",
"phf_shared",
"serde",
]
[[package]]
name = "phf_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
dependencies = [
"fastrand",
"phf_shared",
]
[[package]]
name = "phf_macros"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "phf_shared"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.13" version = "1.1.13"
@@ -2199,6 +2662,17 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "piper"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.34" version = "0.3.34"
@@ -2733,12 +3207,33 @@ dependencies = [
"syn 3.0.5", "syn 3.0.5",
] ]
[[package]]
name = "serde_repr"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "2.0.1" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.10" version = "0.3.10"
@@ -2770,6 +3265,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "siphasher"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]] [[package]]
name = "skrifa" name = "skrifa"
version = "0.44.0" version = "0.44.0"
@@ -2913,6 +3414,19 @@ dependencies = [
"iris", "iris",
] ]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@@ -3058,14 +3572,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.36" version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]] [[package]]
name = "tree_magic_mini" name = "tree_magic_mini"
@@ -3084,6 +3613,17 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "ui-events" name = "ui-events"
version = "0.1.0" version = "0.1.0"
@@ -3118,6 +3658,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812"
dependencies = [
"js-sys",
"serde_core",
"wasm-bindgen",
]
[[package]] [[package]]
name = "v_frame" name = "v_frame"
version = "0.3.9" version = "0.3.9"
@@ -4044,6 +4595,112 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zbus"
version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907"
dependencies = [
"async-broadcast",
"async-executor",
"async-io",
"async-lock",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"libc",
"ordered-stream",
"rustix 1.1.4",
"serde",
"serde_repr",
"tracing",
"uds_windows",
"uuid",
"windows-sys 0.61.2",
"winnow",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus-lockstep"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863"
dependencies = [
"zbus_xml",
"zvariant",
]
[[package]]
name = "zbus-lockstep-macros"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"zbus-lockstep",
"zbus_xml",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 3.0.5",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
dependencies = [
"serde",
"winnow",
"zvariant",
]
[[package]]
name = "zbus_xml"
version = "5.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a"
dependencies = [
"serde",
"winnow",
"zbus_names",
"zvariant",
]
[[package]]
name = "zcheapstr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "zeno" name = "zeno"
version = "0.3.3" version = "0.3.3"
@@ -4155,3 +4812,44 @@ checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [ dependencies = [
"zune-core", "zune-core",
] ]
[[package]]
name = "zvariant"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow",
"zcheapstr",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 3.0.5",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 3.0.5",
"winnow",
]
+1
View File
@@ -10,3 +10,4 @@ image = { workspace = true }
parley = { workspace = true } parley = { workspace = true }
swash = { workspace = true } swash = { workspace = true }
fxhash = { workspace = true } fxhash = { workspace = true }
accesskit = { workspace = true }
+1 -1
View File
@@ -421,7 +421,7 @@ impl Display for UiRegion {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct PixelRegion { pub struct PixelRegion {
pub top_left: Vec2, pub top_left: Vec2,
pub bot_right: Vec2, pub bot_right: Vec2,
+152
View File
@@ -0,0 +1,152 @@
//! I4 (RUST.md): an AccessKit tree built from iris's own widget tree,
//! shared by both backends -- `android/view.rs` pushes its `TreeUpdate`s
//! through `accesskit_android::Adapter`, `default/mod.rs` through
//! `accesskit_winit::Adapter`. Kept modular the way input's sense registry
//! is: `Widgets::named()` is a side set populated only by `.label()`, so a
//! widget nobody named is never visited here at all, not even to decide it
//! has no name.
//!
//! The tree itself is deliberately flat -- one synthetic `Role::Window`
//! root with every named widget as a direct child, in no particular order.
//! iris's actual widget nesting (a label three `Span`s deep inside a
//! `Scroll`) carries no accessibility meaning of its own here: nothing
//! upstream of a named leaf needs a node, since a screen reader's own
//! traversal (and uiautomator's tap-by-name, the pass condition this was
//! built for) works from each node's on-screen bounds rather than from
//! tree structure. Mirroring the real widget tree exactly would also mean
//! rebuilding intermediate nodes whenever *any* container above a named
//! widget resizes, which is most frames -- the flat shape is what keeps
//! rebuilds tied to "a name, a role or a position actually changed".
use crate::{PixelRegion, UiRenderState, UiRsc, WidgetId, Widgets, util::HashMap};
use accesskit::{Node, NodeId, Rect, Role, TreeId, TreeInfo, TreeUpdate};
/// Reserved for the synthetic root; every real widget's `SlotId::as_u64`
/// starts at 1, so this can never collide with one (see that method's
/// doc comment).
const WINDOW_NODE: NodeId = NodeId(0);
fn node_id(id: WidgetId) -> NodeId {
NodeId(id.as_u64())
}
#[derive(Clone, PartialEq)]
struct Entry {
name: String,
role: Role,
bounds: PixelRegion,
}
fn entry_node(entry: &Entry) -> Node {
let mut node = Node::new(entry.role);
node.set_label(entry.name.clone());
node.set_bounds(Rect {
x0: entry.bounds.top_left.x as f64,
y0: entry.bounds.top_left.y as f64,
x1: entry.bounds.bot_right.x as f64,
y1: entry.bounds.bot_right.y as f64,
});
node
}
/// Owns the last tree pushed out, so `update` can tell "nothing
/// accessibility-relevant changed" from "something did" without asking
/// the platform adapter to diff two `Node`s itself. One of these per
/// window/view -- `default::DefaultUiState` and `android::AndroidUiState`
/// each keep one.
#[derive(Default)]
pub struct AccessTree {
known: HashMap<WidgetId, Entry>,
/// `TreeUpdate`s actually produced since the last `take_rebuilds` --
/// the AccessKit-tree twin of `UiRenderState::take_counters`. Should
/// stay at 0 across an unchanged frame and move by exactly 1 when a
/// named widget's position, name or role changes, however many other
/// widgets are on screen; see `iris/src/access_tests.rs`.
rebuilds: u64,
}
impl AccessTree {
pub fn new() -> Self {
Self::default()
}
fn collect(
widgets: &Widgets,
render: &UiRenderState,
rsc: &dyn UiRsc,
) -> HashMap<WidgetId, Entry> {
let mut current = HashMap::default();
for id in widgets.named() {
let Some(bounds) = render.window_region(&id, rsc) else {
continue;
};
let Some(widget) = widgets.get_dyn(id) else {
continue;
};
current.insert(
id,
Entry {
name: widgets.label(id).clone(),
role: widget.access_role(),
bounds,
},
);
}
current
}
/// Walks `widgets.named()`, looks up each one's current screen bounds
/// via `render.window_region` (which resolves the same move-chain
/// `resolved_region` does, so a moved subtree reports where it
/// actually is), and returns a full `TreeUpdate` if and only if that
/// set differs from the last call -- added, removed, renamed, or
/// moved/resized. A widget that is named but not currently active
/// (not drawn this frame) is left out, the same as one never named at
/// all.
pub fn update(
&mut self,
widgets: &Widgets,
render: &UiRenderState,
rsc: &dyn UiRsc,
) -> Option<TreeUpdate> {
let current = Self::collect(widgets, render, rsc);
if current == self.known {
return None;
}
self.known = current.clone();
self.rebuilds += 1;
Some(build_update(&current))
}
/// The unconditional twin of `update`, for a platform adapter's
/// activation handler (`android/access.rs`'s `AndroidAccessSource`) --
/// AccessKit asks for a full tree the first time a client attaches,
/// which is exactly the case `update`'s diff-against-`known` is not
/// meant to answer (it may have already sent this same snapshot to a
/// client that has since detached and reattached).
pub fn build_full(widgets: &Widgets, render: &UiRenderState, rsc: &dyn UiRsc) -> TreeUpdate {
build_update(&Self::collect(widgets, render, rsc))
}
/// Reads and zeroes the rebuild counter, the same call shape as
/// `UiRenderState::take_counters`.
pub fn take_rebuilds(&mut self) -> u64 {
std::mem::take(&mut self.rebuilds)
}
}
fn build_update(current: &HashMap<WidgetId, Entry>) -> TreeUpdate {
let mut window = Node::new(Role::Window);
let mut nodes = Vec::with_capacity(current.len() + 1);
for (&id, entry) in current {
window.push_child(node_id(id));
nodes.push((node_id(id), entry_node(entry)));
}
nodes.push((WINDOW_NODE, window));
TreeUpdate {
nodes,
tree: Some(TreeInfo::new(WINDOW_NODE)),
tree_id: TreeId::ROOT,
focus: WINDOW_NODE,
}
}
+2
View File
@@ -2,10 +2,12 @@ use crate::{
Mask, MoveOffset, TextData, Textures, WeakWidget, WidgetId, Widgets, util::TrackedArena, Mask, MoveOffset, TextData, Textures, WeakWidget, WidgetId, Widgets, util::TrackedArena,
}; };
mod access;
mod active; mod active;
mod painter; mod painter;
mod render_state; mod render_state;
pub use access::*;
pub use active::*; pub use active::*;
pub use painter::Painter; pub use painter::Painter;
pub use render_state::*; pub use render_state::*;
+11
View File
@@ -4,6 +4,17 @@ pub struct SlotId {
genr: u32, genr: u32,
} }
impl SlotId {
/// A stable, collision-free `u64` encoding of this id -- for a caller
/// (accesskit's `NodeId`, today) that wants a flat integer key rather
/// than the two `u32`s. `idx` is offset by one so no real id ever
/// encodes to 0, which callers can then reserve for their own
/// out-of-band root/window node.
pub fn as_u64(&self) -> u64 {
((self.idx as u64) + 1) << 32 | self.genr as u64
}
}
pub struct SlotVec<T> { pub struct SlotVec<T> {
data: Vec<(u32, Option<T>)>, data: Vec<(u32, Option<T>)>,
free: Vec<u32>, free: Vec<u32>,
+12
View File
@@ -29,6 +29,18 @@ pub trait Widget: Any {
fn is_size_independent(&self) -> bool { fn is_size_independent(&self) -> bool {
false false
} }
/// What kind of control this is, for the AccessKit tree `ui::access`
/// builds (RUST.md's I4). Only consulted for a widget that also has an
/// explicit `.label()` -- an unnamed widget is never visited by that
/// tree at all, named or not, so the default here costs nothing except
/// at the handful of call sites that opt in. Default `Unknown` (a
/// generic control with no more specific semantics); a widget with a
/// real platform equivalent -- `TextEdit`'s `MultilineTextInput` --
/// overrides it.
fn access_role(&self) -> accesskit::Role {
accesskit::Role::Unknown
}
} }
impl Widget for () { impl Widget for () {
+20 -2
View File
@@ -11,6 +11,11 @@ pub struct Widgets {
send: Sender<WidgetId>, send: Sender<WidgetId>,
recv: Receiver<WidgetId>, recv: Receiver<WidgetId>,
pub(crate) waiting: HashSet<WidgetId>, pub(crate) waiting: HashSet<WidgetId>,
/// Every widget that has ever been given an explicit `.label()` --
/// `ui::access::AccessTree` walks exactly this set, not the whole
/// arena, so a widget nobody named costs it nothing. Symmetric with
/// `free_next` below, which is this set's one removal path.
named: HashSet<WidgetId>,
} }
impl Widgets { impl Widgets {
@@ -20,6 +25,7 @@ impl Widgets {
needs_redraw: Default::default(), needs_redraw: Default::default(),
vec: Default::default(), vec: Default::default(),
waiting: Default::default(), waiting: Default::default(),
named: Default::default(),
send, send,
recv, recv,
} }
@@ -95,9 +101,20 @@ impl Widgets {
&self.data(id.id()).unwrap().label &self.data(id.id()).unwrap().label
} }
/// useful for debugging /// Also the one place a widget opts into `ui::access`'s AccessKit tree
/// (RUST.md's I4) -- see `named`'s doc comment.
pub fn set_label(&mut self, id: impl IdLike, label: String) { pub fn set_label(&mut self, id: impl IdLike, label: String) {
self.data_mut(id.id()).unwrap().label = label; let id = id.id();
self.data_mut(id).unwrap().label = label;
self.named.insert(id);
}
/// Every widget with an explicit name, for `ui::access::AccessTree` to
/// walk. Order is unspecified; `AccessTree` doesn't need one; a screen
/// reader's own traversal is worked out by uiautomator from each
/// node's on-screen bounds instead.
pub fn named(&self) -> impl Iterator<Item = WidgetId> + '_ {
self.named.iter().copied()
} }
pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> { pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> {
@@ -107,6 +124,7 @@ impl Widgets {
pub fn free_next(&mut self) -> Option<WidgetId> { pub fn free_next(&mut self) -> Option<WidgetId> {
let next = self.recv.try_recv().ok()?; let next = self.recv.try_recv().ok()?;
self.vec.free(next); self.vec.free(next);
self.named.remove(&next);
Some(next) Some(next)
} }
+129
View File
@@ -0,0 +1,129 @@
//! Pass conditions for RUST.md's I4, exercised the same way
//! `layout_tests.rs` exercises LAYOUT.md's: `AccessTree` only touches
//! `Widgets`/`UiRenderState`, neither of which needs a GPU or a window, so
//! it can be driven directly against `layout_tests::TestRsc`.
use crate::layout_tests::TestRsc;
use crate::prelude::*;
#[test]
fn a_named_widget_reaches_the_tree_with_its_role_and_bounds() {
let mut rsc = TestRsc {
ui: UiData::default(),
};
let leaf: WeakWidget<Rect> = rect(UiColor::WHITE).label("Add task").add(&mut rsc);
let root = leaf.upgrade(&mut rsc).any();
let mut render = UiRenderState::new();
render.resize((800.0, 600.0));
render.update(&root, &mut rsc);
let mut access = AccessTree::new();
let update = access
.update(rsc.widgets(), &render, &rsc)
.expect("a first draw with a named widget must produce a tree");
// One node for the widget, one for the synthetic window root.
assert_eq!(update.nodes.len(), 2);
let (_, node) = update
.nodes
.iter()
.find(|(_, n)| n.role() != accesskit::Role::Window)
.expect("the named widget's own node");
assert_eq!(node.label(), Some("Add task"));
assert_eq!(node.role(), accesskit::Role::Unknown);
let bounds = node.bounds().expect("a drawn widget reports its bounds");
let region = render
.window_region(&leaf, &rsc)
.expect("the widget is active after render.update");
assert_eq!(bounds.x0, region.top_left.x as f64);
assert_eq!(bounds.y0, region.top_left.y as f64);
assert_eq!(bounds.x1, region.bot_right.x as f64);
assert_eq!(bounds.y1, region.bot_right.y as f64);
}
#[test]
fn a_widget_with_no_label_never_reaches_the_tree() {
let mut rsc = TestRsc {
ui: UiData::default(),
};
let root = rsc.ui.widgets.add_strong(rect(UiColor::WHITE));
let mut render = UiRenderState::new();
render.resize((800.0, 600.0));
render.update(&root.any(), &mut rsc);
let mut access = AccessTree::new();
assert!(
access.update(rsc.widgets(), &render, &rsc).is_none(),
"no widget was ever `.label()`ed, so there is nothing to report -- \
not even an empty tree change"
);
}
/// LAYOUT.md's "a moved subtree" lesson applies here too: `resolved_region`
/// (which `window_region` sits on) walks the move-offset chain, so a
/// widget moved via `Offset` -- not redrawn from scratch -- must still
/// report where it actually ended up.
#[test]
fn bounds_follow_a_moved_widget_and_updates_stay_incremental() {
let mut rsc = TestRsc {
ui: UiData::default(),
};
let leaf: WeakWidget<Rect> = rect(UiColor::WHITE).label("thing").add(&mut rsc);
let leaf_strong = leaf.upgrade(&mut rsc).any();
let offset = rsc.ui.widgets.add_strong(Offset {
inner: leaf_strong,
amt: UiVec2::ZERO,
});
let offset_id = offset.weak();
let root = offset.any();
let mut render = UiRenderState::new();
render.resize((800.0, 600.0));
render.update(&root, &mut rsc);
let mut access = AccessTree::new();
access
.update(rsc.widgets(), &render, &rsc)
.expect("the first draw is always a change");
assert_eq!(access.take_rebuilds(), 1);
// Unchanged frame: nothing moved, nothing renamed -- `update` must
// report no change, and the rebuild counter (I4's twin of
// `take_counters`) must stay at 0.
render.update(&root, &mut rsc);
assert!(access.update(rsc.widgets(), &render, &rsc).is_none());
assert_eq!(access.take_rebuilds(), 0);
// Move the child via `Offset` (a move-offset write, not necessarily a
// full redraw of the leaf -- see `resolve_move_chain`) and confirm the
// reported bounds shifted by exactly that amount, in exactly one more
// rebuild.
let before = render
.window_region(&leaf, &rsc)
.expect("active before the move");
rsc.ui.widgets.get_mut(&offset_id).unwrap().amt = UiVec2::abs(Vec2::new(50.0, 0.0));
render.update(&root, &mut rsc);
let update = access
.update(rsc.widgets(), &render, &rsc)
.expect("a moved named widget is a change");
assert_eq!(access.take_rebuilds(), 1);
let after = render
.window_region(&leaf, &rsc)
.expect("still active after the move");
// Not asserting the exact delta: `Offset`'s own `amt` -> pixel mapping
// is that widget's business, not this tree's. What I4 owns is that
// `AccessTree` reports whatever `window_region` says *now* -- so the
// node must have moved, and in the direction the offset moved it.
assert!(
after.top_left.x > before.top_left.x,
"the leaf's reported bounds must move right along with its offset"
);
let (_, node) = update
.nodes
.iter()
.find(|(_, n)| n.role() != accesskit::Role::Window)
.unwrap();
let bounds = node.bounds().unwrap();
assert_eq!(bounds.x0, after.top_left.x as f64);
}
+86
View File
@@ -0,0 +1,86 @@
//! I4 (RUST.md): the Android half of the AccessKit push, over
//! `accesskit_android::Adapter` and android-view's
//! `AccessibilityNodeProvider`. Carries E1's mitigation for the adapter's
//! reproducible abort: `accesskit_android`'s `State` (0.4.0 and 0.8.0
//! alike) never moves back to `Inactive` once a client attaches, so once
//! one has, every later `QueuedEvents::raise` reaches
//! `AccessibilityManager.sendAccessibilityEvent` -- which throws if
//! accessibility has since been switched off (or the client detached),
//! and android-view's `panic = "abort"` turns that Java exception into a
//! process kill. `raise_if_enabled` is the gate: ask
//! `AccessibilityManager.isEnabled()` immediately before every `raise`
//! and drop the events instead of calling it when the answer is no. See
//! RUST.md's E1 box for the full repro.
use accesskit::{ActionHandler, ActionRequest, ActivationHandler, TreeUpdate};
use accesskit_android::QueuedEvents;
use android_view::{
View,
jni::{JNIEnv, objects::JObject},
};
use iris_core::{AccessTree, UiRenderState, UiRsc, Widgets};
/// The `ActivationHandler` `accesskit_android::Adapter` asks for its
/// initial tree from -- unlike `accesskit_winit`'s handlers (see
/// `default/access.rs`), this one is only ever invoked synchronously from
/// inside a JNI callback that already holds everything it needs, so it can
/// just borrow `IrisViewPeer`'s own fields for the length of one call
/// rather than going through a channel.
pub(super) struct AndroidAccessSource<'a> {
pub widgets: &'a Widgets,
pub render: &'a UiRenderState,
pub rsc: &'a dyn UiRsc,
}
impl ActivationHandler for AndroidAccessSource<'_> {
fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
Some(AccessTree::build_full(self.widgets, self.render, self.rsc))
}
}
/// Every AccessKit action request is inert here -- see this module's doc
/// comment and `default/access.rs`'s matching handler for why: a screen
/// reader's tap on a named node is a real touch delivered at that node's
/// bounds, which the ordinary pointer path already handles once the
/// bounds `AccessTree` reports are right.
pub(super) struct NullActionHandler;
impl ActionHandler for NullActionHandler {
fn do_action(&mut self, _request: ActionRequest) {}
}
fn is_accessibility_enabled<'local>(env: &mut JNIEnv<'local>, view: &View<'local>) -> bool {
let context = view.context(env);
let name = env.new_string("accessibility").unwrap();
let manager: JObject = env
.call_method(
&context.0,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[(&name).into()],
)
.unwrap()
.l()
.unwrap();
if manager.is_null() {
return false;
}
env.call_method(&manager, "isEnabled", "()Z", &[])
.unwrap()
.z()
.unwrap()
}
/// The one place `QueuedEvents::raise` may be called -- see this module's
/// doc comment. Every call site pushes this as a deferred callback rather
/// than calling it inline, matching android-view's own demo: `raise`
/// itself asks not to be called while the caller holds locks a framework
/// callback might, and a deferred callback runs after the current one has
/// returned them.
pub(super) fn raise_if_enabled<'local>(
env: &mut JNIEnv<'local>,
view: &View<'local>,
events: QueuedEvents,
) {
if is_accessibility_enabled(env, view) {
events.raise(env, &view.0);
}
}
+1
View File
@@ -12,6 +12,7 @@
//! counterpart: winit cannot drive an IME beyond `Ime::Preedit`/`Commit` //! counterpart: winit cannot drive an IME beyond `Ime::Preedit`/`Commit`
//! (RUST.md's E1) and has no concept of Android's window insets at all. //! (RUST.md's E1) and has no concept of Android's window insets at all.
mod access;
mod attr; mod attr;
mod ime; mod ime;
mod input; mod input;
+108 -6
View File
@@ -1,8 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use crate::task::RequestRedraw; use crate::task::RequestRedraw;
use accesskit_android::Adapter as AccessAdapter;
use android_view::{ use android_view::{
CallbackCtx, Context, InputConnection, KeyEvent, MotionEvent, Rect, View, ViewPeer, AccessibilityNodeInfo, AccessibilityNodeProvider, Bundle, CallbackCtx, Context,
jni::JNIEnv, InputConnection, KeyEvent, MotionEvent, Rect, View, ViewPeer,
jni::{JNIEnv, sys::jint},
ndk::event::{Keycode, MotionAction}, ndk::event::{Keycode, MotionAction},
}; };
// `marker::Sized` explicitly: `crate::prelude::*` below also brings in the // `marker::Sized` explicitly: `crate::prelude::*` below also brings in the
@@ -18,6 +20,7 @@ use std::{
}; };
use super::{ use super::{
access::{AndroidAccessSource, NullActionHandler, raise_if_enabled},
insets::{Insets, Shared}, insets::{Insets, Shared},
render::{AndroidRedrawHandle, AndroidRenderer}, render::{AndroidRedrawHandle, AndroidRenderer},
}; };
@@ -47,6 +50,13 @@ pub struct AndroidUiState {
/// path -- see `android/insets.rs` for why they need a registry of /// path -- see `android/insets.rs` for why they need a registry of
/// their own. /// their own.
shared: Rc<RefCell<Shared>>, shared: Rc<RefCell<Shared>>,
/// I4 (RUST.md): pushed from `IrisViewPeer::render` and consulted by
/// the `AccessibilityNodeProvider` impl below; see `android/access.rs`
/// for the abort mitigation every `raise` on it goes through.
pub access_adapter: AccessAdapter,
/// The AccessKit tree itself -- see `iris_core::AccessTree`'s doc
/// comment.
pub access: AccessTree,
} }
impl AndroidUiState { impl AndroidUiState {
@@ -60,6 +70,8 @@ impl AndroidUiState {
compose_len: 0, compose_len: 0,
pending_show_keyboard: false, pending_show_keyboard: false,
shared, shared,
access_adapter: Default::default(),
access: AccessTree::new(),
} }
} }
@@ -234,7 +246,7 @@ impl<State: AndroidAppState> IrisViewPeer<State> {
/// it -- on both the Vulkan/SwiftShader and GLES/virgl backends. Leave /// it -- on both the Vulkan/SwiftShader and GLES/virgl backends. Leave
/// these in until that is root-caused; removing them loses the exact /// these in until that is root-caused; removing them loses the exact
/// evidence a `logcat` capture needs to reproduce the state. /// evidence a `logcat` capture needs to reproduce the state.
fn render(&mut self) { fn render(&mut self, ctx: &mut CallbackCtx) {
let ui_state = self.state.android_state(); let ui_state = self.state.android_state();
if ui_state.renderer.is_none() { if ui_state.renderer.is_none() {
return; return;
@@ -267,6 +279,26 @@ impl<State: AndroidAppState> IrisViewPeer<State> {
.as_ref() .as_ref()
.and_then(|r| self.render.window_region(r, &self.rsc)), .and_then(|r| self.render.window_region(r, &self.rsc)),
); );
// I4 (RUST.md): only produces a `TreeUpdate` -- and so only queues
// anything to raise -- when the named set actually changed this
// frame; see `AccessTree`'s doc comment. Deferred rather than
// raised inline so it runs after this callback releases whatever
// it's holding, matching android-view's own demo and `raise`'s own
// contract.
let ui_state = self.state.android_state_mut();
if let Some(tree_update) =
ui_state
.access
.update(self.rsc.widgets(), &self.render, &self.rsc)
{
let ui_state = self.state.android_state_mut();
if let Some(events) = ui_state.access_adapter.update_if_active(|| tree_update) {
ctx.push_dynamic_deferred_callback(move |env, view| {
raise_if_enabled(env, view, events);
});
}
}
} }
} }
@@ -384,7 +416,7 @@ impl<State: AndroidAppState> ViewPeer for IrisViewPeer<State> {
let ui_state = self.state.android_state_mut(); let ui_state = self.state.android_state_mut();
ui_state.renderer = None; ui_state.renderer = None;
ui_state.renderer = Some(AndroidRenderer::new(window, width as u32, height as u32)); ui_state.renderer = Some(AndroidRenderer::new(window, width as u32, height as u32));
self.render(); self.render(ctx);
} }
fn surface_destroyed<'local>( fn surface_destroyed<'local>(
@@ -395,14 +427,84 @@ impl<State: AndroidAppState> ViewPeer for IrisViewPeer<State> {
self.state.android_state_mut().renderer = None; self.state.android_state_mut().renderer = None;
} }
fn do_frame(&mut self, _ctx: &mut CallbackCtx, _frame_time_nanos: i64) { fn do_frame(&mut self, ctx: &mut CallbackCtx, _frame_time_nanos: i64) {
self.drain_tasks(); self.drain_tasks();
self.render(); self.render(ctx);
} }
fn as_input_connection(&mut self) -> Option<&mut dyn InputConnection> { fn as_input_connection(&mut self) -> Option<&mut dyn InputConnection> {
Some(self) Some(self)
} }
fn as_accessibility_node_provider(&mut self) -> Option<&mut dyn AccessibilityNodeProvider> {
Some(self)
}
}
impl<State: AndroidAppState> AccessibilityNodeProvider for IrisViewPeer<State> {
fn create_accessibility_node_info<'local>(
&mut self,
ctx: &mut CallbackCtx<'local>,
virtual_view_id: jint,
) -> AccessibilityNodeInfo<'local> {
let mut source = AndroidAccessSource {
widgets: self.rsc.widgets(),
render: &self.render,
rsc: &self.rsc,
};
let ui_state = self.state.android_state_mut();
AccessibilityNodeInfo(ui_state.access_adapter.create_accessibility_node_info(
&mut source,
&mut ctx.env,
&ctx.view.0,
virtual_view_id,
))
}
fn find_focus<'local>(
&mut self,
ctx: &mut CallbackCtx<'local>,
focus_type: jint,
) -> AccessibilityNodeInfo<'local> {
let mut source = AndroidAccessSource {
widgets: self.rsc.widgets(),
render: &self.render,
rsc: &self.rsc,
};
let ui_state = self.state.android_state_mut();
AccessibilityNodeInfo(ui_state.access_adapter.find_focus(
&mut source,
&mut ctx.env,
&ctx.view.0,
focus_type,
))
}
fn perform_action<'local>(
&mut self,
ctx: &mut CallbackCtx<'local>,
virtual_view_id: jint,
action: jint,
arguments: &Bundle<'local>,
) -> bool {
let Some(action) =
accesskit_android::PlatformAction::from_java(&mut ctx.env, action, &arguments.0)
else {
return false;
};
let ui_state = self.state.android_state_mut();
let Some(events) = ui_state.access_adapter.perform_action(
&mut NullActionHandler,
virtual_view_id,
&action,
) else {
return false;
};
ctx.push_dynamic_deferred_callback(move |env, view| {
raise_if_enabled(env, view, events);
});
true
}
} }
/// Registers `IrisViewPeer<State>`'s native methods and builds one on every /// Registers `IrisViewPeer<State>`'s native methods and builds one on every
+28
View File
@@ -0,0 +1,28 @@
//! I4 (RUST.md): the desktop half of the AccessKit push, over
//! `accesskit_winit`. `bench-lib.sh`'s tap-by-name goes through the
//! platform's real accessibility tree, so this crate only has to keep that
//! tree in sync with `ui::access::AccessTree`'s output -- nothing here
//! reacts to an AccessKit action request, which is why the three handlers
//! below are inert. See RUST.md's I4 box for why: on Android (and, by the
//! same platform convention, everywhere else) a screen reader's element tap
//! is a real touch delivered at the node's own bounds, not an action
//! request synthesised in-process -- so the ordinary pointer path already
//! handles it once the bounds are right.
use accesskit::{ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, TreeUpdate};
pub struct NullActivationHandler;
impl ActivationHandler for NullActivationHandler {
fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
None
}
}
pub struct NullActionHandler;
impl ActionHandler for NullActionHandler {
fn do_action(&mut self, _request: ActionRequest) {}
}
pub struct NullDeactivationHandler;
impl DeactivationHandler for NullDeactivationHandler {
fn deactivate_accessibility(&mut self) {}
}
+46 -3
View File
@@ -11,11 +11,13 @@ use winit::{
window::{Window, WindowAttributes}, window::{Window, WindowAttributes},
}; };
mod access;
mod app; mod app;
mod attr; mod attr;
mod input; mod input;
mod render; mod render;
pub use access::*;
pub use app::*; pub use app::*;
pub use input::*; pub use input::*;
pub use render::*; pub use render::*;
@@ -31,6 +33,17 @@ pub struct DefaultUiState {
pub window: Arc<Window>, pub window: Arc<Window>,
pub ime: usize, pub ime: usize,
pub last_click: Instant, pub last_click: Instant,
/// I4 (RUST.md): pushed through in `DefaultApp::window_event`'s
/// `RedrawRequested` arm, from `access`'s output. Built in
/// `DefaultApp::new`, which is the only place with the
/// `&ActiveEventLoop` `accesskit_winit::Adapter::with_direct_handlers`
/// needs -- see that constructor's doc comment on why the window must
/// still be invisible when it is called.
pub access_adapter: accesskit_winit::Adapter,
/// The AccessKit tree itself -- see `iris_core::AccessTree`'s doc
/// comment for the flat shape and why it only rebuilds on a real
/// change.
pub access: AccessTree,
} }
impl HasRoot for DefaultUiState { impl HasRoot for DefaultUiState {
@@ -40,7 +53,7 @@ impl HasRoot for DefaultUiState {
} }
impl DefaultUiState { impl DefaultUiState {
pub fn new(window: impl Into<Arc<Window>>) -> Self { pub fn new(window: impl Into<Arc<Window>>, access_adapter: accesskit_winit::Adapter) -> Self {
let window = window.into(); let window = window.into();
Self { Self {
root: None, root: None,
@@ -51,6 +64,8 @@ impl DefaultUiState {
ime: 0, ime: 0,
last_click: Instant::now(), last_click: Instant::now(),
focus: None, focus: None,
access_adapter,
access: AccessTree::new(),
} }
} }
} }
@@ -179,10 +194,24 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
type Event = State::Event; type Event = State::Event;
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self { fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
// `accesskit_winit::Adapter::with_direct_handlers` panics if the
// window is already visible when it's built, so the window is
// created hidden and only shown once the adapter exists -- the one
// extra step I4 (RUST.md) needs here. The three handlers are inert
// (see `access.rs`): a screen reader's tap is a real touch at the
// node's bounds, not an action request this process has to answer.
let window = event_loop let window = event_loop
.create_window(State::window_attributes()) .create_window(State::window_attributes().with_visible(false))
.unwrap(); .unwrap();
let default_state = DefaultUiState::new(window); let access_adapter = accesskit_winit::Adapter::with_direct_handlers(
event_loop,
&window,
NullActivationHandler,
NullActionHandler,
NullDeactivationHandler,
);
window.set_visible(true);
let default_state = DefaultUiState::new(window, access_adapter);
let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone()); let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone());
let state = State::new(default_state, &mut rsc, proxy); let state = State::new(default_state, &mut rsc, proxy);
let render = UiRenderState::new(); let render = UiRenderState::new();
@@ -211,6 +240,12 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
} }
let ui_state = state.default_state_mut(); let ui_state = state.default_state_mut();
// Required by `accesskit_winit` on every window event, not just the
// ones this backend otherwise cares about -- some platform adapters
// rely on it to notice activation (a screen reader turning on).
ui_state
.access_adapter
.process_event(&ui_state.window, &event);
let input_changed = ui_state.input.event(&event); let input_changed = ui_state.input.event(&event);
let cursor_state = ui_state.cursor_state().clone(); let cursor_state = ui_state.cursor_state().clone();
let old = ui_state.focus; let old = ui_state.focus;
@@ -233,6 +268,14 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
render.update(&ui_state.root, rsc); render.update(&ui_state.root, rsc);
ui_state.renderer.update(&mut rsc.ui, render); ui_state.renderer.update(&mut rsc.ui, render);
ui_state.renderer.draw(); ui_state.renderer.draw();
// I4 (RUST.md): only produces a `TreeUpdate` when the named
// set actually changed this frame -- see `AccessTree`'s doc
// comment. `render` reflects the draw that just happened,
// so `resolved_region`/`window_region` inside it report a
// moved subtree's *new* position, not last frame's.
if let Some(tree_update) = ui_state.access.update(rsc.widgets(), render, rsc) {
ui_state.access_adapter.update_if_active(|| tree_update);
}
} }
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
render.resize((size.width, size.height)); render.resize((size.width, size.height));
+5 -3
View File
@@ -7,9 +7,11 @@
use crate::prelude::*; use crate::prelude::*;
/// The minimal `UiRsc` a test needs: just the shared `UiData`, none of the /// The minimal `UiRsc` a test needs: just the shared `UiData`, none of the
/// event/window/state plumbing `DefaultRsc` carries. /// event/window/state plumbing `DefaultRsc` carries. `pub(crate)` so
struct TestRsc { /// `access_tests.rs` (I4, RUST.md) can reuse it rather than keeping a
ui: UiData, /// second copy of the same harness.
pub(crate) struct TestRsc {
pub(crate) ui: UiData,
} }
impl UiRsc for TestRsc { impl UiRsc for TestRsc {
+2
View File
@@ -26,6 +26,8 @@ pub mod state;
pub mod task; pub mod task;
pub mod widget; pub mod widget;
#[cfg(test)]
mod access_tests;
#[cfg(test)] #[cfg(test)]
mod layout_tests; mod layout_tests;
#[cfg(test)] #[cfg(test)]
+9
View File
@@ -114,6 +114,15 @@ impl Widget for TextEdit {
); );
used used
} }
/// I4 (RUST.md): the one override that exists so far -- everything
/// else falls back to `Widget::access_role`'s default `Unknown`.
fn access_role(&self) -> accesskit::Role {
match self.mode {
EditMode::SingleLine => accesskit::Role::TextInput,
EditMode::MultiLine => accesskit::Role::MultilineTextInput,
}
}
} }
const CARET_WIDTH: f32 = 1.0; const CARET_WIDTH: f32 = 1.0;
+8 -1
View File
@@ -177,7 +177,14 @@ where
) )
.on(CursorSense::HoverEnd, move |ctx, rsc| { .on(CursorSense::HoverEnd, move |ctx, rsc| {
ctx.widget(rsc).color = color; ctx.widget(rsc).color = color;
}); })
// I4 (RUST.md): the tabs screen's only named controls, and the
// ones the emulator step at the bottom of that box taps by
// name -- `ui-trace record --do "tap 'pad'"` and so on. `.label`
// slots into this chain like any other widget combinator
// (`RefFnTag` in `core/src/widget/tag.rs`); it does not have to
// be the last thing before `.add`.
.label(label);
(rect, wtext(label).size(30).text_align(Align::CENTER)).stack() (rect, wtext(label).size(30).text_align(Align::CENTER)).stack()
}; };