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 = volume > 0;
71    batch.volume_down_is_pressed = 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    use test_case::test_case;
250
251    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
252    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
253    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
254    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
255    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
256    fn test_touch_button_press(button: TouchButton, expected_key: u32) {
257        let fidl_event =
258            TouchButtonsEvent { pressed_buttons: Some(vec![button]), ..Default::default() };
259        let was_pressed = bit_vec::BitVec::from_elem(6, false);
260        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
261
262        assert_eq!(batch.events.len(), 2);
263        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
264        assert_eq!(batch.events[0].code, expected_key as u16);
265        assert_eq!(batch.events[0].value, 1);
266        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
267        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
268    }
269
270    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
271    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
272    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
273    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
274    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
275    fn test_touch_button_release(button: TouchButton, expected_key: u32) {
276        let fidl_event = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
277        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
278        was_pressed.set(button.into_primitive() as usize, true);
279        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
280
281        assert_eq!(batch.events.len(), 2);
282        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
283        assert_eq!(batch.events[0].code, expected_key as u16);
284        assert_eq!(batch.events[0].value, 0);
285        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
286        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
287    }
288
289    #[test]
290    fn test_touch_button_no_change() {
291        let fidl_event = TouchButtonsEvent {
292            pressed_buttons: Some(vec![TouchButton::Palm]),
293            ..Default::default()
294        };
295        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
296        was_pressed.set(TouchButton::Palm.into_primitive() as usize, true);
297        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
298
299        assert_eq!(batch.events.len(), 0);
300    }
301
302    #[test]
303    fn test_touch_button_multi_press() {
304        // 1. Press Palm
305        let fidl_event1 = TouchButtonsEvent {
306            pressed_buttons: Some(vec![TouchButton::Palm]),
307            ..Default::default()
308        };
309        let was_pressed1 = bit_vec::BitVec::from_elem(6, false);
310        let batch1 = parse_fidl_touch_button_event(&fidl_event1, &was_pressed1);
311
312        assert_eq!(batch1.events.len(), 2);
313        assert_eq!(batch1.events[0].type_, uapi::EV_KEY as u16);
314        assert_eq!(batch1.events[0].code, uapi::KEY_SLEEP as u16);
315        assert_eq!(batch1.events[0].value, 1);
316        assert_eq!(batch1.events[1].type_, uapi::EV_SYN as u16);
317        assert_eq!(batch1.events[1].code, uapi::SYN_REPORT as u16);
318
319        // 2. Hold Palm and press SwipeUp
320        let fidl_event2 = TouchButtonsEvent {
321            pressed_buttons: Some(vec![TouchButton::Palm, TouchButton::SwipeUp]),
322            ..Default::default()
323        };
324        let was_pressed2 = batch1.touch_buttons;
325        let batch2 = parse_fidl_touch_button_event(&fidl_event2, &was_pressed2);
326
327        assert_eq!(batch2.events.len(), 2);
328        assert_eq!(batch2.events[0].type_, uapi::EV_KEY as u16);
329        assert_eq!(batch2.events[0].code, uapi::KEY_UP as u16);
330        assert_eq!(batch2.events[0].value, 1);
331        assert_eq!(batch2.events[1].type_, uapi::EV_SYN as u16);
332        assert_eq!(batch2.events[1].code, uapi::SYN_REPORT as u16);
333
334        // 3. Release Palm, hold SwipeUp
335        let fidl_event3 = TouchButtonsEvent {
336            pressed_buttons: Some(vec![TouchButton::SwipeUp]),
337            ..Default::default()
338        };
339        let was_pressed3 = batch2.touch_buttons;
340        let batch3 = parse_fidl_touch_button_event(&fidl_event3, &was_pressed3);
341
342        assert_eq!(batch3.events.len(), 2);
343        assert_eq!(batch3.events[0].type_, uapi::EV_KEY as u16);
344        assert_eq!(batch3.events[0].code, uapi::KEY_SLEEP as u16);
345        assert_eq!(batch3.events[0].value, 0);
346        assert_eq!(batch3.events[1].type_, uapi::EV_SYN as u16);
347        assert_eq!(batch3.events[1].code, uapi::SYN_REPORT as u16);
348
349        // 4. Release SwipeUp
350        let fidl_event4 = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
351        let was_pressed4 = batch3.touch_buttons;
352        let batch4 = parse_fidl_touch_button_event(&fidl_event4, &was_pressed4);
353
354        assert_eq!(batch4.events.len(), 2);
355        assert_eq!(batch4.events[0].type_, uapi::EV_KEY as u16);
356        assert_eq!(batch4.events[0].code, uapi::KEY_UP as u16);
357        assert_eq!(batch4.events[0].value, 0);
358        assert_eq!(batch4.events[1].type_, uapi::EV_SYN as u16);
359        assert_eq!(batch4.events[1].code, uapi::SYN_REPORT as u16);
360    }
361}