diff --git a/inputd/src/lib.rs b/inputd/src/lib.rs index 0f71bdd213..c8f9e7e532 100644 --- a/inputd/src/lib.rs +++ b/inputd/src/lib.rs @@ -3,6 +3,25 @@ use std::fs::File; use std::io::{Error, Read, Write}; +unsafe fn any_as_u8_slice(p: &T) -> &[u8] { + ::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::()) +} + +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(usize)] +pub enum VtMode { + Text = 0, + Graphic = 1, + Default = 2, +} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VtActivate { + pub vt: usize, + pub mode: VtMode, +} + pub struct Handle(File); impl Handle { @@ -14,11 +33,12 @@ impl Handle { // The return value is the display identifier. It will be used to uniquely // identify the display on activation events. pub fn register(&mut self) -> Result { - Ok(dbg!(self.0.read(&mut [])?)) + Ok(self.0.read(&mut [])?) } - pub fn activate(&mut self, vt: usize) -> Result { - Ok(dbg!(self.0.write(&vt.to_le_bytes())?)) + pub fn activate(&mut self, vt: usize, mode: VtMode) -> Result { + let cmd = VtActivate { vt, mode }; + Ok(self.0.write(unsafe { any_as_u8_slice(&cmd) })?) } } @@ -43,9 +63,14 @@ impl From for CmdTy { } } +#[derive(Debug)] pub enum Cmd { // TODO(andypython): #VT should really need to be a `u8`. - Activate(usize /* #VT */), + Activate { + vt: usize, + mode: VtMode, + }, + Deactivate(usize /* #VT */), Resize { // TODO(andypython): do we really need to pass the VT here? @@ -60,7 +85,7 @@ pub enum Cmd { impl Cmd { fn ty(&self) -> CmdTy { match self { - Cmd::Activate(_) => CmdTy::Activate, + Cmd::Activate { .. } => CmdTy::Activate, Cmd::Deactivate(_) => CmdTy::Deactivate, Cmd::Resize { .. } => CmdTy::Resize, } @@ -74,7 +99,14 @@ pub fn send_comand(file: &mut File, command: Cmd) -> Result<(), syscall::Error> result.push(command.ty() as u8); match command { - Cmd::Activate(vt) | Cmd::Deactivate(vt) => result.extend_from_slice(&vt.to_le_bytes()), + Cmd::Activate { vt, mode } => { + let cmd = VtActivate { vt, mode }; + let bytes = unsafe { any_as_u8_slice(&cmd) }; + + result.extend_from_slice(bytes); + } + + Cmd::Deactivate(vt) => result.extend_from_slice(&vt.to_le_bytes()), Cmd::Resize { vt, width, @@ -105,7 +137,14 @@ pub fn parse_command(buffer: &[u8]) -> Option { let vt = usize::from_le_bytes(parser.next_chunk::().ok()?); match command { - CmdTy::Activate => Some(Cmd::Activate(vt)), + CmdTy::Activate => { + let cmd = unsafe { &*buffer.as_ptr().offset(1).cast::() }; + Some(Cmd::Activate { + vt: cmd.vt, + mode: VtMode::from(cmd.mode), + }) + } + CmdTy::Deactivate => Some(Cmd::Deactivate(vt)), CmdTy::Resize => { let width = parser.next_chunk::().ok()?; diff --git a/inputd/src/main.rs b/inputd/src/main.rs index 91cd76c92a..f2d6f1fd0f 100644 --- a/inputd/src/main.rs +++ b/inputd/src/main.rs @@ -17,7 +17,7 @@ use std::io::{Read, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use inputd::Cmd; +use inputd::{Cmd, VtActivate, VtMode}; use spin::Mutex; @@ -49,6 +49,7 @@ impl Handle { /// requires the system call to return first. Otherwise, it will block indefinitely. struct VtInner { handle_file: File, + mode: VtMode, } struct Vt { @@ -72,8 +73,10 @@ impl Vt { pub fn inner(&self) -> &Mutex { self.inner.call_once(|| { let handle_file = File::open(format!("{}:handle", self.display)).unwrap(); - - Mutex::new(VtInner { handle_file }) + Mutex::new(VtInner { + handle_file, + mode: VtMode::Default, + }) }) } } @@ -88,7 +91,7 @@ struct InputScheme { super_key: bool, active_vt: Option>, - todo: Vec, + todo: Vec, } impl InputScheme { @@ -186,19 +189,19 @@ impl SchemeMut for InputScheme { let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?; match handle { - Handle::Device { .. } | Handle::Consumer { .. } => { - if buf.len() == core::mem::size_of::() { - // The device is requesting to activate a VT. - let vt = - usize::from_le_bytes(buf.try_into().map_err(|_| SysError::new(EINVAL))?); + Handle::Device { device } => { + assert!(buf.len() == core::mem::size_of::()); - self.todo.push(vt); - return Ok(buf.len()); - } else { - unreachable!() - } + // SAFETY: We have verified the size of the buffer above. + let cmd = unsafe { &*buf.as_ptr().cast::() }; + + self.vts.insert(cmd.vt, Vt::new(device.clone(), cmd.vt)); + self.todo.push(cmd.clone()); + + return Ok(buf.len()); } + Handle::Consumer { .. } => unreachable!(), _ => {} } @@ -276,7 +279,10 @@ impl SchemeMut for InputScheme { inputd::send_comand( &mut vt_inner.handle_file, - Cmd::Activate(new_active.index), + Cmd::Activate { + vt: new_active.index, + mode: VtMode::Default, + }, )?; self.active_vt = Some(new_active.clone()); } else { @@ -287,7 +293,7 @@ impl SchemeMut for InputScheme { assert!(handle.is_producer()); - let active_vt = self.active_vt.as_ref().unwrap(); + let active_vt = self.active_vt.as_ref().unwrap(); for handle in self.handles.values_mut() { match handle { Handle::Consumer { @@ -357,17 +363,22 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { scheme.handle(&mut packet); socket_file.write(&packet)?; - while let Some(vt) = scheme.todo.pop() { - let vt = scheme.vts.get_mut(&vt).unwrap(); + while let Some(cmd) = scheme.todo.pop() { + let vt = scheme.vts.get_mut(&cmd.vt).unwrap(); let mut vt_inner = vt.inner().lock(); // Failing to activate a VT is not a fatal error. - if let Err(err) = - inputd::send_comand(&mut vt_inner.handle_file, Cmd::Activate(vt.index)) - { + if let Err(err) = inputd::send_comand( + &mut vt_inner.handle_file, + Cmd::Activate { + vt: vt.index, + mode: VtMode::from(cmd.mode), + }, + ) { log::error!("inputd: failed to activate VT #{}: {err}", vt.index) } + vt_inner.mode = cmd.mode; scheme.active_vt = Some(vt.clone()); } @@ -454,5 +465,53 @@ pub fn setup_logging(level: log::LevelFilter, name: &str) { pub fn main() { #[cfg(target_os = "redox")] setup_logging(log::LevelFilter::Trace, "inputd"); - redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize"); + + let mut args = std::env::args().skip(1); + + if let Some(val) = args.next() { + if val != "-G" { + panic!("inputd: invalid argument: {}", val); + } + + let vt = args.next().unwrap().parse::().unwrap(); + + // On startup, the VESA display driver is started which basically makes use of the framebuffer + // provided by the firmware. The GPU device are latter started by `pcid` (such as `virtio-gpu`). + let mut devices = vec![]; + let schemes = std::fs::read_dir(":").unwrap(); + + for entry in schemes { + let path = entry.unwrap().path(); + let path_str = path + .into_os_string() + .into_string() + .expect("inputd: failed to convert path to string"); + + if path_str.contains("display") { + println!("inputd: found display scheme {}", path_str); + devices.push(path_str); + } + } + + let device = devices + .iter() + .filter(|d| !d.contains("vesa")) + .collect::>(); + let device = if device.is_empty() { + "vesa" + } else { + // TODO: What should we do when there are multiple display devices? + device[0].split("/").nth(2).unwrap() + }; + + dbg!(&device); + let mut handle = + inputd::Handle::new(device).expect("inputd: failed to open display handle"); + + handle + .activate(vt, VtMode::Graphic) + .expect("inputd: failed to activate VT in graphic mode"); + } else { + redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize"); + } } diff --git a/vesad/src/main.rs b/vesad/src/main.rs index 505eb0d95b..08046147ca 100644 --- a/vesad/src/main.rs +++ b/vesad/src/main.rs @@ -98,7 +98,7 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[b daemon.ready().expect("failed to notify parent"); - scheme.inputd_handle.activate(1).unwrap(); + scheme.inputd_handle.activate(1, inputd::VtMode::Default).unwrap(); let mut blocked = Vec::new(); loop { diff --git a/vesad/src/scheme.rs b/vesad/src/scheme.rs index ee781eb1e5..fe9db07eba 100644 --- a/vesad/src/scheme.rs +++ b/vesad/src/scheme.rs @@ -37,6 +37,7 @@ pub struct DisplayScheme { next_id: usize, pub handles: BTreeMap, pub inputd_handle: inputd::Handle, + } impl DisplayScheme { @@ -72,7 +73,7 @@ impl DisplayScheme { vts, next_id: 0, handles: BTreeMap::new(), - inputd_handle + inputd_handle, } } @@ -303,20 +304,33 @@ impl SchemeMut for DisplayScheme { match handle.kind { HandleKind::Input => { - use inputd::Cmd as DisplayCommand; + use inputd::{Cmd as DisplayCommand, VtMode}; let command = inputd::parse_command(buf).unwrap(); + dbg!(&command); match command { - DisplayCommand::Activate(vt) => { + DisplayCommand::Activate { vt, mode } => { let vt_i = VtIndex(vt); if let Some(screens) = self.vts.get_mut(&vt_i) { for (screen_i, screen) in screens.iter_mut() { - screen.redraw( - self.onscreens[screen_i.0], - self.framebuffers[screen_i.0].stride - ); + match mode { + VtMode::Graphic => { + dbg!("yes"); + *screen = Box::new(GraphicScreen::new(Display::new(screen.width(), screen.height()))); + } + + VtMode::Default => { + dbg!("x", &mode); + screen.redraw( + self.onscreens[screen_i.0], + self.framebuffers[screen_i.0].stride + ); + } + + VtMode::Text => todo!() + } } } diff --git a/vesad/src/screen/graphic.rs b/vesad/src/screen/graphic.rs index e0bf22ca35..ca96be28a8 100644 --- a/vesad/src/screen/graphic.rs +++ b/vesad/src/screen/graphic.rs @@ -27,7 +27,7 @@ pub struct GraphicScreen { impl GraphicScreen { pub fn new(display: Display) -> GraphicScreen { GraphicScreen { - display: display, + display, input: VecDeque::new(), sync_rects: Vec::new(), } diff --git a/virtio-core/src/probe.rs b/virtio-core/src/probe.rs index f97ae30f5a..3e4ca9e1e8 100644 --- a/virtio-core/src/probe.rs +++ b/virtio-core/src/probe.rs @@ -11,7 +11,7 @@ use pcid_interface::*; use syscall::Io; use crate::spec::*; -use crate::transport::{Error, Queue, StandardTransport}; +use crate::transport::{Error, StandardTransport}; use crate::utils::{align_down, VolatileCell}; pub struct Device<'a> { @@ -21,6 +21,8 @@ pub struct Device<'a> { pub isr: &'a VolatileCell, } +// FIXME(andypython): `device_space` should not be `Send` nor `Sync`. Take +// it out of `Device`. unsafe impl Send for Device<'_> {} unsafe impl Sync for Device<'_> {} diff --git a/virtio-gpud/src/main.rs b/virtio-gpud/src/main.rs index 73434398e3..4f1f08eb80 100644 --- a/virtio-gpud/src/main.rs +++ b/virtio-gpud/src/main.rs @@ -446,7 +446,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { device.transport.clone(), ))?; - scheme.inputd_handle.activate(scheme.main_vt)?; + // scheme.inputd_handle.activate(scheme.main_vt)?; loop { let mut packet = Packet::default(); diff --git a/virtio-gpud/src/scheme.rs b/virtio-gpud/src/scheme.rs index 5495218cbb..7fe379a24c 100644 --- a/virtio-gpud/src/scheme.rs +++ b/virtio-gpud/src/scheme.rs @@ -249,20 +249,16 @@ impl<'a> Display<'a> { } } -enum Handle { - Vt(usize /* VT index */), +enum Handle<'a> { + Vt { display: Arc>, vt: usize }, Input, } pub struct Scheme<'a> { - vts: BTreeMap>>, - handles: BTreeMap, + handles: BTreeMap>, /// Counter used for file descriptor allocation. next_id: AtomicUsize, displays: Vec>>, - - pub(crate) inputd_handle: inputd::Handle, - pub(crate) main_vt: usize, } impl<'a> Scheme<'a> { @@ -280,19 +276,10 @@ impl<'a> Scheme<'a> { ) .await?; - let mut inputd_handle = inputd::Handle::new("virtio-gpu").unwrap(); - - let mut vts = BTreeMap::new(); - let main_vt = inputd_handle.register().unwrap(); - vts.insert(main_vt, displays[0].clone()); - Ok(Self { - vts, handles: BTreeMap::new(), next_id: AtomicUsize::new(0), - inputd_handle, displays, - main_vt, }) } @@ -363,22 +350,17 @@ impl<'a> SchemeMut for Scheme<'a> { dbg!(vt, id); - if self.displays.get(id).is_none() { - return Err(SysError::new(EINVAL)); - } + let display = self.displays.get(id).ok_or(SysError::new(EINVAL))?; let fd = self.next_id.fetch_add(1, Ordering::SeqCst); - // FIXME: The +1 is a hack. vesad smh - self.handles.insert(fd, Handle::Vt(vt + 1)); + self.handles.insert(fd, Handle::Vt {display: display.clone(), vt }); Ok(fd) } fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result { match self.handles.get(&id).unwrap() { - Handle::Vt(id) => { - let handle = self.vts.get_mut(&(id - 1)).ok_or(SysError::new(EINVAL))?; - let bytes_copied = futures::executor::block_on(handle.get_fpath(buf)).unwrap(); - + Handle::Vt { display, .. } => { + let bytes_copied = futures::executor::block_on(display.get_fpath(buf)).unwrap(); Ok(bytes_copied) } @@ -400,9 +382,8 @@ impl<'a> SchemeMut for Scheme<'a> { fn fmap(&mut self, id: usize, map: &syscall::Map) -> syscall::Result { match self.handles.get(&id).ok_or(SysError::new(EINVAL))? { - Handle::Vt(id) => { - let handle = self.vts.get_mut(&(id - 1)).ok_or(SysError::new(EINVAL))?; - Ok(futures::executor::block_on(handle.map_screen(map.offset)).unwrap()) + Handle::Vt { display, .. } => { + Ok(futures::executor::block_on(display.map_screen(map.offset)).unwrap()) } _ => unreachable!(), } @@ -410,9 +391,8 @@ impl<'a> SchemeMut for Scheme<'a> { fn fsync(&mut self, id: usize) -> syscall::Result { match self.handles.get(&id).ok_or(SysError::new(EINVAL))? { - Handle::Vt(id) => { - let handle = self.vts.get_mut(&id).ok_or(SysError::new(EINVAL))?; - futures::executor::block_on(handle.flush(None)).unwrap(); + Handle::Vt { display, .. } => { + futures::executor::block_on(display.flush(None)).unwrap(); Ok(0) } @@ -428,12 +408,10 @@ impl<'a> SchemeMut for Scheme<'a> { fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result { match self.handles.get(&id).ok_or(SysError::new(EINVAL))? { - Handle::Vt(id) => { - let handle = self.vts.get_mut(&(id - 1)).ok_or(SysError::new(EINVAL))?; - + Handle::Vt { display, .. } => { // The VT is not active and the device is reseted. Ask them to try // again later. - if handle.is_reseted.load(Ordering::SeqCst) { + if display.is_reseted.load(Ordering::SeqCst) { return Err(SysError::new(EAGAIN)); } @@ -445,26 +423,48 @@ impl<'a> SchemeMut for Scheme<'a> { }; for damage in damages { - futures::executor::block_on(handle.flush(Some(damage))).unwrap(); + futures::executor::block_on(display.flush(Some(damage))).unwrap(); } Ok(buf.len()) } Handle::Input => { - use inputd::Cmd as DisplayCommand; + use inputd::{Cmd as DisplayCommand, VtMode}; let command = inputd::parse_command(buf).unwrap(); match command { - DisplayCommand::Activate(vt) => { - let display = self.vts.get(&vt).unwrap(); - futures::executor::block_on(display.init()).unwrap(); + DisplayCommand::Activate { mode, vt } => { + assert!(mode == VtMode::Graphic || mode == VtMode::Default); + let target_vt = vt; + + for handle in self.handles.values() { + if let Handle::Vt { display , vt } = handle { + if *vt != target_vt { + continue; + } + + futures::executor::block_on(display.init()).unwrap(); + } + } } - DisplayCommand::Deactivate(vt) => { - let display = self.vts.get(&vt).ok_or(SysError::new(EINVAL))?; - futures::executor::block_on(display.detach()).unwrap(); + DisplayCommand::Deactivate(target_vt) => { + for handle in self.handles.values() { + if let Handle::Vt { display , vt } = handle { + if *vt != target_vt { + continue; + } + + futures::executor::block_on(display.detach()).unwrap(); + break; + } + } + + // for display in self.displays.iter() { + // futures::executor::block_on(display.detach()).unwrap(); + // } } DisplayCommand::Resize { .. } => {