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
29    // touch_buttons is of size TOUCH_BUTTON_BITVEC_SIZE to hold all TouchButton enums.
30    pub touch_buttons: bit_vec::BitVec,
31}
32
33impl LinuxButtonEventBatch {
34    pub fn new() -> Self {
35        Self {
36            events: vec![],
37            event_time: zx::MonotonicInstant::get(),
38            power_is_pressed: false,
39            function_is_pressed: false,
40            touch_buttons: new_touch_buttons_bitvec(),
41        }
42    }
43}
44
45pub fn parse_fidl_media_button_event(
46    fidl_event: &MediaButtonsEvent,
47    power_was_pressed: bool,
48    function_was_pressed: bool,
49) -> LinuxButtonEventBatch {
50    let mut batch = LinuxButtonEventBatch::new();
51    let time = timeval_from_time(batch.event_time);
52    let sync_event = uapi::input_event {
53        // See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
54        time,
55        type_: uapi::EV_SYN as u16,
56        code: uapi::SYN_REPORT as u16,
57        value: 0,
58    };
59
60    batch.power_is_pressed = fidl_event.power.unwrap_or(false);
61    batch.function_is_pressed = fidl_event.function.unwrap_or(false);
62    for (button_was_pressed, button_is_pressed, key_code) in [
63        (power_was_pressed, batch.power_is_pressed, uapi::KEY_POWER),
64        (function_was_pressed, batch.function_is_pressed, uapi::KEY_VOLUMEDOWN),
65    ] {
66        // Button state changed. Send an event.
67        if button_is_pressed != button_was_pressed {
68            batch.events.push(uapi::input_event {
69                time,
70                type_: uapi::EV_KEY as u16,
71                code: key_code as u16,
72                value: button_is_pressed as i32,
73            });
74            batch.events.push(sync_event);
75        }
76    }
77
78    batch
79}
80
81pub fn parse_fidl_touch_button_event(
82    fidl_event: &TouchButtonsEvent,
83    touch_buttons_were_pressed: &bit_vec::BitVec,
84) -> LinuxButtonEventBatch {
85    let mut batch = LinuxButtonEventBatch::new();
86    let time = timeval_from_time(batch.event_time);
87    let sync_event = uapi::input_event {
88        // See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
89        time,
90        type_: uapi::EV_SYN as u16,
91        code: uapi::SYN_REPORT as u16,
92        value: 0,
93    };
94
95    if let Some(buttons) = &fidl_event.pressed_buttons {
96        for button in buttons {
97            let index = button.into_primitive() as usize;
98            if index < batch.touch_buttons.len() {
99                batch.touch_buttons.set(index, true);
100            }
101        }
102    };
103
104    for (index, key_code) in [
105        (TouchButton::Palm.into_primitive() as usize, uapi::KEY_SLEEP),
106        (TouchButton::SwipeUp.into_primitive() as usize, uapi::KEY_UP),
107        (TouchButton::SwipeLeft.into_primitive() as usize, uapi::KEY_LEFT),
108        (TouchButton::SwipeRight.into_primitive() as usize, uapi::KEY_RIGHT),
109        (TouchButton::SwipeDown.into_primitive() as usize, uapi::KEY_DOWN),
110    ] {
111        let button_was_pressed = touch_buttons_were_pressed.get(index).unwrap_or(false);
112        let button_is_pressed = batch.touch_buttons.get(index).unwrap_or(false);
113
114        // Button state changed. Send an event.
115        if button_is_pressed != button_was_pressed {
116            batch.events.push(uapi::input_event {
117                time,
118                type_: uapi::EV_KEY as u16,
119                code: key_code as u16,
120                value: button_is_pressed as i32,
121            });
122            batch.events.push(sync_event);
123        }
124    }
125
126    batch
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use assert_matches::assert_matches;
133    use pretty_assertions::assert_eq;
134
135    #[test]
136    fn test_ensure_touch_button_max() {
137        // If this test fails, it means a new TouchButton enum has been added
138        // and the TOUCH_BUTTON_BITVEC_SIZE needs to be updated.
139        assert_matches!(TouchButton::from_primitive(MAX_TOUCH_BUTTON_VALUE + 1), None);
140    }
141
142    #[test]
143    fn test_media_button_press_power() {
144        let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
145        let batch = parse_fidl_media_button_event(&fidl_event, false, false);
146
147        assert_eq!(batch.events.len(), 2);
148        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
149        assert_eq!(batch.events[0].code, uapi::KEY_POWER as u16);
150        assert_eq!(batch.events[0].value, 1);
151        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
152        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
153    }
154
155    #[test]
156    fn test_media_button_release_power() {
157        let fidl_event = MediaButtonsEvent { power: Some(false), ..Default::default() };
158        let batch = parse_fidl_media_button_event(&fidl_event, true, 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, 0);
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_no_change() {
170        let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
171        let batch = parse_fidl_media_button_event(&fidl_event, true, false);
172
173        assert_eq!(batch.events.len(), 0);
174    }
175
176    use test_case::test_case;
177
178    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
179    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
180    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
181    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
182    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
183    fn test_touch_button_press(button: TouchButton, expected_key: u32) {
184        let fidl_event =
185            TouchButtonsEvent { pressed_buttons: Some(vec![button]), ..Default::default() };
186        let was_pressed = bit_vec::BitVec::from_elem(6, false);
187        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
188
189        assert_eq!(batch.events.len(), 2);
190        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
191        assert_eq!(batch.events[0].code, expected_key as u16);
192        assert_eq!(batch.events[0].value, 1);
193        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
194        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
195    }
196
197    #[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
198    #[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
199    #[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
200    #[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
201    #[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
202    fn test_touch_button_release(button: TouchButton, expected_key: u32) {
203        let fidl_event = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
204        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
205        was_pressed.set(button.into_primitive() as usize, true);
206        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
207
208        assert_eq!(batch.events.len(), 2);
209        assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
210        assert_eq!(batch.events[0].code, expected_key as u16);
211        assert_eq!(batch.events[0].value, 0);
212        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
213        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
214    }
215
216    #[test]
217    fn test_touch_button_no_change() {
218        let fidl_event = TouchButtonsEvent {
219            pressed_buttons: Some(vec![TouchButton::Palm]),
220            ..Default::default()
221        };
222        let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
223        was_pressed.set(TouchButton::Palm.into_primitive() as usize, true);
224        let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
225
226        assert_eq!(batch.events.len(), 0);
227    }
228
229    #[test]
230    fn test_touch_button_multi_press() {
231        // 1. Press Palm
232        let fidl_event1 = TouchButtonsEvent {
233            pressed_buttons: Some(vec![TouchButton::Palm]),
234            ..Default::default()
235        };
236        let was_pressed1 = bit_vec::BitVec::from_elem(6, false);
237        let batch1 = parse_fidl_touch_button_event(&fidl_event1, &was_pressed1);
238
239        assert_eq!(batch1.events.len(), 2);
240        assert_eq!(batch1.events[0].type_, uapi::EV_KEY as u16);
241        assert_eq!(batch1.events[0].code, uapi::KEY_SLEEP as u16);
242        assert_eq!(batch1.events[0].value, 1);
243        assert_eq!(batch1.events[1].type_, uapi::EV_SYN as u16);
244        assert_eq!(batch1.events[1].code, uapi::SYN_REPORT as u16);
245
246        // 2. Hold Palm and press SwipeUp
247        let fidl_event2 = TouchButtonsEvent {
248            pressed_buttons: Some(vec![TouchButton::Palm, TouchButton::SwipeUp]),
249            ..Default::default()
250        };
251        let was_pressed2 = batch1.touch_buttons;
252        let batch2 = parse_fidl_touch_button_event(&fidl_event2, &was_pressed2);
253
254        assert_eq!(batch2.events.len(), 2);
255        assert_eq!(batch2.events[0].type_, uapi::EV_KEY as u16);
256        assert_eq!(batch2.events[0].code, uapi::KEY_UP as u16);
257        assert_eq!(batch2.events[0].value, 1);
258        assert_eq!(batch2.events[1].type_, uapi::EV_SYN as u16);
259        assert_eq!(batch2.events[1].code, uapi::SYN_REPORT as u16);
260
261        // 3. Release Palm, hold SwipeUp
262        let fidl_event3 = TouchButtonsEvent {
263            pressed_buttons: Some(vec![TouchButton::SwipeUp]),
264            ..Default::default()
265        };
266        let was_pressed3 = batch2.touch_buttons;
267        let batch3 = parse_fidl_touch_button_event(&fidl_event3, &was_pressed3);
268
269        assert_eq!(batch3.events.len(), 2);
270        assert_eq!(batch3.events[0].type_, uapi::EV_KEY as u16);
271        assert_eq!(batch3.events[0].code, uapi::KEY_SLEEP as u16);
272        assert_eq!(batch3.events[0].value, 0);
273        assert_eq!(batch3.events[1].type_, uapi::EV_SYN as u16);
274        assert_eq!(batch3.events[1].code, uapi::SYN_REPORT as u16);
275
276        // 4. Release SwipeUp
277        let fidl_event4 = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
278        let was_pressed4 = batch3.touch_buttons;
279        let batch4 = parse_fidl_touch_button_event(&fidl_event4, &was_pressed4);
280
281        assert_eq!(batch4.events.len(), 2);
282        assert_eq!(batch4.events[0].type_, uapi::EV_KEY as u16);
283        assert_eq!(batch4.events[0].code, uapi::KEY_UP as u16);
284        assert_eq!(batch4.events[0].value, 0);
285        assert_eq!(batch4.events[1].type_, uapi::EV_SYN as u16);
286        assert_eq!(batch4.events[1].code, uapi::SYN_REPORT as u16);
287    }
288}