1use alloc::{collections::VecDeque, sync::Arc};
4use bevy_input_focus::InputFocus;
5use core::cell::RefCell;
6use std::sync::Mutex;
7use winit::event_loop::ActiveEventLoop;
8
9use accesskit::{
10 ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Node, NodeId, Role, Tree,
11 TreeId, TreeUpdate,
12};
13use accesskit_winit::Adapter;
14use bevy_a11y::{
15 AccessibilityNode, AccessibilityRequested, AccessibilitySystems,
16 ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates,
17};
18use bevy_app::{App, Plugin, PostUpdate};
19use bevy_derive::{Deref, DerefMut};
20use bevy_ecs::{entity::EntityHashMap, prelude::*, system::NonSendMarker};
21use bevy_window::{PrimaryWindow, Window, WindowClosed};
22
23thread_local! {
24 pub static ACCESS_KIT_ADAPTERS: RefCell<AccessKitAdapters> = const { RefCell::new(AccessKitAdapters::new()) };
27}
28
29#[derive(Default, Deref, DerefMut)]
31pub struct AccessKitAdapters(pub EntityHashMap<Adapter>);
32
33impl AccessKitAdapters {
34 pub const fn new() -> Self {
36 Self(EntityHashMap::new())
37 }
38}
39
40#[derive(Resource, Default, Deref, DerefMut)]
42pub struct WinitActionRequestHandlers(pub EntityHashMap<Arc<Mutex<WinitActionRequestHandler>>>);
43
44#[derive(Clone, Default, Deref, DerefMut)]
46pub struct WinitActionRequestHandler(pub VecDeque<ActionRequest>);
47
48impl WinitActionRequestHandler {
49 fn new() -> Arc<Mutex<Self>> {
50 Arc::new(Mutex::new(Self(VecDeque::new())))
51 }
52}
53
54struct AccessKitState {
55 name: String,
56 entity: Entity,
57 requested: AccessibilityRequested,
58}
59
60impl AccessKitState {
61 fn new(
62 name: impl Into<String>,
63 entity: Entity,
64 requested: AccessibilityRequested,
65 ) -> Arc<Mutex<Self>> {
66 let name = name.into();
67
68 Arc::new(Mutex::new(Self {
69 name,
70 entity,
71 requested,
72 }))
73 }
74
75 fn build_root(&mut self) -> Node {
76 let mut node = Node::new(Role::Window);
77 node.set_label(self.name.clone());
78 node
79 }
80
81 fn build_initial_tree(&mut self) -> TreeUpdate {
82 let root = self.build_root();
83 let accesskit_window_id = NodeId(self.entity.to_bits());
84 let mut tree = Tree::new(accesskit_window_id);
85 tree.toolkit_name = Some("Bevy".into());
86 tree.toolkit_version = Some(env!("CARGO_PKG_VERSION").into());
87 self.requested.set(true);
88
89 TreeUpdate {
90 nodes: vec![(accesskit_window_id, root)],
91 tree: Some(tree),
92 tree_id: TreeId::ROOT,
93 focus: accesskit_window_id,
94 }
95 }
96}
97
98struct WinitActivationHandler(Arc<Mutex<AccessKitState>>);
99
100impl ActivationHandler for WinitActivationHandler {
101 fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
102 Some(self.0.lock().unwrap().build_initial_tree())
103 }
104}
105
106impl WinitActivationHandler {
107 pub fn new(state: Arc<Mutex<AccessKitState>>) -> Self {
108 Self(state)
109 }
110}
111
112#[derive(Clone, Default)]
113struct WinitActionHandler(Arc<Mutex<WinitActionRequestHandler>>);
114
115impl ActionHandler for WinitActionHandler {
116 fn do_action(&mut self, request: ActionRequest) {
117 let mut requests = self.0.lock().unwrap();
118 requests.push_back(request);
119 }
120}
121
122impl WinitActionHandler {
123 pub fn new(handler: Arc<Mutex<WinitActionRequestHandler>>) -> Self {
124 Self(handler)
125 }
126}
127
128struct WinitDeactivationHandler;
129
130impl DeactivationHandler for WinitDeactivationHandler {
131 fn deactivate_accessibility(&mut self) {}
132}
133
134pub(crate) fn prepare_accessibility_for_window(
136 event_loop: &ActiveEventLoop,
137 winit_window: &winit::window::Window,
138 entity: Entity,
139 name: String,
140 accessibility_requested: AccessibilityRequested,
141 adapters: &mut AccessKitAdapters,
142 handlers: &mut WinitActionRequestHandlers,
143) {
144 let state = AccessKitState::new(name, entity, accessibility_requested);
145 let activation_handler = WinitActivationHandler::new(Arc::clone(&state));
146
147 let action_request_handler = WinitActionRequestHandler::new();
148 let action_handler = WinitActionHandler::new(Arc::clone(&action_request_handler));
149 let deactivation_handler = WinitDeactivationHandler;
150
151 let adapter = Adapter::with_direct_handlers(
152 event_loop,
153 winit_window,
154 activation_handler,
155 action_handler,
156 deactivation_handler,
157 );
158
159 adapters.insert(entity, adapter);
160 handlers.insert(entity, action_request_handler);
161}
162
163fn window_closed(
164 mut handlers: ResMut<WinitActionRequestHandlers>,
165 mut window_closed_reader: MessageReader<WindowClosed>,
166 _non_send_marker: NonSendMarker,
167) {
168 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
169 for WindowClosed { window, .. } in window_closed_reader.read() {
170 adapters.remove(window);
171 handlers.remove(window);
172 }
173 });
174}
175
176fn poll_receivers(
177 handlers: Res<WinitActionRequestHandlers>,
178 mut actions: MessageWriter<ActionRequestWrapper>,
179) {
180 for (_id, handler) in handlers.iter() {
181 let mut handler = handler.lock().unwrap();
182 while let Some(event) = handler.pop_front() {
183 actions.write(ActionRequestWrapper(event));
184 }
185 }
186}
187
188fn should_update_accessibility_nodes(
189 accessibility_requested: Res<AccessibilityRequested>,
190 manage_accessibility_updates: Res<ManageAccessibilityUpdates>,
191) -> bool {
192 accessibility_requested.get() && manage_accessibility_updates.get()
193}
194
195fn update_accessibility_nodes(
196 focus: Option<Res<InputFocus>>,
197 primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
198 nodes: Query<(
199 Entity,
200 &AccessibilityNode,
201 Option<&Children>,
202 Option<&ChildOf>,
203 )>,
204 node_entities: Query<Entity, With<AccessibilityNode>>,
205 _non_send_marker: NonSendMarker,
206) {
207 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
208 let Ok((primary_window_id, primary_window)) = primary_window.single() else {
209 return;
210 };
211 let Some(adapter) = adapters.get_mut(&primary_window_id) else {
212 return;
213 };
214 let Some(focus) = focus else {
215 return;
216 };
217 if focus.is_changed() || !nodes.is_empty() {
218 if let Some(focused_entity) = focus.get()
221 && !node_entities.contains(focused_entity)
222 {
223 return;
224 }
225
226 adapter.update_if_active(|| {
227 update_adapter(
228 nodes,
229 node_entities,
230 primary_window,
231 primary_window_id,
232 focus,
233 )
234 });
235 }
236 });
237}
238
239fn update_adapter(
240 nodes: Query<(
241 Entity,
242 &AccessibilityNode,
243 Option<&Children>,
244 Option<&ChildOf>,
245 )>,
246 node_entities: Query<Entity, With<AccessibilityNode>>,
247 primary_window: &Window,
248 primary_window_id: Entity,
249 focus: Res<InputFocus>,
250) -> TreeUpdate {
251 let mut to_update = vec![];
252 let mut window_children = vec![];
253 for (entity, node, children, child_of) in &nodes {
254 let mut node = (**node).clone();
255 queue_node_for_update(entity, child_of, &node_entities, &mut window_children);
256 add_children_nodes(children, &node_entities, &mut node);
257 let node_id = NodeId(entity.to_bits());
258 to_update.push((node_id, node));
259 }
260 let mut window_node = Node::new(Role::Window);
261 if primary_window.focused {
262 let title = primary_window.title.clone();
263 window_node.set_label(title.into_boxed_str());
264 }
265 window_node.set_children(window_children);
266 let node_id = NodeId(primary_window_id.to_bits());
267 let window_update = (node_id, window_node);
268 to_update.insert(0, window_update);
269 TreeUpdate {
270 nodes: to_update,
271 tree: None,
272 tree_id: TreeId::ROOT,
273 focus: NodeId(focus.get().unwrap_or(primary_window_id).to_bits()),
274 }
275}
276
277#[inline]
278fn queue_node_for_update(
279 node_entity: Entity,
280 child_of: Option<&ChildOf>,
281 node_entities: &Query<Entity, With<AccessibilityNode>>,
282 window_children: &mut Vec<NodeId>,
283) {
284 let should_push = if let Some(child_of) = child_of {
285 !node_entities.contains(child_of.parent())
286 } else {
287 true
288 };
289 if should_push {
290 window_children.push(NodeId(node_entity.to_bits()));
291 }
292}
293
294#[inline]
295fn add_children_nodes(
296 children: Option<&Children>,
297 node_entities: &Query<Entity, With<AccessibilityNode>>,
298 node: &mut Node,
299) {
300 let Some(children) = children else {
301 return;
302 };
303 for child in children {
304 if node_entities.contains(*child) {
305 node.push_child(NodeId(child.to_bits()));
306 }
307 }
308}
309
310pub struct AccessKitPlugin;
312
313impl Plugin for AccessKitPlugin {
314 fn build(&self, app: &mut App) {
315 app.init_resource::<WinitActionRequestHandlers>()
316 .add_message::<ActionRequestWrapper>()
317 .add_systems(
318 PostUpdate,
319 (
320 poll_receivers,
321 update_accessibility_nodes
322 .run_if(should_update_accessibility_nodes)
323 .after(bevy_input_focus::InputFocusSystems::FocusChangeEvents),
329 window_closed
330 .before(poll_receivers)
331 .before(update_accessibility_nodes),
332 )
333 .in_set(AccessibilitySystems::Update),
334 );
335 }
336}