#![no_std]
#![warn(unused)]
#![warn(missing_debug_implementations)]
#![allow(unused_unsafe)]
#![allow(unreachable_code)]
#![allow(dead_code)]
#![cfg_attr(test, allow(unused_imports))]
#![warn(missing_docs)]
#![deny(intra_doc_link_resolution_failure)]
#[macro_use]
extern crate sunrise_libuser as libuser;
extern crate alloc;
#[macro_use]
extern crate lazy_static;
mod vbe;
mod terminal;
use crate::vbe::{VBEColor, FRAMEBUFFER, Framebuffer};
use core::cmp::{min, max};
use core::sync::atomic::{AtomicU32, Ordering};
use alloc::vec::Vec;
use alloc::boxed::Box;
use alloc::sync::{Arc, Weak};
use crate::libuser::syscalls;
use crate::libuser::futures::{WaitableManager, WorkQueue};
use crate::libuser::ipc::server::{port_handler, new_session_wrapper};
use sunrise_libuser::futures_rs::future::FutureObj;
use crate::libuser::types::*;
use spin::Mutex;
use crate::libuser::error::Error;
use crate::libuser::syscalls::MemoryPermissions;
use sunrise_libutils::align_up;
use libuser::mem::{find_free_address, PAGE_SIZE};
use crate::libuser::vi::{IBuffer as IBufferInterface, IBufferProxy, ViInterface as IViInterface};
use sunrise_libuser::window::Color;
use sunrise_libuser::twili::IPipeProxy;
#[derive(Default, Debug, Clone)]
struct ViInterface;
impl IViInterface for ViInterface {
fn create_buffer(&mut self, manager: WorkQueue<'static>, sharedmem: SharedMemory, top: i32, left: i32, width: u32, height: u32,) -> Result<IBufferProxy, Error> {
let size = align_up(width * height * 4, PAGE_SIZE as _);
let addr = find_free_address(size as _, PAGE_SIZE)?;
let mapped = sharedmem.map(addr, size as _, MemoryPermissions::READABLE)?;
let buf = IBuffer {
buffer: Arc::new(Buffer {
mem: mapped,
top,
left,
width,
height
})
};
BUFFERS.lock().push(Arc::downgrade(&buf.buffer));
let (server, client) = syscalls::create_session(false, 0)?;
let wrapper = new_session_wrapper(manager.clone(), server, buf, IBuffer::dispatch);
manager.spawn(FutureObj::new(Box::new(wrapper)));
Ok(IBufferProxy::from(client))
}
fn get_screen_resolution(&mut self, _manager: WorkQueue<'static>) -> Result<(u32, u32,), Error> {
let fb = FRAMEBUFFER.lock();
Ok((fb.width() as _, fb.height() as _))
}
fn get_font_height(&mut self, _manager: WorkQueue<'static>) -> Result<u32, Error> {
Ok(terminal::font_height() as u32)
}
fn create_terminal(&mut self, manager: WorkQueue<'static>, sharedmem: SharedMemory, top: i32, left: i32, width: u32, height: u32,) -> Result<IPipeProxy, Error> {
use terminal::{TerminalPipe, Terminal};
use sunrise_libuser::twili::IPipeAsync;
let terminal = TerminalPipe::new(Terminal::new(sharedmem, top, left, width, height)?);
let (server, client) = syscalls::create_session(false, 0)?;
let wrapper = new_session_wrapper(manager.clone(), server, terminal, TerminalPipe::dispatch);
manager.spawn(FutureObj::new(Box::new(wrapper)));
Ok(IPipeProxy::from(client))
}
}
static BUFFERS: Mutex<Vec<Weak<Buffer>>> = Mutex::new(Vec::new());
static BACKBUFFER_ARR: Mutex<[VBEColor; 3840 * 2160]> = Mutex::new([VBEColor::rgb(0, 0, 0); 3840 * 2160]);
fn get_intersect((atop, aleft, awidth, aheight): (u32, u32, u32, u32), (btop, bleft, bwidth, bheight): (u32, u32, u32, u32)) -> Option<(u32, u32, u32, u32)> {
if atop > (btop + bheight) || btop > atop + aheight {
return None
};
if aleft > (bleft + bwidth) || bleft > aleft + awidth {
return None
};
let top = max(atop, btop);
let left = max(aleft, bleft);
let height = min(atop + aheight, btop + bheight) - top;
let width = min(aleft + awidth, bleft + bwidth) - left;
Some((top, left, width, height))
}
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_wrap)]
fn draw(buf: &Buffer, framebuffer: &mut Framebuffer<'_>, top: u32, left: u32, width: u32, height: u32) {
#[allow(clippy::cast_ptr_alignment)]
fn cast_to_u32(mapped: &MappedSharedMemory) -> &[AtomicU32] {
unsafe {
core::slice::from_raw_parts(mapped.as_ptr() as *const AtomicU32, mapped.len() / 4)
}
}
let data = cast_to_u32(&buf.mem);
let (dtop, dleft, dwidth, dheight) = buf.get_real_bounds(framebuffer.width() as u32, framebuffer.height() as u32);
if let Some(intersect) = get_intersect((dtop, dleft, dwidth, dheight), (top, left, width, height)) {
let (top, left, width, height) = intersect;
let mut curtop = top;
while curtop < top + height {
let mut curleft = left;
while curleft < left + width {
let dataidx = ((curtop as i32 - buf.top) as u32 * buf.width + (curleft as i32 - buf.left) as u32) as usize;
let fbidx = framebuffer.get_px_offset(curleft as usize, curtop as usize) as usize;
let color = data[dataidx].load(Ordering::Relaxed);
let color: Color = unsafe {
core::mem::transmute(color)
};
framebuffer.get_fb()[fbidx] = VBEColor::rgb(color.r, color.g, color.b);
curleft += 1;
}
curtop += 1;
}
}
}
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_wrap)]
fn get_real_bounds((top, left, width, height): (i32, i32, u32, u32), framebuffer_width: u32, framebuffer_height: u32) -> (u32, u32, u32, u32) {
let dtop = min(max(top, 0) as u32, framebuffer_height);
let dleft = min(max(left, 0) as u32, framebuffer_width);
assert!(width < i32::max_value() as u32);
assert!(height < i32::max_value() as u32);
let dwidth = min(max(left + width as i32, 0) as u32, framebuffer_width) - dleft;
let dheight = min(max(top + height as i32, 0) as u32, framebuffer_height) - dtop;
(dtop, dleft, dwidth, dheight)
}
#[derive(Debug)]
#[allow(clippy::missing_docs_in_private_items)]
struct Buffer {
top: i32,
left: i32,
width: u32,
height: u32,
mem: MappedSharedMemory
}
impl Buffer {
fn get_real_bounds(&self, framebuffer_width: u32, framebuffer_height: u32) -> (u32, u32, u32, u32) {
get_real_bounds((self.top, self.left, self.width, self.height), framebuffer_width, framebuffer_height)
}
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
#[allow(clippy::cast_ptr_alignment)]
pub fn get_buffer(&self) -> &[AtomicU32] {
unsafe {
core::slice::from_raw_parts(self.mem.as_ptr() as *const AtomicU32, self.mem.len() / 4)
}
}
pub fn get_px_offset(&self, x: usize, y: usize) -> usize {
assert!(y < self.height() as usize, "{} {}", y, self.height());
assert!(x < self.width() as usize);
y * self.width() as usize + x
}
fn draw(&self) {
core::sync::atomic::fence(Ordering::Acquire);
let (fullscreen_width, fullscreen_height, bpp) = {
let fb = FRAMEBUFFER.lock();
(fb.width(), fb.height(), fb.bpp())
};
let mut backbuffer_arr = BACKBUFFER_ARR.lock();
let mut framebuffer = Framebuffer::new_buffer(&mut *backbuffer_arr, fullscreen_width, fullscreen_height, bpp);
let (dtop, dleft, dwidth, dheight) = self.get_real_bounds(framebuffer.width() as u32, framebuffer.height() as u32);
framebuffer.clear_at(dleft as _, dtop as _, dwidth as _, dheight as _);
BUFFERS.lock().retain(|buffer| {
if let Some(buffer) = buffer.upgrade() {
draw(&*buffer, &mut framebuffer, dtop, dleft, dwidth, dheight);
true
} else {
false
}
});
let screen_in_backbuffer = &mut framebuffer.get_fb()[0..(fullscreen_width * fullscreen_height)];
FRAMEBUFFER.lock().get_fb().copy_from_slice(screen_in_backbuffer);
}
}
#[derive(Debug, Clone)]
struct IBuffer {
buffer: Arc<Buffer>,
}
impl Drop for IBuffer {
fn drop(&mut self) {
let (fullscreen_width, fullscreen_height, bpp) = {
let fb = FRAMEBUFFER.lock();
(fb.width(), fb.height(), fb.bpp())
};
let mut backbuffer_arr = BACKBUFFER_ARR.lock();
let mut framebuffer = Framebuffer::new_buffer(&mut *backbuffer_arr, fullscreen_width, fullscreen_height, bpp);
let (dtop, dleft, dwidth, dheight) = self.buffer.get_real_bounds(framebuffer.width() as u32, framebuffer.height() as u32);
framebuffer.clear_at(dleft as _, dtop as _, dwidth as _, dheight as _);
BUFFERS.lock().retain(|buffer| {
if let Some(buffer) = buffer.upgrade() {
if Arc::ptr_eq(&self.buffer, &buffer) {
false
} else {
draw(&*buffer, &mut framebuffer, dtop, dleft, dwidth, dheight);
true
}
} else {
false
}
});
let screen_in_backbuffer = &mut framebuffer.get_fb()[0..(fullscreen_width * fullscreen_height)];
FRAMEBUFFER.lock().get_fb().copy_from_slice(screen_in_backbuffer);
}
}
impl IBufferInterface for IBuffer {
#[inline(never)]
fn draw(&mut self, _manager: WorkQueue<'static>) -> Result<(), Error> {
self.buffer.draw();
Ok(())
}
}
fn main() {
let mut man = WaitableManager::new();
let handler = port_handler(man.work_queue(), "vi:", ViInterface::dispatch).unwrap();
man.work_queue().spawn(FutureObj::new(Box::new(handler)));
man.run();
}
kip_header!(HEADER = sunrise_libuser::caps::KipHeader {
magic: *b"KIP1",
name: *b"vi\0\0\0\0\0\0\0\0\0\0",
title_id: 0x020000000000002D,
process_category: sunrise_libuser::caps::ProcessCategory::KernelBuiltin,
main_thread_priority: 0,
default_cpu_core: 0,
flags: 0,
reserved: 0,
stack_page_count: 16,
});
capabilities!(CAPABILITIES = Capabilities {
svcs: [
sunrise_libuser::syscalls::nr::SleepThread,
sunrise_libuser::syscalls::nr::ExitProcess,
sunrise_libuser::syscalls::nr::CloseHandle,
sunrise_libuser::syscalls::nr::WaitSynchronization,
sunrise_libuser::syscalls::nr::OutputDebugString,
sunrise_libuser::syscalls::nr::SetThreadArea,
sunrise_libuser::syscalls::nr::ReplyAndReceiveWithUserBuffer,
sunrise_libuser::syscalls::nr::AcceptSession,
sunrise_libuser::syscalls::nr::CreateSession,
sunrise_libuser::syscalls::nr::ClearEvent,
sunrise_libuser::syscalls::nr::ConnectToNamedPort,
sunrise_libuser::syscalls::nr::SendSyncRequestWithUserBuffer,
sunrise_libuser::syscalls::nr::SetHeapSize,
sunrise_libuser::syscalls::nr::QueryMemory,
sunrise_libuser::syscalls::nr::MapSharedMemory,
sunrise_libuser::syscalls::nr::UnmapSharedMemory,
sunrise_libuser::syscalls::nr::MapFramebuffer,
],
});
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_get_real_bounds_normal() {
assert_eq!(get_real_bounds((0, 0, 1280, 800), 1280, 800), (0, 0, 1280, 800));
assert_eq!(get_real_bounds((500, 700, 20, 150), 1280, 800), (500, 700, 20, 150));
}
#[test]
fn check_get_real_bounds_width() {
assert_eq!(get_real_bounds((0, 0, 1500, 800), 1280, 800), (0, 0, 1280, 800));
}
#[test]
fn check_get_real_bounds_height() {
assert_eq!(get_real_bounds((0, 0, 1280, 1000), 1280, 800), (0, 0, 1280, 800));
}
#[test]
fn check_get_real_bounds_top() {
assert_eq!(get_real_bounds((-15, 0, 1280, 50), 1280, 800), (0, 0, 1280, 35));
assert_eq!(get_real_bounds((-15, 0, 1280, 800), 1280, 800), (0, 0, 1280, 785));
}
#[test]
fn check_get_real_bounds_left() {
assert_eq!(get_real_bounds((0, -15, 50, 800), 1280, 800), (0, 0, 35, 800));
assert_eq!(get_real_bounds((0, -15, 1280, 800), 1280, 800), (0, 0, 1265, 800));
}
#[test]
fn check_get_real_bounds_leftwidth() {
assert_eq!(get_real_bounds((0, -15, 1580, 800), 1280, 800), (0, 0, 1280, 800));
}
#[test]
fn check_get_real_bounds_topheight() {
assert_eq!(get_real_bounds((-15, 0, 1280, 1000), 1280, 800), (0, 0, 1280, 800));
}
#[test]
fn check_get_real_bounds_pedantic() {
assert_eq!(get_real_bounds((-5000, 0, 1280, 1000), 1280, 800), (0, 0, 1280, 0));
}
}