| /* |
| * Copyright 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //! The rust component of libinput. |
| |
| mod data_store; |
| mod input; |
| mod input_verifier; |
| mod keyboard_classification_config; |
| mod keyboard_classifier; |
| |
| pub use data_store::{DataStore, DefaultFileReaderWriter}; |
| pub use input::{ |
| DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton, |
| MotionFlags, Source, |
| }; |
| pub use input_verifier::{InputVerifier, NotifyMotionArgs}; |
| pub use keyboard_classifier::KeyboardClassifier; |
| |
| #[cxx::bridge(namespace = "android::input")] |
| #[allow(clippy::needless_maybe_sized)] |
| #[allow(unsafe_op_in_unsafe_fn)] |
| mod ffi { |
| #[namespace = "android"] |
| unsafe extern "C++" { |
| include!("ffi/FromRustToCpp.h"); |
| fn shouldLog(tag: &str) -> bool; |
| } |
| |
| #[namespace = "android::input::verifier"] |
| extern "Rust" { |
| /// Used to validate the incoming motion stream. |
| /// This class is not thread-safe. |
| /// State is stored in the "InputVerifier" object |
| /// that can be created via the 'create' method. |
| /// Usage: |
| /// |
| /// ```ignore |
| /// Box<InputVerifier> verifier = create("inputChannel name"); |
| /// result = process_movement(verifier, ...); |
| /// if (result) { |
| /// crash(result.error_message()); |
| /// } |
| /// ``` |
| type InputVerifier; |
| #[cxx_name = create] |
| fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>; |
| #[allow(clippy::too_many_arguments)] |
| fn process_movement( |
| verifier: &mut InputVerifier, |
| device_id: i32, |
| source: u32, |
| action: u32, |
| action_button: u32, |
| pointer_properties: &[RustPointerProperties], |
| flags: u32, |
| button_state: u32, |
| ) -> String; |
| fn reset_device(verifier: &mut InputVerifier, device_id: i32); |
| } |
| |
| #[namespace = "android::input::keyboardClassifier"] |
| extern "Rust" { |
| /// Used to classify a keyboard into alphabetic and non-alphabetic |
| type KeyboardClassifier; |
| #[cxx_name = create] |
| fn create_keyboard_classifier() -> Box<KeyboardClassifier>; |
| #[cxx_name = notifyKeyboardChanged] |
| fn notify_keyboard_changed( |
| classifier: &mut KeyboardClassifier, |
| device_id: i32, |
| identifier: RustInputDeviceIdentifier, |
| device_classes: u32, |
| ); |
| #[cxx_name = getKeyboardType] |
| fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32; |
| #[cxx_name = isFinalized] |
| fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool; |
| #[cxx_name = processKey] |
| fn process_key( |
| classifier: &mut KeyboardClassifier, |
| device_id: i32, |
| evdev_code: i32, |
| modifier_state: u32, |
| ); |
| } |
| |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
| pub struct RustPointerProperties { |
| pub id: i32, |
| } |
| |
| #[derive(Debug)] |
| pub struct RustInputDeviceIdentifier { |
| pub name: String, |
| pub location: String, |
| pub unique_id: String, |
| pub bus: u16, |
| pub vendor: u16, |
| pub product: u16, |
| pub version: u16, |
| pub descriptor: String, |
| } |
| } |
| |
| use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties}; |
| |
| fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier> { |
| Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"), verify_buttons)) |
| } |
| |
| #[allow(clippy::too_many_arguments)] |
| fn process_movement( |
| verifier: &mut InputVerifier, |
| device_id: i32, |
| source: u32, |
| action: u32, |
| action_button: u32, |
| pointer_properties: &[RustPointerProperties], |
| flags: u32, |
| button_state: u32, |
| ) -> String { |
| let Some(converted_source) = Source::from_bits(source) else { |
| panic!( |
| "The conversion of source 0x{source:08x} failed, please check if some sources have not \ |
| been added to Source." |
| ); |
| }; |
| let Some(motion_flags) = MotionFlags::from_bits(flags) else { |
| panic!( |
| "The conversion of flags 0x{:08x} failed, please check if some flags have not been \ |
| added to MotionFlags.", |
| flags |
| ); |
| }; |
| let Some(motion_action_button) = MotionButton::from_bits(action_button) else { |
| panic!( |
| "The conversion of action button 0x{action_button:08x} failed, please check if some \ |
| buttons need to be added to MotionButton." |
| ); |
| }; |
| let Some(motion_button_state) = MotionButton::from_bits(button_state) else { |
| panic!( |
| "The conversion of button state 0x{button_state:08x} failed, please check if some \ |
| buttons need to be added to MotionButton." |
| ); |
| }; |
| let motion_action = MotionAction::from_code(action, motion_action_button); |
| if motion_action_button != MotionButton::empty() { |
| match motion_action { |
| MotionAction::ButtonPress { action_button: _ } |
| | MotionAction::ButtonRelease { action_button: _ } => {} |
| _ => { |
| return format!( |
| "Invalid {motion_action} event: has action button {motion_action_button:?} but \ |
| is not a button action" |
| ); |
| } |
| } |
| } |
| let result = verifier.process_movement(NotifyMotionArgs { |
| device_id: DeviceId(device_id), |
| source: converted_source, |
| action: motion_action, |
| pointer_properties, |
| flags: motion_flags, |
| button_state: motion_button_state, |
| }); |
| match result { |
| Ok(()) => "".to_string(), |
| Err(e) => e, |
| } |
| } |
| |
| fn reset_device(verifier: &mut InputVerifier, device_id: i32) { |
| verifier.reset_device(DeviceId(device_id)); |
| } |
| |
| fn create_keyboard_classifier() -> Box<KeyboardClassifier> { |
| // Future design: Make this data store singleton by passing it to C++ side and making it global |
| // and pass by reference to components that need to store persistent data. |
| // |
| // Currently only used by rust keyboard classifier so keeping it here. |
| let data_store = DataStore::new(Box::new(DefaultFileReaderWriter::new( |
| "/data/system/inputflinger-data.json".to_string(), |
| ))); |
| Box::new(KeyboardClassifier::new(data_store)) |
| } |
| |
| fn notify_keyboard_changed( |
| classifier: &mut KeyboardClassifier, |
| device_id: i32, |
| identifier: RustInputDeviceIdentifier, |
| device_classes: u32, |
| ) { |
| let classes = DeviceClass::from_bits(device_classes); |
| if classes.is_none() { |
| panic!( |
| "The conversion of device class 0x{:08x} failed, please check if some device classes |
| have not been added to DeviceClass.", |
| device_classes |
| ); |
| } |
| classifier.notify_keyboard_changed(InputDevice { |
| device_id: DeviceId(device_id), |
| identifier, |
| classes: classes.unwrap(), |
| }); |
| } |
| |
| fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32 { |
| classifier.get_keyboard_type(DeviceId(device_id)) as u32 |
| } |
| |
| fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool { |
| classifier.is_finalized(DeviceId(device_id)) |
| } |
| |
| fn process_key( |
| classifier: &mut KeyboardClassifier, |
| device_id: i32, |
| evdev_code: i32, |
| meta_state: u32, |
| ) { |
| let modifier_state = ModifierState::from_bits(meta_state); |
| if modifier_state.is_none() { |
| panic!( |
| "The conversion of meta state 0x{:08x} failed, please check if some meta state |
| have not been added to ModifierState.", |
| meta_state |
| ); |
| } |
| classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap()); |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::create_input_verifier; |
| use crate::process_movement; |
| use crate::RustPointerProperties; |
| |
| const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }]; |
| |
| #[test] |
| fn verify_nonbutton_action_with_action_button() { |
| let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true); |
| assert!(process_movement( |
| &mut verifier, |
| 1, |
| input_bindgen::AINPUT_SOURCE_MOUSE, |
| input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, |
| input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, |
| &BASE_POINTER_PROPERTIES, |
| 0, |
| 0, |
| ) |
| .contains("button action")); |
| } |
| |
| #[test] |
| fn verify_nonbutton_action_with_action_button_and_button_state() { |
| let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true); |
| assert!(process_movement( |
| &mut verifier, |
| 1, |
| input_bindgen::AINPUT_SOURCE_MOUSE, |
| input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, |
| input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, |
| &BASE_POINTER_PROPERTIES, |
| 0, |
| input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, |
| ) |
| .contains("button action")); |
| } |
| } |