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