inputd: Let graphics drivers pull vt activation events from inputd

Previously inputd would directly push vt activation events to the
graphics driver, which required being quite lazy to prevent deadlocks as
well as the graphics driver having a location where events can be pushed
to. By having graphics drivers pull the vt activation events instead,
the effective control flow becomes simpler and it becomes easier to
correctly handle multiple graphics drivers on the system. For example it
becomes possible for multiple graphics drivers to present displays for a
single VT as well as making it easier to provide a handoff from the
early framebuffer to a real graphics driver.
This commit is contained in:
bjorn3
2024-12-21 18:09:15 +01:00
parent 273cbda872
commit 8e92e2c743
7 changed files with 484 additions and 490 deletions
Generated
+1
View File
@@ -1681,6 +1681,7 @@ dependencies = [
"ransid",
"redox-daemon",
"redox-scheme",
"redox_event",
"redox_syscall",
]
+2 -1
View File
@@ -1,13 +1,14 @@
[package]
name = "vesad"
version = "0.1.0"
edition = "2018"
edition = "2021"
[dependencies]
orbclient = "0.3.27"
ransid = "0.4"
redox_syscall = "0.5"
redox-daemon = "0.1"
redox_event = "0.4.1"
common = { path = "../../common" }
inputd = { path = "../../inputd" }
+120 -64
View File
@@ -2,15 +2,15 @@
extern crate orbclient;
extern crate syscall;
use redox_scheme::{RequestKind, SignalBehavior, Socket, V2};
use event::{user_data, EventQueue};
use libredox::errno::{EAGAIN, EINTR};
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket, V2};
use std::env;
use std::fs::File;
use std::os::fd::AsRawFd;
use syscall::EVENT_READ;
use crate::{
framebuffer::FrameBuffer,
scheme::{DisplayScheme, HandleKind},
};
use crate::{framebuffer::FrameBuffer, scheme::DisplayScheme};
mod display;
mod framebuffer;
@@ -80,14 +80,38 @@ fn main() {
}
fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[()]) -> ! {
let socket: Socket<V2> =
Socket::create("display.vesa").expect("vesad: failed to create display scheme");
Socket::nonblock("display.vesa").expect("vesad: failed to create display scheme");
let mut scheme = DisplayScheme::new(framebuffers, &spec);
let _ = File::open("/scheme/debug/disable-graphical-debug");
let mut inputd_control_handle = inputd::ControlHandle::new().unwrap();
user_data! {
enum Source {
Input,
Scheme,
}
}
let event_queue: EventQueue<Source> =
EventQueue::new().expect("vesad: failed to create event queue");
event_queue
.subscribe(
scheme.inputd_handle.inner().as_raw_fd() as usize,
Source::Input,
event::EventFlags::READ,
)
.unwrap();
event_queue
.subscribe(
socket.inner().raw(),
Source::Scheme,
event::EventFlags::READ,
)
.unwrap();
let _ = File::open("/scheme/debug/disable-graphical-debug");
libredox::call::setrens(0, 0).expect("vesad: failed to enter null namespace");
daemon.ready().expect("failed to notify parent");
@@ -95,67 +119,99 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[(
inputd_control_handle.activate_vt(1).unwrap();
let mut blocked = Vec::new();
loop {
let Some(request) = socket
.next_request(SignalBehavior::Restart)
.expect("vesad: failed to read display scheme")
else {
// Scheme likely got unmounted
std::process::exit(0);
};
match request.kind() {
RequestKind::Call(call_request) => {
if let Some(resp) = call_request.handle_scheme_block_mut(&mut scheme) {
socket
.write_response(resp, SignalBehavior::Restart)
.expect("vesad: failed to write display scheme");
} else {
blocked.push(call_request);
let all = [Source::Input, Source::Scheme];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("vesad: failed to get next event").user_data))
{
match event {
Source::Input => {
while let Some(vt_event) = scheme
.inputd_handle
.read_vt_event()
.expect("vesad: failed to read display handle")
{
scheme.handle_vt_event(vt_event);
}
}
RequestKind::Cancellation(_cancellation_request) => {}
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => unreachable!(),
}
Source::Scheme => {
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!("vesad: failed to read display scheme: {err}"),
};
// If there are blocked readers, try to handle them.
{
let mut i = 0;
while i < blocked.len() {
if let Some(resp) = blocked[i].handle_scheme_block_mut(&mut scheme) {
socket
.write_response(resp, SignalBehavior::Restart)
.expect("vesad: failed to write display scheme");
blocked.remove(i);
} else {
i += 1;
match request.kind() {
RequestKind::Call(call_request) => {
if let Some(resp) = call_request.handle_scheme_block_mut(&mut scheme) {
socket
.write_response(resp, SignalBehavior::Restart)
.expect("vesad: failed to write display scheme");
} else {
blocked.push(call_request);
}
}
RequestKind::Cancellation(cancellation_request) => {
if let Some(i) = blocked.iter().position(|req| {
req.request().request_id() == cancellation_request.id
}) {
let blocked_req = blocked.remove(i);
let resp =
Response::new(&blocked_req, Err(syscall::Error::new(EINTR)));
socket
.write_response(resp, SignalBehavior::Restart)
.expect("vesad: failed to write display scheme");
}
}
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => {
unreachable!()
}
}
// If there are blocked readers, try to handle them.
{
let mut i = 0;
while i < blocked.len() {
if let Some(resp) = blocked[i].handle_scheme_block_mut(&mut scheme) {
socket
.write_response(resp, SignalBehavior::Restart)
.expect("vesad: failed to write display scheme");
blocked.remove(i);
} else {
i += 1;
}
}
}
for (handle_id, handle) in scheme.handles.iter_mut() {
if handle.notified_read || !handle.events.contains(EVENT_READ) {
continue;
}
let can_read = scheme
.vts
.get(&handle.vt)
.and_then(|screens| screens.get(&handle.screen))
.map_or(false, |screen| screen.can_read());
if can_read {
handle.notified_read = true;
socket
.post_fevent(*handle_id, EVENT_READ.bits())
.expect("vesad: failed to write display event");
} else {
handle.notified_read = false;
}
}
}
}
}
for (handle_id, handle) in scheme.handles.iter_mut() {
if handle.notified_read || !handle.events.contains(EVENT_READ) {
continue;
}
let can_read = if let HandleKind::Screen(vt_i, screen_i) = handle.kind {
scheme
.vts
.get(&vt_i)
.and_then(|screens| screens.get(&screen_i))
.map_or(false, |screen| screen.can_read())
} else {
false
};
if can_read {
handle.notified_read = true;
socket
.post_fevent(*handle_id, EVENT_READ.bits())
.expect("vesad: failed to write display event");
} else {
handle.notified_read = false;
}
}
}
panic!();
}
+76 -124
View File
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use std::str;
use inputd::{VtEvent, VtEventKind};
use redox_scheme::SchemeBlockMut;
use syscall::{Error, EventFlags, MapFlags, Result, EBADF, EINVAL, ENOENT, O_NONBLOCK};
@@ -12,15 +13,11 @@ pub struct VtIndex(usize);
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug)]
pub struct ScreenIndex(usize);
#[derive(Clone)]
pub enum HandleKind {
Input,
Screen(VtIndex, ScreenIndex),
}
#[derive(Clone)]
pub struct Handle {
pub kind: HandleKind,
pub vt: VtIndex,
pub screen: ScreenIndex,
pub flags: usize,
pub events: EventFlags,
pub notified_read: bool,
@@ -32,7 +29,7 @@ pub struct DisplayScheme {
pub vts: BTreeMap<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>,
next_id: usize,
pub handles: BTreeMap<usize, Handle>,
_inputd_handle: inputd::DisplayHandle,
pub inputd_handle: inputd::DisplayHandle,
}
impl DisplayScheme {
@@ -56,7 +53,7 @@ impl DisplayScheme {
vts,
next_id: 0,
handles: BTreeMap::new(),
_inputd_handle: inputd_handle,
inputd_handle,
}
}
@@ -84,6 +81,32 @@ impl DisplayScheme {
}
}
}
pub fn handle_vt_event(&mut self, vt_event: VtEvent) {
match vt_event.kind {
VtEventKind::Activate => {
let vt_i = VtIndex(vt_event.vt);
if let Some(screens) = self.vts.get_mut(&vt_i) {
for (screen_i, screen) in screens.iter_mut() {
screen.redraw(&mut self.framebuffers[screen_i.0]);
}
}
self.active = vt_i;
}
VtEventKind::Deactivate => {
// Nothing to do for deactivate :)
}
VtEventKind::Resize => {
self.resize(
vt_event.width as usize,
vt_event.height as usize,
vt_event.stride as usize,
);
}
}
}
}
impl SchemeBlockMut for DisplayScheme {
@@ -94,21 +117,6 @@ impl SchemeBlockMut for DisplayScheme {
_uid: u32,
_gid: u32,
) -> Result<Option<usize>> {
if path_str == "handle" {
let id = self.next_id;
self.next_id += 1;
self.handles.insert(
id,
Handle {
kind: HandleKind::Input,
flags,
events: EventFlags::empty(),
notified_read: false,
},
);
return Ok(Some(id));
}
let mut parts = path_str.split('/');
let mut vt_screen = parts.next().unwrap_or("").split('.');
let vt_i = VtIndex(vt_screen.next().unwrap_or("").parse::<usize>().unwrap_or(1));
@@ -121,7 +129,9 @@ impl SchemeBlockMut for DisplayScheme {
self.handles.insert(
id,
Handle {
kind: HandleKind::Screen(vt_i, screen_i),
vt: vt_i,
screen: screen_i,
flags,
events: EventFlags::empty(),
notified_read: false,
@@ -164,39 +174,25 @@ impl SchemeBlockMut for DisplayScheme {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
handle.notified_read = false;
if let HandleKind::Screen(_vt_i, _screen_i) = handle.kind {
handle.events = flags;
Ok(Some(syscall::EventFlags::empty()))
} else {
Err(Error::new(EBADF))
}
handle.events = flags;
Ok(Some(syscall::EventFlags::empty()))
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let path_str = match handle.kind {
HandleKind::Input => {
//TODO: allow inputs associated with other framebuffers?
format!(
"display:input/{}/{}",
self.framebuffers[0].width, self.framebuffers[0].height
)
}
HandleKind::Screen(vt_i, screen_i) => {
if let Some(screens) = self.vts.get(&vt_i) {
if let Some(screen) = screens.get(&screen_i) {
format!(
"display:{}.{}/{}/{}",
vt_i.0, screen_i.0, screen.width, screen.height
)
} else {
return Err(Error::new(EBADF));
}
let path_str = {
if let Some(screens) = self.vts.get(&handle.vt) {
if let Some(screen) = screens.get(&handle.screen) {
format!(
"display:{}.{}/{}/{}",
handle.vt.0, handle.screen.0, screen.width, screen.height
)
} else {
return Err(Error::new(EBADF));
}
} else {
return Err(Error::new(EBADF));
}
};
@@ -214,14 +210,12 @@ impl SchemeBlockMut for DisplayScheme {
fn fsync(&mut self, id: usize) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
if let HandleKind::Screen(vt_i, screen_i) = handle.kind {
if let Some(screens) = self.vts.get_mut(&vt_i) {
if let Some(screen) = screens.get_mut(&screen_i) {
if vt_i == self.active {
screen.sync(&mut self.framebuffers[screen_i.0]);
}
return Ok(Some(0));
if let Some(screens) = self.vts.get_mut(&handle.vt) {
if let Some(screen) = screens.get_mut(&handle.screen) {
if handle.vt == self.active {
screen.sync(&mut self.framebuffers[handle.screen.0]);
}
return Ok(Some(0));
}
}
@@ -237,18 +231,16 @@ impl SchemeBlockMut for DisplayScheme {
) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
if let HandleKind::Screen(vt_i, screen_i) = handle.kind {
if let Some(screens) = self.vts.get_mut(&vt_i) {
if let Some(screen) = screens.get_mut(&screen_i) {
let nread = screen.read(buf)?;
if nread != 0 {
return Ok(Some(nread));
if let Some(screens) = self.vts.get_mut(&handle.vt) {
if let Some(screen) = screens.get_mut(&handle.screen) {
let nread = screen.read(buf)?;
if nread != 0 {
return Ok(Some(nread));
} else {
if handle.flags & O_NONBLOCK == O_NONBLOCK {
return Ok(Some(0));
} else {
if handle.flags & O_NONBLOCK == O_NONBLOCK {
return Ok(Some(0));
} else {
return Ok(None);
}
return Ok(None);
}
}
}
@@ -266,56 +258,18 @@ impl SchemeBlockMut for DisplayScheme {
) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
match handle.kind {
HandleKind::Input => {
use inputd::Cmd as DisplayCommand;
let command = inputd::parse_command(buf).unwrap();
match command {
DisplayCommand::Activate { vt } => {
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(&mut self.framebuffers[screen_i.0]);
}
}
self.active = vt_i;
}
DisplayCommand::Resize {
width,
height,
stride,
..
} => {
self.resize(width as usize, height as usize, stride as usize);
}
// Nothing to do for deactivate :)
DisplayCommand::Deactivate(_) => {}
}
Ok(Some(buf.len()))
}
HandleKind::Screen(vt_i, screen_i) => {
if let Some(screens) = self.vts.get_mut(&vt_i) {
if let Some(screen) = screens.get_mut(&screen_i) {
let count = screen.write(buf)?;
if vt_i == self.active {
screen.sync(&mut self.framebuffers[screen_i.0]);
}
Ok(Some(count))
} else {
Err(Error::new(EBADF))
}
} else {
Err(Error::new(EBADF))
if let Some(screens) = self.vts.get_mut(&handle.vt) {
if let Some(screen) = screens.get_mut(&handle.screen) {
let count = screen.write(buf)?;
if handle.vt == self.active {
screen.sync(&mut self.framebuffers[handle.screen.0]);
}
Ok(Some(count))
} else {
Err(Error::new(EBADF))
}
} else {
Err(Error::new(EBADF))
}
}
@@ -332,14 +286,12 @@ impl SchemeBlockMut for DisplayScheme {
) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
if let HandleKind::Screen(vt_i, screen_i) = handle.kind {
if let Some(screens) = self.vts.get(&vt_i) {
if let Some(screen) = screens.get(&screen_i) {
if off as usize + size <= screen.offscreen.len() * 4 {
return Ok(Some(screen.offscreen.as_ptr() as usize + off as usize));
} else {
return Err(Error::new(EINVAL));
}
if let Some(screens) = self.vts.get(&handle.vt) {
if let Some(screen) = screens.get(&handle.screen) {
if off as usize + size <= screen.offscreen.len() * 4 {
return Ok(Some(screen.offscreen.as_ptr() as usize + off as usize));
} else {
return Err(Error::new(EINVAL));
}
}
}
+74 -103
View File
@@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::sync::Arc;
use common::{dma::Dma, sgl};
use inputd::Damage;
use inputd::{Damage, VtEvent, VtEventKind};
use redox_scheme::SchemeMut;
use syscall::{Error as SysError, MapFlags, EAGAIN, EINVAL, PAGE_SIZE};
@@ -246,12 +246,9 @@ impl<'a> Display<'a> {
}
}
enum Handle<'a> {
Vt {
display: Arc<Display<'a>>,
vt: usize,
},
Input,
struct Handle<'a> {
display: Arc<Display<'a>>,
vt: usize,
}
pub struct Scheme<'a> {
@@ -331,17 +328,52 @@ impl<'a> Scheme<'a> {
Ok(response)
}
// FIXME wire this up
fn handle_vt_event(&mut self, vt_event: VtEvent) {
match vt_event.kind {
VtEventKind::Activate => {
log::info!("activate {}", vt_event.vt);
for handle in self.handles.values() {
if handle.vt != vt_event.vt {
continue;
}
log::warn!("virtio-gpu: activating");
futures::executor::block_on(handle.display.init()).unwrap();
}
}
VtEventKind::Deactivate => {
log::info!("deactivate {}", vt_event.vt);
for handle in self.handles.values() {
if handle.vt != vt_event.vt {
continue;
}
log::warn!("virtio-gpu: deactivating");
futures::executor::block_on(handle.display.detach()).unwrap();
break;
}
// for display in self.displays.iter() {
// futures::executor::block_on(display.detach()).unwrap();
// }
}
VtEventKind::Resize => {
log::warn!("virtio-gpu: resize is not implemented yet")
}
}
}
}
impl<'a> SchemeMut for Scheme<'a> {
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result<usize> {
if path == "handle" {
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, Handle::Input);
return Ok(fd);
}
let mut parts = path.split('/');
let mut screen = parts.next().unwrap_or("").split('.');
@@ -355,7 +387,7 @@ impl<'a> SchemeMut for Scheme<'a> {
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(
fd,
Handle::Vt {
Handle {
display: display.clone(),
vt,
},
@@ -364,25 +396,15 @@ impl<'a> SchemeMut for Scheme<'a> {
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
match self.handles.get(&id).unwrap() {
Handle::Vt { display, .. } => {
let bytes_copied = futures::executor::block_on(display.get_fpath(buf)).unwrap();
Ok(bytes_copied)
}
Handle::Input => unreachable!(),
}
let handle = self.handles.get(&id).unwrap();
let bytes_copied = futures::executor::block_on(handle.display.get_fpath(buf)).unwrap();
Ok(bytes_copied)
}
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
Handle::Vt { display, .. } => {
futures::executor::block_on(display.flush(None)).unwrap();
Ok(0)
}
_ => unreachable!(),
}
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
futures::executor::block_on(handle.display.flush(None)).unwrap();
Ok(0)
}
fn read(
@@ -404,73 +426,26 @@ impl<'a> SchemeMut for Scheme<'a> {
_offset: u64,
_fcntl_flags: u32,
) -> syscall::Result<usize> {
match self.handles.get(&id).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 display.is_reseted.load(Ordering::SeqCst) {
return Err(SysError::new(EAGAIN));
}
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
let damages = unsafe {
core::slice::from_raw_parts(
buf.as_ptr() as *const Damage,
buf.len() / core::mem::size_of::<Damage>(),
)
};
for damage in damages {
futures::executor::block_on(display.flush(Some(damage))).unwrap();
}
Ok(buf.len())
}
Handle::Input => {
use inputd::Cmd as DisplayCommand;
let command = inputd::parse_command(buf).unwrap();
match command {
DisplayCommand::Activate { vt } => {
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(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 { .. } => {
log::warn!("virtio-gpu: resize is not implemented yet")
}
}
Ok(buf.len())
}
// The VT is not active and the device is reseted. Ask them to try
// again later.
if handle.display.is_reseted.load(Ordering::SeqCst) {
return Err(SysError::new(EAGAIN));
}
let damages = unsafe {
core::slice::from_raw_parts(
buf.as_ptr() as *const Damage,
buf.len() / core::mem::size_of::<Damage>(),
)
};
for damage in damages {
futures::executor::block_on(handle.display.flush(Some(damage))).unwrap();
}
Ok(buf.len())
}
fn close(&mut self, _id: usize) -> syscall::Result<usize> {
@@ -484,12 +459,8 @@ impl<'a> SchemeMut for Scheme<'a> {
flags: MapFlags,
) -> syscall::Result<usize> {
log::info!("KSMSG MMAP {} {:?} {} {}", id, flags, offset, size);
match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
Handle::Vt { display, .. } => Ok(futures::executor::block_on(
display.map_screen(offset as usize),
)
.unwrap() as usize),
_ => unreachable!(),
}
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
let ptr = futures::executor::block_on(handle.display.map_screen(offset as usize)).unwrap();
Ok(ptr as usize)
}
}
+39 -109
View File
@@ -2,9 +2,15 @@
use std::fs::File;
use std::io::{Error, Read, Write};
use std::mem::size_of;
use std::os::fd::{AsFd, BorrowedFd};
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::<T>())
std::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
}
unsafe fn any_as_u8_slice_mut<T: Sized>(p: &mut T) -> &mut [u8] {
std::slice::from_raw_parts_mut((p as *mut T) as *mut u8, size_of::<T>())
}
#[derive(Debug, Clone)]
@@ -27,6 +33,28 @@ impl DisplayHandle {
self.0.read(&mut [])
}
pub fn read_vt_event(&mut self) -> Result<Option<VtEvent>, Error> {
let mut event = VtEvent {
kind: VtEventKind::Resize,
vt: usize::MAX,
width: u32::MAX,
height: u32::MAX,
stride: u32::MAX,
};
let nread = self.0.read(unsafe { any_as_u8_slice_mut(&mut event) })?;
if nread == 0 {
Ok(None)
} else {
assert_eq!(nread, size_of::<VtEvent>());
Ok(Some(event))
}
}
pub fn inner(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
pub struct ControlHandle(File);
@@ -43,121 +71,23 @@ impl ControlHandle {
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
enum CmdTy {
Unknown = 0,
#[derive(Debug)]
#[repr(usize)]
pub enum VtEventKind {
Activate,
Deactivate,
Resize,
}
impl From<u8> for CmdTy {
fn from(value: u8) -> Self {
match value {
1 => CmdTy::Activate,
2 => CmdTy::Deactivate,
3 => CmdTy::Resize,
_ => CmdTy::Unknown,
}
}
}
#[derive(Debug)]
pub enum Cmd {
// TODO(andypython): #VT should really need to be a `u8`.
Activate {
vt: usize,
},
#[repr(C)]
pub struct VtEvent {
pub kind: VtEventKind,
pub vt: usize,
Deactivate(usize /* #VT */),
Resize {
// TODO(andypython): do we really need to pass the VT here?
vt: usize,
width: u32,
height: u32,
stride: u32,
},
}
impl Cmd {
fn ty(&self) -> CmdTy {
match self {
Cmd::Activate { .. } => CmdTy::Activate,
Cmd::Deactivate(_) => CmdTy::Deactivate,
Cmd::Resize { .. } => CmdTy::Resize,
}
}
}
pub fn send_comand(file: &mut File, command: Cmd) -> Result<(), libredox::error::Error> {
use std::os::fd::AsRawFd;
let mut result = vec![];
result.push(command.ty() as u8);
match command {
Cmd::Activate { vt } => {
let cmd = VtActivate { vt };
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,
height,
stride,
} => {
result.extend_from_slice(&vt.to_le_bytes());
result.extend(width.to_le_bytes());
result.extend(height.to_le_bytes());
result.extend(stride.to_le_bytes());
}
};
let written = libredox::call::write(file.as_raw_fd() as usize, &result)?;
// XXX: Ensure all of the data is written.
assert_eq!(written, result.len());
Ok(())
}
pub fn parse_command(buffer: &[u8]) -> Option<Cmd> {
const U32_SIZE: usize = core::mem::size_of::<u32>();
const USIZE_SIZE: usize = core::mem::size_of::<usize>();
let mut parser = buffer.iter().cloned();
let command = CmdTy::from(parser.next()?);
let vt = usize::from_le_bytes(parser.next_chunk::<USIZE_SIZE>().ok()?);
match command {
CmdTy::Activate => {
let cmd = unsafe { &*buffer.as_ptr().offset(1).cast::<VtActivate>() };
Some(Cmd::Activate { vt: cmd.vt })
}
CmdTy::Deactivate => Some(Cmd::Deactivate(vt)),
CmdTy::Resize => {
let width = parser.next_chunk::<U32_SIZE>().ok()?;
let height = parser.next_chunk::<U32_SIZE>().ok()?;
let stride = parser.next_chunk::<U32_SIZE>().ok()?;
Some(Cmd::Resize {
vt,
width: u32::from_le_bytes(width),
height: u32::from_le_bytes(height),
stride: u32::from_le_bytes(stride),
})
}
CmdTy::Unknown => None,
}
pub width: u32,
pub height: u32,
pub stride: u32,
}
#[repr(packed)]
+172 -89
View File
@@ -11,11 +11,12 @@
//! Read events from `input:consumer`. Optionally, set the `EVENT_READ` flag to be notified when
//! events are available.
use core::mem::size_of;
use std::collections::BTreeMap;
use std::fs::File;
use std::mem::transmute;
use std::sync::atomic::{AtomicUsize, Ordering};
use inputd::{Cmd, VtActivate};
use inputd::{VtActivate, VtEvent, VtEventKind};
use redox_scheme::{RequestKind, SchemeMut, SignalBehavior, Socket, V2};
@@ -31,6 +32,9 @@ enum Handle {
vt: usize,
},
Display {
events: EventFlags,
pending: Vec<VtEvent>,
notified: bool,
device: String,
},
Control,
@@ -44,28 +48,14 @@ impl Handle {
struct Vt {
display: String,
index: usize,
/// This is *required* to be lazily initialized since opening the handle to the display
/// requires the system call to return first. Otherwise, it will block indefinitely.
handle_file: Option<File>,
}
impl Vt {
fn new(display: impl Into<String>, index: usize) -> Self {
fn new(display: impl Into<String>) -> Self {
Self {
display: display.into(),
handle_file: None,
index,
}
}
fn send_command(&mut self, cmd: Cmd) -> Result<(), libredox::error::Error> {
let handle_file = self
.handle_file
.get_or_insert_with(|| File::open(format!("/scheme/{}/handle", self.display)).unwrap());
inputd::send_comand(handle_file, cmd)
}
}
struct InputScheme {
@@ -78,7 +68,6 @@ struct InputScheme {
super_key: bool,
active_vt: Option<usize>,
pending_activate: Option<VtActivate>,
has_new_events: bool,
}
@@ -93,10 +82,67 @@ impl InputScheme {
super_key: false,
active_vt: None,
pending_activate: None,
has_new_events: false,
}
}
fn switch_vt(&mut self, new_active: usize) -> syscall::Result<()> {
if let Some(active_vt) = self.active_vt {
if new_active == active_vt {
return Ok(());
}
}
if !self.vts.contains_key(&new_active) {
log::warn!("inputd: switch to non-existent VT #{new_active} was requested");
return Ok(());
}
log::info!(
"inputd: switching from VT #{} to VT #{new_active}",
self.active_vt.unwrap_or(0)
);
for handle in self.handles.values_mut() {
match handle {
Handle::Display {
pending,
notified,
device,
..
} => {
if let Some(active_vt) = self.active_vt {
if &self.vts[&active_vt].display == &*device {
pending.push(VtEvent {
kind: VtEventKind::Deactivate,
vt: self.active_vt.unwrap(),
width: 0,
height: 0,
stride: 0,
});
*notified = false;
}
}
if &self.vts[&new_active].display == &*device {
pending.push(VtEvent {
kind: VtEventKind::Activate,
vt: new_active,
width: 0,
height: 0,
stride: 0,
});
*notified = false;
}
}
_ => continue,
}
}
self.active_vt = Some(new_active);
Ok(())
}
}
impl SchemeMut for InputScheme {
@@ -123,7 +169,12 @@ impl SchemeMut for InputScheme {
}
"handle" => {
let display = path_parts.collect::<Vec<_>>().join(".");
Handle::Display { device: display }
Handle::Display {
events: EventFlags::empty(),
pending: Vec::new(),
notified: false,
device: display,
}
}
"control" => Handle::Control,
@@ -175,12 +226,32 @@ impl SchemeMut for InputScheme {
Ok(copy)
}
Handle::Display { device } => {
assert!(buf.is_empty());
Handle::Display {
pending, device, ..
} => {
// FIXME Create new VT through a write instead and return a NewVt event on read
// This allows also returning events for VT (de)activation from the display handle
// rather than pushing them to the graphics driver.
if buf.is_empty() {
// Trying to do an empty read creates a new VT.
let vt = self.next_vt_id.fetch_add(1, Ordering::SeqCst);
log::info!("inputd: created VT #{vt} for {device}");
self.vts.insert(vt, Vt::new(device.clone()));
Ok(vt)
} else if buf.len() % size_of::<VtEvent>() == 0 {
let copy = core::cmp::min(pending.len(), buf.len() / size_of::<VtEvent>());
let vt = self.next_vt_id.fetch_add(1, Ordering::SeqCst);
self.vts.insert(vt, Vt::new(device.clone(), vt));
Ok(vt)
for (i, event) in pending.drain(..copy).enumerate() {
buf[i * size_of::<VtEvent>()..(i + 1) * size_of::<VtEvent>()]
.copy_from_slice(&unsafe {
transmute::<VtEvent, [u8; size_of::<VtEvent>()]>(event)
});
}
Ok(copy * size_of::<VtEvent>())
} else {
log::error!("inputd: display tried to read incorrectly sized event");
return Err(SysError::new(EINVAL));
}
}
Handle::Producer => {
@@ -207,7 +278,7 @@ impl SchemeMut for InputScheme {
match handle {
Handle::Control => {
if buf.len() != core::mem::size_of::<VtActivate>() {
if buf.len() != size_of::<VtActivate>() {
log::error!("inputd: control tried to write incorrectly sized command");
return Err(SysError::new(EINVAL));
}
@@ -215,7 +286,7 @@ impl SchemeMut for InputScheme {
// SAFETY: We have verified the size of the buffer above.
let cmd = unsafe { &*buf.as_ptr().cast::<VtActivate>() };
self.pending_activate = Some(cmd.clone());
self.switch_vt(cmd.vt)?;
return Ok(buf.len());
}
@@ -238,11 +309,11 @@ impl SchemeMut for InputScheme {
let events = unsafe {
core::slice::from_raw_parts(
buf.as_ptr() as *const Event,
buf.len() / core::mem::size_of::<Event>(),
buf.len() / size_of::<Event>(),
)
};
'out: for event in events.iter() {
for event in events.iter() {
let mut new_active_opt = None;
match event.to_option() {
EventOption::Key(key_event) => match key_event.scancode {
@@ -270,42 +341,41 @@ impl SchemeMut for InputScheme {
},
EventOption::Resize(resize_event) => {
let active_vt = self.vts.get_mut(&self.active_vt.unwrap()).unwrap();
active_vt.send_command(Cmd::Resize {
vt: active_vt.index,
width: resize_event.width,
height: resize_event.height,
for handle in self.handles.values_mut() {
match handle {
Handle::Display {
pending,
notified,
device,
..
} => {
if &self.vts[&self.active_vt.unwrap()].display == &*device {
pending.push(VtEvent {
kind: VtEventKind::Resize,
vt: self.active_vt.unwrap(),
width: resize_event.width,
height: resize_event.height,
// TODO(andypython): Figure out how to get the stride.
stride: resize_event.width,
})?;
// TODO(andypython): Figure out how to get the stride.
stride: resize_event.width,
});
*notified = false;
}
}
_ => continue,
}
}
}
_ => continue,
}
if let Some(new_active) = new_active_opt {
if new_active == self.vts[&self.active_vt.unwrap()].index {
continue 'out;
}
if self.vts.contains_key(&new_active) {
let active_vt = self.vts.get_mut(&self.active_vt.unwrap()).unwrap();
active_vt.send_command(Cmd::Deactivate(active_vt.index))?;
}
if let Some(new_active) = self.vts.get_mut(&new_active) {
new_active.send_command(Cmd::Activate {
vt: new_active.index,
})?;
self.active_vt = Some(new_active.index);
} else {
log::warn!("inputd: switch to non-existent VT #{new_active} was requested");
}
self.switch_vt(new_active)?;
}
}
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
assert!(handle.is_producer());
let active_vt = self.active_vt.unwrap();
@@ -348,8 +418,17 @@ impl SchemeMut for InputScheme {
*notified = false;
Ok(EventFlags::empty())
}
Handle::Producer | Handle::Control | Handle::Display { .. } => {
log::error!("inputd: producer, control or display tried to use an event queue");
Handle::Display {
ref mut events,
ref mut notified,
..
} => {
*events = flags;
*notified = false;
Ok(EventFlags::empty())
}
Handle::Producer | Handle::Control => {
log::error!("inputd: producer or control tried to use an event queue");
Err(SysError::new(EINVAL))
}
}
@@ -385,47 +464,51 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => unreachable!(),
}
if let Some(cmd) = scheme.pending_activate.take() {
if let Some(vt) = scheme.vts.get_mut(&cmd.vt) {
// Failing to activate a VT is not a fatal error.
if let Err(err) = vt.send_command(Cmd::Activate { vt: vt.index }) {
log::error!("inputd: failed to activate VT #{}: {err}", vt.index)
}
scheme.active_vt = Some(vt.index);
} else {
log::error!("inputd: failed to activate non-existent VT #{}", cmd.vt)
}
}
if !scheme.has_new_events {
continue;
}
for (id, handle) in scheme.handles.iter_mut() {
if let Handle::Consumer {
events,
pending,
ref mut notified,
vt,
} = handle
{
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
continue;
match handle {
Handle::Consumer {
events,
pending,
ref mut notified,
vt,
} => {
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
continue;
}
let active_vt = scheme.active_vt.unwrap();
// The activate VT is not the same as the VT that the consumer is listening to
// for events.
if active_vt != *vt {
continue;
}
// Notify the consumer that we have some events to read. Yum yum.
socket_file.post_fevent(*id, EventFlags::EVENT_READ.bits())?;
*notified = true;
}
Handle::Display {
events,
pending,
ref mut notified,
..
} => {
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
continue;
}
let active_vt = scheme.active_vt.unwrap();
// Notify the consumer that we have some events to read. Yum yum.
socket_file.post_fevent(*id, EventFlags::EVENT_READ.bits())?;
// The activate VT is not the same as the VT that the consumer is listening to
// for events.
if active_vt != *vt {
continue;
*notified = true;
}
// Notify the consumer that we have some events to read. Yum yum.
socket_file.post_fevent(*id, EventFlags::EVENT_READ.bits())?;
*notified = true;
_ => {}
}
}
}