Windows drops the accessibility adapter the reference never had (#2975)

Under Proton-based Wine, `tabtip.exe` is a UI Automation client, and `bevy_winit` gives every window an AccessKit provider. That traffic faults in `rpcrt4` on blocks the game has reused, which damages the heap and later crashes the client at varying addresses. Blocking `tabtip.exe` stops the faults and the crash, and other programs in the same bottle show no such faults.

The reference's window has no provider: `WoW.exe` imports no accessibility library and names none of its calls. benilla builds no AccessKit tree either.

`VideoPlugin` now drops the adapter when Windows reports a new window, which restores winit's window procedure. Other platforms keep theirs.

No test: the adapter needs a real window and the fault a UI Automation client. Built for macOS and Windows.

Refs #2932

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-11-02 06:43:05 +07:00
committed by sindoring
parent f55f42a28e
commit dfb9cfc9f5
3 changed files with 23 additions and 1 deletions
+20
View File
@@ -495,9 +495,29 @@ impl Plugin for VideoPlugin {
.after(crate::cvars::sync_cvars),
),
);
#[cfg(windows)]
app.add_message::<bevy::window::WindowCreated>()
.add_systems(First, drop_accessibility_adapter);
}
}
/// Drops the AccessKit adapter `bevy_winit` gives a new window, which restores winit's window
/// procedure. The reference's window serves no accessibility client (`WoW.exe` imports no such
/// library and names none of its calls), and benilla builds no tree for one to read. The adapter's
/// UI Automation provider is not inert where a client runs: Proton-based Wine's `tabtip.exe` queries
/// the focused window, and that traffic faults in `rpcrt4` on reused memory and damages the heap.
#[cfg_attr(not(windows), allow(dead_code))]
fn drop_accessibility_adapter(
mut created: MessageReader<bevy::window::WindowCreated>,
_main_thread: bevy::ecs::system::NonSendMarker,
) {
bevy::winit::accessibility::ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
for created in created.read() {
adapters.remove(&created.window);
}
});
}
/// How many `RestartGx()` calls the interface has made. A counter, not a flag: each applier keeps
/// its own last value, so one bump forces one re-assertion in each, in any order.
#[derive(Resource, Default, Clone, Copy, PartialEq, Eq, Debug)]
@@ -96,6 +96,7 @@ const VERDICTS: &[(&str, Verdict)] = &[
("MouseScrollUnit", Plumbing),
("NativeKey", Plumbing),
("NativeKeyCode", Plumbing),
("ACCESS_KIT_ADAPTERS", Plumbing),
("ExitCondition", Plumbing),
("Monitor", Plumbing),
("MonitorSelection", Plumbing),
@@ -104,6 +105,7 @@ const VERDICTS: &[(&str, Verdict)] = &[
("RawHandleWrapper", Plumbing),
("VideoMode", Plumbing),
("Window", Plumbing),
("WindowCreated", Plumbing),
("WindowLevel", Plumbing),
("WindowMode", Plumbing),
("WindowPlugin", Plumbing),
+1 -1
View File
@@ -501,7 +501,7 @@ Each entry is the first sentence of a `Deviation:` comment, linking to where it
- `gxWindow "0"` is borderless fullscreen, not 1.12's exclusive mode-set at `gxResolution`, because Wayland has no client mode-setting (winit ignores `Fullscreen::Exclusive` there), and X11's XRandR changes the desktop's mode and a crash leaves it changed. Bevy's `WindowMode::Fullscreen` also panics on a live change that cannot resolve a monitor (`bevy_winit::winit_windows:91`, `system.rs:333`). ([`src/video.rs:5`](../crates/benilla-app/src/video.rs#L5))
- On Windows, NVIDIA's Vulkan driver (610.88 seen) crashes inside `vkQueuePresentKHR` on the first present after any swapchain resize while the present mode is `Fifo`, `FifoRelaxed` or `AutoVsync` (a maximize is a resize). The fault is in the driver's own DXGI present (`d3d11.dll`): it stays with `VK_LAYER_NV_present` disabled, and the validation layer reports nothing before it. `Mailbox`, `AutoNoVsync` and DX12 survive, so there "vsync on" is `AutoNoVsync` paced to the monitor's refresh rate (`present_choice`) because that keeps the synced look without the crash. `$WOW_PRESENT=fifo` restores plain `Fifo`. ([`src/video.rs:17`](../crates/benilla-app/src/video.rs#L17))
- Sizes are logical px, because the windowed `gxResolution` is a logical inner size here. Read at each verb (`DisplayLive`), so the list follows the display and `widescreen`; the reference builds its list once and never invalidates it. ([`src/video.rs:619`](../crates/benilla-app/src/video.rs#L619))
- Sizes are logical px, because the windowed `gxResolution` is a logical inner size here. Read at each verb (`DisplayLive`), so the list follows the display and `widescreen`; the reference builds its list once and never invalidates it. ([`src/video.rs:639`](../crates/benilla-app/src/video.rs#L639))
#### vplates