@@ -10,6 +10,7 @@ use bevy_ecs::{
1010} ;
1111use bevy_input:: {
1212 gestures:: * ,
13+ keyboard:: { Key , KeyCode , KeyboardInput } ,
1314 mouse:: { MouseButtonInput , MouseMotion , MouseScrollUnit , MouseWheel } ,
1415} ;
1516use bevy_log:: { trace, warn} ;
@@ -23,8 +24,9 @@ use winit::{
2324 application:: ApplicationHandler ,
2425 dpi:: PhysicalSize ,
2526 event,
26- event:: { DeviceEvent , DeviceId , StartCause , WindowEvent } ,
27+ event:: { DeviceEvent , DeviceId , Modifiers , StartCause , WindowEvent } ,
2728 event_loop:: { ActiveEventLoop , ControlFlow , EventLoop } ,
29+ keyboard:: ModifiersKeyState ,
2830 window:: WindowId ,
2931} ;
3032
@@ -78,6 +80,10 @@ pub(crate) struct WinitAppRunnerState {
7880 bevy_window_events : Vec < bevy_window:: WindowEvent > ,
7981 /// Raw Winit window events to send
8082 raw_winit_events : Vec < RawWinitWindowEvent > ,
83+ /// Latest winit modifier state per window, buffered for reconciliation
84+ /// against `WinitWindowPressedKeys` once all of this batch's key events
85+ /// have been seen (see `reconcile_pending_modifiers`).
86+ pending_modifiers : Vec < ( Entity , Modifiers ) > ,
8187
8288 windows_system_state : SystemState <
8389 Query <
@@ -116,6 +122,7 @@ impl WinitAppRunnerState {
116122 startup_forced_updates : 5 ,
117123 bevy_window_events : Vec :: new ( ) ,
118124 raw_winit_events : Vec :: new ( ) ,
125+ pending_modifiers : Vec :: new ( ) ,
119126 windows_system_state,
120127 scheduled_tick_start : None ,
121128 }
@@ -281,6 +288,21 @@ impl ApplicationHandler<WinitUserEvent> for WinitAppRunnerState {
281288 }
282289 self . bevy_window_events . send ( keyboard_input) ;
283290 }
291+ WindowEvent :: ModifiersChanged ( mods) => {
292+ // Buffer the latest state; reconciled at end-of-batch (see
293+ // `reconcile_pending_modifiers`) so real key events in the
294+ // same batch land in `WinitWindowPressedKeys` first and are
295+ // not double-reported.
296+ if let Some ( entry) = self
297+ . pending_modifiers
298+ . iter_mut ( )
299+ . find ( |( entity, _) | * entity == window)
300+ {
301+ entry. 1 = mods;
302+ } else {
303+ self . pending_modifiers . push ( ( window, mods) ) ;
304+ }
305+ }
284306 WindowEvent :: CursorMoved { position, .. } => {
285307 let physical_position = DVec2 :: new ( position. x , position. y ) ;
286308
@@ -773,7 +795,119 @@ impl WinitAppRunnerState {
773795 }
774796 }
775797
798+ /// Apply the final per-window modifier state of this event batch to
799+ /// `WinitWindowPressedKeys`, synthesizing `KeyboardInput` events for any
800+ /// modifier key whose transition winit never delivered as a key event.
801+ /// That happens when the OS consumes a shortcut chord (the keyup lands
802+ /// elsewhere — e.g. the macOS Cmd+Shift+5 screen-capture overlay takes
803+ /// the keyups without any focus transition, especially on the web) or
804+ /// when focus returns with a modifier already held (the keydown predates
805+ /// focus). Winit re-derives modifier state from input events, so a stale
806+ /// modifier heals on the next input event even when no focus transition
807+ /// was ever reported.
808+ ///
809+ /// Running at end-of-batch means the batch's real key events have
810+ /// already been applied to `WinitWindowPressedKeys`, so ordinary
811+ /// modifier presses and releases synthesize nothing.
812+ fn reconcile_pending_modifiers ( & mut self ) {
813+ if self . pending_modifiers . is_empty ( ) {
814+ return ;
815+ }
816+
817+ let pending = core:: mem:: take ( & mut self . pending_modifiers ) ;
818+ let mut synthesized = Vec :: new ( ) ;
819+
820+ for ( window, mods) in pending {
821+ let Some ( mut pressed_keys) = self . world_mut ( ) . get_mut :: < WinitWindowPressedKeys > ( window)
822+ else {
823+ continue ;
824+ } ;
825+
826+ let state = mods. state ( ) ;
827+ for ( reported, logical_key, sides) in [
828+ (
829+ state. super_key ( ) ,
830+ Key :: Super ,
831+ [
832+ ( KeyCode :: SuperLeft , mods. lsuper_state ( ) ) ,
833+ ( KeyCode :: SuperRight , mods. rsuper_state ( ) ) ,
834+ ] ,
835+ ) ,
836+ (
837+ state. control_key ( ) ,
838+ Key :: Control ,
839+ [
840+ ( KeyCode :: ControlLeft , mods. lcontrol_state ( ) ) ,
841+ ( KeyCode :: ControlRight , mods. rcontrol_state ( ) ) ,
842+ ] ,
843+ ) ,
844+ (
845+ state. alt_key ( ) ,
846+ Key :: Alt ,
847+ [
848+ ( KeyCode :: AltLeft , mods. lalt_state ( ) ) ,
849+ ( KeyCode :: AltRight , mods. ralt_state ( ) ) ,
850+ ] ,
851+ ) ,
852+ (
853+ state. shift_key ( ) ,
854+ Key :: Shift ,
855+ [
856+ ( KeyCode :: ShiftLeft , mods. lshift_state ( ) ) ,
857+ ( KeyCode :: ShiftRight , mods. rshift_state ( ) ) ,
858+ ] ,
859+ ) ,
860+ ] {
861+ let held: Vec < KeyCode > = sides
862+ . iter ( )
863+ . map ( |( key_code, _) | * key_code)
864+ . filter ( |key_code| pressed_keys. 0 . contains_key ( key_code) )
865+ . collect ( ) ;
866+
867+ if reported && held. is_empty ( ) {
868+ // pick the side winit reports as pressed where the
869+ // platform knows it, defaulting to left (the web backend
870+ // can't distinguish sides)
871+ let key_code = sides
872+ . iter ( )
873+ . find ( |( _, side) | * side == ModifiersKeyState :: Pressed )
874+ . map ( |( key_code, _) | * key_code)
875+ . unwrap_or ( sides[ 0 ] . 0 ) ;
876+ pressed_keys. 0 . insert ( key_code, logical_key. clone ( ) ) ;
877+ synthesized. push ( KeyboardInput {
878+ key_code,
879+ logical_key,
880+ state : bevy_input:: ButtonState :: Pressed ,
881+ repeat : false ,
882+ window,
883+ text : None ,
884+ } ) ;
885+ } else if !reported {
886+ for key_code in held {
887+ let Some ( logical_key) = pressed_keys. 0 . remove ( & key_code) else {
888+ continue ;
889+ } ;
890+ synthesized. push ( KeyboardInput {
891+ key_code,
892+ logical_key,
893+ state : bevy_input:: ButtonState :: Released ,
894+ repeat : false ,
895+ window,
896+ text : None ,
897+ } ) ;
898+ }
899+ }
900+ }
901+ }
902+
903+ for event in synthesized {
904+ self . bevy_window_events . send ( event) ;
905+ }
906+ }
907+
776908 fn forward_bevy_events ( & mut self ) {
909+ self . reconcile_pending_modifiers ( ) ;
910+
777911 let raw_winit_events = self . raw_winit_events . drain ( ..) . collect :: < Vec < _ > > ( ) ;
778912 let window_events = self . bevy_window_events . drain ( ..) . collect :: < Vec < _ > > ( ) ;
779913 let world = self . world_mut ( ) ;
0 commit comments