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:
+109
-64
@@ -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
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user