@@ -47,6 +47,7 @@ pub struct StickyKeysFilter {
4747 next : Box < dyn Filter + Send + Sync > ,
4848 listener : ModifierStateListener ,
4949 data : Data ,
50+ down_key_map : HashMap < i32 , HashSet < i32 > > ,
5051}
5152
5253#[ derive( Default ) ]
@@ -69,15 +70,34 @@ impl StickyKeysFilter {
6970 next : Box < dyn Filter + Send + Sync > ,
7071 listener : ModifierStateListener ,
7172 ) -> StickyKeysFilter {
72- Self { next, listener, data : Default :: default ( ) }
73+ Self { next, listener, data : Default :: default ( ) , down_key_map : HashMap :: new ( ) }
7374 }
7475}
7576
7677impl Filter for StickyKeysFilter {
7778 fn notify_key ( & mut self , event : & KeyEvent ) {
79+ let down = event. action == KeyEventAction :: DOWN ;
7880 let up = event. action == KeyEventAction :: UP ;
7981 let mut modifier_state = self . data . modifier_state ;
8082 let mut locked_modifier_state = self . data . locked_modifier_state ;
83+ if down {
84+ let down_keys = self . down_key_map . entry ( event. deviceId ) . or_default ( ) ;
85+ down_keys. insert ( event. keyCode ) ;
86+ } else {
87+ if !self . down_key_map . contains_key ( & event. deviceId ) {
88+ self . next . notify_key ( event) ;
89+ return ;
90+ }
91+ let down_keys = self . down_key_map . get_mut ( & event. deviceId ) . unwrap ( ) ;
92+ if !down_keys. contains ( & event. keyCode ) {
93+ self . next . notify_key ( event) ;
94+ return ;
95+ }
96+ down_keys. remove ( & event. keyCode ) ;
97+ if down_keys. is_empty ( ) {
98+ self . down_key_map . remove ( & event. deviceId ) ;
99+ }
100+ }
81101 if !is_ephemeral_modifier_key ( event. keyCode ) {
82102 // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
83103 // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
@@ -130,6 +150,7 @@ impl Filter for StickyKeysFilter {
130150 self . data . locked_modifier_state = ModifierState :: None ;
131151 self . listener . modifier_state_changed ( ModifierState :: None , ModifierState :: None ) ;
132152 }
153+ self . down_key_map . retain ( |key, _| device_infos. iter ( ) . any ( |x| * key == x. deviceId ) ) ;
133154 self . next . notify_devices_changed ( device_infos) ;
134155 }
135156
@@ -166,6 +187,7 @@ impl Filter for StickyKeysFilter {
166187 result += & format ! ( "\t modifier_state = {:?}\n " , self . data. modifier_state) ;
167188 result += & format ! ( "\t locked_modifier_state = {:?}\n " , self . data. locked_modifier_state) ;
168189 result += & format ! ( "\t contributing_devices = {:?}\n " , self . data. contributing_devices) ;
190+ result += & format ! ( "\t down_key_map = {:?}\n " , self . down_key_map) ;
169191 self . next . dump ( dump_str + & result)
170192 }
171193}
@@ -321,6 +343,31 @@ mod tests {
321343 }
322344 }
323345
346+ #[ test]
347+ fn test_notify_key_passes_ephemeral_modifier_keys_if_only_key_up_occurs ( ) {
348+ let test_filter = TestFilter :: new ( ) ;
349+ let test_callbacks = TestCallbacks :: new ( ) ;
350+ let mut sticky_keys_filter = setup_filter (
351+ Box :: new ( test_filter. clone ( ) ) ,
352+ Arc :: new ( RwLock :: new ( Strong :: new ( Box :: new ( test_callbacks. clone ( ) ) ) ) ) ,
353+ ) ;
354+ let key_codes = & [
355+ KEYCODE_ALT_LEFT ,
356+ KEYCODE_ALT_RIGHT ,
357+ KEYCODE_CTRL_LEFT ,
358+ KEYCODE_CTRL_RIGHT ,
359+ KEYCODE_SHIFT_LEFT ,
360+ KEYCODE_SHIFT_RIGHT ,
361+ KEYCODE_META_LEFT ,
362+ KEYCODE_META_RIGHT ,
363+ ] ;
364+ for key_code in key_codes. iter ( ) {
365+ let event = KeyEvent { keyCode : * key_code, ..BASE_KEY_UP } ;
366+ sticky_keys_filter. notify_key ( & event) ;
367+ assert_eq ! ( test_filter. last_event( ) . unwrap( ) , event) ;
368+ }
369+ }
370+
324371 #[ test]
325372 fn test_notify_key_passes_non_ephemeral_modifier_keys ( ) {
326373 let test_filter = TestFilter :: new ( ) ;
@@ -436,6 +483,26 @@ mod tests {
436483 assert_eq ! ( test_callbacks. get_last_locked_modifier_state( ) , ModifierState :: None ) ;
437484 }
438485
486+ #[ test]
487+ fn test_modifier_state_unchanged_on_non_modifier_key_up_without_down ( ) {
488+ let test_filter = TestFilter :: new ( ) ;
489+ let test_callbacks = TestCallbacks :: new ( ) ;
490+ let mut sticky_keys_filter = setup_filter (
491+ Box :: new ( test_filter. clone ( ) ) ,
492+ Arc :: new ( RwLock :: new ( Strong :: new ( Box :: new ( test_callbacks. clone ( ) ) ) ) ) ,
493+ ) ;
494+ sticky_keys_filter. notify_key ( & KeyEvent { keyCode : KEYCODE_CTRL_LEFT , ..BASE_KEY_DOWN } ) ;
495+ sticky_keys_filter. notify_key ( & KeyEvent { keyCode : KEYCODE_CTRL_LEFT , ..BASE_KEY_UP } ) ;
496+
497+ sticky_keys_filter. notify_key ( & KeyEvent { keyCode : KEY_A , ..BASE_KEY_UP } ) ;
498+
499+ assert_eq ! (
500+ test_callbacks. get_last_modifier_state( ) ,
501+ ModifierState :: CtrlLeftOn | ModifierState :: CtrlOn
502+ ) ;
503+ assert_eq ! ( test_callbacks. get_last_locked_modifier_state( ) , ModifierState :: None ) ;
504+ }
505+
439506 #[ test]
440507 fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press ( ) {
441508 let test_filter = TestFilter :: new ( ) ;
0 commit comments