use event::EventQueue; use inputd::ConsumerHandleEvent; use libredox::errno::{EAGAIN, EINTR}; use orbclient::Event; use redox_scheme::{ scheme::{Op, SchemeResponse, SchemeState, SchemeSync}, CallerCtx, RequestKind, Response, SignalBehavior, Socket, }; use std::env; use syscall::{EOPNOTSUPP, EVENT_READ}; use crate::scheme::{FbconScheme, Handle, VtIndex}; mod display; mod keymap; mod scheme; mod text; fn main() { daemon::SchemeDaemon::new(daemon); } fn daemon(daemon: daemon::SchemeDaemon) -> ! { let vt_ids = env::args() .skip(1) .map(|arg| arg.parse().expect("invalid vt number")) .collect::>(); common::setup_logging( "graphics", "fbcond", "fbcond", common::output_level(), common::file_level(), ); let mut event_queue = EventQueue::new().expect("fbcond: failed to create event queue"); // FIXME listen for resize events from inputd and handle them let mut socket = Socket::nonblock().expect("fbcond: failed to create fbcon scheme"); event_queue .subscribe( socket.inner().raw(), VtIndex::SCHEMA_SENTINEL, event::EventFlags::READ, ) .expect("fbcond: failed to subscribe to scheme events"); // Open the kernel debug (serial) scheme and subscribe to its readable // events, so serial-line input can be fed into the console. Combined with // the output mirror in TextScreen, this makes the framebuffer console fully // usable over a serial line (headless login). let serial_fd = libredox::call::open( "/scheme/debug", (libredox::flag::O_RDWR | libredox::flag::O_NONBLOCK) as _, 0, ) .ok(); if let Some(fd) = serial_fd { let _ = event_queue.subscribe(fd, VtIndex::SERIAL_SENTINEL, event::EventFlags::READ); } let mut state = SchemeState::new(); let mut scheme = FbconScheme::new(&vt_ids, &mut event_queue); let _ = daemon.ready_sync_scheme(&socket, &mut scheme); // This is not possible for now as fbcond needs to open new displays at runtime for graphics // driver handoff. In the future inputd may directly pass a handle to the display instead. // libredox::call::setrens(0, 0).expect("fbcond: failed to enter null namespace"); let mut blocked = Vec::new(); // Handle all events that could have happened before registering with the event queue. handle_event( &mut socket, &mut scheme, &mut state, &mut blocked, VtIndex::SCHEMA_SENTINEL, ); for vt_i in scheme.vts.keys().copied().collect::>() { handle_event(&mut socket, &mut scheme, &mut state, &mut blocked, vt_i); } for event in event_queue { let event = event.expect("fbcond: failed to read event from event queue"); // Pick up any display handoff whose (blocking) open has completed on its // worker. Cheap and non-blocking; done per event so the swap lands as // soon as the driver starts serving, without the console ever waiting on // it. Output produced meanwhile is buffered and flushed on install. for vt in scheme.vts.values_mut() { vt.poll_pending_handoff(); } if let Some(fd) = serial_fd { if event.user_data == VtIndex::SERIAL_SENTINEL { let mut buf = [0u8; 256]; loop { match libredox::call::read(fd, &mut buf) { Ok(0) => break, Ok(n) => { for vt in scheme.vts.values_mut() { vt.push_input_bytes(&buf[..n]); } } Err(err) if err.errno() == EAGAIN => break, Err(_) => break, } } // Run a scheme pass so blocked getty reads get their fevent // notification now that input is available. handle_event( &mut socket, &mut scheme, &mut state, &mut blocked, VtIndex::SCHEMA_SENTINEL, ); continue; } } handle_event( &mut socket, &mut scheme, &mut state, &mut blocked, event.user_data, ); } std::process::exit(0); } fn handle_event( socket: &mut Socket, scheme: &mut FbconScheme, state: &mut SchemeState, blocked: &mut Vec<(Op, CallerCtx)>, event: VtIndex, ) { match event { VtIndex::SCHEMA_SENTINEL => loop { let request = match socket.next_request(SignalBehavior::Restart) { Ok(Some(request)) => request, Ok(None) => { // Scheme likely got unmounted std::process::exit(0); } Err(err) if err.errno == EAGAIN => { break; } Err(err) => panic!("fbcond: failed to read display scheme: {err}"), }; match request.kind() { RequestKind::Call(req) => { let caller = req.caller(); let mut op = match req.op() { Ok(op) => op, Err(req) => { let _ = socket .write_response( Response::err(EOPNOTSUPP, req), SignalBehavior::Restart, ) .expect("fbcond: failed to write responses to fbcon scheme"); continue; } }; match op.handle_sync_dont_consume(&caller, scheme, state) { SchemeResponse::Opened(Err(e)) | SchemeResponse::Regular(Err(e)) if libredox::error::Error::from(e).is_wouldblock() && !op.is_explicitly_nonblock() => { blocked.push((op, caller)); } SchemeResponse::Regular(r) => { let _ = socket .write_response(Response::new(r, op), SignalBehavior::Restart) .expect("fbcond: failed to write responses to fbcon scheme"); } SchemeResponse::Opened(o) => { let _ = socket .write_response( Response::open_dup_like(o, op), SignalBehavior::Restart, ) .expect("fbcond: failed to write responses to fbcon scheme"); } SchemeResponse::RegularAndNotifyOnDetach(status) => { let _ = socket .write_response( Response::new_notify_on_detach(status, op), SignalBehavior::Restart, ) .expect("fbcond: failed to write scheme"); } } } RequestKind::OnClose { id } => { scheme.on_close(id); } RequestKind::Cancellation(cancellation_request) => { if let Some(i) = blocked .iter() .position(|(_op, caller)| caller.id == cancellation_request.id) { let (blocked_req, _) = blocked.remove(i); let resp = Response::err(EINTR, blocked_req); socket .write_response(resp, SignalBehavior::Restart) .expect("vesad: failed to write display scheme"); } } _ => {} } }, vt_i => { let vt = scheme.vts.get_mut(&vt_i).unwrap(); let mut events = [Event::new(); 16]; loop { match vt .display .input_handle .read_events(&mut events) .expect("fbcond: Error while reading events") { ConsumerHandleEvent::Events(&[]) => break, ConsumerHandleEvent::Events(events) => { for event in events { vt.input(event) } } ConsumerHandleEvent::Handoff => vt.handle_handoff(), } } } } // If there are blocked readers, try to handle them. { let mut i = 0; while i < blocked.len() { let (op, caller) = blocked .get_mut(i) .expect("vesad: Failed to get blocked request"); let resp = match op.handle_sync_dont_consume(&caller, scheme, state) { SchemeResponse::Opened(Err(e)) | SchemeResponse::Regular(Err(e)) if libredox::error::Error::from(e).is_wouldblock() && !op.is_explicitly_nonblock() => { i += 1; continue; } SchemeResponse::Regular(r) => { let (op, _) = blocked.remove(i); Response::new(r, op) } SchemeResponse::Opened(o) => { let (op, _) = blocked.remove(i); Response::open_dup_like(o, op) } SchemeResponse::RegularAndNotifyOnDetach(status) => { let (op, _) = blocked.remove(i); Response::new_notify_on_detach(status, op) } }; let _ = socket .write_response(resp, SignalBehavior::Restart) .expect("vesad: failed to write display scheme"); } } for (handle_id, handle) in scheme.handles.iter_mut() { let handle = match handle { Handle::SchemeRoot | Handle::Scrollback(_) => continue, Handle::Vt(handle) => handle, }; if !handle.events.contains(EVENT_READ) { continue; } let can_read = scheme .vts .get(&handle.vt_i) .map_or(false, |console| console.can_read()); if can_read { if !handle.notified_read { handle.notified_read = true; let response = Response::post_fevent(*handle_id, EVENT_READ.bits()); socket .write_response(response, SignalBehavior::Restart) .expect("fbcond: failed to write display event"); } } else { handle.notified_read = false; } } }