Loading services/inputflinger/rust/bounce_keys_filter.rs +12 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use input::KeyboardType; use log::debug; use std::any::Any; use std::collections::{HashMap, HashSet}; #[derive(Debug)] Loading Loading @@ -134,6 +135,17 @@ impl Filter for BounceKeysFilter { self.next.destroy(); } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { self.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { self.next.restore(state); } fn dump(&mut self, dump_str: String) -> String { let mut result = "Bounce Keys filter: \n".to_string(); result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns); Loading services/inputflinger/rust/input_filter.rs +33 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ use crate::slow_keys_filter::SlowKeysFilter; use crate::sticky_keys_filter::StickyKeysFilter; use input::ModifierState; use log::{error, info}; use std::any::Any; use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock}; /// Virtual keyboard device ID Loading @@ -43,6 +45,11 @@ pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); fn destroy(&mut self); fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>>; fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>); fn dump(&mut self, dump_str: String) -> String; } Loading Loading @@ -105,6 +112,7 @@ impl IInputFilter for InputFilter { fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> { { let mut state = self.state.lock().unwrap(); let saved_state = state.first_filter.save(HashMap::new()); state.first_filter.destroy(); let mut first_filter: Box<dyn Filter + Send + Sync> = Box::new(BaseFilter::new(self.callbacks.clone())); Loading Loading @@ -138,6 +146,7 @@ impl IInputFilter for InputFilter { ); } state.first_filter = first_filter; state.first_filter.restore(&saved_state); } Result::Ok(()) } Loading Loading @@ -175,6 +184,18 @@ impl Filter for BaseFilter { // do nothing } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { // do nothing state } fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { // do nothing } fn dump(&mut self, dump_str: String) -> String { // do nothing dump_str Loading Loading @@ -367,6 +388,8 @@ pub mod test_filter { use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, }; use std::any::Any; use std::collections::HashMap; use std::sync::{Arc, RwLock, RwLockWriteGuard}; #[derive(Default)] Loading Loading @@ -415,6 +438,16 @@ pub mod test_filter { fn destroy(&mut self) { self.inner().is_destroy_called = true; } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { // do nothing state } fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { // do nothing } fn dump(&mut self, dump_str: String) -> String { // do nothing dump_str Loading services/inputflinger/rust/slow_keys_filter.rs +15 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use input::KeyboardType; use log::debug; use std::collections::HashSet; use std::any::Any; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; // Policy flags from Input.h Loading Loading @@ -187,6 +188,19 @@ impl Filter for SlowKeysFilter { slow_filter.next.destroy(); } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { let mut slow_filter = self.write_inner(); slow_filter.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { let mut slow_filter = self.write_inner(); slow_filter.next.restore(state); } fn dump(&mut self, dump_str: String) -> String { let mut slow_filter = self.write_inner(); let mut result = "Slow Keys filter: \n".to_string(); Loading services/inputflinger/rust/sticky_keys_filter.rs +90 −24 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; use input::ModifierState; use std::collections::HashSet; use std::any::Any; use std::collections::{HashMap, HashSet}; // Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h const KEYCODE_ALT_LEFT: i32 = 57; Loading @@ -40,10 +41,17 @@ const KEYCODE_META_LEFT: i32 = 117; const KEYCODE_META_RIGHT: i32 = 118; const KEYCODE_FUNCTION: i32 = 119; const KEYCODE_NUM_LOCK: i32 = 143; static STICKY_KEYS_DATA: &str = "sticky_keys_data"; pub struct StickyKeysFilter { next: Box<dyn Filter + Send + Sync>, listener: ModifierStateListener, data: Data, } #[derive(Default)] /// Data that will be saved and restored across configuration changes struct Data { /// Tracking devices that contributed to the modifier state. contributing_devices: HashSet<i32>, /// State describing the current enabled modifiers. This contain both locked and non-locked Loading @@ -61,21 +69,15 @@ impl StickyKeysFilter { next: Box<dyn Filter + Send + Sync>, listener: ModifierStateListener, ) -> StickyKeysFilter { Self { next, listener, contributing_devices: HashSet::new(), modifier_state: ModifierState::None, locked_modifier_state: ModifierState::None, } Self { next, listener, data: Default::default() } } } impl Filter for StickyKeysFilter { fn notify_key(&mut self, event: &KeyEvent) { let up = event.action == KeyEventAction::UP; let mut modifier_state = self.modifier_state; let mut locked_modifier_state = self.locked_modifier_state; let mut modifier_state = self.data.modifier_state; let mut locked_modifier_state = self.data.locked_modifier_state; if !is_ephemeral_modifier_key(event.keyCode) { // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with Loading @@ -93,7 +95,7 @@ impl Filter for StickyKeysFilter { } } else if up { // Update contributing devices to track keyboards self.contributing_devices.insert(event.deviceId); self.data.contributing_devices.insert(event.deviceId); // If ephemeral modifier key, capture the key and update the sticky modifier states let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode); let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode); Loading @@ -108,38 +110,62 @@ impl Filter for StickyKeysFilter { modifier_state |= modifier_key_mask; } } if self.modifier_state != modifier_state || self.locked_modifier_state != locked_modifier_state if self.data.modifier_state != modifier_state || self.data.locked_modifier_state != locked_modifier_state { self.modifier_state = modifier_state; self.locked_modifier_state = locked_modifier_state; self.data.modifier_state = modifier_state; self.data.locked_modifier_state = locked_modifier_state; self.listener.modifier_state_changed(modifier_state, locked_modifier_state); } } fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { // Clear state if all contributing devices removed self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); if self.contributing_devices.is_empty() && (self.modifier_state != ModifierState::None || self.locked_modifier_state != ModifierState::None) self.data.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); if self.data.contributing_devices.is_empty() && (self.data.modifier_state != ModifierState::None || self.data.locked_modifier_state != ModifierState::None) { self.modifier_state = ModifierState::None; self.locked_modifier_state = ModifierState::None; self.data.modifier_state = ModifierState::None; self.data.locked_modifier_state = ModifierState::None; self.listener.modifier_state_changed(ModifierState::None, ModifierState::None); } self.next.notify_devices_changed(device_infos); } fn save( &mut self, mut state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { let data = Data { contributing_devices: self.data.contributing_devices.clone(), modifier_state: self.data.modifier_state, locked_modifier_state: self.data.locked_modifier_state, }; state.insert(STICKY_KEYS_DATA, Box::new(data)); self.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { if let Some(value) = state.get(STICKY_KEYS_DATA) { if let Some(data) = value.downcast_ref::<Data>() { self.data.contributing_devices = data.contributing_devices.clone(); self.data.modifier_state = data.modifier_state; self.data.locked_modifier_state = data.locked_modifier_state; } } self.next.restore(state) } fn destroy(&mut self) { self.next.destroy(); } fn dump(&mut self, dump_str: String) -> String { let mut result = "Sticky Keys filter: \n".to_string(); result += &format!("\tmodifier_state = {:?}\n", self.modifier_state); result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state); result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices); result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state); result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state); result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices); self.next.dump(dump_str + &result) } } Loading Loading @@ -245,6 +271,7 @@ mod tests { }; use input::KeyboardType; use input::ModifierState; use std::collections::HashMap; use std::sync::{Arc, RwLock}; static DEVICE_ID: i32 = 1; Loading Loading @@ -451,6 +478,45 @@ mod tests { ); } #[test] fn test_modifier_state_restored_on_recreation() { let test_filter = TestFilter::new(); let test_callbacks = TestCallbacks::new(); let mut sticky_keys_filter = setup_filter( Box::new(test_filter.clone()), Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), ); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); let saved_state = sticky_keys_filter.save(HashMap::new()); sticky_keys_filter.destroy(); // Create a new Sticky keys filter let test_filter = TestFilter::new(); let mut sticky_keys_filter = setup_filter( Box::new(test_filter.clone()), Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), ); sticky_keys_filter.restore(&saved_state); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); assert_eq!( test_callbacks.get_last_locked_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); } #[test] fn test_key_events_have_sticky_modifier_state() { let test_filter = TestFilter::new(); Loading Loading
services/inputflinger/rust/bounce_keys_filter.rs +12 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use input::KeyboardType; use log::debug; use std::any::Any; use std::collections::{HashMap, HashSet}; #[derive(Debug)] Loading Loading @@ -134,6 +135,17 @@ impl Filter for BounceKeysFilter { self.next.destroy(); } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { self.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { self.next.restore(state); } fn dump(&mut self, dump_str: String) -> String { let mut result = "Bounce Keys filter: \n".to_string(); result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns); Loading
services/inputflinger/rust/input_filter.rs +33 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ use crate::slow_keys_filter::SlowKeysFilter; use crate::sticky_keys_filter::StickyKeysFilter; use input::ModifierState; use log::{error, info}; use std::any::Any; use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock}; /// Virtual keyboard device ID Loading @@ -43,6 +45,11 @@ pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); fn destroy(&mut self); fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>>; fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>); fn dump(&mut self, dump_str: String) -> String; } Loading Loading @@ -105,6 +112,7 @@ impl IInputFilter for InputFilter { fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> { { let mut state = self.state.lock().unwrap(); let saved_state = state.first_filter.save(HashMap::new()); state.first_filter.destroy(); let mut first_filter: Box<dyn Filter + Send + Sync> = Box::new(BaseFilter::new(self.callbacks.clone())); Loading Loading @@ -138,6 +146,7 @@ impl IInputFilter for InputFilter { ); } state.first_filter = first_filter; state.first_filter.restore(&saved_state); } Result::Ok(()) } Loading Loading @@ -175,6 +184,18 @@ impl Filter for BaseFilter { // do nothing } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { // do nothing state } fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { // do nothing } fn dump(&mut self, dump_str: String) -> String { // do nothing dump_str Loading Loading @@ -367,6 +388,8 @@ pub mod test_filter { use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, }; use std::any::Any; use std::collections::HashMap; use std::sync::{Arc, RwLock, RwLockWriteGuard}; #[derive(Default)] Loading Loading @@ -415,6 +438,16 @@ pub mod test_filter { fn destroy(&mut self) { self.inner().is_destroy_called = true; } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { // do nothing state } fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { // do nothing } fn dump(&mut self, dump_str: String) -> String { // do nothing dump_str Loading
services/inputflinger/rust/slow_keys_filter.rs +15 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use input::KeyboardType; use log::debug; use std::collections::HashSet; use std::any::Any; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; // Policy flags from Input.h Loading Loading @@ -187,6 +188,19 @@ impl Filter for SlowKeysFilter { slow_filter.next.destroy(); } fn save( &mut self, state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { let mut slow_filter = self.write_inner(); slow_filter.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { let mut slow_filter = self.write_inner(); slow_filter.next.restore(state); } fn dump(&mut self, dump_str: String) -> String { let mut slow_filter = self.write_inner(); let mut result = "Slow Keys filter: \n".to_string(); Loading
services/inputflinger/rust/sticky_keys_filter.rs +90 −24 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; use input::ModifierState; use std::collections::HashSet; use std::any::Any; use std::collections::{HashMap, HashSet}; // Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h const KEYCODE_ALT_LEFT: i32 = 57; Loading @@ -40,10 +41,17 @@ const KEYCODE_META_LEFT: i32 = 117; const KEYCODE_META_RIGHT: i32 = 118; const KEYCODE_FUNCTION: i32 = 119; const KEYCODE_NUM_LOCK: i32 = 143; static STICKY_KEYS_DATA: &str = "sticky_keys_data"; pub struct StickyKeysFilter { next: Box<dyn Filter + Send + Sync>, listener: ModifierStateListener, data: Data, } #[derive(Default)] /// Data that will be saved and restored across configuration changes struct Data { /// Tracking devices that contributed to the modifier state. contributing_devices: HashSet<i32>, /// State describing the current enabled modifiers. This contain both locked and non-locked Loading @@ -61,21 +69,15 @@ impl StickyKeysFilter { next: Box<dyn Filter + Send + Sync>, listener: ModifierStateListener, ) -> StickyKeysFilter { Self { next, listener, contributing_devices: HashSet::new(), modifier_state: ModifierState::None, locked_modifier_state: ModifierState::None, } Self { next, listener, data: Default::default() } } } impl Filter for StickyKeysFilter { fn notify_key(&mut self, event: &KeyEvent) { let up = event.action == KeyEventAction::UP; let mut modifier_state = self.modifier_state; let mut locked_modifier_state = self.locked_modifier_state; let mut modifier_state = self.data.modifier_state; let mut locked_modifier_state = self.data.locked_modifier_state; if !is_ephemeral_modifier_key(event.keyCode) { // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with Loading @@ -93,7 +95,7 @@ impl Filter for StickyKeysFilter { } } else if up { // Update contributing devices to track keyboards self.contributing_devices.insert(event.deviceId); self.data.contributing_devices.insert(event.deviceId); // If ephemeral modifier key, capture the key and update the sticky modifier states let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode); let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode); Loading @@ -108,38 +110,62 @@ impl Filter for StickyKeysFilter { modifier_state |= modifier_key_mask; } } if self.modifier_state != modifier_state || self.locked_modifier_state != locked_modifier_state if self.data.modifier_state != modifier_state || self.data.locked_modifier_state != locked_modifier_state { self.modifier_state = modifier_state; self.locked_modifier_state = locked_modifier_state; self.data.modifier_state = modifier_state; self.data.locked_modifier_state = locked_modifier_state; self.listener.modifier_state_changed(modifier_state, locked_modifier_state); } } fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { // Clear state if all contributing devices removed self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); if self.contributing_devices.is_empty() && (self.modifier_state != ModifierState::None || self.locked_modifier_state != ModifierState::None) self.data.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); if self.data.contributing_devices.is_empty() && (self.data.modifier_state != ModifierState::None || self.data.locked_modifier_state != ModifierState::None) { self.modifier_state = ModifierState::None; self.locked_modifier_state = ModifierState::None; self.data.modifier_state = ModifierState::None; self.data.locked_modifier_state = ModifierState::None; self.listener.modifier_state_changed(ModifierState::None, ModifierState::None); } self.next.notify_devices_changed(device_infos); } fn save( &mut self, mut state: HashMap<&'static str, Box<dyn Any + Send + Sync>>, ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> { let data = Data { contributing_devices: self.data.contributing_devices.clone(), modifier_state: self.data.modifier_state, locked_modifier_state: self.data.locked_modifier_state, }; state.insert(STICKY_KEYS_DATA, Box::new(data)); self.next.save(state) } fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) { if let Some(value) = state.get(STICKY_KEYS_DATA) { if let Some(data) = value.downcast_ref::<Data>() { self.data.contributing_devices = data.contributing_devices.clone(); self.data.modifier_state = data.modifier_state; self.data.locked_modifier_state = data.locked_modifier_state; } } self.next.restore(state) } fn destroy(&mut self) { self.next.destroy(); } fn dump(&mut self, dump_str: String) -> String { let mut result = "Sticky Keys filter: \n".to_string(); result += &format!("\tmodifier_state = {:?}\n", self.modifier_state); result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state); result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices); result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state); result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state); result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices); self.next.dump(dump_str + &result) } } Loading Loading @@ -245,6 +271,7 @@ mod tests { }; use input::KeyboardType; use input::ModifierState; use std::collections::HashMap; use std::sync::{Arc, RwLock}; static DEVICE_ID: i32 = 1; Loading Loading @@ -451,6 +478,45 @@ mod tests { ); } #[test] fn test_modifier_state_restored_on_recreation() { let test_filter = TestFilter::new(); let test_callbacks = TestCallbacks::new(); let mut sticky_keys_filter = setup_filter( Box::new(test_filter.clone()), Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), ); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); let saved_state = sticky_keys_filter.save(HashMap::new()); sticky_keys_filter.destroy(); // Create a new Sticky keys filter let test_filter = TestFilter::new(); let mut sticky_keys_filter = setup_filter( Box::new(test_filter.clone()), Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), ); sticky_keys_filter.restore(&saved_state); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); assert_eq!( test_callbacks.get_last_locked_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); } #[test] fn test_key_events_have_sticky_modifier_state() { let test_filter = TestFilter::new(); Loading