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