Open
Description
Bevy version
0.16.0
Relevant system information
- Rust version:
rustc 1.85.0 (4d91de4e4 2025-02-17)
- Operating system: macOS Sequoia 15.5 (24F74)
- Graphics adapter info:
`AdapterInfo { name: "Apple M1", vendor: 0, device: 0, device_type: IntegratedGpu, driver: "", driver_info: "", backend: Metal }`
What I did
Ran the multiple_windows
example provided in the bevy repository.
Steps to reproduce:
- Run the example.
- Close the second window before the primary window.
What went wrong
- Expected behavior:
The application should handle the second window closing gracefully and continue running with the first window. - Actual behavior:
The application crashes immediately when the second window is closed first. No crash occurs when the first window is closed before the second.
Additional information
- After investigation, I tracked the crash down to the
AccessKitAdapter
removal logic in thewindow_closed
system of theaccessibility
module in thebevy_winit
crate. - As a workaround, I delayed the
AccessKitAdapter
removal in thewindow_closed
system by 1 frame, which prevented the crash. However, this is admittedly a hacky solution and likely not ideal for upstream.
fn window_closed(
// Removed
// mut adapters: NonSendMut<AccessKitAdapters>,
// Added
mut removal_queue: ResMut<AdapterRemovalQueue>,
mut handlers: ResMut<WinitActionRequestHandlers>,
mut events: EventReader<WindowClosed>,
) {
for WindowClosed { window, .. } in events.read() {
// Removed
// adapters.remove(window);
// Added
removal_queue.to_remove.push(*window);
removal_queue.frame_delay = 1;
handlers.remove(window);
}
}
impl Plugin for AccessKitPlugin {
fn build(&self, app: &mut App) {
app.init_non_send_resource::<AccessKitAdapters>()
.init_resource::<WinitActionRequestHandlers>()
// Added
.init_resource::<AdapterRemovalQueue>()
.add_event::<ActionRequestWrapper>()
.add_systems(
PostUpdate,
(
poll_receivers,
update_accessibility_nodes.run_if(should_update_accessibility_nodes),
window_closed
.before(poll_receivers)
.before(update_accessibility_nodes),
// Added
delayed_adapter_removal.after(window_closed),
)
.in_set(AccessibilitySystem::Update),
);
}
}
// Added
#[derive(Resource, Default)]
struct AdapterRemovalQueue {
to_remove: Vec<Entity>,
frame_delay: usize,
}
// Added
fn delayed_adapter_removal(
mut adapters: NonSendMut<AccessKitAdapters>,
mut removal_queue: ResMut<AdapterRemovalQueue>,
) {
if removal_queue.frame_delay > 0 {
removal_queue.frame_delay -= 1;
return;
}
for window in removal_queue.to_remove.drain(..) {
println!("Removing adapter after delay for {:?}", window);
adapters.remove(&window);
}
}