mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-07 14:11:19 +00:00
433 lines
12 KiB
Rust
433 lines
12 KiB
Rust
|
use ref_thread_local::{ref_thread_local, refmanager::RefMut, RefThreadLocal};
|
||
|
use std::convert::TryInto;
|
||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||
|
use wasmer_wasi::state::{WasiFile, WasiFs, ALL_RIGHTS, VIRTUAL_ROOT_FD};
|
||
|
|
||
|
use minifb::{Key, Scale, Window, WindowOptions};
|
||
|
|
||
|
ref_thread_local! {
|
||
|
pub(crate) static managed FRAMEBUFFER_STATE: FrameBufferState =
|
||
|
FrameBufferState::new();
|
||
|
}
|
||
|
|
||
|
fn get_fb_state<'a>() -> RefMut<'a, FrameBufferState> {
|
||
|
FRAMEBUFFER_STATE.borrow_mut()
|
||
|
}
|
||
|
|
||
|
pub const MAX_X: u32 = 8192;
|
||
|
pub const MAX_Y: u32 = 4320;
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub enum FrameBufferFileType {
|
||
|
Buffer,
|
||
|
Resolution,
|
||
|
IndexDisplay,
|
||
|
Input,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub(crate) struct FrameBufferState {
|
||
|
// double buffered
|
||
|
pub data_1: Vec<u32>,
|
||
|
pub data_2: Vec<u32>,
|
||
|
|
||
|
pub x_size: u32,
|
||
|
pub y_size: u32,
|
||
|
pub front_buffer: bool,
|
||
|
|
||
|
pub window: Window,
|
||
|
}
|
||
|
|
||
|
impl FrameBufferState {
|
||
|
pub fn new() -> Self {
|
||
|
let x = 100;
|
||
|
let y = 200;
|
||
|
|
||
|
let window = Self::create_window(x, y);
|
||
|
|
||
|
Self {
|
||
|
data_1: vec![0; x * y],
|
||
|
data_2: vec![0; x * y],
|
||
|
|
||
|
x_size: x as u32,
|
||
|
y_size: y as u32,
|
||
|
front_buffer: true,
|
||
|
|
||
|
window,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn create_window(x: usize, y: usize) -> Window {
|
||
|
Window::new(
|
||
|
"Wasmer Experimental FrameBuffer",
|
||
|
x,
|
||
|
y,
|
||
|
WindowOptions {
|
||
|
resize: true,
|
||
|
scale: Scale::X1,
|
||
|
..WindowOptions::default()
|
||
|
},
|
||
|
)
|
||
|
.unwrap()
|
||
|
}
|
||
|
|
||
|
pub fn resize(&mut self, x: u32, y: u32) -> Option<()> {
|
||
|
if x >= MAX_X || y >= MAX_Y {
|
||
|
return None;
|
||
|
}
|
||
|
self.x_size = x;
|
||
|
self.y_size = x;
|
||
|
|
||
|
self.data_1.resize((x * y) as usize, 0);
|
||
|
self.data_2.resize((x * y) as usize, 0);
|
||
|
|
||
|
self.window = Self::create_window(x as usize, y as usize);
|
||
|
|
||
|
Some(())
|
||
|
}
|
||
|
|
||
|
pub fn draw(&mut self) {
|
||
|
self.window.update_with_buffer(if self.front_buffer {
|
||
|
&self.data_1[..]
|
||
|
} else {
|
||
|
&self.data_2[..]
|
||
|
});
|
||
|
}
|
||
|
|
||
|
pub fn get_buffer(&self) -> &[u32] {
|
||
|
if self.front_buffer {
|
||
|
&self.data_1[..]
|
||
|
} else {
|
||
|
&self.data_2[..]
|
||
|
}
|
||
|
}
|
||
|
pub fn get_buffer_mut(&mut self) -> &mut [u32] {
|
||
|
if self.front_buffer {
|
||
|
&mut self.data_1[..]
|
||
|
} else {
|
||
|
&mut self.data_2[..]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
// the real index into u32s and whether to use the front buffer or the back buffer
|
||
|
fn get_idx_info(&self, idx: usize) -> Option<(usize, bool)> {
|
||
|
let mut base_idx = idx / 4;
|
||
|
let mut front_buffer = true;
|
||
|
|
||
|
if base_idx >= self.data_1.len() {
|
||
|
base_idx -= self.data_1.len();
|
||
|
front_buffer = false;
|
||
|
|
||
|
if base_idx >= self.data_2.len() {
|
||
|
return None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Some((base_idx, front_buffer))
|
||
|
}
|
||
|
|
||
|
pub fn get_byte(&self, idx: usize) -> Option<u8> {
|
||
|
let (base_idx, front_buffer) = self.get_idx_info(idx)?;
|
||
|
|
||
|
let shift = idx % 4;
|
||
|
let shift_amt = 8 * shift;
|
||
|
|
||
|
if front_buffer {
|
||
|
Some((self.data_1[base_idx] >> shift_amt) as u8)
|
||
|
} else {
|
||
|
Some((self.data_2[base_idx] >> shift_amt) as u8)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn set_byte(&mut self, idx: usize, val: u8) -> Option<()> {
|
||
|
let (base_idx, front_buffer) = self.get_idx_info(idx)?;
|
||
|
|
||
|
let shift = idx % 4;
|
||
|
let shift_amt = 8 * shift;
|
||
|
|
||
|
if front_buffer {
|
||
|
self.data_1[base_idx] &= !(0xFF << shift_amt);
|
||
|
self.data_1[base_idx] |= ((val as u32) << shift_amt) & (0xFF << shift_amt);
|
||
|
} else {
|
||
|
self.data_2[base_idx] &= !(0xFF << shift_amt);
|
||
|
self.data_2[base_idx] |= ((val as u32) << shift_amt) & (0xFF << shift_amt);
|
||
|
}
|
||
|
Some(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub struct FrameBuffer {
|
||
|
fb_type: FrameBufferFileType,
|
||
|
cursor: u32,
|
||
|
}
|
||
|
|
||
|
impl Read for FrameBuffer {
|
||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||
|
let fb_state = get_fb_state();
|
||
|
let cursor = self.cursor as usize;
|
||
|
match self.fb_type {
|
||
|
FrameBufferFileType::Buffer => {
|
||
|
let mut bytes_copied = 0;
|
||
|
|
||
|
for i in 0..buf.len() {
|
||
|
if let Some(byte) = fb_state.get_byte(cursor + i) {
|
||
|
buf[i] = byte;
|
||
|
bytes_copied += 1;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.cursor += bytes_copied;
|
||
|
Ok(bytes_copied as usize)
|
||
|
}
|
||
|
FrameBufferFileType::Resolution => {
|
||
|
let resolution_data = format!("{}x{}", fb_state.x_size, fb_state.y_size);
|
||
|
|
||
|
let mut bytes = resolution_data.bytes().skip(cursor);
|
||
|
let bytes_to_copy = std::cmp::min(buf.len(), bytes.clone().count());
|
||
|
|
||
|
for i in 0..bytes_to_copy {
|
||
|
buf[i] = bytes.next().unwrap();
|
||
|
}
|
||
|
|
||
|
self.cursor += bytes_to_copy as u32;
|
||
|
Ok(bytes_to_copy)
|
||
|
}
|
||
|
|
||
|
FrameBufferFileType::IndexDisplay => {
|
||
|
if buf.len() == 0 {
|
||
|
Ok(0)
|
||
|
} else {
|
||
|
buf[0] = fb_state.front_buffer as u8 + b'0';
|
||
|
Ok(1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FrameBufferFileType::Input => {
|
||
|
// do input
|
||
|
unimplemented!()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Seek for FrameBuffer {
|
||
|
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
||
|
match pos {
|
||
|
SeekFrom::Current(offset) => {
|
||
|
let result: std::io::Result<u64> = (self.cursor as i64)
|
||
|
.checked_add(offset)
|
||
|
.and_then(|v| v.try_into().ok())
|
||
|
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::InvalidInput));
|
||
|
|
||
|
if let Ok(n) = result {
|
||
|
self.cursor = n as u32;
|
||
|
}
|
||
|
result
|
||
|
}
|
||
|
SeekFrom::Start(offset) => {
|
||
|
self.cursor = offset as u32;
|
||
|
Ok(offset)
|
||
|
}
|
||
|
SeekFrom::End(_) => unimplemented!("Seek from end not yet implemented"),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Write for FrameBuffer {
|
||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||
|
let mut fb_state = get_fb_state();
|
||
|
let cursor = self.cursor as usize;
|
||
|
match self.fb_type {
|
||
|
FrameBufferFileType::Buffer => {
|
||
|
let mut bytes_copied = 0;
|
||
|
|
||
|
for i in 0..buf.len() {
|
||
|
if fb_state.set_byte(cursor + i, buf[i]).is_none() {
|
||
|
// TODO: check if we should return an error here
|
||
|
break;
|
||
|
}
|
||
|
bytes_copied += 1;
|
||
|
}
|
||
|
|
||
|
self.cursor += bytes_copied;
|
||
|
Ok(bytes_copied as usize)
|
||
|
}
|
||
|
FrameBufferFileType::Resolution => {
|
||
|
let resolution_data = format!("{}x{}", fb_state.x_size, fb_state.y_size);
|
||
|
let mut byte_vec: Vec<u8> = resolution_data.bytes().collect();
|
||
|
let upper_limit = std::cmp::min(buf.len(), byte_vec.len() - cursor as usize);
|
||
|
|
||
|
for i in 0..upper_limit {
|
||
|
byte_vec[i] = buf[i];
|
||
|
}
|
||
|
|
||
|
let mut parse_str = String::new();
|
||
|
for b in byte_vec.iter() {
|
||
|
parse_str.push(*b as char);
|
||
|
}
|
||
|
let result: Vec<&str> = parse_str.split('x').collect();
|
||
|
if result.len() != 2 {
|
||
|
return Ok(0);
|
||
|
}
|
||
|
if let Ok((n1, n2)) = result[0]
|
||
|
.parse::<u32>()
|
||
|
.and_then(|n1| result[1].parse::<u32>().map(|n2| (n1, n2)))
|
||
|
{
|
||
|
if fb_state.resize(n1, n2).is_some() {
|
||
|
return Ok(upper_limit);
|
||
|
}
|
||
|
}
|
||
|
Ok(0)
|
||
|
}
|
||
|
|
||
|
FrameBufferFileType::IndexDisplay => {
|
||
|
if buf.len() == 0 {
|
||
|
Ok(0)
|
||
|
} else {
|
||
|
match buf[0] {
|
||
|
b'0' => fb_state.front_buffer = true,
|
||
|
b'1' => fb_state.front_buffer = false,
|
||
|
_ => (),
|
||
|
}
|
||
|
fb_state.draw();
|
||
|
Ok(1)
|
||
|
}
|
||
|
}
|
||
|
FrameBufferFileType::Input => Ok(0),
|
||
|
}
|
||
|
}
|
||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||
|
Ok(())
|
||
|
}
|
||
|
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||
|
self.write(buf).map(|_| ())
|
||
|
}
|
||
|
fn write_fmt(&mut self, _fmt: std::fmt::Arguments) -> std::io::Result<()> {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl WasiFile for FrameBuffer {
|
||
|
fn last_accessed(&self) -> u64 {
|
||
|
0
|
||
|
}
|
||
|
fn last_modified(&self) -> u64 {
|
||
|
0
|
||
|
}
|
||
|
fn created_time(&self) -> u64 {
|
||
|
0
|
||
|
}
|
||
|
fn size(&self) -> u64 {
|
||
|
0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn initialize(fs: &mut WasiFs) -> Result<(), String> {
|
||
|
let frame_buffer_file = Box::new(FrameBuffer {
|
||
|
fb_type: FrameBufferFileType::Buffer,
|
||
|
cursor: 0,
|
||
|
});
|
||
|
let resolution_file = Box::new(FrameBuffer {
|
||
|
fb_type: FrameBufferFileType::Resolution,
|
||
|
cursor: 0,
|
||
|
});
|
||
|
let index_file = Box::new(FrameBuffer {
|
||
|
fb_type: FrameBufferFileType::IndexDisplay,
|
||
|
cursor: 0,
|
||
|
});
|
||
|
let input_file = Box::new(FrameBuffer {
|
||
|
fb_type: FrameBufferFileType::Input,
|
||
|
cursor: 0,
|
||
|
});
|
||
|
|
||
|
let dev_fd = unsafe {
|
||
|
fs.open_dir_all(
|
||
|
VIRTUAL_ROOT_FD,
|
||
|
"dev".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb: Failed to create dev folder {:?}", e))?
|
||
|
};
|
||
|
|
||
|
let fb_fd = unsafe {
|
||
|
fs.open_dir_all(
|
||
|
VIRTUAL_ROOT_FD,
|
||
|
"sys/class/graphics/wasmerfb".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb: Failed to create dev folder {:?}", e))?
|
||
|
};
|
||
|
|
||
|
let fd = fs
|
||
|
.open_file_at(
|
||
|
dev_fd,
|
||
|
input_file,
|
||
|
"input".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb: Failed to init framebuffer {:?}", e))?;
|
||
|
|
||
|
println!("Input open on fd {}", fd);
|
||
|
|
||
|
let fd = fs
|
||
|
.open_file_at(
|
||
|
dev_fd,
|
||
|
frame_buffer_file,
|
||
|
"wasmerfb0".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb: Failed to init framebuffer {:?}", e))?;
|
||
|
|
||
|
println!("Framebuffer open on fd {}", fd);
|
||
|
|
||
|
let fd = fs
|
||
|
.open_file_at(
|
||
|
fb_fd,
|
||
|
resolution_file,
|
||
|
"virtual_size".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb_resolution: Failed to init framebuffer {:?}", e))?;
|
||
|
|
||
|
println!("Framebuffer resolution open on fd {}", fd);
|
||
|
|
||
|
let fd = fs
|
||
|
.open_file_at(
|
||
|
fb_fd,
|
||
|
index_file,
|
||
|
"buffer_index_display".to_string(),
|
||
|
ALL_RIGHTS,
|
||
|
ALL_RIGHTS,
|
||
|
0,
|
||
|
)
|
||
|
.map_err(|e| format!("fb_index_display: Failed to init framebuffer {:?}", e))?;
|
||
|
|
||
|
println!("Framebuffer draw open on fd {}", fd);
|
||
|
|
||
|
Ok(())
|
||
|
}
|