diff --git a/graphics/fbcond/src/display.rs b/graphics/fbcond/src/display.rs index 01f58ed2c8..39b615e5a9 100644 --- a/graphics/fbcond/src/display.rs +++ b/graphics/fbcond/src/display.rs @@ -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 { 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), +} + +pub struct Display { + pub input_handle: File, + cmd_tx: Sender, + pub map: Arc>, +} + impl Display { pub fn open_vt(vt: usize) -> io::Result { - 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::(), + ), + ) + .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 { + 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::(), - ), - )?; - Ok(()) - } - } -} - -impl Drop for Display { - fn drop(&mut self) { - unsafe { - display_fd_unmap(self.offscreen); - } + pub fn sync_rects(&mut self, sync_rects: Vec) { + self.cmd_tx + .send(DisplayCommand::SyncRects(sync_rects)) + .unwrap(); } } diff --git a/graphics/fbcond/src/text.rs b/graphics/fbcond/src/text.rs index 7f75c6fbc5..46afcc738c 100644 --- a/graphics/fbcond/src/text.rs +++ b/graphics/fbcond/src/text.rs @@ -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 { + 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()) } diff --git a/graphics/vesad/src/screen.rs b/graphics/vesad/src/screen.rs index 471340ce86..2b86e83d28 100644 --- a/graphics/vesad/src/screen.rs +++ b/graphics/vesad/src/screen.rs @@ -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, - pub sync_rects: Vec, + pub sync_rects: Vec, } impl GraphicScreen { @@ -117,22 +108,22 @@ impl GraphicScreen { pub fn write(&mut self, buf: &[u8]) -> Result { let sync_rects = unsafe { slice::from_raw_parts( - buf.as_ptr() as *const SyncRect, - buf.len() / mem::size_of::(), + buf.as_ptr() as *const Damage, + buf.len() / mem::size_of::(), ) }; self.sync_rects.extend_from_slice(sync_rects); - Ok(sync_rects.len() * mem::size_of::()) + Ok(sync_rects.len() * mem::size_of::()) } 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); } diff --git a/inputd/src/lib.rs b/inputd/src/lib.rs index e48539f683..907ea53a9b 100644 --- a/inputd/src/lib.rs +++ b/inputd/src/lib.rs @@ -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,