xref: /aosp_15_r20/frameworks/native/services/inputflinger/rust/sticky_keys_filter.rs (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Sticky keys input filter implementation.
18 //! Sticky keys is an accessibility feature that assists users who have physical disabilities or
19 //! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing
20 //! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift,
21 //! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed.
22 use crate::input_filter::{Filter, ModifierStateListener};
23 use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
24     DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
25 };
26 use input::ModifierState;
27 use std::any::Any;
28 use std::collections::{HashMap, HashSet};
29 
30 // Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
31 const KEYCODE_ALT_LEFT: i32 = 57;
32 const KEYCODE_ALT_RIGHT: i32 = 58;
33 const KEYCODE_SHIFT_LEFT: i32 = 59;
34 const KEYCODE_SHIFT_RIGHT: i32 = 60;
35 const KEYCODE_SYM: i32 = 63;
36 const KEYCODE_CTRL_LEFT: i32 = 113;
37 const KEYCODE_CTRL_RIGHT: i32 = 114;
38 const KEYCODE_CAPS_LOCK: i32 = 115;
39 const KEYCODE_SCROLL_LOCK: i32 = 116;
40 const KEYCODE_META_LEFT: i32 = 117;
41 const KEYCODE_META_RIGHT: i32 = 118;
42 const KEYCODE_FUNCTION: i32 = 119;
43 const KEYCODE_NUM_LOCK: i32 = 143;
44 static STICKY_KEYS_DATA: &str = "sticky_keys_data";
45 
46 pub struct StickyKeysFilter {
47     next: Box<dyn Filter + Send + Sync>,
48     listener: ModifierStateListener,
49     data: Data,
50     down_key_map: HashMap<i32, HashSet<i32>>,
51 }
52 
53 #[derive(Default)]
54 /// Data that will be saved and restored across configuration changes
55 struct Data {
56     /// Tracking devices that contributed to the modifier state.
57     contributing_devices: HashSet<i32>,
58     /// State describing the current enabled modifiers. This contain both locked and non-locked
59     /// modifier state bits.
60     modifier_state: ModifierState,
61     /// State describing the current locked modifiers. These modifiers will not be cleared on a
62     /// non-modifier key press. They will be cleared only if the locked modifier key is pressed
63     /// again.
64     locked_modifier_state: ModifierState,
65 }
66 
67 impl StickyKeysFilter {
68     /// Create a new StickyKeysFilter instance.
new( next: Box<dyn Filter + Send + Sync>, listener: ModifierStateListener, ) -> StickyKeysFilter69     pub fn new(
70         next: Box<dyn Filter + Send + Sync>,
71         listener: ModifierStateListener,
72     ) -> StickyKeysFilter {
73         Self { next, listener, data: Default::default(), down_key_map: HashMap::new() }
74     }
75 }
76 
77 impl Filter for StickyKeysFilter {
notify_key(&mut self, event: &KeyEvent)78     fn notify_key(&mut self, event: &KeyEvent) {
79         let down = event.action == KeyEventAction::DOWN;
80         let up = event.action == KeyEventAction::UP;
81         let mut modifier_state = self.data.modifier_state;
82         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         }
101         if !is_ephemeral_modifier_key(event.keyCode) {
102             // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
103             // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
104             // the KeyEvent.
105             let old_modifier_state = ModifierState::from_bits(event.metaState as u32).unwrap();
106             let mut new_event = *event;
107             // Send the current modifier state with the key event before clearing non-locked
108             // modifier state
109             new_event.metaState =
110                 (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state).bits() as i32;
111             self.next.notify_key(&new_event);
112             if up && !is_modifier_key(event.keyCode) {
113                 modifier_state =
114                     clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state;
115             }
116         } else if up {
117             // Update contributing devices to track keyboards
118             self.data.contributing_devices.insert(event.deviceId);
119             // If ephemeral modifier key, capture the key and update the sticky modifier states
120             let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
121             let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
122             if locked_modifier_state & modifier_key_mask != ModifierState::None {
123                 locked_modifier_state &= !symmetrical_modifier_key_mask;
124                 modifier_state &= !symmetrical_modifier_key_mask;
125             } else if modifier_key_mask & modifier_state != ModifierState::None {
126                 locked_modifier_state |= modifier_key_mask;
127                 modifier_state =
128                     (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
129             } else {
130                 modifier_state |= modifier_key_mask;
131             }
132         }
133         if self.data.modifier_state != modifier_state
134             || self.data.locked_modifier_state != locked_modifier_state
135         {
136             self.data.modifier_state = modifier_state;
137             self.data.locked_modifier_state = locked_modifier_state;
138             self.listener.modifier_state_changed(modifier_state, locked_modifier_state);
139         }
140     }
141 
notify_devices_changed(&mut self, device_infos: &[DeviceInfo])142     fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
143         // Clear state if all contributing devices removed
144         self.data.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
145         if self.data.contributing_devices.is_empty()
146             && (self.data.modifier_state != ModifierState::None
147                 || self.data.locked_modifier_state != ModifierState::None)
148         {
149             self.data.modifier_state = ModifierState::None;
150             self.data.locked_modifier_state = ModifierState::None;
151             self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
152         }
153         self.down_key_map.retain(|key, _| device_infos.iter().any(|x| *key == x.deviceId));
154         self.next.notify_devices_changed(device_infos);
155     }
156 
save( &mut self, mut state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>>157     fn save(
158         &mut self,
159         mut state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
160     ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
161         let data = Data {
162             contributing_devices: self.data.contributing_devices.clone(),
163             modifier_state: self.data.modifier_state,
164             locked_modifier_state: self.data.locked_modifier_state,
165         };
166         state.insert(STICKY_KEYS_DATA, Box::new(data));
167         self.next.save(state)
168     }
169 
restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>)170     fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
171         if let Some(value) = state.get(STICKY_KEYS_DATA) {
172             if let Some(data) = value.downcast_ref::<Data>() {
173                 self.data.contributing_devices = data.contributing_devices.clone();
174                 self.data.modifier_state = data.modifier_state;
175                 self.data.locked_modifier_state = data.locked_modifier_state;
176             }
177         }
178         self.next.restore(state)
179     }
180 
destroy(&mut self)181     fn destroy(&mut self) {
182         self.next.destroy();
183     }
184 
dump(&mut self, dump_str: String) -> String185     fn dump(&mut self, dump_str: String) -> String {
186         let mut result = "Sticky Keys filter: \n".to_string();
187         result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state);
188         result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state);
189         result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices);
190         result += &format!("\tdown_key_map = {:?}\n", self.down_key_map);
191         self.next.dump(dump_str + &result)
192     }
193 }
194 
is_modifier_key(keycode: i32) -> bool195 fn is_modifier_key(keycode: i32) -> bool {
196     matches!(
197         keycode,
198         KEYCODE_ALT_LEFT
199             | KEYCODE_ALT_RIGHT
200             | KEYCODE_SHIFT_LEFT
201             | KEYCODE_SHIFT_RIGHT
202             | KEYCODE_CTRL_LEFT
203             | KEYCODE_CTRL_RIGHT
204             | KEYCODE_META_LEFT
205             | KEYCODE_META_RIGHT
206             | KEYCODE_SYM
207             | KEYCODE_FUNCTION
208             | KEYCODE_CAPS_LOCK
209             | KEYCODE_NUM_LOCK
210             | KEYCODE_SCROLL_LOCK
211     )
212 }
213 
is_ephemeral_modifier_key(keycode: i32) -> bool214 fn is_ephemeral_modifier_key(keycode: i32) -> bool {
215     matches!(
216         keycode,
217         KEYCODE_ALT_LEFT
218             | KEYCODE_ALT_RIGHT
219             | KEYCODE_SHIFT_LEFT
220             | KEYCODE_SHIFT_RIGHT
221             | KEYCODE_CTRL_LEFT
222             | KEYCODE_CTRL_RIGHT
223             | KEYCODE_META_LEFT
224             | KEYCODE_META_RIGHT
225     )
226 }
227 
get_ephemeral_modifier_key_mask(keycode: i32) -> ModifierState228 fn get_ephemeral_modifier_key_mask(keycode: i32) -> ModifierState {
229     match keycode {
230         KEYCODE_ALT_LEFT => ModifierState::AltLeftOn | ModifierState::AltOn,
231         KEYCODE_ALT_RIGHT => ModifierState::AltRightOn | ModifierState::AltOn,
232         KEYCODE_SHIFT_LEFT => ModifierState::ShiftLeftOn | ModifierState::ShiftOn,
233         KEYCODE_SHIFT_RIGHT => ModifierState::ShiftRightOn | ModifierState::ShiftOn,
234         KEYCODE_CTRL_LEFT => ModifierState::CtrlLeftOn | ModifierState::CtrlOn,
235         KEYCODE_CTRL_RIGHT => ModifierState::CtrlRightOn | ModifierState::CtrlOn,
236         KEYCODE_META_LEFT => ModifierState::MetaLeftOn | ModifierState::MetaOn,
237         KEYCODE_META_RIGHT => ModifierState::MetaRightOn | ModifierState::MetaOn,
238         _ => ModifierState::None,
239     }
240 }
241 
242 /// Modifier mask including both left and right versions of a modifier key.
get_symmetrical_modifier_key_mask(keycode: i32) -> ModifierState243 fn get_symmetrical_modifier_key_mask(keycode: i32) -> ModifierState {
244     match keycode {
245         KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => {
246             ModifierState::AltLeftOn | ModifierState::AltRightOn | ModifierState::AltOn
247         }
248         KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
249             ModifierState::ShiftLeftOn | ModifierState::ShiftRightOn | ModifierState::ShiftOn
250         }
251         KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
252             ModifierState::CtrlLeftOn | ModifierState::CtrlRightOn | ModifierState::CtrlOn
253         }
254         KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
255             ModifierState::MetaLeftOn | ModifierState::MetaRightOn | ModifierState::MetaOn
256         }
257         _ => ModifierState::None,
258     }
259 }
260 
clear_ephemeral_modifier_state(modifier_state: ModifierState) -> ModifierState261 fn clear_ephemeral_modifier_state(modifier_state: ModifierState) -> ModifierState {
262     modifier_state
263         & !(ModifierState::AltLeftOn
264             | ModifierState::AltRightOn
265             | ModifierState::AltOn
266             | ModifierState::ShiftLeftOn
267             | ModifierState::ShiftRightOn
268             | ModifierState::ShiftOn
269             | ModifierState::CtrlLeftOn
270             | ModifierState::CtrlRightOn
271             | ModifierState::CtrlOn
272             | ModifierState::MetaLeftOn
273             | ModifierState::MetaRightOn
274             | ModifierState::MetaOn)
275 }
276 
277 #[cfg(test)]
278 mod tests {
279     use crate::input_filter::{
280         test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener,
281     };
282     use crate::sticky_keys_filter::{
283         StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
284         KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
285         KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
286         KEYCODE_SHIFT_RIGHT, KEYCODE_SYM,
287     };
288     use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
289     use binder::Strong;
290     use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
291         DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
292         KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
293     };
294     use input::KeyboardType;
295     use input::ModifierState;
296     use std::collections::HashMap;
297     use std::sync::{Arc, RwLock};
298 
299     static DEVICE_ID: i32 = 1;
300     static KEY_A: i32 = 29;
301     static BASE_KEY_DOWN: KeyEvent = KeyEvent {
302         id: 1,
303         deviceId: DEVICE_ID,
304         downTime: 0,
305         readTime: 0,
306         eventTime: 0,
307         source: Source::KEYBOARD,
308         displayId: 0,
309         policyFlags: 0,
310         action: KeyEventAction::DOWN,
311         flags: 0,
312         keyCode: 0,
313         scanCode: 0,
314         metaState: 0,
315     };
316 
317     static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN };
318 
319     #[test]
test_notify_key_consumes_ephemeral_modifier_keys()320     fn test_notify_key_consumes_ephemeral_modifier_keys() {
321         let test_filter = TestFilter::new();
322         let test_callbacks = TestCallbacks::new();
323         let mut sticky_keys_filter = setup_filter(
324             Box::new(test_filter.clone()),
325             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
326         );
327         let key_codes = &[
328             KEYCODE_ALT_LEFT,
329             KEYCODE_ALT_RIGHT,
330             KEYCODE_CTRL_LEFT,
331             KEYCODE_CTRL_RIGHT,
332             KEYCODE_SHIFT_LEFT,
333             KEYCODE_SHIFT_RIGHT,
334             KEYCODE_META_LEFT,
335             KEYCODE_META_RIGHT,
336         ];
337         for key_code in key_codes.iter() {
338             sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN });
339             assert!(test_filter.last_event().is_none());
340 
341             sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP });
342             assert!(test_filter.last_event().is_none());
343         }
344     }
345 
346     #[test]
test_notify_key_passes_ephemeral_modifier_keys_if_only_key_up_occurs()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 
371     #[test]
test_notify_key_passes_non_ephemeral_modifier_keys()372     fn test_notify_key_passes_non_ephemeral_modifier_keys() {
373         let test_filter = TestFilter::new();
374         let test_callbacks = TestCallbacks::new();
375         let mut sticky_keys_filter = setup_filter(
376             Box::new(test_filter.clone()),
377             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
378         );
379         let key_codes = &[
380             KEYCODE_CAPS_LOCK,
381             KEYCODE_NUM_LOCK,
382             KEYCODE_SCROLL_LOCK,
383             KEYCODE_FUNCTION,
384             KEYCODE_SYM,
385         ];
386         for key_code in key_codes.iter() {
387             let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN };
388             sticky_keys_filter.notify_key(&event);
389             assert_eq!(test_filter.last_event().unwrap(), event);
390             let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
391             sticky_keys_filter.notify_key(&event);
392             assert_eq!(test_filter.last_event().unwrap(), event);
393         }
394     }
395 
396     #[test]
test_notify_key_passes_non_modifier_keys()397     fn test_notify_key_passes_non_modifier_keys() {
398         let test_filter = TestFilter::new();
399         let test_callbacks = TestCallbacks::new();
400         let mut sticky_keys_filter = setup_filter(
401             Box::new(test_filter.clone()),
402             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
403         );
404         let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN };
405         sticky_keys_filter.notify_key(&event);
406         assert_eq!(test_filter.last_event().unwrap(), event);
407 
408         let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP };
409         sticky_keys_filter.notify_key(&event);
410         assert_eq!(test_filter.last_event().unwrap(), event);
411     }
412 
413     #[test]
test_modifier_state_updated_on_modifier_key_press()414     fn test_modifier_state_updated_on_modifier_key_press() {
415         let mut test_filter = TestFilter::new();
416         let mut test_callbacks = TestCallbacks::new();
417         let mut sticky_keys_filter = setup_filter(
418             Box::new(test_filter.clone()),
419             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
420         );
421         let test_states = &[
422             (KEYCODE_ALT_LEFT, ModifierState::AltOn | ModifierState::AltLeftOn),
423             (KEYCODE_ALT_RIGHT, ModifierState::AltOn | ModifierState::AltRightOn),
424             (KEYCODE_CTRL_LEFT, ModifierState::CtrlOn | ModifierState::CtrlLeftOn),
425             (KEYCODE_CTRL_RIGHT, ModifierState::CtrlOn | ModifierState::CtrlRightOn),
426             (KEYCODE_SHIFT_LEFT, ModifierState::ShiftOn | ModifierState::ShiftLeftOn),
427             (KEYCODE_SHIFT_RIGHT, ModifierState::ShiftOn | ModifierState::ShiftRightOn),
428             (KEYCODE_META_LEFT, ModifierState::MetaOn | ModifierState::MetaLeftOn),
429             (KEYCODE_META_RIGHT, ModifierState::MetaOn | ModifierState::MetaRightOn),
430         ];
431         for test_state in test_states.iter() {
432             test_filter.clear();
433             test_callbacks.clear();
434             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
435             assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
436             assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
437 
438             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
439             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
440             assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
441 
442             // Re-send keys to lock it
443             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
444             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
445             assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
446 
447             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
448             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
449             assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
450 
451             // Re-send keys to clear
452             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
453             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
454             assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
455 
456             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
457             assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
458             assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
459         }
460     }
461 
462     #[test]
test_modifier_state_cleared_on_non_modifier_key_press()463     fn test_modifier_state_cleared_on_non_modifier_key_press() {
464         let test_filter = TestFilter::new();
465         let test_callbacks = TestCallbacks::new();
466         let mut sticky_keys_filter = setup_filter(
467             Box::new(test_filter.clone()),
468             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
469         );
470         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
471         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
472 
473         assert_eq!(
474             test_callbacks.get_last_modifier_state(),
475             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
476         );
477         assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
478 
479         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
480         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
481 
482         assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
483         assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
484     }
485 
486     #[test]
test_modifier_state_unchanged_on_non_modifier_key_up_without_down()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 
506     #[test]
test_locked_modifier_state_not_cleared_on_non_modifier_key_press()507     fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
508         let test_filter = TestFilter::new();
509         let test_callbacks = TestCallbacks::new();
510         let mut sticky_keys_filter = setup_filter(
511             Box::new(test_filter.clone()),
512             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
513         );
514         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
515         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
516 
517         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
518         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
519 
520         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN });
521         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP });
522 
523         assert_eq!(
524             test_callbacks.get_last_modifier_state(),
525             ModifierState::ShiftLeftOn
526                 | ModifierState::ShiftOn
527                 | ModifierState::CtrlLeftOn
528                 | ModifierState::CtrlOn
529         );
530         assert_eq!(
531             test_callbacks.get_last_locked_modifier_state(),
532             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
533         );
534 
535         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
536         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
537 
538         assert_eq!(
539             test_callbacks.get_last_modifier_state(),
540             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
541         );
542         assert_eq!(
543             test_callbacks.get_last_locked_modifier_state(),
544             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
545         );
546     }
547 
548     #[test]
test_modifier_state_restored_on_recreation()549     fn test_modifier_state_restored_on_recreation() {
550         let test_filter = TestFilter::new();
551         let test_callbacks = TestCallbacks::new();
552         let mut sticky_keys_filter = setup_filter(
553             Box::new(test_filter.clone()),
554             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
555         );
556         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
557         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
558 
559         let saved_state = sticky_keys_filter.save(HashMap::new());
560         sticky_keys_filter.destroy();
561 
562         // Create a new Sticky keys filter
563         let test_filter = TestFilter::new();
564         let mut sticky_keys_filter = setup_filter(
565             Box::new(test_filter.clone()),
566             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
567         );
568         sticky_keys_filter.restore(&saved_state);
569         assert_eq!(
570             test_callbacks.get_last_modifier_state(),
571             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
572         );
573         assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
574 
575         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
576         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
577         assert_eq!(
578             test_callbacks.get_last_modifier_state(),
579             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
580         );
581         assert_eq!(
582             test_callbacks.get_last_locked_modifier_state(),
583             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
584         );
585     }
586 
587     #[test]
test_key_events_have_sticky_modifier_state()588     fn test_key_events_have_sticky_modifier_state() {
589         let test_filter = TestFilter::new();
590         let test_callbacks = TestCallbacks::new();
591         let mut sticky_keys_filter = setup_filter(
592             Box::new(test_filter.clone()),
593             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
594         );
595         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
596         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
597 
598         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
599         assert_eq!(
600             test_filter.last_event().unwrap().metaState as u32,
601             (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
602         );
603 
604         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
605         assert_eq!(
606             test_filter.last_event().unwrap().metaState as u32,
607             (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
608         );
609     }
610 
611     #[test]
test_modifier_state_not_cleared_until_all_devices_removed()612     fn test_modifier_state_not_cleared_until_all_devices_removed() {
613         let test_filter = TestFilter::new();
614         let test_callbacks = TestCallbacks::new();
615         let mut sticky_keys_filter = setup_filter(
616             Box::new(test_filter.clone()),
617             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
618         );
619         sticky_keys_filter.notify_key(&KeyEvent {
620             deviceId: 1,
621             keyCode: KEYCODE_CTRL_LEFT,
622             ..BASE_KEY_DOWN
623         });
624         sticky_keys_filter.notify_key(&KeyEvent {
625             deviceId: 1,
626             keyCode: KEYCODE_CTRL_LEFT,
627             ..BASE_KEY_UP
628         });
629 
630         sticky_keys_filter.notify_key(&KeyEvent {
631             deviceId: 2,
632             keyCode: KEYCODE_CTRL_LEFT,
633             ..BASE_KEY_DOWN
634         });
635         sticky_keys_filter.notify_key(&KeyEvent {
636             deviceId: 2,
637             keyCode: KEYCODE_CTRL_LEFT,
638             ..BASE_KEY_UP
639         });
640 
641         sticky_keys_filter.notify_devices_changed(&[DeviceInfo {
642             deviceId: 2,
643             external: true,
644             keyboardType: KeyboardType::Alphabetic as i32,
645         }]);
646         assert_eq!(
647             test_callbacks.get_last_modifier_state(),
648             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
649         );
650         assert_eq!(
651             test_callbacks.get_last_locked_modifier_state(),
652             ModifierState::CtrlLeftOn | ModifierState::CtrlOn
653         );
654 
655         sticky_keys_filter.notify_devices_changed(&[]);
656         assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
657         assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
658     }
659 
setup_filter( next: Box<dyn Filter + Send + Sync>, callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, ) -> StickyKeysFilter660     fn setup_filter(
661         next: Box<dyn Filter + Send + Sync>,
662         callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
663     ) -> StickyKeysFilter {
664         StickyKeysFilter::new(next, ModifierStateListener::new(callbacks))
665     }
666 }
667