Merge branch 'fbcond_nonblocking' into 'master'

fbcond: Make sure we never block on the graphics driver

See merge request redox-os/drivers!211
This commit is contained in:
Jeremy Soller
2024-12-22 20:13:22 +00:00
4 changed files with 168 additions and 123 deletions
+109 -64
View File
@@ -1,7 +1,10 @@
use inputd::Damage;
use libredox::flag;
use std::fs::OpenOptions;
use std::mem;
use std::os::unix::fs::OpenOptionsExt;
use std::sync::mpsc::{self, Sender};
use std::sync::{Arc, Mutex};
use std::{
fs::File,
io,
@@ -11,20 +14,14 @@ use std::{
};
use syscall::{O_CLOEXEC, O_NONBLOCK, O_RDWR};
// Keep synced with vesad
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct SyncRect {
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
fn display_fd_map(width: usize, height: usize, display_fd: usize) -> syscall::Result<*mut [u32]> {
fn display_fd_map(
width: usize,
height: usize,
display_file: &mut File,
) -> syscall::Result<DisplayMap> {
unsafe {
let display_ptr = libredox::call::mmap(libredox::call::MmapArgs {
fd: display_fd,
fd: display_file.as_raw_fd() as usize,
offset: 0,
length: (width * height * 4),
prot: flag::PROT_READ | flag::PROT_WRITE,
@@ -32,7 +29,11 @@ fn display_fd_map(width: usize, height: usize, display_fd: usize) -> syscall::Re
addr: core::ptr::null_mut(),
})?;
let display_slice = slice::from_raw_parts_mut(display_ptr as *mut u32, width * height);
Ok(display_slice)
Ok(DisplayMap {
offscreen: display_slice,
width,
height,
})
}
}
@@ -40,34 +41,111 @@ unsafe fn display_fd_unmap(image: *mut [u32]) {
let _ = libredox::call::munmap(image as *mut (), image.len());
}
pub struct Display {
pub input_handle: File,
pub display_file: File,
pub struct DisplayMap {
pub offscreen: *mut [u32],
pub width: usize,
pub height: usize,
}
unsafe impl Send for DisplayMap {}
unsafe impl Sync for DisplayMap {}
impl Drop for DisplayMap {
fn drop(&mut self) {
unsafe {
display_fd_unmap(self.offscreen);
}
}
}
enum DisplayCommand {
Resize { width: usize, height: usize },
SyncRects(Vec<Damage>),
}
pub struct Display {
pub input_handle: File,
cmd_tx: Sender<DisplayCommand>,
pub map: Arc<Mutex<DisplayMap>>,
}
impl Display {
pub fn open_vt(vt: usize) -> io::Result<Self> {
let mut buffer = [0; 1024];
let input_handle = OpenOptions::new()
let mut input_handle = OpenOptions::new()
.read(true)
.custom_flags(O_NONBLOCK as i32)
.open(format!("/scheme/input/consumer/{vt}"))?;
let fd = input_handle.as_raw_fd();
let display_path = Self::display_path(&mut input_handle)?;
let (mut display_file, width, height) = Self::open_display(&display_path)?;
let map = Arc::new(Mutex::new(
display_fd_map(width, height, &mut display_file)
.unwrap_or_else(|e| panic!("failed to map display '{display_path}: {e}")),
));
let (cmd_tx, cmd_rx) = mpsc::channel();
let map_clone = map.clone();
std::thread::spawn(move || {
while let Ok(cmd) = cmd_rx.recv() {
match cmd {
DisplayCommand::Resize { width, height } => {
match display_fd_map(width, height, &mut display_file) {
Ok(ok) => {
*map_clone.lock().unwrap() = ok;
}
Err(err) => {
eprintln!(
"failed to resize display to {}x{}: {}",
width, height, err
);
}
}
}
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();
}
}
libredox::call::fsync(display_file.as_raw_fd() as usize).unwrap();
}
}
}
});
Ok(Self {
input_handle,
cmd_tx,
map,
})
}
fn display_path(input_handle: &mut File) -> io::Result<String> {
let mut buffer = [0; 1024];
let fd = input_handle.as_raw_fd();
let written = libredox::call::fpath(fd as usize, &mut buffer)
.expect("init: failed to get the path to the display device");
assert!(written <= buffer.len());
let display_path =
std::str::from_utf8(&buffer[..written]).expect("init: display path UTF-8 check failed");
Ok(std::str::from_utf8(&buffer[..written])
.expect("init: display path UTF-8 check failed")
.to_owned())
}
fn open_display(display_path: &str) -> io::Result<(File, usize, usize)> {
let display_file =
libredox::call::open(display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
libredox::call::open(&display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
.map(|socket| unsafe { File::from_raw_fd(socket as RawFd) })
.unwrap_or_else(|err| {
panic!("failed to open display {}: {}", display_path, err);
@@ -84,15 +162,7 @@ impl Display {
let path = Self::url_parts(&url)?;
let (width, height) = Self::parse_display_path(path);
let offscreen_buffer = display_fd_map(width, height, display_file.as_raw_fd() as usize)
.unwrap_or_else(|e| panic!("failed to map display '{display_path}: {e}"));
Ok(Self {
input_handle,
display_file,
offscreen: offscreen_buffer,
width,
height,
})
Ok((display_file, width, height))
}
fn url_parts(url: &str) -> io::Result<&str> {
@@ -121,39 +191,14 @@ impl Display {
}
pub fn resize(&mut self, width: usize, height: usize) {
match display_fd_map(width, height, self.display_file.as_raw_fd() as usize) {
Ok(ok) => {
unsafe {
display_fd_unmap(self.offscreen);
}
self.offscreen = ok;
self.width = width;
self.height = height;
}
Err(err) => {
eprintln!("failed to resize display to {}x{}: {}", width, height, err);
}
}
self.cmd_tx
.send(DisplayCommand::Resize { width, height })
.unwrap();
}
pub fn sync_rect(&mut self, sync_rect: SyncRect) -> syscall::Result<()> {
unsafe {
libredox::call::write(
self.display_file.as_raw_fd().as_raw_fd() as usize,
slice::from_raw_parts(
&sync_rect as *const SyncRect as *const u8,
mem::size_of::<SyncRect>(),
),
)?;
Ok(())
}
}
}
impl Drop for Display {
fn drop(&mut self) {
unsafe {
display_fd_unmap(self.offscreen);
}
pub fn sync_rects(&mut self, sync_rects: Vec<Damage>) {
self.cmd_tx
.send(DisplayCommand::SyncRects(sync_rects))
.unwrap();
}
}
+47 -40
View File
@@ -2,13 +2,13 @@ extern crate ransid;
use std::collections::{BTreeSet, VecDeque};
use std::convert::{TryFrom, TryInto};
use std::os::fd::AsRawFd;
use std::{cmp, ptr};
use inputd::Damage;
use orbclient::{Event, EventOption, FONT};
use syscall::error::*;
use crate::display::{Display, SyncRect};
use crate::display::{Display, DisplayMap};
pub struct TextScreen {
console: ransid::Console,
@@ -21,7 +21,8 @@ pub struct TextScreen {
impl TextScreen {
pub fn new(display: Display) -> TextScreen {
TextScreen {
console: ransid::Console::new(display.width / 8, display.height / 16),
// Width and height will be filled in on the next write to the console
console: ransid::Console::new(0, 0),
display,
changed: BTreeSet::new(),
ctrl: false,
@@ -30,16 +31,16 @@ impl TextScreen {
}
/// Draw a rectangle
fn rect(display: &mut Display, x: usize, y: usize, w: usize, h: usize, color: u32) {
let start_y = cmp::min(display.height, y);
let end_y = cmp::min(display.height, y + h);
fn rect(map: &mut DisplayMap, x: usize, y: usize, w: usize, h: usize, color: u32) {
let start_y = cmp::min(map.height, y);
let end_y = cmp::min(map.height, y + h);
let start_x = cmp::min(display.width, x);
let len = cmp::min(display.width, x + w) - start_x;
let start_x = cmp::min(map.width, x);
let len = cmp::min(map.width, x + w) - start_x;
let mut offscreen_ptr = display.offscreen as *mut u8 as usize;
let mut offscreen_ptr = map.offscreen as *mut u8 as usize;
let stride = display.width * 4;
let stride = map.width * 4;
let offset = y * stride + start_x * 4;
offscreen_ptr += offset;
@@ -57,16 +58,16 @@ impl TextScreen {
}
/// Invert a rectangle
fn invert(display: &mut Display, x: usize, y: usize, w: usize, h: usize) {
let start_y = cmp::min(display.height, y);
let end_y = cmp::min(display.height, y + h);
fn invert(map: &mut DisplayMap, x: usize, y: usize, w: usize, h: usize) {
let start_y = cmp::min(map.height, y);
let end_y = cmp::min(map.height, y + h);
let start_x = cmp::min(display.width, x);
let len = cmp::min(display.width, x + w) - start_x;
let start_x = cmp::min(map.width, x);
let len = cmp::min(map.width, x + w) - start_x;
let mut offscreen_ptr = display.offscreen as *mut u8 as usize;
let mut offscreen_ptr = map.offscreen as *mut u8 as usize;
let stride = display.width * 4;
let stride = map.width * 4;
let offset = y * stride + start_x * 4;
offscreen_ptr += offset;
@@ -90,7 +91,7 @@ impl TextScreen {
/// Draw a character
fn char(
display: &mut Display,
map: &mut DisplayMap,
x: usize,
y: usize,
character: char,
@@ -98,8 +99,8 @@ impl TextScreen {
_bold: bool,
_italic: bool,
) {
if x + 8 <= display.width && y + 16 <= display.height {
let mut dst = display.offscreen as *mut u8 as usize + (y * display.width + x) * 4;
if x + 8 <= map.width && y + 16 <= map.height {
let mut dst = map.offscreen as *mut u8 as usize + (y * map.width + x) * 4;
let font_i = 16 * (character as usize);
if font_i + 16 <= FONT.len() {
@@ -112,7 +113,7 @@ impl TextScreen {
}
}
}
dst += display.width * 4;
dst += map.width * 4;
}
}
}
@@ -121,8 +122,6 @@ impl TextScreen {
pub fn resize(&mut self, width: usize, height: usize) {
self.display
.resize(width.try_into().unwrap(), height.try_into().unwrap());
self.console.state.w = width / 8;
self.console.state.h = height / 16;
}
pub fn input(&mut self, event: &Event) {
@@ -223,18 +222,23 @@ impl TextScreen {
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
let mut map = self.display.map.lock().unwrap();
self.console.state.w = map.width / 8;
self.console.state.h = map.height / 16;
self.console.state.bottom_margin = (map.height / 16).saturating_sub(1);
if self.console.state.cursor
&& self.console.state.x < self.console.state.w
&& self.console.state.y < self.console.state.h
{
let x = self.console.state.x;
let y = self.console.state.y;
Self::invert(&mut self.display, x * 8, y * 16, 8, 16);
Self::invert(&mut map, x * 8, y * 16, 8, 16);
self.changed.insert(y);
}
{
let display = &mut self.display;
let changed = &mut self.changed;
let input = &mut self.input;
self.console.write(buf, |event| match event {
@@ -246,14 +250,14 @@ impl TextScreen {
bold,
..
} => {
Self::char(display, x * 8, y * 16, c, color.as_rgb(), bold, false);
Self::char(&mut map, x * 8, y * 16, c, color.as_rgb(), bold, false);
changed.insert(y);
}
ransid::Event::Input { data } => {
input.extend(data);
}
ransid::Event::Rect { x, y, w, h, color } => {
Self::rect(display, x * 8, y * 16, w * 8, h * 16, color.as_rgb());
Self::rect(&mut map, x * 8, y * 16, w * 8, h * 16, color.as_rgb());
for y2 in y..y + h {
changed.insert(y2);
}
@@ -267,8 +271,8 @@ impl TextScreen {
w,
h,
} => {
let width = display.width;
let pixels = unsafe { &mut *display.offscreen };
let width = map.width;
let pixels = unsafe { &mut *map.offscreen };
for raw_y in 0..h {
let y = if from_y > to_y { raw_y } else { h - raw_y - 1 };
@@ -306,21 +310,24 @@ impl TextScreen {
{
let x = self.console.state.x;
let y = self.console.state.y;
Self::invert(&mut self.display, x * 8, y * 16, 8, 16);
Self::invert(&mut map, x * 8, y * 16, 8, 16);
self.changed.insert(y);
}
let width = self.display.width.try_into().unwrap();
for &change in self.changed.iter() {
self.display.sync_rect(SyncRect {
x: 0,
y: i32::try_from(change).unwrap() * 16,
w: width,
h: 16,
})?;
}
let width = map.width.try_into().unwrap();
drop(map);
self.display.sync_rects(
self.changed
.iter()
.map(|&change| Damage {
x: 0,
y: i32::try_from(change).unwrap() * 16,
width,
height: 16,
})
.collect(),
);
self.changed.clear();
libredox::call::fsync(self.display.display_file.as_raw_fd().as_raw_fd() as usize)?;
Ok(buf.len())
}
+10 -19
View File
@@ -2,28 +2,19 @@ use std::collections::VecDeque;
use std::convert::TryInto;
use std::{cmp, mem, ptr, slice};
use inputd::Damage;
use orbclient::{Event, ResizeEvent};
use syscall::error::*;
use crate::display::OffscreenBuffer;
use crate::framebuffer::FrameBuffer;
// Keep synced with orbital
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct SyncRect {
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
pub struct GraphicScreen {
pub width: usize,
pub height: usize,
pub offscreen: OffscreenBuffer,
pub input: VecDeque<Event>,
pub sync_rects: Vec<SyncRect>,
pub sync_rects: Vec<Damage>,
}
impl GraphicScreen {
@@ -117,22 +108,22 @@ impl GraphicScreen {
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
let sync_rects = unsafe {
slice::from_raw_parts(
buf.as_ptr() as *const SyncRect,
buf.len() / mem::size_of::<SyncRect>(),
buf.as_ptr() as *const Damage,
buf.len() / mem::size_of::<Damage>(),
)
};
self.sync_rects.extend_from_slice(sync_rects);
Ok(sync_rects.len() * mem::size_of::<SyncRect>())
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.w.try_into().unwrap_or(0);
let h = sync_rect.h.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);
let start_y = cmp::min(self.height, y);
let end_y = cmp::min(self.height, y + h);
@@ -161,11 +152,11 @@ 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(SyncRect {
self.sync_rects.push(Damage {
x: 0,
y: 0,
w: width,
h: height,
width,
height,
});
self.sync(framebuffer);
}
+2
View File
@@ -90,6 +90,8 @@ pub struct VtEvent {
pub stride: u32,
}
// Keep synced with orbital's SyncRect
#[derive(Debug, Copy, Clone)]
#[repr(packed)]
pub struct Damage {
pub x: i32,