blob: 285acf6456a249e75dc3d3f317523439e7347ca1 [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl_fuchsia_ui_input::{MediaButtonsEvent, TouchButton, TouchButtonsEvent};
use starnix_types::time::timeval_from_time;
use starnix_uapi::uapi;
// The maximum value of the TouchButton enum.
const MAX_TOUCH_BUTTON_VALUE: u32 = 5;
// Guarantees the TouchButton bitvec can hold raw values of all TouchButton enums.
const TOUCH_BUTTON_BITVEC_SIZE: u32 = MAX_TOUCH_BUTTON_VALUE + 1;
pub fn new_touch_buttons_bitvec() -> bit_vec::BitVec {
bit_vec::BitVec::from_elem(TOUCH_BUTTON_BITVEC_SIZE as usize, false)
}
pub struct LinuxButtonEventBatch {
pub events: Vec<uapi::input_event>,
// Because FIDL button events do not carry a timestamp, we perform a direct
// clock read during conversion and assign this value as the timestamp for
// all generated Linux button events in a single batch.
pub event_time: zx::MonotonicInstant,
pub power_is_pressed: bool,
pub function_is_pressed: bool,
// touch_buttons is of size TOUCH_BUTTON_BITVEC_SIZE to hold all TouchButton enums.
pub touch_buttons: bit_vec::BitVec,
}
impl LinuxButtonEventBatch {
pub fn new() -> Self {
Self {
events: vec![],
event_time: zx::MonotonicInstant::get(),
power_is_pressed: false,
function_is_pressed: false,
touch_buttons: new_touch_buttons_bitvec(),
}
}
}
pub fn parse_fidl_media_button_event(
fidl_event: &MediaButtonsEvent,
power_was_pressed: bool,
function_was_pressed: bool,
) -> LinuxButtonEventBatch {
let mut batch = LinuxButtonEventBatch::new();
let time = timeval_from_time(batch.event_time);
let sync_event = uapi::input_event {
// See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
time,
type_: uapi::EV_SYN as u16,
code: uapi::SYN_REPORT as u16,
value: 0,
};
batch.power_is_pressed = fidl_event.power.unwrap_or(false);
batch.function_is_pressed = fidl_event.function.unwrap_or(false);
for (button_was_pressed, button_is_pressed, key_code) in [
(power_was_pressed, batch.power_is_pressed, uapi::KEY_POWER),
(function_was_pressed, batch.function_is_pressed, uapi::KEY_VOLUMEDOWN),
] {
// Button state changed. Send an event.
if button_is_pressed != button_was_pressed {
batch.events.push(uapi::input_event {
time,
type_: uapi::EV_KEY as u16,
code: key_code as u16,
value: button_is_pressed as i32,
});
batch.events.push(sync_event);
}
}
batch
}
pub fn parse_fidl_touch_button_event(
fidl_event: &TouchButtonsEvent,
touch_buttons_were_pressed: &bit_vec::BitVec,
) -> LinuxButtonEventBatch {
let mut batch = LinuxButtonEventBatch::new();
let time = timeval_from_time(batch.event_time);
let sync_event = uapi::input_event {
// See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
time,
type_: uapi::EV_SYN as u16,
code: uapi::SYN_REPORT as u16,
value: 0,
};
if let Some(buttons) = &fidl_event.pressed_buttons {
for button in buttons {
let index = button.into_primitive() as usize;
if index < batch.touch_buttons.len() {
batch.touch_buttons.set(index, true);
}
}
};
for (index, key_code) in [
(TouchButton::Palm.into_primitive() as usize, uapi::KEY_SLEEP),
(TouchButton::SwipeUp.into_primitive() as usize, uapi::KEY_UP),
(TouchButton::SwipeLeft.into_primitive() as usize, uapi::KEY_LEFT),
(TouchButton::SwipeRight.into_primitive() as usize, uapi::KEY_RIGHT),
(TouchButton::SwipeDown.into_primitive() as usize, uapi::KEY_DOWN),
] {
let button_was_pressed = touch_buttons_were_pressed.get(index).unwrap_or(false);
let button_is_pressed = batch.touch_buttons.get(index).unwrap_or(false);
// Button state changed. Send an event.
if button_is_pressed != button_was_pressed {
batch.events.push(uapi::input_event {
time,
type_: uapi::EV_KEY as u16,
code: key_code as u16,
value: button_is_pressed as i32,
});
batch.events.push(sync_event);
}
}
batch
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use pretty_assertions::assert_eq;
#[test]
fn test_ensure_touch_button_max() {
// If this test fails, it means a new TouchButton enum has been added
// and the TOUCH_BUTTON_BITVEC_SIZE needs to be updated.
assert_matches!(TouchButton::from_primitive(MAX_TOUCH_BUTTON_VALUE + 1), None);
}
#[test]
fn test_media_button_press_power() {
let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
let batch = parse_fidl_media_button_event(&fidl_event, false, false);
assert_eq!(batch.events.len(), 2);
assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch.events[0].code, uapi::KEY_POWER as u16);
assert_eq!(batch.events[0].value, 1);
assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
}
#[test]
fn test_media_button_release_power() {
let fidl_event = MediaButtonsEvent { power: Some(false), ..Default::default() };
let batch = parse_fidl_media_button_event(&fidl_event, true, false);
assert_eq!(batch.events.len(), 2);
assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch.events[0].code, uapi::KEY_POWER as u16);
assert_eq!(batch.events[0].value, 0);
assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
}
#[test]
fn test_media_button_no_change() {
let fidl_event = MediaButtonsEvent { power: Some(true), ..Default::default() };
let batch = parse_fidl_media_button_event(&fidl_event, true, false);
assert_eq!(batch.events.len(), 0);
}
use test_case::test_case;
#[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
#[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
#[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
#[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
#[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
fn test_touch_button_press(button: TouchButton, expected_key: u32) {
let fidl_event =
TouchButtonsEvent { pressed_buttons: Some(vec![button]), ..Default::default() };
let was_pressed = bit_vec::BitVec::from_elem(6, false);
let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
assert_eq!(batch.events.len(), 2);
assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch.events[0].code, expected_key as u16);
assert_eq!(batch.events[0].value, 1);
assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
}
#[test_case(TouchButton::Palm, uapi::KEY_SLEEP; "palm")]
#[test_case(TouchButton::SwipeUp, uapi::KEY_UP; "swipe up")]
#[test_case(TouchButton::SwipeLeft, uapi::KEY_LEFT; "swipe left")]
#[test_case(TouchButton::SwipeRight, uapi::KEY_RIGHT; "swipe right")]
#[test_case(TouchButton::SwipeDown, uapi::KEY_DOWN; "swipe down")]
fn test_touch_button_release(button: TouchButton, expected_key: u32) {
let fidl_event = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
was_pressed.set(button.into_primitive() as usize, true);
let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
assert_eq!(batch.events.len(), 2);
assert_eq!(batch.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch.events[0].code, expected_key as u16);
assert_eq!(batch.events[0].value, 0);
assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
}
#[test]
fn test_touch_button_no_change() {
let fidl_event = TouchButtonsEvent {
pressed_buttons: Some(vec![TouchButton::Palm]),
..Default::default()
};
let mut was_pressed = bit_vec::BitVec::from_elem(6, false);
was_pressed.set(TouchButton::Palm.into_primitive() as usize, true);
let batch = parse_fidl_touch_button_event(&fidl_event, &was_pressed);
assert_eq!(batch.events.len(), 0);
}
#[test]
fn test_touch_button_multi_press() {
// 1. Press Palm
let fidl_event1 = TouchButtonsEvent {
pressed_buttons: Some(vec![TouchButton::Palm]),
..Default::default()
};
let was_pressed1 = bit_vec::BitVec::from_elem(6, false);
let batch1 = parse_fidl_touch_button_event(&fidl_event1, &was_pressed1);
assert_eq!(batch1.events.len(), 2);
assert_eq!(batch1.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch1.events[0].code, uapi::KEY_SLEEP as u16);
assert_eq!(batch1.events[0].value, 1);
assert_eq!(batch1.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch1.events[1].code, uapi::SYN_REPORT as u16);
// 2. Hold Palm and press SwipeUp
let fidl_event2 = TouchButtonsEvent {
pressed_buttons: Some(vec![TouchButton::Palm, TouchButton::SwipeUp]),
..Default::default()
};
let was_pressed2 = batch1.touch_buttons;
let batch2 = parse_fidl_touch_button_event(&fidl_event2, &was_pressed2);
assert_eq!(batch2.events.len(), 2);
assert_eq!(batch2.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch2.events[0].code, uapi::KEY_UP as u16);
assert_eq!(batch2.events[0].value, 1);
assert_eq!(batch2.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch2.events[1].code, uapi::SYN_REPORT as u16);
// 3. Release Palm, hold SwipeUp
let fidl_event3 = TouchButtonsEvent {
pressed_buttons: Some(vec![TouchButton::SwipeUp]),
..Default::default()
};
let was_pressed3 = batch2.touch_buttons;
let batch3 = parse_fidl_touch_button_event(&fidl_event3, &was_pressed3);
assert_eq!(batch3.events.len(), 2);
assert_eq!(batch3.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch3.events[0].code, uapi::KEY_SLEEP as u16);
assert_eq!(batch3.events[0].value, 0);
assert_eq!(batch3.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch3.events[1].code, uapi::SYN_REPORT as u16);
// 4. Release SwipeUp
let fidl_event4 = TouchButtonsEvent { pressed_buttons: Some(vec![]), ..Default::default() };
let was_pressed4 = batch3.touch_buttons;
let batch4 = parse_fidl_touch_button_event(&fidl_event4, &was_pressed4);
assert_eq!(batch4.events.len(), 2);
assert_eq!(batch4.events[0].type_, uapi::EV_KEY as u16);
assert_eq!(batch4.events[0].code, uapi::KEY_UP as u16);
assert_eq!(batch4.events[0].value, 0);
assert_eq!(batch4.events[1].type_, uapi::EV_SYN as u16);
assert_eq!(batch4.events[1].code, uapi::SYN_REPORT as u16);
}
}