Skip to content

Commit

Permalink
flush key_input cache when Bevy loses focus
Browse files Browse the repository at this point in the history
This helps avoiding stuck key presses after switching from and then back to Bevy
  • Loading branch information
gavlig committed Apr 4, 2024
1 parent 4da4493 commit 9200f2c
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 1 deletion.
21 changes: 21 additions & 0 deletions crates/bevy_input/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ pub struct KeyboardInput {
pub window: Entity,
}

/// Gets generated from `bevy_winit::winit_runner`
///
/// Used for clearing all cached states to avoid having 'stuck' key presses
/// when, for example, switching between windows with 'Alt-Tab' or using any other
/// OS specific key combination that leads to Bevy window losing focus and not receiving any
/// input events
#[derive(Event, Debug, Clone, PartialEq, Eq, Reflect)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct KeyboardFocusLost;

/// Updates the [`ButtonInput<KeyCode>`] resource with the latest [`KeyboardInput`] events.
///
/// ## Differences
Expand All @@ -114,6 +128,7 @@ pub struct KeyboardInput {
pub fn keyboard_input_system(
mut key_input: ResMut<ButtonInput<KeyCode>>,
mut keyboard_input_events: EventReader<KeyboardInput>,
mut focus_events: EventReader<KeyboardFocusLost>,
) {
// Avoid clearing if it's not empty to ensure change detection is not triggered.
key_input.bypass_change_detection().clear();
Expand All @@ -126,6 +141,12 @@ pub fn keyboard_input_system(
ButtonState::Released => key_input.release(*key_code),
}
}

// Release all cached input to avoid having stuck input when switching between windows in os
if !focus_events.is_empty() {
key_input.release_all();
focus_events.clear();
}
}

/// Contains the platform-native physical key identifier
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub mod prelude {
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput};
use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput};
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
use touch::{touch_screen_input_system, TouchInput, Touches};
use touchpad::{TouchpadMagnify, TouchpadRotate};
Expand Down Expand Up @@ -69,6 +69,7 @@ impl Plugin for InputPlugin {
app
// keyboard
.add_event::<KeyboardInput>()
.add_event::<KeyboardFocusLost>()
.init_resource::<ButtonInput<KeyCode>>()
.add_systems(PreUpdate, keyboard_input_system.in_set(InputSystem))
// mouse
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use bevy_ecs::event::ManualEventReader;
use bevy_ecs::prelude::*;
use bevy_ecs::system::SystemState;
use bevy_input::{
keyboard::KeyboardFocusLost,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touchpad::{TouchpadMagnify, TouchpadRotate},
};
Expand Down Expand Up @@ -583,6 +584,9 @@ fn handle_winit_event(
WindowEvent::Focused(focused) => {
win.focused = focused;
winit_events.send(WindowFocused { window, focused });
if !focused {
winit_events.send(KeyboardFocusLost);
}
}
WindowEvent::Occluded(occluded) => {
winit_events.send(WindowOccluded { window, occluded });
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_winit/src/winit_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use bevy_ecs::prelude::*;
use bevy_input::keyboard::KeyboardInput;
use bevy_input::touch::TouchInput;
use bevy_input::{
keyboard::KeyboardFocusLost,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
touchpad::{TouchpadMagnify, TouchpadRotate},
};
Expand Down Expand Up @@ -61,6 +62,7 @@ pub enum WinitEvent {
TouchInput(TouchInput),

KeyboardInput(KeyboardInput),
KeyboardFocusLost(KeyboardFocusLost),
}

impl From<ApplicationLifetime> for WinitEvent {
Expand Down Expand Up @@ -188,6 +190,11 @@ impl From<KeyboardInput> for WinitEvent {
Self::KeyboardInput(e)
}
}
impl From<KeyboardFocusLost> for WinitEvent {
fn from(e: KeyboardFocusLost) -> Self {
Self::KeyboardFocusLost(e)
}
}

/// Forwards buffered [`WinitEvent`] events to the app.
pub(crate) fn forward_winit_events(buffered_events: &mut Vec<WinitEvent>, app: &mut App) {
Expand Down Expand Up @@ -271,6 +278,9 @@ pub(crate) fn forward_winit_events(buffered_events: &mut Vec<WinitEvent>, app: &
WinitEvent::KeyboardInput(e) => {
app.world_mut().send_event(e);
}
WinitEvent::KeyboardFocusLost(e) => {
app.world_mut().send_event(e);
}
}
}
app.world_mut()
Expand Down

0 comments on commit 9200f2c

Please sign in to comment.