Merge branch 'graphics_rework' into 'master'
Move text console handling from vesad to a new fbcond daemon See merge request redox-os/drivers!128
This commit is contained in:
Generated
+44
-61
@@ -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",
|
||||
|
||||
@@ -8,6 +8,7 @@ members = [
|
||||
"block-io-wrapper",
|
||||
"common",
|
||||
"e1000d",
|
||||
"fbcond",
|
||||
"ided",
|
||||
"ihdad",
|
||||
"ixgbed",
|
||||
|
||||
@@ -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 = []
|
||||
@@ -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<Self> {
|
||||
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::<usize>()
|
||||
.unwrap_or(0);
|
||||
let height = path_parts
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.parse::<usize>()
|
||||
.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::<SyncRect>(),
|
||||
),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
display_fd_unmap(self.offscreen, self.width * self.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<R: Read, T: Copy>(mut r: R, buf: &mut [T]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
r.read(slice::from_raw_parts_mut(
|
||||
buf.as_mut_ptr() as *mut u8,
|
||||
buf.len() * mem::size_of::<T>(),
|
||||
))
|
||||
.map(|count| count / mem::size_of::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let vt_ids = env::args()
|
||||
.skip(1)
|
||||
.map(|arg| arg.parse().expect("invalid vt number"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>() {
|
||||
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<Packet>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<VtIndex, TextScreen>,
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, Handle>,
|
||||
pub inputd_handle: inputd::Handle,
|
||||
}
|
||||
|
||||
impl FbconScheme {
|
||||
pub fn new(vt_ids: &[usize], event_queue: &mut EventQueue<VtIndex>) -> 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<usize> {
|
||||
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<usize> {
|
||||
let vt_i = VtIndex(path_str.parse::<usize>().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<usize> {
|
||||
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<syscall::EventFlags> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
self.handles.remove(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
@@ -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<usize>,
|
||||
ctrl: bool,
|
||||
input: VecDeque<u8>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
if self.input.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.input.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextScreen {
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
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<usize> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(usize)]
|
||||
pub enum VtMode {
|
||||
Text = 0,
|
||||
Graphic = 1,
|
||||
Default = 2,
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
-274
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-13
@@ -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<FrameBuffer>, spec: &[bool]) -> ! {
|
||||
fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[()]) -> ! {
|
||||
let mut socket = File::create(":display.vesa").expect("vesad: failed to create display scheme");
|
||||
|
||||
let mut scheme = DisplayScheme::new(framebuffers, &spec);
|
||||
|
||||
+22
-67
@@ -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<FrameBuffer>,
|
||||
onscreens: Vec<&'static mut [u32]>,
|
||||
active: VtIndex,
|
||||
pub vts: BTreeMap<VtIndex, BTreeMap<ScreenIndex, Box<dyn Screen>>>,
|
||||
pub vts: BTreeMap<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>,
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, Handle>,
|
||||
pub inputd_handle: inputd::Handle,
|
||||
@@ -42,7 +37,7 @@ pub struct DisplayScheme {
|
||||
}
|
||||
|
||||
impl DisplayScheme {
|
||||
pub fn new(mut framebuffers: Vec<FrameBuffer>, spec: &[bool]) -> DisplayScheme {
|
||||
pub fn new(mut framebuffers: Vec<FrameBuffer>, 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::<VtIndex, BTreeMap<ScreenIndex, Box<dyn Screen>>>::new();
|
||||
let mut vts = BTreeMap::<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>::new();
|
||||
|
||||
for &vt_type in spec.iter() {
|
||||
let mut screens = BTreeMap::<ScreenIndex, Box<dyn Screen>>::new();
|
||||
for &() in spec.iter() {
|
||||
let mut screens = BTreeMap::<ScreenIndex, GraphicScreen>::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::<usize>().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<usize> {
|
||||
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<usize> {
|
||||
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<isize> {
|
||||
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<usize> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Event>,
|
||||
pub sync_rects: Vec<SyncRect>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
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::<Event>()) };
|
||||
|
||||
while i < event_buf.len() && ! self.input.is_empty() {
|
||||
event_buf[i] = self.input.pop_front().unwrap();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
pub fn can_read(&self) -> Option<usize> {
|
||||
if self.input.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.input.len() * mem::size_of::<Event>())
|
||||
}
|
||||
}
|
||||
|
||||
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>()
|
||||
)
|
||||
};
|
||||
|
||||
self.sync_rects.extend_from_slice(sync_rects);
|
||||
|
||||
Ok(sync_rects.len() * mem::size_of::<SyncRect>())
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<Event>,
|
||||
sync_rects: Vec<SyncRect>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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::<Event>()) };
|
||||
|
||||
while i < event_buf.len() && ! self.input.is_empty() {
|
||||
event_buf[i] = self.input.pop_front().unwrap();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
fn can_read(&self) -> Option<usize> {
|
||||
if self.input.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.input.len() * mem::size_of::<Event>())
|
||||
}
|
||||
}
|
||||
|
||||
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>()
|
||||
)
|
||||
};
|
||||
|
||||
self.sync_rects.extend_from_slice(sync_rects);
|
||||
|
||||
Ok(sync_rects.len() * mem::size_of::<SyncRect>())
|
||||
}
|
||||
|
||||
fn seek(&mut self, _pos: isize, _whence: usize) -> Result<usize> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<usize>;
|
||||
|
||||
fn input(&mut self, event: &Event);
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
|
||||
fn can_read(&self) -> Option<usize>;
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
|
||||
fn seek(&mut self, pos: isize, whence: usize) -> Result<usize>;
|
||||
|
||||
fn sync(&mut self, onscreen: &mut [u32], stride: usize);
|
||||
|
||||
fn redraw(&mut self, onscreen: &mut [u32], stride: usize);
|
||||
}
|
||||
@@ -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<usize>,
|
||||
pub ctrl: bool,
|
||||
pub input: VecDeque<u8>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
if self.input.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.input.len())
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
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<usize> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user