Skip to main content

starnix_modules_input_event_conversion/
button_fuchsia_to_linux.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_ui_input::{MediaButtonsEvent, TouchButton, TouchButtonsEvent};
6use starnix_types::time::timeval_from_time;
7use starnix_uapi::uapi;
8
9// The maximum value of the TouchButton enum.
10const MAX_TOUCH_BUTTON_VALUE: u32 = 5;
11
12// Guarantees the TouchButton bitvec can hold raw values of all TouchButton enums.
13const TOUCH_BUTTON_BITVEC_SIZE: u32 = MAX_TOUCH_BUTTON_VALUE + 1;
14
15pub fn new_touch_buttons_bitvec() -> bit_vec::BitVec {
16    bit_vec::BitVec::from_elem(TOUCH_BUTTON_BITVEC_SIZE as usize, false)
17}
18
19pub struct LinuxButtonEventBatch {
20    pub events: Vec<uapi::input_event>,
21
22    // Because FIDL button events do not carry a timestamp, we perform a direct
23    // clock read during conversion and assign this value as the timestamp for
24    // all generated Linux button events in a single batch.
25    pub event_time: zx::MonotonicInstant,
26    pub power_is_pressed: bool,
27    pub function_is_pressed: bool,
28    pub volume_up_is_pressed: bool,
29    pub volume_down_is_pressed: bool,
30
31    // touch_buttons is of size TOUCH_BUTTON_BITVEC_SIZE to hold all TouchButton enums.
32    pub touch_buttons: bit_vec::BitVec,
33}
34
35impl LinuxButtonEventBatch {
36    pub fn new() -> Self {
37        Self {
38            events: vec![],
39            event_time: zx::MonotonicInstant::get(),
40            power_is_pressed: false,
41            function_is_pressed: false,
42            volume_up_is_pressed: false,
43            volume_down_is_pressed: false,
44            touch_buttons: new_touch_buttons_bitvec(),
45        }
46    }
47}
48
49pub fn parse_fidl_media_button_event(
50    fidl_event: &MediaButtonsEvent,
51    power_was_pressed: bool,
52    function_was_pressed: bool,
53    volume_up_was_pressed: bool,
54    volume_down_was_pressed: bool,
55) -> LinuxButtonEventBatch {
56    let mut batch = LinuxButtonEventBatch::new();
57    let time = timeval_from_time(batch.event_time);
58    let sync_event = uapi::input_event {
59        // See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
60        time,
61        type_: uapi::EV_SYN as u16,
62        code: uapi::SYN_REPORT as u16,
63        value: 0,
64    };
65
66    batch.power_is_pressed = fidl_event.power.unwrap_or(false);
67    batch.function_is_pressed = fidl_event.function.unwrap_or(false);
68
69    let volume = fidl_event.volume.unwrap_or(0);
70    batch.volume_up_is_pressed = fidl_event.volume_up.unwrap_or(volume > 0);
71    batch.volume_down_is_pressed = fidl_event.volume_down.unwrap_or(volume < 0);
72
73    for (button_was_pressed, button_is_pressed, key_code) in [
74        (power_was_pressed, batch.power_is_pressed, uapi::KEY_POWER),
75        (function_was_pressed, batch.function_is_pressed, uapi::KEY_VOLUMEDOWN),
76        (volume_up_was_pressed, batch.volume_up_is_pressed, uapi::KEY_VOLUMEUP),
77        (volume_down_was_pressed, batch.volume_down_is_pressed, uapi::KEY_VOLUMEDOWN),
78    ] {
79        // Button state changed. Send an event.
80        if button_is_pressed != button_was_pressed {
81            batch.events.push(uapi::input_event {
82                time,
83                type_: uapi::EV_KEY as u16,
84                code: key_code as u16,
85                value: button_is_pressed as i32,
86            });
87            batch.events.push(sync_event);
88        }
89    }
90
91    batch
92}
93
94pub fn parse_fidl_touch_button_event(
95    fidl_event: &TouchButtonsEvent,
96    touch_buttons_were_pressed: &bit_vec::BitVec,
97) -> LinuxButtonEventBatch {
98    let mut batch = LinuxButtonEventBatch::new();
99    let time = timeval_from_time(batch.event_time);
100    let sync_event = uapi::input_event {
101        // See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
102        time,
103        type_: uapi::EV_SYN as u16,
104        code: uapi::SYN_REPORT as u16,
105        value: 0,
106    };
107
108    if let Some(buttons) = &fidl_event.pressed_buttons {
109        for button in buttons {
110            let index = button.into_primitive() as usize;
111            if index < batch.touch_buttons.len() {
112                batch.touch_buttons.set(index, true);
113            }
114        }
115    };
116
117    for (index, key_code) in [
118        (TouchButton::Palm.into_primitive() as usize, uapi::KEY_SLEEP),
119        (TouchButton::SwipeUp.into_primitive() as usize, uapi::KEY_UP),
120        (TouchButton::SwipeLeft.into_primitive() as usize, uapi::KEY_LEFT),
121        (TouchButton::SwipeRight.into_primitive() as usize, uapi::KEY_RIGHT),
122        (TouchButton::SwipeDown.into_primitive() as usize, uapi::KEY_DOWN),
123    ] {
124        let button_was_pressed = touch_buttons_were_pressed.get(index).unwrap_or(false);
125        let button_is_pressed = batch.touch_buttons.get(index).unwrap_or(false);
126
127        // Button state changed. Send an event.
128        if button_is_pressed != button_was_pressed {
129            batch.events.push(uapi::input_event {
130                time,
131                type_: uapi::EV_KEY as u16,
132                code: key_code as u16,
133                value: button_is_pressed as i32,
134            });
135            batch.events.push(sync_event);
136        }
137    }
138
139    batch
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use assert_matches::assert_matches;
146    use pretty_assertions::assert_eq;
147
148    #[test]
149    fn test_ensure_touch_button_max() {
150        // If this test fails, it means a new TouchButton enum has been added
151        // and the TOUCH_BUTTON_BITVEC_SIZE needs to be updated.
152        assert_matches!(TouchButton::from_primitive(MAX_TOUCH_BUTTON_VALUE + 1), None);
153    }
154
155    #[test]
156    fn test_media_button_press_power() {
157        let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
158        let batch = parse_fidl_media_button_event(&fidl_event, false, false, false, false);
159
160        assert_eq!(batch.events.len(), 2);
161        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
162        assert_eq!(batch.events[0].code, uapi::KEY_POWER as u16);
163        assert_eq!(batch.events[0].value, 1);
164        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
165        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
166    }
167
168    #[test]
169    fn test_media_button_release_power() {
170        let fidl_event = MediaButtonsEvent { power: Some(false), ..Default::default() };
171        let batch = parse_fidl_media_button_event(&fidl_event, true, false, false, false);
172
173        assert_eq!(batch.events.len(), 2);
174        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
175        assert_eq!(batch.events[0].code, uapi::KEY_POWER as u16);
176        assert_eq!(batch.events[0].value, 0);
177        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
178        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
179    }
180
181    #[test]
182    fn test_media_button_no_change() {
183        let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
184        let batch = parse_fidl_media_button_event(&fidl_event, true, false, false, false);
185
186        assert_eq!(batch.events.len(), 0);
187    }
188
189    #[test]
190    fn test_media_button_press_volume_up() {
191        let fidl_event = MediaButtonsEvent { volume: Some(1), ..Default::default() };
192        let batch = parse_fidl_media_button_event(&fidl_event, false, false, false, false);
193
194        assert_eq!(batch.events.len(), 2);
195        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
196        assert_eq!(batch.events[0].code, uapi::KEY_VOLUMEUP as u16);
197        assert_eq!(batch.events[0].value, 1);
198        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
199        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
200        assert!(batch.volume_up_is_pressed);
201        assert!(!batch.volume_down_is_pressed);
202    }
203
204    #[test]
205    fn test_media_button_release_volume_up() {
206        let fidl_event = MediaButtonsEvent { volume: Some(0), ..Default::default() };
207        let batch = parse_fidl_media_button_event(&fidl_event, false, false, true, false);
208
209        assert_eq!(batch.events.len(), 2);
210        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
211        assert_eq!(batch.events[0].code, uapi::KEY_VOLUMEUP as u16);
212        assert_eq!(batch.events[0].value, 0);
213        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
214        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
215        assert!(!batch.volume_up_is_pressed);
216        assert!(!batch.volume_down_is_pressed);
217    }
218
219    #[test]
220    fn test_media_button_press_volume_down() {
221        let fidl_event = MediaButtonsEvent { volume: Some(-1), ..Default::default() };
222        let batch = parse_fidl_media_button_event(&fidl_event, false, false, false, false);
223
224        assert_eq!(batch.events.len(), 2);
225        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
226        assert_eq!(batch.events[0].code, uapi::KEY_VOLUMEDOWN as u16);
227        assert_eq!(batch.events[0].value, 1);
228        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
229        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
230        assert!(!batch.volume_up_is_pressed);
231        assert!(batch.volume_down_is_pressed);
232    }
233
234    #[test]
235    fn test_media_button_release_volume_down() {
236        let fidl_event = MediaButtonsEvent { volume: Some(0), ..Default::default() };
237        let batch = parse_fidl_media_button_event(&fidl_event, false, false, false, true);
238
239        assert_eq!(batch.events.len(), 2);
240        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
241        assert_eq!(batch.events[0].code, uapi::KEY_VOLUMEDOWN as u16);
242        assert_eq!(batch.events[0].value, 0);
243        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
244        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
245        assert!(!batch.volume_up_is_pressed);
246        assert!(!batch.volume_down_is_pressed);
247    }
248
249    #[test]
250    fn test_media_button_press_simultaneous_volume_up_and_down() {
251        let fidl_event = MediaButtonsEvent {
252            volume: Some(0),
253            volume_up: Some(true),
254            volume_down: Some(true),
255            ..Default::default()
256        };
257        let batch = parse_fidl_media_button_event(&fidl_event, false, false, false, false);
258
259        assert_eq!(batch.events.len(), 4);
260        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
261        assert_eq!(batch.events[0].code, uapi::KEY_VOLUMEUP as u16);
262        assert_eq!(batch.events[0].value, 1);
263        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
264        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
265        assert_eq!(batch.events[2].type_, uapi::EV_KEY as u16);
266        assert_eq!(batch.events[2].code, uapi::KEY_VOLUMEDOWN as u16);
267        assert_eq!(batch.events[2].value, 1);
268        assert_eq!(batch.events[3].type_, uapi::EV_SYN as u16);
269        assert_eq!(batch.events[3].code, uapi::SYN_REPORT as u16);
270        assert!(batch.volume_up_is_pressed);
271        assert!(batch.volume_down_is_pressed);
272    }
273
274    use test_case::test_case;
275
276    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
277    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
278    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
279    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
280    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
281    fn test_touch_button_press(button: TouchButton, expected_key: u32) {
282        let fidl_event =
283            TouchButtonsEvent { pressed_buttons: Some(vec![button]), ..Default::default() };
284        let was_pressed = bit_vec::BitVec::from_elem(6, false);
285        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
286
287        assert_eq!(batch.events.len(), 2);
288        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
289        assert_eq!(batch.events[0].code, expected_key as u16);
290        assert_eq!(batch.events[0].value, 1);
291        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
292        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
293    }
294
295    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
296    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
297    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
298    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
299    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
300    fn test_touch_button_release(button: TouchButton, expected_key: u32) {
301        let fidl_event = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
302        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
303        was_pressed.set(button.into_primitive() as usize, true);
304        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
305
306        assert_eq!(batch.events.len(), 2);
307        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
308        assert_eq!(batch.events[0].code, expected_key as u16);
309        assert_eq!(batch.events[0].value, 0);
310        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
311        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
312    }
313
314    #[test]
315    fn test_touch_button_no_change() {
316        let fidl_event = TouchButtonsEvent {
317            pressed_buttons: Some(vec![TouchButton::Palm]),
318            ..Default::default()
319        };
320        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
321        was_pressed.set(TouchButton::Palm.into_primitive() as usize, true);
322        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
323
324        assert_eq!(batch.events.len(), 0);
325    }
326
327    #[test]
328    fn test_touch_button_multi_press() {
329        // 1. Press Palm
330        let fidl_event1 = TouchButtonsEvent {
331            pressed_buttons: Some(vec![TouchButton::Palm]),
332            ..Default::default()
333        };
334        let was_pressed1 = bit_vec::BitVec::from_elem(6, false);
335        let batch1 = parse_fidl_touch_button_event(&fidl_event1, &was_pressed1);
336
337        assert_eq!(batch1.events.len(), 2);
338        assert_eq!(batch1.events[0].type_, uapi::EV_KEY as u16);
339        assert_eq!(batch1.events[0].code, uapi::KEY_SLEEP as u16);
340        assert_eq!(batch1.events[0].value, 1);
341        assert_eq!(batch1.events[1].type_, uapi::EV_SYN as u16);
342        assert_eq!(batch1.events[1].code, uapi::SYN_REPORT as u16);
343
344        // 2. Hold Palm and press SwipeUp
345        let fidl_event2 = TouchButtonsEvent {
346            pressed_buttons: Some(vec![TouchButton::Palm, TouchButton::SwipeUp]),
347            ..Default::default()
348        };
349        let was_pressed2 = batch1.touch_buttons;
350        let batch2 = parse_fidl_touch_button_event(&fidl_event2, &was_pressed2);
351
352        assert_eq!(batch2.events.len(), 2);
353        assert_eq!(batch2.events[0].type_, uapi::EV_KEY as u16);
354        assert_eq!(batch2.events[0].code, uapi::KEY_UP as u16);
355        assert_eq!(batch2.events[0].value, 1);
356        assert_eq!(batch2.events[1].type_, uapi::EV_SYN as u16);
357        assert_eq!(batch2.events[1].code, uapi::SYN_REPORT as u16);
358
359        // 3. Release Palm, hold SwipeUp
360        let fidl_event3 = TouchButtonsEvent {
361            pressed_buttons: Some(vec![TouchButton::SwipeUp]),
362            ..Default::default()
363        };
364        let was_pressed3 = batch2.touch_buttons;
365        let batch3 = parse_fidl_touch_button_event(&fidl_event3, &was_pressed3);
366
367        assert_eq!(batch3.events.len(), 2);
368        assert_eq!(batch3.events[0].type_, uapi::EV_KEY as u16);
369        assert_eq!(batch3.events[0].code, uapi::KEY_SLEEP as u16);
370        assert_eq!(batch3.events[0].value, 0);
371        assert_eq!(batch3.events[1].type_, uapi::EV_SYN as u16);
372        assert_eq!(batch3.events[1].code, uapi::SYN_REPORT as u16);
373
374        // 4. Release SwipeUp
375        let fidl_event4 = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
376        let was_pressed4 = batch3.touch_buttons;
377        let batch4 = parse_fidl_touch_button_event(&fidl_event4, &was_pressed4);
378
379        assert_eq!(batch4.events.len(), 2);
380        assert_eq!(batch4.events[0].type_, uapi::EV_KEY as u16);
381        assert_eq!(batch4.events[0].code, uapi::KEY_UP as u16);
382        assert_eq!(batch4.events[0].value, 0);
383        assert_eq!(batch4.events[1].type_, uapi::EV_SYN as u16);
384        assert_eq!(batch4.events[1].code, uapi::SYN_REPORT as u16);
385    }
386}