@@ -72,7 +72,10 @@ impl ClickEventData {
7272}
7373
7474pub struct InputManager {
75- key_descriptors_down : HashSet < KeyDescriptor > ,
75+ /// Tracks which physical keys (identified by physical key + location) are currently pressed.
76+ /// This allows us to match key-up events even when the logical key changes
77+ /// due to modifier state changes (e.g., shift pressed/released).
78+ keys_down_phys_loc : HashSet < ( PhysicalKey , KeyLocation ) > ,
7679
7780 keys_down : HashSet < KeyCode > ,
7881 keys_toggled : HashSet < KeyCode > ,
@@ -89,7 +92,7 @@ pub struct InputManager {
8992impl InputManager {
9093 pub fn new ( gamepad_button_mapping : HashMap < GamepadButton , KeyCode > ) -> Self {
9194 Self {
92- key_descriptors_down : HashSet :: new ( ) ,
95+ keys_down_phys_loc : HashSet :: new ( ) ,
9396 keys_down : HashSet :: new ( ) ,
9497 keys_toggled : HashSet :: new ( ) ,
9598 last_key : KeyCode :: UNKNOWN ,
@@ -156,7 +159,11 @@ impl InputManager {
156159 }
157160
158161 PlayerEvent :: KeyDown { key } => {
159- self . key_descriptors_down . insert ( key) ;
162+ // Track the physical key + location, so we can match key-up events
163+ // even when the logical key changes due to modifier state changes
164+ // (e.g., shift pressed/released).
165+ let physical_location = ( key. physical_key , key. key_location ) ;
166+ self . keys_down_phys_loc . insert ( physical_location) ;
160167
161168 let key_code = self . map_to_key_code ( key) ?;
162169 let key_char = self . map_to_key_char ( key) ;
@@ -168,10 +175,17 @@ impl InputManager {
168175 }
169176 }
170177 PlayerEvent :: KeyUp { key } => {
171- if !self . key_descriptors_down . remove ( & key) {
178+ // Match key-up events by physical key and location, not the exact
179+ // KeyDescriptor. This handles cases where modifier state changes
180+ // between key-down and key-up (e.g., shift pressed/released),
181+ // which would change the logical key (e.g., 'w' -> 'W').
182+ if !self
183+ . keys_down_phys_loc
184+ . remove ( & ( key. physical_key , key. key_location ) )
185+ {
172186 // Ignore spurious KeyUp events that may happen e.g. during IME.
173187 // We assume that in order for a key to generate KeyUp, it had to
174- // generate KeyDown for the same exact KeyDescriptor .
188+ // generate KeyDown for the same physical key and location .
175189
176190 // TODO Apparently this behavior is platform-dependent and
177191 // doesn't happen on Windows. We cannot remove it fully
0 commit comments