diff --git a/lib/wasi-framebuffer/src/lib.rs b/lib/wasi-framebuffer/src/lib.rs index 99e371814..8fdae322f 100644 --- a/lib/wasi-framebuffer/src/lib.rs +++ b/lib/wasi-framebuffer/src/lib.rs @@ -1,18 +1,22 @@ -use ref_thread_local::{ref_thread_local, refmanager::RefMut, RefThreadLocal}; +use std::collections::VecDeque; 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}; +use minifb::{Key, KeyRepeat, MouseButton, Scale, Window, WindowOptions}; -ref_thread_local! { - pub(crate) static managed FRAMEBUFFER_STATE: FrameBufferState = - FrameBufferState::new(); +mod util; + +use util::*; + +use std::cell::RefCell; +std::thread_local! { + pub(crate) static FRAMEBUFFER_STATE: RefCell = + RefCell::new(FrameBufferState::new() +); } -fn get_fb_state<'a>() -> RefMut<'a, FrameBufferState> { - FRAMEBUFFER_STATE.borrow_mut() -} +use std::borrow::BorrowMut; pub const MAX_X: u32 = 8192; pub const MAX_Y: u32 = 4320; @@ -36,9 +40,15 @@ pub(crate) struct FrameBufferState { pub front_buffer: bool, pub window: Window, + + pub last_mouse_pos: (u32, u32), + pub inputs: VecDeque, } impl FrameBufferState { + /// an arbitrary large number + const MAX_INPUTS: usize = 128; + pub fn new() -> Self { let x = 100; let y = 200; @@ -54,6 +64,8 @@ impl FrameBufferState { front_buffer: true, window, + last_mouse_pos: (0, 0), + inputs: VecDeque::with_capacity(Self::MAX_INPUTS), } } @@ -64,7 +76,7 @@ impl FrameBufferState { y, WindowOptions { resize: true, - scale: Scale::X1, + scale: Scale::X4, ..WindowOptions::default() }, ) @@ -86,6 +98,56 @@ impl FrameBufferState { 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 = self.window.get_keys_pressed(KeyRepeat::Yes)?; + for key in keys { + 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[..] @@ -165,52 +227,70 @@ pub struct FrameBuffer { impl Read for FrameBuffer { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let fb_state = get_fb_state(); let cursor = self.cursor as usize; - match self.fb_type { - FrameBufferFileType::Buffer => { - let mut bytes_copied = 0; + 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; + 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 { - break; + buf[0] = fb_state.front_buffer as u8 + b'0'; + Ok(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); + FrameBufferFileType::Input => { + let mut idx = 0; + fb_state.fill_input_buffer(); - 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) + 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) } } - - FrameBufferFileType::Input => { - // do input - unimplemented!() - } - } + }) } fn read_to_end(&mut self, _buf: &mut Vec) -> std::io::Result { @@ -249,66 +329,70 @@ impl Seek for FrameBuffer { impl Write for FrameBuffer { fn write(&mut self, buf: &[u8]) -> std::io::Result { - let mut fb_state = get_fb_state(); let cursor = self.cursor as usize; - match self.fb_type { - FrameBufferFileType::Buffer => { - let mut bytes_copied = 0; + 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; + 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; } - 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); + 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); + for i in 0..upper_limit { + byte_vec[i] = buf[i]; } - } - Ok(0) - } - FrameBufferFileType::IndexDisplay => { - if buf.len() == 0 { + 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) - } else { - match buf[0] { - b'0' => fb_state.front_buffer = true, - b'1' => fb_state.front_buffer = false, - _ => (), - } - fb_state.draw(); - Ok(1) } + + 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), } - FrameBufferFileType::Input => Ok(0), - } + }) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) diff --git a/lib/wasi-framebuffer/src/util.rs b/lib/wasi-framebuffer/src/util.rs index 8b1378917..bfe7a06c2 100644 --- a/lib/wasi-framebuffer/src/util.rs +++ b/lib/wasi-framebuffer/src/util.rs @@ -1 +1,181 @@ +// input encoding +pub const KEY_PRESS: u8 = 1; +pub const MOUSE_MOVE: u8 = 2; +pub const MOUSE_PRESS_LEFT: u8 = 4; +pub const MOUSE_PRESS_RIGHT: u8 = 5; +pub const MOUSE_PRESS_MIDDLE: u8 = 7; + +use minifb::{Key, MouseButton}; + +#[derive(Debug, Clone, Copy)] +pub enum InputEvent { + KeyPress(Key), + MouseEvent(u32, u32, MouseButton), + MouseMoved(u32, u32), +} + +/// Returns the tag as the first return value +/// The data as the second return value +/// and the amount of data to read from it as the third value +pub fn bytes_for_input_event(input_event: InputEvent) -> (u8, [u8; 8], usize) { + let mut data = [0u8; 8]; + match input_event { + InputEvent::KeyPress(k) => { + data[0] = map_key_to_bytes(k); + (KEY_PRESS, data, 1) + } + InputEvent::MouseEvent(x, y, btn) => { + let tag = match btn { + MouseButton::Left => MOUSE_PRESS_LEFT, + MouseButton::Right => MOUSE_PRESS_RIGHT, + MouseButton::Middle => MOUSE_PRESS_MIDDLE, + }; + dbg!(x); + dbg!(y); + let x_bytes = x.to_le_bytes(); + for i in 0..4 { + data[i] = x_bytes[i]; + } + let y_bytes = y.to_le_bytes(); + for i in 0..4 { + data[i + 4] = y_bytes[i]; + } + (tag, data, 8) + } + InputEvent::MouseMoved(x, y) => { + let x_bytes = x.to_le_bytes(); + for i in 0..4 { + data[i] = x_bytes[i]; + } + let y_bytes = y.to_le_bytes(); + for i in 0..4 { + data[i + 4] = y_bytes[i]; + } + (MOUSE_MOVE, data, 8) + } + } +} + +pub fn map_key_to_bytes(key: Key) -> u8 { + match key { + Key::Key0 => b'0', + Key::Key1 => b'1', + Key::Key2 => b'2', + Key::Key3 => b'3', + Key::Key4 => b'4', + Key::Key5 => b'5', + Key::Key6 => b'6', + Key::Key7 => b'7', + Key::Key8 => b'8', + Key::Key9 => b'9', + Key::A => b'A', + Key::B => b'B', + Key::C => b'C', + Key::D => b'D', + Key::E => b'E', + Key::F => b'F', + Key::G => b'G', + Key::H => b'H', + Key::I => b'I', + Key::J => b'J', + Key::K => b'K', + Key::L => b'L', + Key::M => b'M', + Key::N => b'N', + Key::O => b'O', + Key::P => b'P', + Key::Q => b'Q', + Key::R => b'R', + Key::S => b'S', + Key::T => b'T', + Key::U => b'U', + Key::V => b'V', + Key::W => b'W', + Key::X => b'X', + Key::Y => b'Y', + Key::Z => b'Z', + Key::F1 => 131, + Key::F2 => 132, + Key::F3 => 133, + Key::F4 => 134, + Key::F5 => 135, + Key::F6 => 136, + Key::F7 => 137, + Key::F8 => 138, + Key::F9 => 139, + Key::F10 => 140, + Key::F11 => 141, + Key::F12 => 142, + Key::F13 => 143, + Key::F14 => 144, + Key::F15 => 145, + + Key::Down => 146, + Key::Left => 147, + Key::Right => 148, + Key::Up => 149, + Key::Apostrophe => b'\'', + Key::Backquote => b'`', + + Key::Backslash => b'\\', + Key::Comma => b',', + Key::Equal => b'=', + Key::LeftBracket => b'[', + Key::Minus => b'-', + Key::Period => b'.', + Key::RightBracket => b']', + Key::Semicolon => b';', + + Key::Slash => b'/', + Key::Backspace => 128, + Key::Delete => 127, + Key::End => 150, + Key::Enter => b'\n', + + Key::Escape => 28, + + Key::Home => 151, + Key::Insert => 152, + Key::Menu => 153, + + Key::PageDown => 154, + Key::PageUp => 155, + + Key::Pause => 156, + Key::Space => b' ', + Key::Tab => b'\t', + Key::NumLock => 157, + Key::CapsLock => 158, + Key::ScrollLock => 159, + Key::LeftShift => 160, + Key::RightShift => 161, + Key::LeftCtrl => 162, + Key::RightCtrl => 163, + + Key::NumPad0 => 170, + Key::NumPad1 => 171, + Key::NumPad2 => 172, + Key::NumPad3 => 173, + Key::NumPad4 => 174, + Key::NumPad5 => 175, + Key::NumPad6 => 176, + Key::NumPad7 => 177, + Key::NumPad8 => 178, + Key::NumPad9 => 179, + Key::NumPadDot => 180, + Key::NumPadSlash => 181, + Key::NumPadAsterisk => 182, + Key::NumPadMinus => 183, + Key::NumPadPlus => 184, + Key::NumPadEnter => 185, + + Key::LeftAlt => 186, + Key::RightAlt => 187, + + Key::LeftSuper => 188, + Key::RightSuper => 189, + + _ => 255, + } +} diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 657715441..23e5654b7 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -188,6 +188,10 @@ struct Run { #[structopt(flatten)] features: PrestandardFeatures, + #[cfg(feature = "experimental-framebuffer")] + #[structopt(long = "enable-experimental-framebuffer")] + enable_experimental_framebuffer: bool, + /// Application arguments #[structopt(name = "--", raw(multiple = "true"))] args: Vec, @@ -597,7 +601,13 @@ fn execute_wasm(options: &Run) -> Result<(), String> { options.pre_opened_directories.clone(), mapped_dirs, #[cfg(feature = "experimental-framebuffer")] - Some(Box::new(wasmer_wasi_framebuffer::initialize)), + { + if options.enable_experimental_framebuffer { + Some(Box::new(wasmer_wasi_framebuffer::initialize)) + } else { + None + } + }, #[cfg(not(feature = "experimental-framebuffer"))] None, );