diff --git a/Cargo.lock b/Cargo.lock index 434df3e8e5..5bd694432b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "log", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", "spin", ] @@ -59,7 +59,7 @@ dependencies = [ "common", "netutils", "redox-daemon", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -120,15 +120,6 @@ name = "arg_parser" version = "0.1.0" source = "git+https://gitlab.redox-os.org/redox-os/arg-parser.git#1c434b55f3e1a0375ebcca85b3e88db7378e82fa" -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "arrayvec" version = "0.5.2" @@ -386,7 +377,7 @@ dependencies = [ "common", "netutils", "redox-daemon", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -396,6 +387,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fbcond" +version = "0.1.0" +dependencies = [ + "inputd", + "orbclient", + "ransid", + "redox-daemon", + "redox_event 0.2.1", + "redox_syscall 0.4.1", +] + [[package]] name = "fdt" version = "0.1.5" @@ -608,7 +611,7 @@ dependencies = [ "pcid", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", "spin", ] @@ -659,7 +662,7 @@ dependencies = [ "common", "netutils", "redox-daemon", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -685,10 +688,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.0", + "libc", + "redox_syscall 0.4.1", +] [[package]] name = "lived" @@ -755,19 +763,13 @@ dependencies = [ "ntpclient", "pbr", "redox-daemon", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.3.5", "redox_termios", "termion", "url", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "ntpclient" version = "0.0.1" @@ -807,7 +809,7 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" name = "nvmed" version = "0.1.0" dependencies = [ - "arrayvec 0.5.2", + "arrayvec", "bitflags 1.3.2", "block-io-wrapper", "common", @@ -1138,6 +1140,17 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "redox_event" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475e252d7add4825405d2248d530d33e22364ac5477eab816b56efbeec1e2712" +dependencies = [ + "bitflags 2.4.0", + "libredox", + "redox_syscall 0.4.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1185,7 +1198,7 @@ dependencies = [ "pcid", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -1200,7 +1213,7 @@ dependencies = [ "pcid", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -1210,17 +1223,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rusttype" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d729e72445ad579171b01a9231657736b3793a2cf423078e687e20ecb8695a" -dependencies = [ - "arrayvec 0.4.12", - "linked-hash-map", - "stb_truetype 0.2.8", -] - [[package]] name = "ryu" version = "1.0.15" @@ -1236,7 +1238,7 @@ dependencies = [ "log", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", "spin", ] @@ -1387,24 +1389,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stb_truetype" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1bec4382294c5a680fcebd29f8451e8d8c04479a026f6909004e2ab1cb425d" -dependencies = [ - "stb_truetype 0.3.1", -] - -[[package]] -name = "stb_truetype" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" -dependencies = [ - "byteorder 1.4.3", -] - [[package]] name = "strsim" version = "0.8.0" @@ -1679,7 +1663,7 @@ dependencies = [ "common", "orbclient", "redox-daemon", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", ] @@ -1711,7 +1695,6 @@ dependencies = [ "ransid", "redox-daemon", "redox_syscall 0.4.1", - "rusttype", ] [[package]] @@ -1745,7 +1728,7 @@ dependencies = [ "log", "pcid", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", "static_assertions", "thiserror", @@ -1981,7 +1964,7 @@ dependencies = [ "plain", "redox-daemon", "redox-log", - "redox_event", + "redox_event 0.1.0", "redox_syscall 0.4.1", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 2298868ad1..444a5947c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "block-io-wrapper", "common", "e1000d", + "fbcond", "ided", "ihdad", "ixgbed", diff --git a/fbcond/Cargo.toml b/fbcond/Cargo.toml new file mode 100644 index 0000000000..465876c071 --- /dev/null +++ b/fbcond/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "fbcond" +version = "0.1.0" +edition = "2021" + +[dependencies] +orbclient = "0.3.27" +ransid = "0.4" +redox_event = "0.2" +redox_syscall = "0.4" +redox-daemon = "0.1" + +inputd = { path = "../inputd" } + +[features] +default = [] diff --git a/fbcond/src/display.rs b/fbcond/src/display.rs new file mode 100644 index 0000000000..da16fe5804 --- /dev/null +++ b/fbcond/src/display.rs @@ -0,0 +1,159 @@ +use std::fs::OpenOptions; +use std::mem; +use std::os::unix::fs::OpenOptionsExt; +use std::{ + fs::File, + io, + os::fd::RawFd, + os::unix::io::{AsRawFd, FromRawFd}, + slice, +}; +use syscall::{O_CLOEXEC, O_NONBLOCK, O_RDWR}; + +// Keep synced with vesad +#[derive(Clone, Copy)] +#[repr(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]> { + unsafe { + let display_ptr = syscall::fmap( + display_fd, + &syscall::Map { + offset: 0, + size: (width * height * 4), + flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED, + address: 0, + }, + )?; + let display_slice = slice::from_raw_parts_mut(display_ptr as *mut u32, width * height); + Ok(display_slice) + } +} + +unsafe fn display_fd_unmap(image: *mut [u32], size: usize) { + // FIXME use image.len() instead of size once it is stable. + let _ = syscall::funmap(image as *mut u32 as usize, size * 4); +} + +pub struct Display { + pub input_handle: File, + pub display_file: File, + pub offscreen: *mut [u32], + pub width: usize, + pub height: usize, +} + +impl Display { + pub fn open_vt(vt: usize) -> io::Result { + let mut buffer = [0; 1024]; + + let input_handle = OpenOptions::new() + .read(true) + .custom_flags(O_NONBLOCK as i32) + .open(format!("input:consumer/{vt}"))?; + let fd = input_handle.as_raw_fd(); + + let written = syscall::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"); + + let display_file = syscall::open(display_path, O_CLOEXEC | O_NONBLOCK | O_RDWR) + .map(|socket| unsafe { File::from_raw_fd(socket as RawFd) }) + .unwrap_or_else(|err| { + panic!("failed to open display {}: {}", display_path, err); + }); + + let mut buf: [u8; 4096] = [0; 4096]; + let count = + syscall::fpath(display_file.as_raw_fd() as usize, &mut buf).unwrap_or_else(|e| { + panic!("Could not read display path with fpath(): {e}"); + }); + + let url = + String::from_utf8(Vec::from(&buf[..count])).expect("Could not create Utf8 Url String"); + 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, + }) + } + + fn url_parts(url: &str) -> io::Result<&str> { + let mut url_parts = url.split(':'); + url_parts + .next() + .expect("Could not get scheme name from url"); + let path = url_parts.next().expect("Could not get path from url"); + Ok(path) + } + + fn parse_display_path(path: &str) -> (usize, usize) { + let mut path_parts = path.split('/').skip(1); + let width = path_parts + .next() + .unwrap_or("") + .parse::() + .unwrap_or(0); + let height = path_parts + .next() + .unwrap_or("") + .parse::() + .unwrap_or(0); + + (width, height) + } + + 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.width * self.height) as usize); + } + self.offscreen = ok; + self.width = width; + self.height = height; + } + Err(err) => { + eprintln!("failed to resize display to {}x{}: {}", width, height, err); + } + } + } + + pub fn sync_rect(&mut self, sync_rect: SyncRect) -> syscall::Result<()> { + unsafe { + syscall::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, self.width * self.height); + } + } +} diff --git a/fbcond/src/main.rs b/fbcond/src/main.rs new file mode 100644 index 0000000000..cd0a859810 --- /dev/null +++ b/fbcond/src/main.rs @@ -0,0 +1,197 @@ +use event::EventQueue; +use orbclient::Event; +use std::fs::{File, OpenOptions}; +use std::io::{ErrorKind, Read, Write}; +use std::os::fd::AsRawFd; +use std::os::unix::fs::OpenOptionsExt; +use std::{env, io, mem, slice}; +use syscall::{Packet, SchemeMut, EVENT_READ, O_NONBLOCK}; + +use crate::scheme::{FbconScheme, VtIndex}; + +mod display; +mod scheme; +mod text; + +fn read_to_slice(mut r: R, buf: &mut [T]) -> io::Result { + unsafe { + r.read(slice::from_raw_parts_mut( + buf.as_mut_ptr() as *mut u8, + buf.len() * mem::size_of::(), + )) + .map(|count| count / mem::size_of::()) + } +} + +fn main() { + let vt_ids = env::args() + .skip(1) + .map(|arg| arg.parse().expect("invalid vt number")) + .collect::>(); + + redox_daemon::Daemon::new(|daemon| inner(daemon, &vt_ids)).expect("failed to create daemon"); +} +fn inner(daemon: redox_daemon::Daemon, vt_ids: &[usize]) -> ! { + let mut event_queue = EventQueue::new().expect("fbcond: failed to create event queue"); + + // FIXME listen for resize events from inputd and handle them + + let mut socket = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .custom_flags(O_NONBLOCK as i32) + .open(":fbcon") + .expect("fbcond: failed to create fbcon scheme"); + event_queue + .subscribe( + socket.as_raw_fd().as_raw_fd() as usize, + VtIndex::SCHEMA_SENTINEL, + event::EventFlags::READ, + ) + .expect("fbcond: failed to subscribe to scheme events"); + + let mut scheme = FbconScheme::new(vt_ids, &mut event_queue); + + syscall::setrens(0, 0).expect("fbcond: failed to enter null namespace"); + + daemon.ready().expect("failed to notify parent"); + + scheme + .inputd_handle + .activate(1, inputd::VtMode::Default) + .unwrap(); + + let mut blocked = Vec::new(); + + // Handle all events that could have happened before registering with the event queue. + handle_event( + &mut socket, + &mut scheme, + &mut blocked, + VtIndex::SCHEMA_SENTINEL, + ); + for vt_i in scheme.vts.keys().copied().collect::>() { + handle_event(&mut socket, &mut scheme, &mut blocked, vt_i); + } + + for event in event_queue { + let event = event.expect("fbcond: failed to read event from event queue"); + handle_event(&mut socket, &mut scheme, &mut blocked, event.user_data); + } + + std::process::exit(0); +} + +fn handle_event( + socket: &mut File, + scheme: &mut FbconScheme, + blocked: &mut Vec, + event: VtIndex, +) { + match event { + VtIndex::SCHEMA_SENTINEL => { + loop { + let mut packet = Packet::default(); + match socket.read(&mut packet) { + Ok(0) => break, + Err(err) if err.kind() == ErrorKind::WouldBlock => { + break; + } + Ok(_) => {} + Err(err) => { + panic!("fbcond: failed to read display scheme: {err}"); + } + } + + // If it is a read packet, and there is no data, block it. Otherwise, handle packet + if packet.a == syscall::number::SYS_READ + && packet.d > 0 + && scheme.can_read(packet.b).is_none() + { + blocked.push(packet); + } else { + scheme.handle(&mut packet); + socket + .write(&packet) + .expect("fbcond: failed to write display scheme"); + } + } + } + vt_i => { + let vt = scheme.vts.get_mut(&vt_i).unwrap(); + + let mut events = [Event::new(); 16]; + loop { + match read_to_slice(&mut vt.display.input_handle, &mut events) { + Ok(0) => break, + Err(err) if err.kind() == ErrorKind::WouldBlock => { + break; + } + + Ok(count) => { + let events = &mut events[..count]; + for event in events.iter_mut() { + vt.input(event) + } + } + Err(err) => { + panic!("fbcond: Error while reading events: {err}"); + } + } + } + } + } + + // If there are blocked readers, and data is available, handle them + { + let mut i = 0; + while i < blocked.len() { + if scheme.can_read(blocked[i].b).is_some() { + let mut packet = blocked.remove(i); + scheme.handle(&mut packet); + socket + .write(&packet) + .expect("fbcond: failed to write display scheme"); + } else { + i += 1; + } + } + } + + for (handle_id, handle) in scheme.handles.iter_mut() { + if !handle.events.contains(EVENT_READ) { + continue; + } + + // Can't use scheme.can_read() because we borrow handles as mutable. + // (and because it'd treat O_NONBLOCK sockets differently) + let count = scheme + .vts + .get(&handle.vt_i) + .and_then(|console| console.can_read()) + .unwrap_or(0); + + if count > 0 { + if !handle.notified_read { + handle.notified_read = true; + let event_packet = Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: *handle_id, + c: EVENT_READ.bits(), + d: count, + }; + + socket + .write(&event_packet) + .expect("fbcond: failed to write display event"); + } + } else { + handle.notified_read = false; + } + } +} diff --git a/fbcond/src/scheme.rs b/fbcond/src/scheme.rs new file mode 100644 index 0000000000..a64a62cade --- /dev/null +++ b/fbcond/src/scheme.rs @@ -0,0 +1,186 @@ +use std::collections::BTreeMap; +use std::os::fd::AsRawFd; + +use event::{EventQueue, UserData}; +use syscall::{Error, EventFlags, Result, SchemeMut, EBADF, EINVAL, ENOENT, O_NONBLOCK}; + +use crate::display::Display; +use crate::text::TextScreen; + +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug)] +pub struct VtIndex(usize); + +impl VtIndex { + pub const SCHEMA_SENTINEL: VtIndex = VtIndex(usize::MAX); +} + +impl UserData for VtIndex { + fn into_user_data(self) -> usize { + self.0 + } + + fn from_user_data(user_data: usize) -> Self { + VtIndex(user_data) + } +} + +#[derive(Clone)] +pub struct Handle { + pub vt_i: VtIndex, + pub flags: usize, + pub events: EventFlags, + pub notified_read: bool, +} + +pub struct FbconScheme { + pub vts: BTreeMap, + next_id: usize, + pub handles: BTreeMap, + pub inputd_handle: inputd::Handle, +} + +impl FbconScheme { + pub fn new(vt_ids: &[usize], event_queue: &mut EventQueue) -> FbconScheme { + let inputd_handle = inputd::Handle::new("vesa").unwrap(); + + let mut vts = BTreeMap::new(); + + for &vt_i in vt_ids { + let display = Display::open_vt(vt_i).expect("Failed to open display for vt"); + event_queue.subscribe( + display.input_handle.as_raw_fd().as_raw_fd() as usize, + VtIndex(vt_i), + event::EventFlags::READ, + ).expect("Failed to subscribe to input events for vt"); + vts.insert(VtIndex(vt_i), TextScreen::new(display)); + } + + FbconScheme { + vts, + next_id: 0, + handles: BTreeMap::new(), + inputd_handle, + } + } + + pub fn can_read(&self, id: usize) -> Option { + if let Some(handle) = self.handles.get(&id) { + if let Some(console) = self.vts.get(&handle.vt_i) { + console + .can_read() + .or(if handle.flags & O_NONBLOCK == O_NONBLOCK { + Some(0) + } else { + None + }); + } + } + + Some(0) + } + + fn resize(&mut self, width: usize, height: usize, stride: usize) { + for console in self.vts.values_mut() { + console.resize(width, height); + } + } +} + +impl SchemeMut for FbconScheme { + fn open(&mut self, path_str: &str, flags: usize, _uid: u32, _gid: u32) -> Result { + let vt_i = VtIndex(path_str.parse::().map_err(|_| Error::new(ENOENT))?); + if let Some(_console) = self.vts.get_mut(&vt_i) { + let id = self.next_id; + self.next_id += 1; + + self.handles.insert( + id, + Handle { + vt_i, + flags, + events: EventFlags::empty(), + notified_read: false, + }, + ); + + Ok(id) + } else { + Err(Error::new(ENOENT)) + } + } + + fn dup(&mut self, id: usize, buf: &[u8]) -> Result { + if !buf.is_empty() { + return Err(Error::new(EINVAL)); + } + + let handle = self + .handles + .get(&id) + .map(|handle| handle.clone()) + .ok_or(Error::new(EBADF))?; + + let new_id = self.next_id; + self.next_id += 1; + + self.handles.insert(new_id, handle); + + Ok(new_id) + } + + fn fevent(&mut self, id: usize, flags: syscall::EventFlags) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + handle.notified_read = false; + + handle.events = flags; + Ok(syscall::EventFlags::empty()) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let path_str = format!("fbcon:{}", handle.vt_i.0); + + let path = path_str.as_bytes(); + + let mut i = 0; + while i < buf.len() && i < path.len() { + buf[i] = path[i]; + i += 1; + } + + Ok(i) + } + + fn fsync(&mut self, id: usize) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + return Ok(0); + } + + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + if let Some(screen) = self.vts.get_mut(&handle.vt_i) { + return screen.read(buf); + } + + Err(Error::new(EBADF)) + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + if let Some(console) = self.vts.get_mut(&handle.vt_i) { + console.write(buf) + } else { + Err(Error::new(EBADF)) + } + } + + fn close(&mut self, id: usize) -> Result { + self.handles.remove(&id).ok_or(Error::new(EBADF))?; + Ok(0) + } +} diff --git a/fbcond/src/text.rs b/fbcond/src/text.rs new file mode 100644 index 0000000000..bdc24d66f5 --- /dev/null +++ b/fbcond/src/text.rs @@ -0,0 +1,327 @@ +extern crate ransid; + +use std::collections::{BTreeSet, VecDeque}; +use std::convert::{TryFrom, TryInto}; +use std::os::fd::AsRawFd; +use std::{cmp, ptr}; + +use orbclient::{Event, EventOption, FONT}; +use syscall::error::*; + +use crate::display::{Display, SyncRect}; + +pub struct TextScreen { + console: ransid::Console, + pub display: Display, + changed: BTreeSet, + ctrl: bool, + input: VecDeque, +} + +impl TextScreen { + pub fn new(display: Display) -> TextScreen { + TextScreen { + console: ransid::Console::new(display.width / 8, display.height / 16), + display, + changed: BTreeSet::new(), + ctrl: false, + input: VecDeque::new(), + } + } + + /// 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); + + let start_x = cmp::min(display.width, x); + let len = cmp::min(display.width, x + w) - start_x; + + let mut offscreen_ptr = display.offscreen as *mut u8 as usize; + + let stride = display.width * 4; + + let offset = y * stride + start_x * 4; + offscreen_ptr += offset; + + let mut rows = end_y - start_y; + while rows > 0 { + for i in 0..len { + unsafe { + *(offscreen_ptr as *mut u32).add(i) = color; + } + } + offscreen_ptr += stride; + rows -= 1; + } + } + + /// 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); + + let start_x = cmp::min(display.width, x); + let len = cmp::min(display.width, x + w) - start_x; + + let mut offscreen_ptr = display.offscreen as *mut u8 as usize; + + let stride = display.width * 4; + + let offset = y * stride + start_x * 4; + offscreen_ptr += offset; + + let mut rows = end_y - start_y; + while rows > 0 { + let mut row_ptr = offscreen_ptr; + let mut cols = len; + while cols > 0 { + unsafe { + let color = *(row_ptr as *mut u32); + *(row_ptr as *mut u32) = !color; + } + row_ptr += 4; + cols -= 1; + } + offscreen_ptr += stride; + rows -= 1; + } + } + + /// Draw a character + fn char( + display: &mut Display, + x: usize, + y: usize, + character: char, + color: u32, + _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; + + let font_i = 16 * (character as usize); + if font_i + 16 <= FONT.len() { + for row in 0..16 { + let row_data = FONT[font_i + row]; + for col in 0..8 { + if (row_data >> (7 - col)) & 1 == 1 { + unsafe { + *((dst + col * 4) as *mut u32) = color; + } + } + } + dst += display.width * 4; + } + } + } + } + + 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) { + let mut buf = vec![]; + + match event.to_option() { + EventOption::Key(key_event) => { + if key_event.scancode == 0x1D { + self.ctrl = key_event.pressed; + } else if key_event.pressed { + match key_event.scancode { + 0x0E => { + // Backspace + buf.extend_from_slice(b"\x7F"); + } + 0x47 => { + // Home + buf.extend_from_slice(b"\x1B[H"); + } + 0x48 => { + // Up + buf.extend_from_slice(b"\x1B[A"); + } + 0x49 => { + // Page up + buf.extend_from_slice(b"\x1B[5~"); + } + 0x4B => { + // Left + buf.extend_from_slice(b"\x1B[D"); + } + 0x4D => { + // Right + buf.extend_from_slice(b"\x1B[C"); + } + 0x4F => { + // End + buf.extend_from_slice(b"\x1B[F"); + } + 0x50 => { + // Down + buf.extend_from_slice(b"\x1B[B"); + } + 0x51 => { + // Page down + buf.extend_from_slice(b"\x1B[6~"); + } + 0x52 => { + // Insert + buf.extend_from_slice(b"\x1B[2~"); + } + 0x53 => { + // Delete + buf.extend_from_slice(b"\x1B[3~"); + } + _ => { + let c = match key_event.character { + c @ 'A'..='Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char, + c @ 'a'..='z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char, + c => c, + }; + + if c != '\0' { + let mut b = [0; 4]; + buf.extend_from_slice(c.encode_utf8(&mut b).as_bytes()); + } + } + } + } + } + _ => (), //TODO: Mouse in terminal + } + + for &b in buf.iter() { + self.input.push_back(b); + } + } + + pub fn can_read(&self) -> Option { + if self.input.is_empty() { + None + } else { + Some(self.input.len()) + } + } +} + +impl TextScreen { + pub fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + + while i < buf.len() && !self.input.is_empty() { + buf[i] = self.input.pop_front().unwrap(); + i += 1; + } + + Ok(i) + } + + pub fn write(&mut self, buf: &[u8]) -> Result { + 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.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 { + ransid::Event::Char { + x, + y, + c, + color, + bold, + .. + } => { + Self::char(display, 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()); + for y2 in y..y + h { + changed.insert(y2); + } + } + ransid::Event::ScreenBuffer { .. } => (), + ransid::Event::Move { + from_x, + from_y, + to_x, + to_y, + w, + h, + } => { + let width = display.width; + let pixels = unsafe { &mut *display.offscreen }; + + for raw_y in 0..h { + let y = if from_y > to_y { raw_y } else { h - raw_y - 1 }; + + for pixel_y in 0..16 { + { + let off_from = ((from_y + y) * 16 + pixel_y) * width + from_x * 8; + let off_to = ((to_y + y) * 16 + pixel_y) * width + to_x * 8; + let len = w * 8; + + if off_from + len <= pixels.len() && off_to + len <= pixels.len() { + unsafe { + let data_ptr = pixels.as_mut_ptr() as *mut u32; + ptr::copy( + data_ptr.offset(off_from as isize), + data_ptr.offset(off_to as isize), + len, + ); + } + } + } + } + + changed.insert(to_y + y); + } + } + ransid::Event::Resize { .. } => (), + ransid::Event::Title { .. } => (), + }); + } + + 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.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, + })?; + } + self.changed.clear(); + syscall::fsync(self.display.display_file.as_raw_fd().as_raw_fd() as usize)?; + + Ok(buf.len()) + } +} diff --git a/inputd/src/lib.rs b/inputd/src/lib.rs index a3d1020678..4a52c09403 100644 --- a/inputd/src/lib.rs +++ b/inputd/src/lib.rs @@ -10,7 +10,6 @@ unsafe fn any_as_u8_slice(p: &T) -> &[u8] { #[derive(Debug, Copy, Clone, PartialEq)] #[repr(usize)] pub enum VtMode { - Text = 0, Graphic = 1, Default = 2, } diff --git a/vesad/Cargo.toml b/vesad/Cargo.toml index e3880f63db..85d12fc9fb 100644 --- a/vesad/Cargo.toml +++ b/vesad/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [dependencies] orbclient = "0.3.27" ransid = "0.4" -rusttype = { version = "0.2", optional = true } redox_syscall = "0.4" redox-daemon = "0.1" diff --git a/vesad/res/DejaVu-Fonts-License.txt b/vesad/res/DejaVu-Fonts-License.txt deleted file mode 100644 index 69399804ca..0000000000 --- a/vesad/res/DejaVu-Fonts-License.txt +++ /dev/null @@ -1,97 +0,0 @@ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. -Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - -Bitstream Vera Fonts Copyright ------------------------------- - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is -a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute the -Font Software, including without limitation the rights to use, copy, merge, -publish, distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to the -following conditions: - -The above copyright and trademark notices and this permission notice shall -be included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional glyphs or characters may be added to the Fonts, only if the fonts -are renamed to names not containing either the words "Bitstream" or the word -"Vera". - -This License becomes null and void to the extent applicable to Fonts or Font -Software that has been modified and is distributed under the "Bitstream -Vera" names. - -The Font Software may be sold as part of a larger software package but no -copy of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING -ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE -FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font Software -without prior written authorization from the Gnome Foundation or Bitstream -Inc., respectively. For further information, contact: fonts at gnome dot -org. - -Arev Fonts Copyright ------------------------------- - -Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and -associated documentation files (the "Font Software"), to reproduce -and distribute the modifications to the Bitstream Vera Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to -the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Tavmjong Bah" or the word "Arev". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the -"Tavmjong Bah Arev" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the name of Tavmjong Bah shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Font Software without prior written authorization -from Tavmjong Bah. For further information, contact: tavmjong @ free -. fr. \ No newline at end of file diff --git a/vesad/res/DejaVuSansMono-Bold.ttf b/vesad/res/DejaVuSansMono-Bold.ttf deleted file mode 100644 index 9c716794f7..0000000000 Binary files a/vesad/res/DejaVuSansMono-Bold.ttf and /dev/null differ diff --git a/vesad/res/DejaVuSansMono-BoldOblique.ttf b/vesad/res/DejaVuSansMono-BoldOblique.ttf deleted file mode 100644 index d6536a5cc2..0000000000 Binary files a/vesad/res/DejaVuSansMono-BoldOblique.ttf and /dev/null differ diff --git a/vesad/res/DejaVuSansMono-Oblique.ttf b/vesad/res/DejaVuSansMono-Oblique.ttf deleted file mode 100644 index e1af341304..0000000000 Binary files a/vesad/res/DejaVuSansMono-Oblique.ttf and /dev/null differ diff --git a/vesad/res/DejaVuSansMono.ttf b/vesad/res/DejaVuSansMono.ttf deleted file mode 100644 index 8b7bb2a4e1..0000000000 Binary files a/vesad/res/DejaVuSansMono.ttf and /dev/null differ diff --git a/vesad/src/display.rs b/vesad/src/display.rs index 7f5626eaff..f746c5ac14 100644 --- a/vesad/src/display.rs +++ b/vesad/src/display.rs @@ -1,25 +1,7 @@ -#[cfg(feature="rusttype")] -extern crate rusttype; - use std::alloc::{self, Layout}; -use std::{cmp, ptr}; +use std::ptr; use std::ptr::NonNull; -#[cfg(feature="rusttype")] -use self::rusttype::{Font, FontCollection, Scale, point}; - -#[cfg(not(feature="rusttype"))] -use orbclient::FONT; - -#[cfg(feature="rusttype")] -static FONT: &'static [u8] = include_bytes!("../res/DejaVuSansMono.ttf"); -#[cfg(feature="rusttype")] -static FONT_BOLD: &'static [u8] = include_bytes!("../res/DejaVuSansMono-Bold.ttf"); -#[cfg(feature="rusttype")] -static FONT_BOLD_ITALIC: &'static [u8] = include_bytes!("../res/DejaVuSansMono-BoldOblique.ttf"); -#[cfg(feature="rusttype")] -static FONT_ITALIC: &'static [u8] = include_bytes!("../res/DejaVuSansMono-Oblique.ttf"); - pub struct OffscreenBuffer { ptr: NonNull<[u32]>, } @@ -32,7 +14,7 @@ impl OffscreenBuffer { } #[inline] - fn new(len: usize) -> Self { + pub fn new(len: usize) -> Self { let layout = Self::layout(len); let ptr = unsafe { alloc::alloc_zeroed(layout) }; let ptr = ptr::slice_from_raw_parts_mut(ptr.cast(), len); @@ -57,257 +39,3 @@ impl std::ops::DerefMut for OffscreenBuffer { unsafe { self.ptr.as_mut() } } } - -/// A display -pub struct Display { - pub width: usize, - pub height: usize, - pub offscreen: OffscreenBuffer, - #[cfg(feature="rusttype")] - pub font: Font<'static>, - #[cfg(feature="rusttype")] - pub font_bold: Font<'static>, - #[cfg(feature="rusttype")] - pub font_bold_italic: Font<'static>, - #[cfg(feature="rusttype")] - pub font_italic: Font<'static> -} - -impl Display { - #[cfg(not(feature="rusttype"))] - pub fn new(width: usize, height: usize) -> Display { - let size = width * height; - let offscreen = OffscreenBuffer::new(size); - Display { - width, - height, - offscreen, - } - } - - #[cfg(feature="rusttype")] - pub fn new(width: usize, height: usize) -> Display { - let size = width * height; - let offscreen = OffscreenBuffer::new(size); - Display { - width, - height, - offscreen, - font: FontCollection::from_bytes(FONT).into_font().unwrap(), - font_bold: FontCollection::from_bytes(FONT_BOLD).into_font().unwrap(), - font_bold_italic: FontCollection::from_bytes(FONT_BOLD_ITALIC).into_font().unwrap(), - font_italic: FontCollection::from_bytes(FONT_ITALIC).into_font().unwrap() - } - } - - pub fn resize(&mut self, width: usize, height: usize) { - if width != self.width || height != self.height { - println!("Resize display to {}, {}", width, height); - - let size = width * height; - let mut offscreen = OffscreenBuffer::new(size); - - { - let mut old_ptr = self.offscreen.as_ptr(); - let mut new_ptr = offscreen.as_mut_ptr(); - - for _y in 0..cmp::min(height, self.height) { - unsafe { - ptr::copy( - old_ptr as *const u8, - new_ptr as *mut u8, - cmp::min(width, self.width) * 4 - ); - if width > self.width { - ptr::write_bytes( - new_ptr.offset(self.width as isize), - 0, - width - self.width - ); - } - old_ptr = old_ptr.offset(self.width as isize); - new_ptr = new_ptr.offset(width as isize); - } - } - - if height > self.height { - for _y in self.height..height { - unsafe { - ptr::write_bytes(new_ptr, 0, width); - new_ptr = new_ptr.offset(width as isize); - } - } - } - } - - self.width = width; - self.height = height; - - self.offscreen = offscreen; - } else { - println!("Display is already {}, {}", width, height); - } - } - - /// Draw a rectangle - pub fn rect(&mut self, x: usize, y: usize, w: usize, h: usize, color: u32) { - let start_y = cmp::min(self.height, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width, x); - let len = cmp::min(self.width, x + w) - start_x; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - - let stride = self.width * 4; - - let offset = y * stride + start_x * 4; - offscreen_ptr += offset; - - let mut rows = end_y - start_y; - while rows > 0 { - for i in 0..len { - unsafe { - *(offscreen_ptr as *mut u32).add(i) = color; - } - } - offscreen_ptr += stride; - rows -= 1; - } - } - - /// Invert a rectangle - pub fn invert(&mut self, x: usize, y: usize, w: usize, h: usize) { - let start_y = cmp::min(self.height, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width, x); - let len = cmp::min(self.width, x + w) - start_x; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - - let stride = self.width * 4; - - let offset = y * stride + start_x * 4; - offscreen_ptr += offset; - - let mut rows = end_y - start_y; - while rows > 0 { - let mut row_ptr = offscreen_ptr; - let mut cols = len; - while cols > 0 { - unsafe { - let color = *(row_ptr as *mut u32); - *(row_ptr as *mut u32) = !color; - } - row_ptr += 4; - cols -= 1; - } - offscreen_ptr += stride; - rows -= 1; - } - } - - /// Draw a character - #[cfg(not(feature="rusttype"))] - pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, _bold: bool, _italic: bool) { - if x + 8 <= self.width && y + 16 <= self.height { - let mut dst = self.offscreen.as_mut_ptr() as usize + (y * self.width + x) * 4; - - let font_i = 16 * (character as usize); - if font_i + 16 <= FONT.len() { - for row in 0..16 { - let row_data = FONT[font_i + row]; - for col in 0..8 { - if (row_data >> (7 - col)) & 1 == 1 { - unsafe { *((dst + col * 4) as *mut u32) = color; } - } - } - dst += self.width * 4; - } - } - } - } - - /// Draw a character - #[cfg(feature="rusttype")] - pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, bold: bool, italic: bool) { - let width = self.width; - let height = self.height; - let offscreen = self.offscreen.as_mut_ptr() as usize; - - let font = if bold && italic { - &self.font_bold_italic - } else if bold { - &self.font_bold - } else if italic { - &self.font_italic - } else { - &self.font - }; - - if let Some(glyph) = font.glyph(character){ - let scale = Scale::uniform(16.0); - let v_metrics = font.v_metrics(scale); - let point = point(0.0, v_metrics.ascent); - let glyph = glyph.scaled(scale).positioned(point); - if let Some(bb) = glyph.pixel_bounding_box() { - glyph.draw(|off_x, off_y, v| { - let off_x = x + (off_x as i32 + bb.min.x) as usize; - let off_y = y + (off_y as i32 + bb.min.y) as usize; - // There's still a possibility that the glyph clips the boundaries of the bitmap - if off_x < width && off_y < height { - if v > 0.0 { - let f_a = (v * 255.0) as u32; - let f_r = (((color >> 16) & 0xFF) * f_a)/255; - let f_g = (((color >> 8) & 0xFF) * f_a)/255; - let f_b = ((color & 0xFF) * f_a)/255; - - let offscreen_ptr = (offscreen + (off_y * width + off_x) * 4) as *mut u32; - - let bg = unsafe { *offscreen_ptr }; - - let b_a = 255 - f_a; - let b_r = (((bg >> 16) & 0xFF) * b_a)/255; - let b_g = (((bg >> 8) & 0xFF) * b_a)/255; - let b_b = ((bg & 0xFF) * b_a)/255; - - let c = ((f_r + b_r) << 16) | ((f_g + b_g) << 8) | (f_b + b_b); - - unsafe { *offscreen_ptr = c; } - } - } - }); - } - } - } - - /// Copy from offscreen to onscreen - pub fn sync(&mut self, x: usize, y: usize, w: usize, h: usize, onscreen: &mut [u32], stride: usize) { - let start_y = cmp::min(self.height, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width, x); - let len = (cmp::min(self.width, x + w) - start_x) * 4; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - let mut onscreen_ptr = onscreen.as_mut_ptr() as usize; - - offscreen_ptr += (y * self.width + start_x) * 4; - onscreen_ptr += (y * stride + start_x) * 4; - - let mut rows = end_y - start_y; - while rows > 0 { - unsafe { - ptr::copy( - offscreen_ptr as *const u8, - onscreen_ptr as *mut u8, - len - ); - } - offscreen_ptr += self.width * 4; - onscreen_ptr += stride * 4; - rows -= 1; - } - } -} diff --git a/vesad/src/main.rs b/vesad/src/main.rs index 47bd535022..9f1d8f2901 100644 --- a/vesad/src/main.rs +++ b/vesad/src/main.rs @@ -12,22 +12,16 @@ use crate::{ scheme::{DisplayScheme, HandleKind} }; -pub mod display; -pub mod framebuffer; -pub mod scheme; -pub mod screen; +mod display; +mod framebuffer; +mod scheme; +mod screen; fn main() { let mut spec = Vec::new(); - for arg in env::args().skip(1) { - if arg == "T" { - spec.push(false); - } else if arg == "G" { - spec.push(true); - } else { - eprintln!("vesad: unknown screen type: {}", arg); - } + for _ in env::args().skip(1) { + spec.push(()); } let width = usize::from_str_radix( @@ -89,7 +83,7 @@ fn main() { redox_daemon::Daemon::new(|daemon| inner(daemon, framebuffers, &spec)).expect("failed to create daemon"); } -fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[bool]) -> ! { +fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[()]) -> ! { let mut socket = File::create(":display.vesa").expect("vesad: failed to create display scheme"); let mut scheme = DisplayScheme::new(framebuffers, &spec); diff --git a/vesad/src/scheme.rs b/vesad/src/scheme.rs index cc5a70fcd1..7f12cf0f9c 100644 --- a/vesad/src/scheme.rs +++ b/vesad/src/scheme.rs @@ -1,14 +1,9 @@ use std::collections::BTreeMap; use std::{mem, ptr, slice, str}; -use orbclient::{Event, EventOption}; -use syscall::{Error, EventFlags, EACCES, EBADF, EINVAL, ENOENT, Map, O_NONBLOCK, Result, SchemeMut, PAGE_SIZE, Packet, KSMSG_MMAP_PREP, KSMSG_MMAP, MapFlags, ESKMSG, SKMSG_PROVIDE_MMAP}; +use syscall::{Error, EventFlags, EBADF, EINVAL, ENOENT, O_NONBLOCK, Result, SchemeMut, PAGE_SIZE, MapFlags}; -use crate::{ - display::Display, - framebuffer::FrameBuffer, - screen::{Screen, GraphicScreen, TextScreen}, -}; +use crate::{framebuffer::FrameBuffer, screen::GraphicScreen}; #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug)] pub struct VtIndex(usize); @@ -34,7 +29,7 @@ pub struct DisplayScheme { framebuffers: Vec, onscreens: Vec<&'static mut [u32]>, active: VtIndex, - pub vts: BTreeMap>>, + pub vts: BTreeMap>, next_id: usize, pub handles: BTreeMap, pub inputd_handle: inputd::Handle, @@ -42,7 +37,7 @@ pub struct DisplayScheme { } impl DisplayScheme { - pub fn new(mut framebuffers: Vec, spec: &[bool]) -> DisplayScheme { + pub fn new(mut framebuffers: Vec, spec: &[()]) -> DisplayScheme { let mut inputd_handle = inputd::Handle::new("vesa").unwrap(); let mut onscreens = Vec::new(); @@ -52,17 +47,13 @@ impl DisplayScheme { }); } - let mut vts = BTreeMap::>>::new(); + let mut vts = BTreeMap::>::new(); - for &vt_type in spec.iter() { - let mut screens = BTreeMap::>::new(); + for &() in spec.iter() { + let mut screens = BTreeMap::::new(); for fb_i in 0..framebuffers.len() { let fb = &framebuffers[fb_i]; - screens.insert(ScreenIndex(fb_i), if vt_type { - Box::new(GraphicScreen::new(Display::new(fb.width, fb.height))) - } else { - Box::new(TextScreen::new(Display::new(fb.width, fb.height))) - }); + screens.insert(ScreenIndex(fb_i), GraphicScreen::new(fb.width, fb.height)); } vts.insert(VtIndex(inputd_handle.register().unwrap()), screens); } @@ -166,7 +157,7 @@ impl SchemeMut for DisplayScheme { vt_screen.next().unwrap_or("").parse::().unwrap_or(0) ); if let Some(screens) = self.vts.get_mut(&vt_i) { - if let Some(screen) = screens.get_mut(&screen_i) { + if screens.get_mut(&screen_i).is_some() { let id = self.next_id; self.next_id += 1; @@ -214,22 +205,6 @@ impl SchemeMut for DisplayScheme { } } - /* - fn fmap(&mut self, id: usize, map: &Map) -> Result { - 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) { - return screen.map(map.offset, map.size); - } - } - } - - Err(Error::new(EBADF)) - } - */ - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; @@ -240,7 +215,7 @@ impl SchemeMut for DisplayScheme { }, 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()) + format!("display:{}.{}/{}/{}", vt_i.0, screen_i.0, screen.width, screen.height) } else { return Err(Error::new(EBADF)); } @@ -299,30 +274,20 @@ impl SchemeMut for DisplayScheme { match handle.kind { HandleKind::Input => { - use inputd::{Cmd as DisplayCommand, VtMode}; - + use inputd::Cmd as DisplayCommand; + let command = inputd::parse_command(buf).unwrap(); match command { - DisplayCommand::Activate { vt, mode } => { + DisplayCommand::Activate { vt, mode: _ } => { let vt_i = VtIndex(vt); if let Some(screens) = self.vts.get_mut(&vt_i) { for (screen_i, screen) in screens.iter_mut() { - match mode { - VtMode::Graphic => { - *screen = Box::new(GraphicScreen::new(Display::new(screen.width(), screen.height()))); - } - - VtMode::Default => { - screen.redraw( - self.onscreens[screen_i.0], - self.framebuffers[screen_i.0].stride - ); - } - - VtMode::Text => todo!() - } + screen.redraw( + self.onscreens[screen_i.0], + self.framebuffers[screen_i.0].stride + ); } } @@ -359,20 +324,6 @@ impl SchemeMut for DisplayScheme { } } - fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { - 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) { - return screen.seek(pos, whence).map(|pos| pos as isize); - } - } - } - - Err(Error::new(EBADF)) - } - fn close(&mut self, id: usize) -> Result { self.handles.remove(&id).ok_or(Error::new(EBADF))?; Ok(0) @@ -383,7 +334,11 @@ impl SchemeMut for DisplayScheme { 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) { - return screen.map(off as usize, size); + if off as usize + size <= screen.offscreen.len() * 4 { + return Ok(screen.offscreen.as_ptr() as usize + off as usize); + } else { + return Err(Error::new(EINVAL)); + } } } } diff --git a/vesad/src/screen.rs b/vesad/src/screen.rs new file mode 100644 index 0000000000..680c2bce9c --- /dev/null +++ b/vesad/src/screen.rs @@ -0,0 +1,170 @@ +use std::collections::VecDeque; +use std::convert::TryInto; +use std::{mem, slice, cmp, ptr}; + +use orbclient::{Event, ResizeEvent}; +use syscall::error::*; + +use crate::display::OffscreenBuffer; + +// Keep synced with orbital +#[derive(Clone, Copy)] +#[repr(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, +} + +impl GraphicScreen { + pub fn new(width: usize, height: usize) -> GraphicScreen { + GraphicScreen { + width, + height, + offscreen: OffscreenBuffer::new(width * height), + input: VecDeque::new(), + sync_rects: Vec::new(), + } + } +} + +impl GraphicScreen { + pub fn resize(&mut self, width: usize, height: usize) { + //TODO: Fix issue with mapped screens + + if width != self.width || height != self.height { + println!("Resize display to {}, {}", width, height); + + let size = width * height; + let mut offscreen = OffscreenBuffer::new(size); + + let mut old_ptr = self.offscreen.as_ptr(); + let mut new_ptr = offscreen.as_mut_ptr(); + + for _y in 0..cmp::min(height, self.height) { + unsafe { + ptr::copy( + old_ptr as *const u8, + new_ptr as *mut u8, + cmp::min(width, self.width) * 4 + ); + if width > self.width { + ptr::write_bytes( + new_ptr.offset(self.width as isize), + 0, + width - self.width + ); + } + old_ptr = old_ptr.offset(self.width as isize); + new_ptr = new_ptr.offset(width as isize); + } + } + + if height > self.height { + for _y in self.height..height { + unsafe { + ptr::write_bytes(new_ptr, 0, width); + new_ptr = new_ptr.offset(width as isize); + } + } + } + + self.width = width; + self.height = height; + + self.offscreen = offscreen; + } else { + println!("Display is already {}, {}", width, height); + }; + + self.input.push_back(ResizeEvent { + width: width as u32, + height: height as u32, + }.to_event()); + } + + pub fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + + let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::()) }; + + while i < event_buf.len() && ! self.input.is_empty() { + event_buf[i] = self.input.pop_front().unwrap(); + i += 1; + } + + Ok(i * mem::size_of::()) + } + + pub fn can_read(&self) -> Option { + if self.input.is_empty() { + None + } else { + Some(self.input.len() * mem::size_of::()) + } + } + + 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::() + ) + }; + + self.sync_rects.extend_from_slice(sync_rects); + + Ok(sync_rects.len() * mem::size_of::()) + } + + pub fn sync(&mut self, onscreen: &mut [u32], stride: usize) { + 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 start_y = cmp::min(self.height, y); + let end_y = cmp::min(self.height, y + h); + + let start_x = cmp::min(self.width, x); + let len = (cmp::min(self.width, x + w) - start_x) * 4; + + let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; + let mut onscreen_ptr = onscreen.as_mut_ptr() as usize; + + offscreen_ptr += (y * self.width + start_x) * 4; + onscreen_ptr += (y * stride + start_x) * 4; + + let mut rows = end_y - start_y; + while rows > 0 { + unsafe { + ptr::copy( + offscreen_ptr as *const u8, + onscreen_ptr as *mut u8, + len + ); + } + offscreen_ptr += self.width * 4; + onscreen_ptr += stride * 4; + rows -= 1; + }; + } + } + + pub fn redraw(&mut self, onscreen: &mut [u32], stride: usize) { + let width = self.width.try_into().unwrap(); + let height = self.height.try_into().unwrap(); + self.sync_rects.push(SyncRect { x: 0, y: 0, w: width, h: height }); + self.sync(onscreen, stride); + } +} diff --git a/vesad/src/screen/graphic.rs b/vesad/src/screen/graphic.rs deleted file mode 100644 index ca96be28a8..0000000000 --- a/vesad/src/screen/graphic.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::collections::VecDeque; -use std::convert::TryInto; -use std::{mem, slice}; - -use orbclient::{Event, ResizeEvent}; -use syscall::error::*; - -use crate::display::Display; -use crate::screen::Screen; - -// Keep synced with orbital -#[derive(Clone, Copy)] -#[repr(packed)] -struct SyncRect { - x: i32, - y: i32, - w: i32, - h: i32, -} - -pub struct GraphicScreen { - pub display: Display, - pub input: VecDeque, - sync_rects: Vec, -} - -impl GraphicScreen { - pub fn new(display: Display) -> GraphicScreen { - GraphicScreen { - display, - input: VecDeque::new(), - sync_rects: Vec::new(), - } - } -} - -impl Screen for GraphicScreen { - fn width(&self) -> usize { - self.display.width - } - - fn height(&self) -> usize { - self.display.height - } - - fn resize(&mut self, width: usize, height: usize) { - //TODO: Fix issue with mapped screens - self.display.resize(width, height); - self.input.push_back(ResizeEvent { - width: width as u32, - height: height as u32, - }.to_event()); - } - - fn map(&self, offset: usize, size: usize) -> Result { - if offset + size <= self.display.offscreen.len() * 4 { - Ok(self.display.offscreen.as_ptr() as usize + offset) - } else { - Err(Error::new(EINVAL)) - } - } - - fn input(&mut self, event: &Event) { - self.input.push_back(*event); - } - - fn read(&mut self, buf: &mut [u8]) -> Result { - let mut i = 0; - - let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::()) }; - - while i < event_buf.len() && ! self.input.is_empty() { - event_buf[i] = self.input.pop_front().unwrap(); - i += 1; - } - - Ok(i * mem::size_of::()) - } - - fn can_read(&self) -> Option { - if self.input.is_empty() { - None - } else { - Some(self.input.len() * mem::size_of::()) - } - } - - 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::() - ) - }; - - self.sync_rects.extend_from_slice(sync_rects); - - Ok(sync_rects.len() * mem::size_of::()) - } - - fn seek(&mut self, _pos: isize, _whence: usize) -> Result { - Ok(0) - } - - fn sync(&mut self, onscreen: &mut [u32], stride: usize) { - for sync_rect in self.sync_rects.drain(..) { - self.display.sync( - sync_rect.x.try_into().unwrap_or(0), - sync_rect.y.try_into().unwrap_or(0), - sync_rect.w.try_into().unwrap_or(0), - sync_rect.h.try_into().unwrap_or(0), - onscreen, - stride, - ); - } - } - - fn redraw(&mut self, onscreen: &mut [u32], stride: usize) { - let width = self.display.width; - let height = self.display.height; - self.display.sync(0, 0, width, height, onscreen, stride); - } -} diff --git a/vesad/src/screen/mod.rs b/vesad/src/screen/mod.rs deleted file mode 100644 index e254e1c4fe..0000000000 --- a/vesad/src/screen/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub use self::graphic::GraphicScreen; -pub use self::text::TextScreen; - -use orbclient::Event; -use syscall::Result; - -mod graphic; -mod text; - -pub trait Screen { - fn width(&self) -> usize; - - fn height(&self) -> usize; - - fn resize(&mut self, width: usize, height: usize); - - fn map(&self, offset: usize, size: usize) -> Result; - - fn input(&mut self, event: &Event); - - fn read(&mut self, buf: &mut [u8]) -> Result; - - fn can_read(&self) -> Option; - - fn write(&mut self, buf: &[u8]) -> Result; - - fn seek(&mut self, pos: isize, whence: usize) -> Result; - - fn sync(&mut self, onscreen: &mut [u32], stride: usize); - - fn redraw(&mut self, onscreen: &mut [u32], stride: usize); -} diff --git a/vesad/src/screen/text.rs b/vesad/src/screen/text.rs deleted file mode 100644 index 834aca6cf2..0000000000 --- a/vesad/src/screen/text.rs +++ /dev/null @@ -1,226 +0,0 @@ -extern crate ransid; - -use std::collections::{BTreeSet, VecDeque}; -use std::ptr; - -use orbclient::{Event, EventOption}; -use syscall::error::*; - -use crate::display::Display; -use crate::screen::Screen; - -pub struct TextScreen { - pub console: ransid::Console, - pub display: Display, - pub changed: BTreeSet, - pub ctrl: bool, - pub input: VecDeque, -} - -impl TextScreen { - pub fn new(display: Display) -> TextScreen { - TextScreen { - console: ransid::Console::new(display.width/8, display.height/16), - display: display, - changed: BTreeSet::new(), - ctrl: false, - input: VecDeque::new(), - } - } -} - -impl Screen for TextScreen { - fn width(&self) -> usize { - self.console.state.w - } - - fn height(&self) -> usize { - self.console.state.h - } - - fn resize(&mut self, width: usize, height: usize) { - self.display.resize(width, height); - self.console.state.w = width / 8; - self.console.state.h = height / 16; - } - - fn map(&self, _offset: usize, _size: usize) -> Result { - Err(Error::new(EBADF)) - } - - fn input(&mut self, event: &Event) { - let mut buf = vec![]; - - match event.to_option() { - EventOption::Key(key_event) => { - if key_event.scancode == 0x1D { - self.ctrl = key_event.pressed; - } else if key_event.pressed { - match key_event.scancode { - 0x0E => { // Backspace - buf.extend_from_slice(b"\x7F"); - }, - 0x47 => { // Home - buf.extend_from_slice(b"\x1B[H"); - }, - 0x48 => { // Up - buf.extend_from_slice(b"\x1B[A"); - }, - 0x49 => { // Page up - buf.extend_from_slice(b"\x1B[5~"); - }, - 0x4B => { // Left - buf.extend_from_slice(b"\x1B[D"); - }, - 0x4D => { // Right - buf.extend_from_slice(b"\x1B[C"); - }, - 0x4F => { // End - buf.extend_from_slice(b"\x1B[F"); - }, - 0x50 => { // Down - buf.extend_from_slice(b"\x1B[B"); - }, - 0x51 => { // Page down - buf.extend_from_slice(b"\x1B[6~"); - }, - 0x52 => { // Insert - buf.extend_from_slice(b"\x1B[2~"); - }, - 0x53 => { // Delete - buf.extend_from_slice(b"\x1B[3~"); - }, - _ => { - let c = match key_event.character { - c @ 'A' ..= 'Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char, - c @ 'a' ..= 'z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char, - c => c - }; - - if c != '\0' { - let mut b = [0; 4]; - buf.extend_from_slice(c.encode_utf8(&mut b).as_bytes()); - } - } - } - } - }, - _ => () //TODO: Mouse in terminal - } - - for &b in buf.iter() { - self.input.push_back(b); - } - } - - fn read(&mut self, buf: &mut [u8]) -> Result { - let mut i = 0; - - while i < buf.len() && ! self.input.is_empty() { - buf[i] = self.input.pop_front().unwrap(); - i += 1; - } - - Ok(i) - } - - fn can_read(&self) -> Option { - if self.input.is_empty() { - None - } else { - Some(self.input.len()) - } - } - - fn write(&mut self, buf: &[u8]) -> Result { - 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.display.invert(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 { - ransid::Event::Char { x, y, c, color, bold, .. } => { - display.char(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 } => { - display.rect(x * 8, y * 16, w * 8, h * 16, color.as_rgb()); - for y2 in y..y + h { - changed.insert(y2); - } - }, - ransid::Event::ScreenBuffer { .. } => (), - ransid::Event::Move {from_x, from_y, to_x, to_y, w, h } => { - let width = display.width; - let pixels = &mut display.offscreen; - - for raw_y in 0..h { - let y = if from_y > to_y { - raw_y - } else { - h - raw_y - 1 - }; - - for pixel_y in 0..16 { - { - let off_from = ((from_y + y) * 16 + pixel_y) * width + from_x * 8; - let off_to = ((to_y + y) * 16 + pixel_y) * width + to_x * 8; - let len = w * 8; - - if off_from + len <= pixels.len() && off_to + len <= pixels.len() { - unsafe { - let data_ptr = pixels.as_mut_ptr() as *mut u32; - ptr::copy(data_ptr.offset(off_from as isize), data_ptr.offset(off_to as isize), len); - } - } - } - } - - changed.insert(to_y + y); - } - }, - ransid::Event::Resize { .. } => (), - ransid::Event::Title { .. } => () - } - }); - } - - 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.display.invert(x * 8, y * 16, 8, 16); - self.changed.insert(y); - } - - Ok(buf.len()) - } - - fn seek(&mut self, _pos: isize, _whence: usize) -> Result { - Ok(0) - } - - fn sync(&mut self, onscreen: &mut [u32], stride: usize) { - let width = self.display.width; - for change in self.changed.iter() { - self.display.sync(0, change * 16, width, 16, onscreen, stride); - } - self.changed.clear(); - } - - fn redraw(&mut self, onscreen: &mut [u32], stride: usize) { - let width = self.display.width; - let height = self.display.height; - self.display.sync(0, 0, width, height, onscreen, stride); - self.changed.clear(); - } -}