• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! The rust component of libinput.
18 
19 mod data_store;
20 mod input;
21 mod input_verifier;
22 mod keyboard_classification_config;
23 mod keyboard_classifier;
24 
25 pub use data_store::{DataStore, DefaultFileReaderWriter};
26 pub use input::{
27     DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton,
28     MotionFlags, Source,
29 };
30 pub use input_verifier::{InputVerifier, NotifyMotionArgs};
31 pub use keyboard_classifier::KeyboardClassifier;
32 
33 #[cxx::bridge(namespace = "android::input")]
34 #[allow(clippy::needless_maybe_sized)]
35 #[allow(unsafe_op_in_unsafe_fn)]
36 mod ffi {
37     #[namespace = "android"]
38     unsafe extern "C++" {
39         include!("ffi/FromRustToCpp.h");
shouldLog(tag: &str) -> bool40         fn shouldLog(tag: &str) -> bool;
41     }
42 
43     #[namespace = "android::input::verifier"]
44     extern "Rust" {
45         /// Used to validate the incoming motion stream.
46         /// This class is not thread-safe.
47         /// State is stored in the "InputVerifier" object
48         /// that can be created via the 'create' method.
49         /// Usage:
50         ///
51         /// ```ignore
52         /// Box<InputVerifier> verifier = create("inputChannel name");
53         /// result = process_movement(verifier, ...);
54         /// if (result) {
55         ///    crash(result.error_message());
56         /// }
57         /// ```
58         type InputVerifier;
59         #[cxx_name = create]
create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>60         fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>;
61         #[allow(clippy::too_many_arguments)]
process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, button_state: u32, ) -> String62         fn process_movement(
63             verifier: &mut InputVerifier,
64             device_id: i32,
65             source: u32,
66             action: u32,
67             action_button: u32,
68             pointer_properties: &[RustPointerProperties],
69             flags: u32,
70             button_state: u32,
71         ) -> String;
reset_device(verifier: &mut InputVerifier, device_id: i32)72         fn reset_device(verifier: &mut InputVerifier, device_id: i32);
73     }
74 
75     #[namespace = "android::input::keyboardClassifier"]
76     extern "Rust" {
77         /// Used to classify a keyboard into alphabetic and non-alphabetic
78         type KeyboardClassifier;
79         #[cxx_name = create]
create_keyboard_classifier() -> Box<KeyboardClassifier>80         fn create_keyboard_classifier() -> Box<KeyboardClassifier>;
81         #[cxx_name = notifyKeyboardChanged]
notify_keyboard_changed( classifier: &mut KeyboardClassifier, device_id: i32, identifier: RustInputDeviceIdentifier, device_classes: u32, )82         fn notify_keyboard_changed(
83             classifier: &mut KeyboardClassifier,
84             device_id: i32,
85             identifier: RustInputDeviceIdentifier,
86             device_classes: u32,
87         );
88         #[cxx_name = getKeyboardType]
get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u3289         fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32;
90         #[cxx_name = isFinalized]
is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool91         fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool;
92         #[cxx_name = processKey]
process_key( classifier: &mut KeyboardClassifier, device_id: i32, evdev_code: i32, modifier_state: u32, )93         fn process_key(
94             classifier: &mut KeyboardClassifier,
95             device_id: i32,
96             evdev_code: i32,
97             modifier_state: u32,
98         );
99     }
100 
101     #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
102     pub struct RustPointerProperties {
103         pub id: i32,
104     }
105 
106     #[derive(Debug)]
107     pub struct RustInputDeviceIdentifier {
108         pub name: String,
109         pub location: String,
110         pub unique_id: String,
111         pub bus: u16,
112         pub vendor: u16,
113         pub product: u16,
114         pub version: u16,
115         pub descriptor: String,
116     }
117 }
118 
119 use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties};
120 
create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>121 fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier> {
122     Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"), verify_buttons))
123 }
124 
125 #[allow(clippy::too_many_arguments)]
process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, button_state: u32, ) -> String126 fn process_movement(
127     verifier: &mut InputVerifier,
128     device_id: i32,
129     source: u32,
130     action: u32,
131     action_button: u32,
132     pointer_properties: &[RustPointerProperties],
133     flags: u32,
134     button_state: u32,
135 ) -> String {
136     let Some(converted_source) = Source::from_bits(source) else {
137         panic!(
138             "The conversion of source 0x{source:08x} failed, please check if some sources have not \
139              been added to Source."
140         );
141     };
142     let Some(motion_flags) = MotionFlags::from_bits(flags) else {
143         panic!(
144             "The conversion of flags 0x{:08x} failed, please check if some flags have not been \
145             added to MotionFlags.",
146             flags
147         );
148     };
149     let Some(motion_action_button) = MotionButton::from_bits(action_button) else {
150         panic!(
151             "The conversion of action button 0x{action_button:08x} failed, please check if some \
152              buttons need to be added to MotionButton."
153         );
154     };
155     let Some(motion_button_state) = MotionButton::from_bits(button_state) else {
156         panic!(
157             "The conversion of button state 0x{button_state:08x} failed, please check if some \
158              buttons need to be added to MotionButton."
159         );
160     };
161     let motion_action = MotionAction::from_code(action, motion_action_button);
162     if motion_action_button != MotionButton::empty() {
163         match motion_action {
164             MotionAction::ButtonPress { action_button: _ }
165             | MotionAction::ButtonRelease { action_button: _ } => {}
166             _ => {
167                 return format!(
168                     "Invalid {motion_action} event: has action button {motion_action_button:?} but \
169                      is not a button action"
170                 );
171             }
172         }
173     }
174     let result = verifier.process_movement(NotifyMotionArgs {
175         device_id: DeviceId(device_id),
176         source: converted_source,
177         action: motion_action,
178         pointer_properties,
179         flags: motion_flags,
180         button_state: motion_button_state,
181     });
182     match result {
183         Ok(()) => "".to_string(),
184         Err(e) => e,
185     }
186 }
187 
reset_device(verifier: &mut InputVerifier, device_id: i32)188 fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
189     verifier.reset_device(DeviceId(device_id));
190 }
191 
create_keyboard_classifier() -> Box<KeyboardClassifier>192 fn create_keyboard_classifier() -> Box<KeyboardClassifier> {
193     // Future design: Make this data store singleton by passing it to C++ side and making it global
194     // and pass by reference to components that need to store persistent data.
195     //
196     // Currently only used by rust keyboard classifier so keeping it here.
197     let data_store = DataStore::new(Box::new(DefaultFileReaderWriter::new(
198         "/data/system/inputflinger-data.json".to_string(),
199     )));
200     Box::new(KeyboardClassifier::new(data_store))
201 }
202 
notify_keyboard_changed( classifier: &mut KeyboardClassifier, device_id: i32, identifier: RustInputDeviceIdentifier, device_classes: u32, )203 fn notify_keyboard_changed(
204     classifier: &mut KeyboardClassifier,
205     device_id: i32,
206     identifier: RustInputDeviceIdentifier,
207     device_classes: u32,
208 ) {
209     let classes = DeviceClass::from_bits(device_classes);
210     if classes.is_none() {
211         panic!(
212             "The conversion of device class 0x{:08x} failed, please check if some device classes
213              have not been added to DeviceClass.",
214             device_classes
215         );
216     }
217     classifier.notify_keyboard_changed(InputDevice {
218         device_id: DeviceId(device_id),
219         identifier,
220         classes: classes.unwrap(),
221     });
222 }
223 
get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32224 fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32 {
225     classifier.get_keyboard_type(DeviceId(device_id)) as u32
226 }
227 
is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool228 fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool {
229     classifier.is_finalized(DeviceId(device_id))
230 }
231 
process_key( classifier: &mut KeyboardClassifier, device_id: i32, evdev_code: i32, meta_state: u32, )232 fn process_key(
233     classifier: &mut KeyboardClassifier,
234     device_id: i32,
235     evdev_code: i32,
236     meta_state: u32,
237 ) {
238     let modifier_state = ModifierState::from_bits(meta_state);
239     if modifier_state.is_none() {
240         panic!(
241             "The conversion of meta state 0x{:08x} failed, please check if some meta state
242              have not been added to ModifierState.",
243             meta_state
244         );
245     }
246     classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
247 }
248 
249 #[cfg(test)]
250 mod tests {
251     use crate::create_input_verifier;
252     use crate::process_movement;
253     use crate::RustPointerProperties;
254 
255     const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
256 
257     #[test]
verify_nonbutton_action_with_action_button()258     fn verify_nonbutton_action_with_action_button() {
259         let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
260         assert!(process_movement(
261             &mut verifier,
262             1,
263             input_bindgen::AINPUT_SOURCE_MOUSE,
264             input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
265             input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
266             &BASE_POINTER_PROPERTIES,
267             0,
268             0,
269         )
270         .contains("button action"));
271     }
272 
273     #[test]
verify_nonbutton_action_with_action_button_and_button_state()274     fn verify_nonbutton_action_with_action_button_and_button_state() {
275         let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
276         assert!(process_movement(
277             &mut verifier,
278             1,
279             input_bindgen::AINPUT_SOURCE_MOUSE,
280             input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
281             input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
282             &BASE_POINTER_PROPERTIES,
283             0,
284             input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
285         )
286         .contains("button action"));
287     }
288 }
289