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 tree = Tree::new(accesskit_window_id);
85 self.requested.set(true);
86
87 TreeUpdate {
88 nodes: vec![(accesskit_window_id, root)],
89 tree: Some(tree),
90 tree_id: TreeId::ROOT,
91 focus: accesskit_window_id,
92 }
93 }
94}
95
96struct WinitActivationHandler(Arc<Mutex<AccessKitState>>);
97
98impl ActivationHandler for WinitActivationHandler {
99 fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
100 Some(self.0.lock().unwrap().build_initial_tree())
101 }
102}
103
104impl WinitActivationHandler {
105 pub fn new(state: Arc<Mutex<AccessKitState>>) -> Self {
106 Self(state)
107 }
108}
109
110#[derive(Clone, Default)]
111struct WinitActionHandler(Arc<Mutex<WinitActionRequestHandler>>);
112
113impl ActionHandler for WinitActionHandler {
114 fn do_action(&mut self, request: ActionRequest) {
115 let mut requests = self.0.lock().unwrap();
116 requests.push_back(request);
117 }
118}
119
120impl WinitActionHandler {
121 pub fn new(handler: Arc<Mutex<WinitActionRequestHandler>>) -> Self {
122 Self(handler)
123 }
124}
125
126struct WinitDeactivationHandler;
127
128impl DeactivationHandler for WinitDeactivationHandler {
129 fn deactivate_accessibility(&mut self) {}
130}
131
132pub(crate) fn prepare_accessibility_for_window(
134 event_loop: &ActiveEventLoop,
135 winit_window: &winit::window::Window,
136 entity: Entity,
137 name: String,
138 accessibility_requested: AccessibilityRequested,
139 adapters: &mut AccessKitAdapters,
140 handlers: &mut WinitActionRequestHandlers,
141) {
142 let state = AccessKitState::new(name, entity, accessibility_requested);
143 let activation_handler = WinitActivationHandler::new(Arc::clone(&state));
144
145 let action_request_handler = WinitActionRequestHandler::new();
146 let action_handler = WinitActionHandler::new(Arc::clone(&action_request_handler));
147 let deactivation_handler = WinitDeactivationHandler;
148
149 let adapter = Adapter::with_direct_handlers(
150 event_loop,
151 winit_window,
152 activation_handler,
153 action_handler,
154 deactivation_handler,
155 );
156
157 adapters.insert(entity, adapter);
158 handlers.insert(entity, action_request_handler);
159}
160
161fn window_closed(
162 mut handlers: ResMut<WinitActionRequestHandlers>,
163 mut window_closed_reader: MessageReader<WindowClosed>,
164 _non_send_marker: NonSendMarker,
165) {
166 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
167 for WindowClosed { window, .. } in window_closed_reader.read() {
168 adapters.remove(window);
169 handlers.remove(window);
170 }
171 });
172}
173
174fn poll_receivers(
175 handlers: Res<WinitActionRequestHandlers>,
176 mut actions: MessageWriter<ActionRequestWrapper>,
177) {
178 for (_id, handler) in handlers.iter() {
179 let mut handler = handler.lock().unwrap();
180 while let Some(event) = handler.pop_front() {
181 actions.write(ActionRequestWrapper(event));
182 }
183 }
184}
185
186fn should_update_accessibility_nodes(
187 accessibility_requested: Res<AccessibilityRequested>,
188 manage_accessibility_updates: Res<ManageAccessibilityUpdates>,
189) -> bool {
190 accessibility_requested.get() && manage_accessibility_updates.get()
191}
192
193fn update_accessibility_nodes(
194 focus: Option<Res<InputFocus>>,
195 primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
196 nodes: Query<(
197 Entity,
198 &AccessibilityNode,
199 Option<&Children>,
200 Option<&ChildOf>,
201 )>,
202 node_entities: Query<Entity, With<AccessibilityNode>>,
203 _non_send_marker: NonSendMarker,
204) {
205 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
206 let Ok((primary_window_id, primary_window)) = primary_window.single() else {
207 return;
208 };
209 let Some(adapter) = adapters.get_mut(&primary_window_id) else {
210 return;
211 };
212 let Some(focus) = focus else {
213 return;
214 };
215 if focus.is_changed() || !nodes.is_empty() {
216 if let Some(focused_entity) = focus.get()
219 && !node_entities.contains(focused_entity)
220 {
221 return;
222 }
223
224 adapter.update_if_active(|| {
225 update_adapter(
226 nodes,
227 node_entities,
228 primary_window,
229 primary_window_id,
230 focus,
231 )
232 });
233 }
234 });
235}
236
237fn update_adapter(
238 nodes: Query<(
239 Entity,
240 &AccessibilityNode,
241 Option<&Children>,
242 Option<&ChildOf>,
243 )>,
244 node_entities: Query<Entity, With<AccessibilityNode>>,
245 primary_window: &Window,
246 primary_window_id: Entity,
247 focus: Res<InputFocus>,
248) -> TreeUpdate {
249 let mut to_update = vec![];
250 let mut window_children = vec![];
251 for (entity, node, children, child_of) in &nodes {
252 let mut node = (**node).clone();
253 queue_node_for_update(entity, child_of, &node_entities, &mut window_children);
254 add_children_nodes(children, &node_entities, &mut node);
255 let node_id = NodeId(entity.to_bits());
256 to_update.push((node_id, node));
257 }
258 let mut window_node = Node::new(Role::Window);
259 if primary_window.focused {
260 let title = primary_window.title.clone();
261 window_node.set_label(title.into_boxed_str());
262 }
263 window_node.set_children(window_children);
264 let node_id = NodeId(primary_window_id.to_bits());
265 let window_update = (node_id, window_node);
266 to_update.insert(0, window_update);
267 TreeUpdate {
268 nodes: to_update,
269 tree: None,
270 tree_id: TreeId::ROOT,
271 focus: NodeId(focus.get().unwrap_or(primary_window_id).to_bits()),
272 }
273}
274
275#[inline]
276fn queue_node_for_update(
277 node_entity: Entity,
278 child_of: Option<&ChildOf>,
279 node_entities: &Query<Entity, With<AccessibilityNode>>,
280 window_children: &mut Vec<NodeId>,
281) {
282 let should_push = if let Some(child_of) = child_of {
283 !node_entities.contains(child_of.parent())
284 } else {
285 true
286 };
287 if should_push {
288 window_children.push(NodeId(node_entity.to_bits()));
289 }
290}
291
292#[inline]
293fn add_children_nodes(
294 children: Option<&Children>,
295 node_entities: &Query<Entity, With<AccessibilityNode>>,
296 node: &mut Node,
297) {
298 let Some(children) = children else {
299 return;
300 };
301 for child in children {
302 if node_entities.contains(*child) {
303 node.push_child(NodeId(child.to_bits()));
304 }
305 }
306}
307
308pub struct AccessKitPlugin;
310
311impl Plugin for AccessKitPlugin {
312 fn build(&self, app: &mut App) {
313 app.init_resource::<WinitActionRequestHandlers>()
314 .add_message::<ActionRequestWrapper>()
315 .add_systems(
316 PostUpdate,
317 (
318 poll_receivers,
319 update_accessibility_nodes
320 .run_if(should_update_accessibility_nodes)
321 .after(bevy_input_focus::InputFocusSystems::FocusChangeEvents),
327 window_closed
328 .before(poll_receivers)
329 .before(update_accessibility_nodes),
330 )
331 .in_set(AccessibilitySystems::Update),
332 );
333 }
334}