Skip to content

Commit

Permalink
Fix phantom key presses in winit on focus change (bevyengine#13299)
Browse files Browse the repository at this point in the history
On Linux/X11, changing focus into a winit window will produce winit
KeyboardInput events with a "is_synthetic=true" flag that are not
intended to be used. Bevy erroneously passes them on to the user,
resulting in phantom key presses.

This patch properly filters them out.

For example, pressing Alt+Tab to focus a bevy winit window results in a
permanently stuck Tab key until the user presses Tab once again to
produce a winit KeyboardInput release event.

The Tab key press event that causes this problem is "synthetic", should
not be used according to the winit devs, and simply ignoring it fixes
this problem.

Reference: https://docs.rs/winit/0.30.0/winit/event/enum.WindowEvent.html#variant.KeyboardInput.field.is_synthetic
Relevant discussion: rust-windowing/winit#3543

Synthetic key **releases** are still evaluated though, as they are
essential for correct release key handling. For example, if the user
binds the key combination Alt+1 to the action "move the window to
workspace 1", places the bevy game in workspace 2, focuses the game and
presses Alt+1, then the key release event for the "1" key will be
synthetic. If we would filter out all synthetic keys, the bevy game
would think that the 1 key remains pressed forever, until the user
manually presses+releases the key again inside bevy.
  • Loading branch information
hut committed Jun 5, 2024
1 parent 2eb9d5c commit 814963c
Showing 1 changed file with 15 additions and 9 deletions.
24 changes: 15 additions & 9 deletions crates/bevy_winit/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use approx::relative_eq;
use bevy_app::{App, AppExit, PluginsState};
use bevy_ecs::change_detection::{DetectChanges, NonSendMut, Res};
use bevy_ecs::entity::Entity;
use bevy_ecs::event::{EventWriter, ManualEventReader};
use bevy_ecs::event::{ElementState, EventWriter, ManualEventReader};
use bevy_ecs::prelude::*;
use bevy_ecs::system::SystemState;
use bevy_ecs::world::FromWorld;
Expand Down Expand Up @@ -223,16 +223,22 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
);
}
WindowEvent::CloseRequested => self.winit_events.send(WindowCloseRequested { window }),
WindowEvent::KeyboardInput { ref event, .. } => {
if event.state.is_pressed() {
if let Some(char) = &event.text {
let char = char.clone();
#[allow(deprecated)]
self.winit_events.send(ReceivedCharacter { window, char });
WindowEvent::KeyboardInput {
ref event,
is_synthetic,
..
} => {
if !(is_synthetic && event.state == ElementState::Pressed) {
if event.state.is_pressed() {
if let Some(char) = &event.text {
let char = char.clone();
#[allow(deprecated)]
self.winit_events.send(ReceivedCharacter { window, char });
}
}
self.winit_events
.send(converters::convert_keyboard_input(event, window));
}
self.winit_events
.send(converters::convert_keyboard_input(event, window));
}
WindowEvent::CursorMoved { position, .. } => {
let physical_position = DVec2::new(position.x, position.y);
Expand Down

0 comments on commit 814963c

Please sign in to comment.