use serde::{Deserialize, Serialize}; use std::collections::{BTreeSet, VecDeque}; use std::convert::TryInto; use std::io::{Read, Seek, SeekFrom, Write}; use wasmer_runtime_core::debug; use wasmer_wasi::{ state::{Fd, WasiFile, WasiFs, WasiFsError, ALL_RIGHTS, VIRTUAL_ROOT_FD}, types::*, }; use minifb::{Key, KeyRepeat, MouseButton, Scale, Window, WindowOptions}; mod util; use util::*; use std::cell::RefCell; std::thread_local! { pub(crate) static FRAMEBUFFER_STATE: RefCell = RefCell::new(FrameBufferState::new() ); } pub const MAX_X: u32 = 8192; pub const MAX_Y: u32 = 4320; #[derive(Debug, Serialize, Deserialize)] pub enum FrameBufferFileType { Buffer, Resolution, IndexDisplay, Input, } #[derive(Debug)] pub(crate) struct FrameBufferState { // double buffered pub data_1: Vec, pub data_2: Vec, pub x_size: u32, pub y_size: u32, pub front_buffer: bool, pub window: Window, pub last_mouse_pos: (u32, u32), pub inputs: VecDeque, pub keys_pressed: BTreeSet, } impl FrameBufferState { /// an arbitrary large number const MAX_INPUTS: usize = 128; 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, last_mouse_pos: (0, 0), inputs: VecDeque::with_capacity(Self::MAX_INPUTS), keys_pressed: BTreeSet::new(), } } fn create_window(x: usize, y: usize) -> Window { Window::new( "Wasmer Experimental FrameBuffer", x, y, WindowOptions { resize: true, scale: Scale::X4, ..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(()) } fn push_input_event(&mut self, input_event: InputEvent) -> Option<()> { if self.inputs.len() >= Self::MAX_INPUTS { return None; } self.inputs.push_back(input_event); Some(()) } pub fn fill_input_buffer(&mut self) -> Option<()> { let keys_pressed = self.keys_pressed.iter().cloned().collect::>(); for key in keys_pressed { if self.window.is_key_released(key) { self.keys_pressed.remove(&key); self.push_input_event(InputEvent::KeyRelease(key))?; } } let keys = self.window.get_keys_pressed(KeyRepeat::No)?; for key in keys { self.keys_pressed.insert(key.clone()); self.push_input_event(InputEvent::KeyPress(key))?; } let mouse_position = self.window.get_mouse_pos(minifb::MouseMode::Clamp)?; if mouse_position.0 as u32 != self.last_mouse_pos.0 || mouse_position.1 as u32 != self.last_mouse_pos.1 { self.last_mouse_pos = (mouse_position.0 as u32, mouse_position.1 as u32); self.push_input_event(InputEvent::MouseMoved( self.last_mouse_pos.0, self.last_mouse_pos.1, ))?; } if self.window.get_mouse_down(MouseButton::Left) { self.push_input_event(InputEvent::MouseEvent( mouse_position.0 as u32, mouse_position.1 as u32, MouseButton::Left, ))?; } if self.window.get_mouse_down(MouseButton::Right) { self.push_input_event(InputEvent::MouseEvent( mouse_position.0 as u32, mouse_position.1 as u32, MouseButton::Right, ))?; } if self.window.get_mouse_down(MouseButton::Middle) { self.push_input_event(InputEvent::MouseEvent( mouse_position.0 as u32, mouse_position.1 as u32, MouseButton::Middle, ))?; } Some(()) } pub fn draw(&mut self) { self.window .update_with_buffer(if self.front_buffer { &self.data_1[..] } else { &self.data_2[..] }) .expect("Internal error! Failed to draw to framebuffer"); } #[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 { 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, Serialize, Deserialize)] pub struct FrameBuffer { fb_type: FrameBufferFileType, cursor: u32, } impl Read for FrameBuffer { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let cursor = self.cursor as usize; FRAMEBUFFER_STATE.with(|fb| { let mut fb_state = fb.borrow_mut(); 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 => { let mut idx = 0; fb_state.fill_input_buffer(); while let Some(next_elem) = fb_state.inputs.front() { let remaining_length = buf.len() - idx; let (tag_byte, data, size) = bytes_for_input_event(*next_elem); if remaining_length > 1 + size { buf[idx] = tag_byte; for i in 0..size { buf[idx + 1 + i] = data[i]; } idx += 1 + size; } else { break; } fb_state.inputs.pop_front().unwrap(); } Ok(idx) } } }) } fn read_to_end(&mut self, _buf: &mut Vec) -> std::io::Result { unimplemented!() } fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result { 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 { match pos { SeekFrom::Current(offset) => { let result: std::io::Result = (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 { let cursor = self.cursor as usize; FRAMEBUFFER_STATE.with(|fb| { let mut fb_state = fb.borrow_mut(); 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 = 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::() .and_then(|n1| result[1].parse::().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, _ => (), } // TODO: probably remove this //fb_state.fill_input_buffer(); 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!() } } #[typetag::serde] 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 } fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError> { Ok(()) } fn unlink(&mut self) -> Result<(), WasiFsError> { panic!("TODO(mark): actually implement this"); } fn bytes_available(&self) -> Result { Ok(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/wasmerfb0".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, Fd::READ, "input".to_string(), ALL_RIGHTS, ALL_RIGHTS, 0, ) .map_err(|e| format!("fb: Failed to init framebuffer {:?}", e))?; debug!("Input open on fd {}", _fd); let _fd = fs .open_file_at( dev_fd, frame_buffer_file, Fd::READ | Fd::WRITE, "wasmerfb0".to_string(), ALL_RIGHTS, ALL_RIGHTS, 0, ) .map_err(|e| format!("fb: Failed to init framebuffer {:?}", e))?; debug!("Framebuffer open on fd {}", _fd); let _fd = fs .open_file_at( fb_fd, resolution_file, Fd::READ | Fd::WRITE, "virtual_size".to_string(), ALL_RIGHTS, ALL_RIGHTS, 0, ) .map_err(|e| format!("fb_resolution: Failed to init framebuffer {:?}", e))?; debug!("Framebuffer resolution open on fd {}", _fd); let _fd = fs .open_file_at( fb_fd, index_file, Fd::READ | Fd::WRITE, "buffer_index_display".to_string(), ALL_RIGHTS, ALL_RIGHTS, 0, ) .map_err(|e| format!("fb_index_display: Failed to init framebuffer {:?}", e))?; debug!("Framebuffer draw open on fd {}", _fd); Ok(()) }