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
+257 -44
View File
@@ -3,16 +3,37 @@ mod scheme;
mod translate;
mod types;
use std::collections::VecDeque;
use std::env;
use std::fs::File;
use std::io::Read;
use std::fs::OpenOptions;
use std::io::{ErrorKind, Read};
use std::mem::{size_of, MaybeUninit};
#[cfg(target_os = "redox")]
use std::os::fd::AsRawFd;
use std::os::unix::fs::OpenOptionsExt;
use std::process;
#[cfg(not(target_os = "redox"))]
use std::thread;
#[cfg(not(target_os = "redox"))]
use std::time::Duration;
use log::{error, info, LevelFilter, Metadata, Record};
use redox_scheme::{SignalBehavior, Socket};
use orbclient::{Event, EventOption};
use redox_scheme::{Request, SignalBehavior, Socket};
use syscall::error::EAGAIN;
use syscall::flag::O_NONBLOCK;
use scheme::EvdevScheme;
#[cfg(target_os = "redox")]
use event::{EventFlags as QueueEventFlags, RawEventQueue};
#[cfg(target_os = "redox")]
const SCHEME_QUEUE_TOKEN: usize = 1;
#[cfg(target_os = "redox")]
const INPUT_QUEUE_TOKEN: usize = 2;
struct StderrLogger {
level: LevelFilter,
}
@@ -29,64 +50,256 @@ impl log::Log for StderrLogger {
fn flush(&self) {}
}
fn read_input_events(scheme: &mut EvdevScheme) -> Result<(), String> {
let mut input_file =
File::open("/scheme/input").map_err(|e| format!("failed to open /scheme/input: {}", e))?;
struct InputConsumer {
file: File,
partial: Vec<u8>,
}
let mut buf = [0u8; 256];
match input_file.read(&mut buf) {
Ok(n) if n > 0 => {
let data = &buf[..n];
for &byte in data {
let pressed = (byte & 0x80) == 0;
let key = byte & 0x7F;
scheme.feed_keyboard_event(key, pressed);
impl InputConsumer {
fn open() -> Result<Self, String> {
let file = OpenOptions::new()
.read(true)
.custom_flags(O_NONBLOCK as i32)
.open("/scheme/input/consumer")
.map_err(|e| format!("failed to open /scheme/input/consumer: {e}"))?;
Ok(Self {
file,
partial: Vec::new(),
})
}
#[cfg(target_os = "redox")]
fn fd(&self) -> usize {
self.file.as_raw_fd() as usize
}
fn read_available(&mut self, scheme: &mut EvdevScheme) -> Result<bool, String> {
let event_size = size_of::<Event>();
let mut buf = vec![0u8; event_size * 32];
let mut progress = false;
loop {
match self.file.read(&mut buf) {
Ok(0) => break,
Ok(count) => {
progress = true;
self.partial.extend_from_slice(&buf[..count]);
while self.partial.len() >= event_size {
let event = read_event_from_bytes(&self.partial[..event_size]);
self.partial.drain(..event_size);
dispatch_input_event(event, scheme);
}
}
Err(err) if err.kind() == ErrorKind::WouldBlock => break,
Err(err) => return Err(format!("failed to read /scheme/input/consumer: {err}")),
}
}
Ok(_) => {}
Err(e) => {
error!("evdevd: failed to read input: {}", e);
Ok(progress)
}
}
#[derive(Clone, Copy, Debug, Default)]
struct SchemePoll {
mounted: bool,
progress: bool,
}
fn read_event_from_bytes(bytes: &[u8]) -> Event {
let mut event = MaybeUninit::<Event>::uninit();
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), event.as_mut_ptr() as *mut u8, bytes.len());
event.assume_init()
}
}
fn dispatch_input_event(event: Event, scheme: &mut EvdevScheme) {
match event.to_option() {
EventOption::Key(key) => scheme.feed_keyboard_event(key.scancode, key.pressed),
EventOption::Mouse(mouse) => scheme.feed_touchpad_position(mouse.x, mouse.y),
EventOption::MouseRelative(mouse) => scheme.feed_mouse_move(mouse.dx, mouse.dy),
EventOption::Button(button) => {
scheme.feed_mouse_buttons(button.left, button.middle, button.right)
}
EventOption::Scroll(scroll) => scheme.feed_mouse_scroll(scroll.x, scroll.y),
_ => {}
}
}
fn is_would_block_socket(err: &syscall::Error) -> bool {
err.errno == EAGAIN
}
fn write_scheme_response(socket: &Socket, response: redox_scheme::Response) -> Result<(), String> {
socket
.write_response(response, SignalBehavior::Restart)
.map_err(|e| format!("failed to write response: {e}"))?;
Ok(())
}
fn handle_request(
request: Request,
scheme: &mut EvdevScheme,
pending_requests: &mut VecDeque<Request>,
socket: &Socket,
) -> Result<bool, String> {
match request.handle_scheme_block_mut(scheme) {
Ok(response) => {
write_scheme_response(socket, response)?;
Ok(true)
}
Err(request) => {
pending_requests.push_back(request);
Ok(true)
}
}
}
fn flush_pending_requests(
scheme: &mut EvdevScheme,
pending_requests: &mut VecDeque<Request>,
socket: &Socket,
) -> Result<bool, String> {
let mut progress = false;
let pending_len = pending_requests.len();
for _ in 0..pending_len {
let Some(request) = pending_requests.pop_front() else {
break;
};
match request.handle_scheme_block_mut(scheme) {
Ok(response) => {
write_scheme_response(socket, response)?;
progress = true;
}
Err(request) => pending_requests.push_back(request),
}
}
Ok(progress)
}
fn read_scheme_requests(
socket: &Socket,
scheme: &mut EvdevScheme,
pending_requests: &mut VecDeque<Request>,
) -> Result<SchemePoll, String> {
let mut poll = SchemePoll {
mounted: true,
progress: false,
};
loop {
match socket.next_request(SignalBehavior::Restart) {
Ok(Some(request)) => {
poll.progress =
handle_request(request, scheme, pending_requests, socket)? || poll.progress;
}
Ok(None) => {
poll.mounted = false;
break;
}
Err(err) if is_would_block_socket(&err) => break,
Err(err) => return Err(format!("failed to read scheme request: {err}")),
}
}
Ok(poll)
}
#[cfg(target_os = "redox")]
fn run_redox_event_loop(
socket: &Socket,
scheme: &mut EvdevScheme,
input: &mut InputConsumer,
pending_requests: &mut VecDeque<Request>,
) -> Result<(), String> {
let event_queue =
RawEventQueue::new().map_err(|e| format!("failed to create event queue: {e}"))?;
event_queue
.subscribe(
socket.inner().raw(),
SCHEME_QUEUE_TOKEN,
QueueEventFlags::READ,
)
.map_err(|e| format!("failed to subscribe scheme socket: {e}"))?;
event_queue
.subscribe(input.fd(), INPUT_QUEUE_TOKEN, QueueEventFlags::READ)
.map_err(|e| format!("failed to subscribe input consumer: {e}"))?;
loop {
let raw_event = event_queue
.next_event()
.map_err(|e| format!("failed to wait for events: {e}"))?;
match raw_event.user_data {
SCHEME_QUEUE_TOKEN => {
let poll = read_scheme_requests(socket, scheme, pending_requests)?;
if !poll.mounted {
info!("evdevd: scheme unmounted, exiting");
break;
}
}
INPUT_QUEUE_TOKEN => {
let _ = input.read_available(scheme)?;
}
_ => {}
}
let _ = flush_pending_requests(scheme, pending_requests, socket)?;
}
Ok(())
}
#[cfg(not(target_os = "redox"))]
fn run_host_event_loop(
socket: &Socket,
scheme: &mut EvdevScheme,
input: &mut InputConsumer,
pending_requests: &mut VecDeque<Request>,
) -> Result<(), String> {
loop {
let mut progress = input.read_available(scheme)?;
let poll = read_scheme_requests(socket, scheme, pending_requests)?;
if !poll.mounted {
info!("evdevd: scheme unmounted, exiting");
break;
}
progress |= poll.progress;
progress |= flush_pending_requests(scheme, pending_requests, socket)?;
if !progress {
thread::sleep(Duration::from_millis(10));
}
}
Ok(())
}
fn run() -> Result<(), String> {
let mut scheme = EvdevScheme::new();
let mut input = InputConsumer::open()?;
let mut pending_requests = VecDeque::new();
let socket =
Socket::create("evdev").map_err(|e| format!("failed to register evdev scheme: {}", e))?;
Socket::nonblock("evdev").map_err(|e| format!("failed to register evdev scheme: {}", e))?;
info!("evdevd: registered scheme:evdev");
info!("evdevd: consuming orbclient::Event from /scheme/input/consumer");
loop {
let request = match socket.next_request(SignalBehavior::Restart) {
Ok(Some(r)) => r,
Ok(None) => {
info!("evdevd: scheme unmounted, exiting");
break;
}
Err(e) => {
error!("evdevd: failed to read scheme request: {}", e);
continue;
}
};
let response = match request.handle_scheme_block_mut(&mut scheme) {
Ok(r) => r,
Err(_req) => {
error!("evdevd: failed to handle request");
continue;
}
};
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
error!("evdevd: failed to write response: {}", e);
}
let _ = read_input_events(&mut scheme);
#[cfg(target_os = "redox")]
{
run_redox_event_loop(&socket, &mut scheme, &mut input, &mut pending_requests)
}
Ok(())
#[cfg(not(target_os = "redox"))]
{
run_host_event_loop(&socket, &mut scheme, &mut input, &mut pending_requests)
}
}
fn main() {