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:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user