Add runtime tools and Red Bear service wiring

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:50:42 +01:00
parent fd60edc823
commit 51f3c21121
62 changed files with 9613 additions and 881 deletions
+168 -31
View File
@@ -1,6 +1,12 @@
use std::collections::VecDeque;
use std::collections::BTreeMap;
use crate::types::{InputEvent, InputId, BUS_VIRTUAL};
use crate::translate::{KEYBOARD_KEY_CODES, MOUSE_BUTTON_CODES, TOUCHPAD_KEY_CODES};
use crate::types::{
AbsInfo, InputId, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_SLOT, ABS_MT_TOUCH_MAJOR,
ABS_MT_TRACKING_ID, ABS_PRESSURE, ABS_X, ABS_Y, BUS_VIRTUAL, EV_ABS, EV_KEY, EV_LED, EV_MSC,
EV_REL, EV_REP, EV_SYN, INPUT_PROP_POINTER, KEY_MAX, LED_CAPSL, LED_MAX, LED_NUML, LED_SCROLLL,
MSC_SCAN, REL_HWHEEL, REL_WHEEL, REL_X, REL_Y, REP_DELAY, REP_PERIOD,
};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DeviceKind {
@@ -10,17 +16,17 @@ pub enum DeviceKind {
}
pub struct InputDevice {
pub id: usize,
pub kind: DeviceKind,
pub name: String,
pub input_id: InputId,
pub event_buf: VecDeque<InputEvent>,
pub key_state: [u8; KEY_MAX / 8 + 1],
pub led_state: [u8; LED_MAX / 8 + 1],
pub custom_abs: BTreeMap<u16, AbsInfo>,
}
impl InputDevice {
pub fn new_keyboard(id: usize) -> Self {
InputDevice {
id,
kind: DeviceKind::Keyboard,
name: format!("Redox Keyboard {}", id),
input_id: InputId {
@@ -29,13 +35,14 @@ impl InputDevice {
product: id as u16,
version: 1,
},
event_buf: VecDeque::new(),
key_state: [0u8; KEY_MAX / 8 + 1],
led_state: [0u8; LED_MAX / 8 + 1],
custom_abs: BTreeMap::new(),
}
}
pub fn new_mouse(id: usize) -> Self {
InputDevice {
id,
kind: DeviceKind::Mouse,
name: format!("Redox Mouse {}", id),
input_id: InputId {
@@ -44,13 +51,14 @@ impl InputDevice {
product: (id + 0x10) as u16,
version: 1,
},
event_buf: VecDeque::new(),
key_state: [0u8; KEY_MAX / 8 + 1],
led_state: [0u8; LED_MAX / 8 + 1],
custom_abs: BTreeMap::new(),
}
}
pub fn new_touchpad(id: usize) -> Self {
InputDevice {
id,
kind: DeviceKind::Touchpad,
name: format!("Redox Touchpad {}", id),
input_id: InputId {
@@ -59,37 +67,166 @@ impl InputDevice {
product: (id + 0x20) as u16,
version: 1,
},
event_buf: VecDeque::new(),
key_state: [0u8; KEY_MAX / 8 + 1],
led_state: [0u8; LED_MAX / 8 + 1],
custom_abs: BTreeMap::new(),
}
}
pub fn push_event(&mut self, event: InputEvent) {
self.event_buf.push_back(event);
}
pub fn push_events(&mut self, events: &[InputEvent]) {
for &ev in events {
self.event_buf.push_back(ev);
pub fn supported_event_types(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Keyboard => bitmap_from_codes(&[EV_SYN, EV_KEY, EV_MSC, EV_LED, EV_REP]),
DeviceKind::Mouse => bitmap_from_codes(&[EV_SYN, EV_KEY, EV_REL]),
DeviceKind::Touchpad => bitmap_from_codes(&[EV_SYN, EV_KEY, EV_ABS]),
}
}
pub fn pop_bytes(&mut self, buf: &mut [u8]) -> usize {
let event_count = buf.len() / InputEvent::SIZE;
let mut written = 0;
for _ in 0..event_count {
match self.event_buf.pop_front() {
Some(ev) => {
let bytes = ev.to_bytes();
buf[written..written + InputEvent::SIZE].copy_from_slice(&bytes);
written += InputEvent::SIZE;
}
None => break,
pub fn supported_keys(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Keyboard => bitmap_from_codes(KEYBOARD_KEY_CODES),
DeviceKind::Mouse => bitmap_from_codes(MOUSE_BUTTON_CODES),
DeviceKind::Touchpad => bitmap_from_codes(TOUCHPAD_KEY_CODES),
}
}
pub fn supported_rel(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Mouse => bitmap_from_codes(&[REL_X, REL_Y, REL_WHEEL, REL_HWHEEL]),
_ => Vec::new(),
}
}
pub fn supported_abs(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Touchpad => bitmap_from_codes(&[
ABS_X,
ABS_Y,
ABS_PRESSURE,
ABS_MT_SLOT,
ABS_MT_TOUCH_MAJOR,
ABS_MT_POSITION_X,
ABS_MT_POSITION_Y,
ABS_MT_TRACKING_ID,
]),
_ => Vec::new(),
}
}
pub fn supported_msc(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Keyboard => bitmap_from_codes(&[MSC_SCAN]),
_ => Vec::new(),
}
}
pub fn supported_leds(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Keyboard => bitmap_from_codes(&[LED_NUML, LED_CAPSL, LED_SCROLLL]),
_ => Vec::new(),
}
}
pub fn supported_rep(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Keyboard => bitmap_from_codes(&[REP_DELAY, REP_PERIOD]),
_ => Vec::new(),
}
}
pub fn supported_props(&self) -> Vec<u8> {
match self.kind {
DeviceKind::Mouse | DeviceKind::Touchpad => bitmap_from_codes(&[INPUT_PROP_POINTER]),
DeviceKind::Keyboard => Vec::new(),
}
}
pub fn abs_info(&self, axis: u16) -> AbsInfo {
if let Some(abs_info) = self.custom_abs.get(&axis) {
return *abs_info;
}
if self.kind != DeviceKind::Touchpad {
return AbsInfo::default();
}
match axis {
ABS_X | ABS_MT_POSITION_X => AbsInfo {
minimum: 0,
maximum: 65_535,
resolution: 1,
..AbsInfo::default()
},
ABS_Y | ABS_MT_POSITION_Y => AbsInfo {
minimum: 0,
maximum: 65_535,
resolution: 1,
..AbsInfo::default()
},
ABS_PRESSURE => AbsInfo {
minimum: 0,
maximum: 255,
resolution: 1,
..AbsInfo::default()
},
ABS_MT_TOUCH_MAJOR => AbsInfo {
minimum: 0,
maximum: 255,
resolution: 1,
..AbsInfo::default()
},
ABS_MT_SLOT => AbsInfo {
minimum: 0,
maximum: 9,
..AbsInfo::default()
},
ABS_MT_TRACKING_ID => AbsInfo {
minimum: 0,
maximum: i32::MAX,
..AbsInfo::default()
},
_ => AbsInfo::default(),
}
}
pub fn set_abs_info(&mut self, axis: u16, abs_info: AbsInfo) {
self.custom_abs.insert(axis, abs_info);
}
pub fn update_key_state(&mut self, code: u16, pressed: bool) {
let byte = (code / 8) as usize;
let bit = code % 8;
if byte < self.key_state.len() {
if pressed {
self.key_state[byte] |= 1 << bit;
} else {
self.key_state[byte] &= !(1 << bit);
}
}
written
}
pub fn has_events(&self) -> bool {
!self.event_buf.is_empty()
pub fn update_led_state(&mut self, code: u16, lit: bool) {
let byte = (code / 8) as usize;
let bit = code % 8;
if byte < self.led_state.len() {
if lit {
self.led_state[byte] |= 1 << bit;
} else {
self.led_state[byte] &= !(1 << bit);
}
}
}
}
fn bitmap_from_codes(codes: &[u16]) -> Vec<u8> {
let Some(max) = codes.iter().copied().max() else {
return Vec::new();
};
let mut bitmap = vec![0u8; (usize::from(max) / 8) + 1];
for &code in codes {
let index = usize::from(code / 8);
let bit = 1u8 << (code % 8);
bitmap[index] |= bit;
}
bitmap
}