Merge branch 'graphics_handover' into 'master'
graphics: Implement handoff from vesad to virtio-gpud See merge request redox-os/drivers!213
This commit is contained in:
@@ -60,6 +60,7 @@ impl Drop for DisplayMap {
|
||||
|
||||
enum DisplayCommand {
|
||||
Resize { width: usize, height: usize },
|
||||
ReopenForHandoff { display_path: String },
|
||||
SyncRects(Vec<Damage>),
|
||||
}
|
||||
|
||||
@@ -104,21 +105,39 @@ impl Display {
|
||||
}
|
||||
}
|
||||
}
|
||||
DisplayCommand::SyncRects(sync_rects) => {
|
||||
for sync_rect in sync_rects {
|
||||
unsafe {
|
||||
libredox::call::write(
|
||||
display_file.as_raw_fd() as usize,
|
||||
slice::from_raw_parts(
|
||||
&sync_rect as *const Damage as *const u8,
|
||||
mem::size_of::<Damage>(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
DisplayCommand::ReopenForHandoff { display_path } => {
|
||||
eprintln!("fbcond: Performing handoff for '{display_path}'");
|
||||
|
||||
let (mut new_display_file, width, height) =
|
||||
Self::open_display(&display_path).unwrap();
|
||||
|
||||
eprintln!("fbcond: Opened new display '{display_path}'");
|
||||
|
||||
match display_fd_map(width, height, &mut new_display_file) {
|
||||
Ok(ok) => {
|
||||
*map_clone.lock().unwrap() = ok;
|
||||
display_file = new_display_file;
|
||||
|
||||
eprintln!("fbcond: Mapped new display '{display_path}'");
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"failed to resize display to {}x{}: {}",
|
||||
width, height, err
|
||||
);
|
||||
}
|
||||
}
|
||||
libredox::call::fsync(display_file.as_raw_fd() as usize).unwrap();
|
||||
}
|
||||
DisplayCommand::SyncRects(sync_rects) => unsafe {
|
||||
libredox::call::write(
|
||||
display_file.as_raw_fd() as usize,
|
||||
slice::from_raw_parts(
|
||||
sync_rects.as_ptr() as *const u8,
|
||||
sync_rects.len() * mem::size_of::<Damage>(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -130,6 +149,20 @@ impl Display {
|
||||
})
|
||||
}
|
||||
|
||||
/// Re-open the display after a handoff.
|
||||
///
|
||||
/// Once re-opening is finished, you must call [`resize`] to map the new framebuffer.
|
||||
///
|
||||
/// Warning: This must be called in a background thread to avoid a deadlock when the
|
||||
/// graphics driver (indirectly) writes logs to fbcond.
|
||||
pub fn reopen_for_handoff(&mut self) {
|
||||
let display_path = Self::display_path(&mut self.input_handle).unwrap();
|
||||
|
||||
self.cmd_tx
|
||||
.send(DisplayCommand::ReopenForHandoff { display_path })
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn display_path(input_handle: &mut File) -> io::Result<String> {
|
||||
let mut buffer = [0; 1024];
|
||||
let fd = input_handle.as_raw_fd();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![feature(io_error_more)]
|
||||
|
||||
use event::EventQueue;
|
||||
use orbclient::Event;
|
||||
use std::fs::{File, OpenOptions};
|
||||
@@ -55,7 +57,9 @@ fn inner(daemon: redox_daemon::Daemon, vt_ids: &[usize]) -> ! {
|
||||
|
||||
let mut inputd_control_handle = inputd::ControlHandle::new().unwrap();
|
||||
|
||||
libredox::call::setrens(0, 0).expect("fbcond: failed to enter null namespace");
|
||||
// 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");
|
||||
|
||||
daemon.ready().expect("failed to notify parent");
|
||||
|
||||
@@ -127,6 +131,9 @@ fn handle_event(
|
||||
Err(err) if err.kind() == ErrorKind::WouldBlock => {
|
||||
break;
|
||||
}
|
||||
Err(err) if err.kind() == ErrorKind::StaleNetworkFileHandle => {
|
||||
vt.handle_handoff();
|
||||
}
|
||||
|
||||
Ok(count) => {
|
||||
let events = &mut events[..count];
|
||||
|
||||
@@ -119,6 +119,10 @@ impl TextScreen {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_handoff(&mut self) {
|
||||
self.display.reopen_for_handoff();
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: usize, height: usize) {
|
||||
self.display
|
||||
.resize(width.try_into().unwrap(), height.try_into().unwrap());
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct DisplayScheme {
|
||||
|
||||
impl DisplayScheme {
|
||||
pub fn new(framebuffers: Vec<FrameBuffer>, spec: &[()]) -> DisplayScheme {
|
||||
let mut inputd_handle = inputd::DisplayHandle::new("vesa").unwrap();
|
||||
let mut inputd_handle = inputd::DisplayHandle::new_early("vesa").unwrap();
|
||||
|
||||
let mut vts = BTreeMap::<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>::new();
|
||||
|
||||
@@ -213,7 +213,7 @@ impl SchemeBlockMut for DisplayScheme {
|
||||
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]);
|
||||
screen.redraw(&mut self.framebuffers[handle.screen.0]);
|
||||
}
|
||||
return Ok(Some(0));
|
||||
}
|
||||
@@ -260,11 +260,13 @@ impl SchemeBlockMut for DisplayScheme {
|
||||
|
||||
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]);
|
||||
screen
|
||||
.write(buf, Some(&mut self.framebuffers[handle.screen.0]))
|
||||
.map(|count| Some(count))
|
||||
} else {
|
||||
screen.write(buf, None).map(|count| Some(count))
|
||||
}
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ pub struct GraphicScreen {
|
||||
pub height: usize,
|
||||
pub offscreen: OffscreenBuffer,
|
||||
pub input: VecDeque<Event>,
|
||||
pub sync_rects: Vec<Damage>,
|
||||
}
|
||||
|
||||
impl GraphicScreen {
|
||||
@@ -24,7 +23,6 @@ impl GraphicScreen {
|
||||
height,
|
||||
offscreen: OffscreenBuffer::new(width * height),
|
||||
input: VecDeque::new(),
|
||||
sync_rects: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +103,7 @@ impl GraphicScreen {
|
||||
!self.input.is_empty()
|
||||
}
|
||||
|
||||
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
pub fn write(&mut self, buf: &[u8], framebuffer: Option<&mut FrameBuffer>) -> Result<usize> {
|
||||
let sync_rects = unsafe {
|
||||
slice::from_raw_parts(
|
||||
buf.as_ptr() as *const Damage,
|
||||
@@ -113,30 +111,35 @@ impl GraphicScreen {
|
||||
)
|
||||
};
|
||||
|
||||
self.sync_rects.extend_from_slice(sync_rects);
|
||||
if let Some(framebuffer) = framebuffer {
|
||||
self.sync(framebuffer, sync_rects);
|
||||
}
|
||||
|
||||
Ok(sync_rects.len() * mem::size_of::<Damage>())
|
||||
}
|
||||
|
||||
pub fn sync(&mut self, framebuffer: &mut FrameBuffer) {
|
||||
for sync_rect in self.sync_rects.drain(..) {
|
||||
let x = sync_rect.x.try_into().unwrap_or(0);
|
||||
let y = sync_rect.y.try_into().unwrap_or(0);
|
||||
let w = sync_rect.width.try_into().unwrap_or(0);
|
||||
let h = sync_rect.height.try_into().unwrap_or(0);
|
||||
pub fn sync(&mut self, framebuffer: &mut FrameBuffer, sync_rects: &[Damage]) {
|
||||
for sync_rect in sync_rects {
|
||||
let sync_rect = sync_rect.clip(
|
||||
self.height.try_into().unwrap(),
|
||||
self.width.try_into().unwrap(),
|
||||
);
|
||||
|
||||
let start_y = cmp::min(self.height, y);
|
||||
let end_y = cmp::min(self.height, y + h);
|
||||
let start_x: usize = sync_rect.x.try_into().unwrap_or(0);
|
||||
let start_y: usize = sync_rect.y.try_into().unwrap_or(0);
|
||||
let w: usize = sync_rect.width.try_into().unwrap_or(0);
|
||||
let h: usize = sync_rect.height.try_into().unwrap_or(0);
|
||||
|
||||
let start_x = cmp::min(self.width, x);
|
||||
let row_pixel_count = cmp::min(self.width, x + w) - start_x;
|
||||
let end_y = start_y + h;
|
||||
|
||||
let row_pixel_count = w;
|
||||
|
||||
let mut offscreen_ptr = self.offscreen.as_mut_ptr();
|
||||
let mut onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable
|
||||
|
||||
unsafe {
|
||||
offscreen_ptr = offscreen_ptr.add(y * self.width + start_x);
|
||||
onscreen_ptr = onscreen_ptr.add(y * framebuffer.stride + start_x);
|
||||
offscreen_ptr = offscreen_ptr.add(start_y * self.width + start_x);
|
||||
onscreen_ptr = onscreen_ptr.add(start_y * framebuffer.stride + start_x);
|
||||
|
||||
let mut rows = end_y - start_y;
|
||||
while rows > 0 {
|
||||
@@ -152,12 +155,14 @@ impl GraphicScreen {
|
||||
pub fn redraw(&mut self, framebuffer: &mut FrameBuffer) {
|
||||
let width = self.width.try_into().unwrap();
|
||||
let height = self.height.try_into().unwrap();
|
||||
self.sync_rects.push(Damage {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
self.sync(framebuffer);
|
||||
self.sync(
|
||||
framebuffer,
|
||||
&[Damage {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,12 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use event::{user_data, EventQueue};
|
||||
use libredox::errno::EAGAIN;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
use redox_scheme::{RequestKind, SignalBehavior, Socket, V2};
|
||||
use virtio_core::transport::{self, Queue};
|
||||
use virtio_core::utils::VolatileCell;
|
||||
use virtio_core::MSIX_PRIMARY_VECTOR;
|
||||
|
||||
@@ -171,7 +169,7 @@ impl Default for ControlHeader {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GpuRect {
|
||||
pub x: u32,
|
||||
@@ -394,7 +392,7 @@ pub struct XferToHost2d {
|
||||
}
|
||||
|
||||
impl XferToHost2d {
|
||||
pub fn new(resource_id: ResourceId, rect: GpuRect) -> Self {
|
||||
pub fn new(resource_id: ResourceId, rect: GpuRect, offset: u64) -> Self {
|
||||
Self {
|
||||
header: ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::TransferToHost2d),
|
||||
@@ -402,8 +400,8 @@ impl XferToHost2d {
|
||||
},
|
||||
|
||||
rect,
|
||||
offset,
|
||||
resource_id,
|
||||
offset: 0,
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
@@ -411,19 +409,6 @@ impl XferToHost2d {
|
||||
|
||||
static DEVICE: spin::Once<virtio_core::Device> = spin::Once::new();
|
||||
|
||||
fn reinit(control_queue: Arc<Queue>, cursor_queue: Arc<Queue>) -> Result<(), transport::Error> {
|
||||
let device = DEVICE.get().unwrap();
|
||||
|
||||
virtio_core::reinit(device)?;
|
||||
device.transport.finalize_features();
|
||||
|
||||
device.transport.reinit_queue(control_queue.clone());
|
||||
device.transport.reinit_queue(cursor_queue.clone());
|
||||
|
||||
device.transport.run_device();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
let mut pcid_handle = PciFunctionHandle::connect_default()?;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::{dma::Dma, sgl};
|
||||
@@ -16,7 +16,7 @@ use virtio_core::utils::VolatileCell;
|
||||
|
||||
use crate::*;
|
||||
|
||||
impl Into<GpuRect> for &Damage {
|
||||
impl Into<GpuRect> for Damage {
|
||||
fn into(self) -> GpuRect {
|
||||
GpuRect {
|
||||
x: self.x as u32,
|
||||
@@ -40,8 +40,6 @@ pub struct Display<'a> {
|
||||
height: u32,
|
||||
|
||||
id: usize,
|
||||
|
||||
is_reseted: AtomicBool,
|
||||
}
|
||||
|
||||
impl<'a> Display<'a> {
|
||||
@@ -64,28 +62,9 @@ impl<'a> Display<'a> {
|
||||
transport,
|
||||
|
||||
id,
|
||||
|
||||
is_reseted: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(&self, vt: usize) -> Result<(), Error> {
|
||||
if !self.is_reseted.load(Ordering::SeqCst) {
|
||||
// The device is already initialized.
|
||||
self.set_scanout(vt).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.is_reseted.store(false, Ordering::SeqCst);
|
||||
|
||||
log::info!("virtio-gpu: initializing GPU after a reset");
|
||||
|
||||
crate::reinit(self.control_queue.clone(), self.cursor_queue.clone())?;
|
||||
self.set_scanout(vt).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_fpath(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let path = format!("display.virtio-gpu:3.0/{}/{}", self.width, self.height);
|
||||
|
||||
@@ -198,59 +177,47 @@ impl<'a> Display<'a> {
|
||||
}
|
||||
|
||||
/// If `damage` is `None`, the entire screen is flushed.
|
||||
async fn flush(&self, vt: usize, damage: Option<&Damage>) -> Result<(), Error> {
|
||||
if vt != *self.active_vt.borrow() {
|
||||
async fn flush(&self, vt: usize, damage: Option<&[Damage]>) -> Result<(), Error> {
|
||||
let Some(&res_id) = self.vts_res.borrow().get(&vt) else {
|
||||
// The resource is not yet created. Ignore the damage. We will write the entire backing
|
||||
// storage to the resource once we create the resource, which is equivalent to damaging
|
||||
// the entire resource.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let damage = if let Some(damage) = damage {
|
||||
damage.into()
|
||||
} else {
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
}
|
||||
};
|
||||
|
||||
let req = Dma::new(XferToHost2d::new(
|
||||
self.vts_res.borrow()[&vt],
|
||||
res_id,
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
},
|
||||
0,
|
||||
))?;
|
||||
let header = self.send_request(req).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
self.flush_resource(ResourceFlush::new(
|
||||
self.vts_res.borrow()[&vt],
|
||||
damage.clone(),
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This detaches any backing pages from the display and unrefs the resource. Also resets the
|
||||
/// device, which is required to go back to legacy mode.
|
||||
async fn detach(&self) -> Result<(), Error> {
|
||||
for (_vt, res_id) in self.vts_res.borrow_mut().drain() {
|
||||
let request = Dma::new(DetachBacking::new(res_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let request = Dma::new(ResourceUnref::new(res_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
if let Some(damage) = damage {
|
||||
for damage in damage {
|
||||
self.flush_resource(ResourceFlush::new(
|
||||
res_id,
|
||||
damage.clip(self.width as i32, self.height as i32).into(),
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
self.flush_resource(ResourceFlush::new(
|
||||
res_id,
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
},
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Go back to legacy mode.
|
||||
self.transport.reset();
|
||||
self.is_reseted.store(true, Ordering::SeqCst);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -283,9 +250,7 @@ impl<'a> Scheme<'a> {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut inputd_handle = inputd::DisplayHandle::new("virtio-gpu").unwrap();
|
||||
// FIXME make vesad handoff control over all it's VT's instead
|
||||
inputd_handle.register_vt().unwrap();
|
||||
let inputd_handle = inputd::DisplayHandle::new("virtio-gpu").unwrap();
|
||||
|
||||
Ok(Self {
|
||||
handles: BTreeMap::new(),
|
||||
@@ -319,11 +284,6 @@ impl<'a> Scheme<'a> {
|
||||
transport.clone(),
|
||||
id,
|
||||
);
|
||||
// FIXME this is a hack to avoid breaking things while we need to co-exist with vesad
|
||||
// Somehow necessary to ensure that creating a resource on the first reinitialization
|
||||
// after this detach doesn't fail.
|
||||
display.init(1).await?;
|
||||
display.detach().await?;
|
||||
|
||||
result.push(Arc::new(display));
|
||||
}
|
||||
@@ -357,7 +317,7 @@ impl<'a> Scheme<'a> {
|
||||
for display in &self.displays {
|
||||
log::warn!("virtio-gpu: activating");
|
||||
|
||||
futures::executor::block_on(display.init(vt_event.vt)).unwrap();
|
||||
futures::executor::block_on(display.set_scanout(vt_event.vt)).unwrap();
|
||||
|
||||
*display.active_vt.borrow_mut() = vt_event.vt;
|
||||
}
|
||||
@@ -365,21 +325,7 @@ impl<'a> Scheme<'a> {
|
||||
|
||||
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();
|
||||
// }
|
||||
// nothing to do :)
|
||||
}
|
||||
|
||||
VtEventKind::Resize => {
|
||||
@@ -424,6 +370,11 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
|
||||
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
|
||||
if handle.vt != *handle.display.active_vt.borrow() {
|
||||
// This is a protection against background VT's spamming us with flush requests. We will
|
||||
// flush the resource on the next scanout anyway
|
||||
return Ok(0);
|
||||
}
|
||||
futures::executor::block_on(handle.display.flush(handle.vt, None)).unwrap();
|
||||
Ok(0)
|
||||
}
|
||||
@@ -449,23 +400,20 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
// The VT is not active and the device is reseted. Ignore the damage. We will recreate the
|
||||
// backing storage from scratch next time we initialize, which is equivalent to damaging the
|
||||
// entire buffer.
|
||||
if handle.display.is_reseted.load(Ordering::SeqCst) {
|
||||
if handle.vt != *handle.display.active_vt.borrow() {
|
||||
// This is a protection against background VT's spamming us with flush requests. We will
|
||||
// flush the resource on the next scanout anyway
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
let damages = unsafe {
|
||||
let damage = 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(handle.vt, Some(damage))).unwrap();
|
||||
}
|
||||
futures::executor::block_on(handle.display.flush(handle.vt, Some(damage))).unwrap();
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![feature(iter_next_chunk)]
|
||||
|
||||
use std::cmp;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, Read, Write};
|
||||
use std::mem::size_of;
|
||||
@@ -27,6 +28,11 @@ impl DisplayHandle {
|
||||
Ok(Self(File::open(path)?))
|
||||
}
|
||||
|
||||
pub fn new_early<S: Into<String>>(device_name: S) -> Result<Self, Error> {
|
||||
let path = format!("/scheme/input/handle_early/display/{}", device_name.into());
|
||||
Ok(Self(File::open(path)?))
|
||||
}
|
||||
|
||||
// The return value is the display identifier. It will be used to uniquely
|
||||
// identify the display on activation events.
|
||||
pub fn register_vt(&mut self) -> Result<usize, Error> {
|
||||
@@ -99,3 +105,19 @@ pub struct Damage {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl Damage {
|
||||
#[must_use]
|
||||
pub fn clip(mut self, width: i32, height: i32) -> Self {
|
||||
// Clip damage
|
||||
self.x = cmp::min(self.x, width);
|
||||
if self.x + self.width > width {
|
||||
self.width -= cmp::min(self.width, width - self.x);
|
||||
}
|
||||
self.y = cmp::min(self.y, height);
|
||||
if self.y + self.height > height {
|
||||
self.height = cmp::min(self.height, height - self.y);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
+99
-4
@@ -18,6 +18,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use inputd::{VtActivate, VtEvent, VtEventKind};
|
||||
|
||||
use libredox::errno::ESTALE;
|
||||
use redox_scheme::{RequestKind, SchemeMut, SignalBehavior, Socket, V2};
|
||||
|
||||
use orbclient::{Event, EventOption};
|
||||
@@ -28,6 +29,9 @@ enum Handle {
|
||||
Consumer {
|
||||
events: EventFlags,
|
||||
pending: Vec<u8>,
|
||||
/// We return an ESTALE error once to indicate that a handoff to a different graphics driver
|
||||
/// is necessary.
|
||||
needs_handoff: bool,
|
||||
notified: bool,
|
||||
vt: usize,
|
||||
},
|
||||
@@ -36,6 +40,8 @@ enum Handle {
|
||||
pending: Vec<VtEvent>,
|
||||
notified: bool,
|
||||
device: String,
|
||||
/// Control of all VT's gets handed over from earlyfb devices to the first non-earlyfb device.
|
||||
is_earlyfb: bool,
|
||||
},
|
||||
Control,
|
||||
}
|
||||
@@ -46,6 +52,7 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Vt {
|
||||
display: String,
|
||||
}
|
||||
@@ -69,6 +76,7 @@ struct InputScheme {
|
||||
active_vt: Option<usize>,
|
||||
|
||||
has_new_events: bool,
|
||||
maybe_perform_handoff_to: Option<String>,
|
||||
}
|
||||
|
||||
impl InputScheme {
|
||||
@@ -83,6 +91,7 @@ impl InputScheme {
|
||||
active_vt: None,
|
||||
|
||||
has_new_events: false,
|
||||
maybe_perform_handoff_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,17 +172,40 @@ impl SchemeMut for InputScheme {
|
||||
Handle::Consumer {
|
||||
events: EventFlags::empty(),
|
||||
pending: Vec::new(),
|
||||
needs_handoff: false,
|
||||
notified: false,
|
||||
vt: target,
|
||||
}
|
||||
}
|
||||
"handle" => {
|
||||
"handle_early" => {
|
||||
let display = path_parts.collect::<Vec<_>>().join(".");
|
||||
Handle::Display {
|
||||
events: EventFlags::empty(),
|
||||
pending: Vec::new(),
|
||||
notified: false,
|
||||
device: display,
|
||||
is_earlyfb: true,
|
||||
}
|
||||
}
|
||||
"handle" => {
|
||||
let display = path_parts.collect::<Vec<_>>().join(".");
|
||||
self.maybe_perform_handoff_to = Some(display.clone());
|
||||
Handle::Display {
|
||||
events: EventFlags::empty(),
|
||||
pending: if let Some(active_vt) = self.active_vt {
|
||||
vec![VtEvent {
|
||||
kind: VtEventKind::Activate,
|
||||
vt: active_vt,
|
||||
width: 0,
|
||||
height: 0,
|
||||
stride: 0,
|
||||
}]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
notified: false,
|
||||
device: display,
|
||||
is_earlyfb: false,
|
||||
}
|
||||
}
|
||||
"control" => Handle::Control,
|
||||
@@ -216,7 +248,17 @@ impl SchemeMut for InputScheme {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
match handle {
|
||||
Handle::Consumer { pending, .. } => {
|
||||
Handle::Consumer {
|
||||
pending,
|
||||
needs_handoff,
|
||||
..
|
||||
} => {
|
||||
if *needs_handoff {
|
||||
*needs_handoff = false;
|
||||
// Indicates that handoff to a new graphics driver is necessary.
|
||||
return Err(SysError::new(ESTALE));
|
||||
}
|
||||
|
||||
let copy = core::cmp::min(pending.len(), buf.len());
|
||||
|
||||
for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
@@ -464,6 +506,55 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => unreachable!(),
|
||||
}
|
||||
|
||||
if let Some(display) = scheme.maybe_perform_handoff_to.take() {
|
||||
let early_displays = scheme
|
||||
.handles
|
||||
.values()
|
||||
.filter_map(|handle| match handle {
|
||||
Handle::Display {
|
||||
device,
|
||||
is_earlyfb: true,
|
||||
..
|
||||
} => Some(&**device),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let vts = scheme
|
||||
.vts
|
||||
.iter_mut()
|
||||
.filter_map(|(&i, vt)| {
|
||||
if early_displays.contains(&&*vt.display) {
|
||||
vt.display = display.clone();
|
||||
|
||||
scheme.has_new_events = true;
|
||||
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for handle in scheme.handles.values_mut() {
|
||||
match handle {
|
||||
Handle::Consumer {
|
||||
needs_handoff,
|
||||
notified,
|
||||
vt,
|
||||
..
|
||||
} => {
|
||||
if !vts.contains(vt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*needs_handoff = true;
|
||||
*notified = false;
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !scheme.has_new_events {
|
||||
continue;
|
||||
}
|
||||
@@ -473,10 +564,14 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
Handle::Consumer {
|
||||
events,
|
||||
pending,
|
||||
needs_handoff,
|
||||
ref mut notified,
|
||||
vt,
|
||||
} => {
|
||||
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||
if (!*needs_handoff && pending.is_empty())
|
||||
|| *notified
|
||||
|| !events.contains(EventFlags::EVENT_READ)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -484,7 +579,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
|
||||
// The activate VT is not the same as the VT that the consumer is listening to
|
||||
// for events.
|
||||
if active_vt != *vt {
|
||||
if !*needs_handoff && active_vt != *vt {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user