diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index d9c4c7c5b7..ba61ed7401 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -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`] resource with the latest [`KeyboardInput`] events. /// /// ## Differences @@ -114,6 +128,7 @@ pub struct KeyboardInput { pub fn keyboard_input_system( mut key_input: ResMut>, mut keyboard_input_events: EventReader, + mut focus_events: EventReader, ) { // Avoid clearing if it's not empty to ensure change detection is not triggered. key_input.bypass_change_detection().clear(); @@ -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 diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 3ea2c942f4..e5d4765a57 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -42,7 +42,7 @@ use bevy_app::prelude::*; use bevy_ecs::prelude::*; use bevy_reflect::Reflect; use gestures::*; -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}; @@ -69,6 +69,7 @@ impl Plugin for InputPlugin { app // keyboard .add_event::() + .add_event::() .init_resource::>() .add_systems(PreUpdate, keyboard_input_system.in_set(InputSystem)) // mouse diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index bf082aff2c..fa9c085a8c 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -8,6 +8,7 @@ use bevy_ecs::system::SystemState; use bevy_ecs::world::FromWorld; use bevy_input::{ gestures::*, + keyboard::KeyboardFocusLost, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, }; use bevy_log::{error, trace, warn}; @@ -306,6 +307,9 @@ impl ApplicationHandler for WinitAppRunnerState { WindowEvent::Focused(focused) => { win.focused = focused; self.winit_events.send(WindowFocused { window, focused }); + if !focused { + self.winit_events.send(KeyboardFocusLost); + } } WindowEvent::Occluded(occluded) => { self.winit_events.send(WindowOccluded { window, occluded }); @@ -701,6 +705,9 @@ impl WinitAppRunnerState { WinitEvent::KeyboardInput(e) => { world.send_event(e); } + WinitEvent::KeyboardFocusLost(e) => { + world.send_event(e); + } } } diff --git a/crates/bevy_winit/src/winit_event.rs b/crates/bevy_winit/src/winit_event.rs index 23988f6ad9..9244b1265d 100644 --- a/crates/bevy_winit/src/winit_event.rs +++ b/crates/bevy_winit/src/winit_event.rs @@ -6,6 +6,7 @@ use bevy_input::keyboard::KeyboardInput; use bevy_input::touch::TouchInput; use bevy_input::{ gestures::*, + keyboard::KeyboardFocusLost, mouse::{MouseButtonInput, MouseMotion, MouseWheel}, }; use bevy_reflect::Reflect; @@ -63,6 +64,7 @@ pub enum WinitEvent { TouchInput(TouchInput), KeyboardInput(KeyboardInput), + KeyboardFocusLost(KeyboardFocusLost), } impl From for WinitEvent { @@ -200,3 +202,8 @@ impl From for WinitEvent { Self::KeyboardInput(e) } } +impl From for WinitEvent { + fn from(e: KeyboardFocusLost) -> Self { + Self::KeyboardFocusLost(e) + } +}