use num_traits::FromPrimitive;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::{fs, io};
pub mod color;
use color::*;
pub struct FrameBufferCtx {
frame_buffer_handle: File,
resolution_handle: File,
index_handle: File,
input_handle: File,
resolution: (u32, u32),
}
impl FrameBufferCtx {
pub fn new(x: u32, y: u32) -> Result<Self, String> {
Self::new_inner(x, y).map_err(|e| {
format!(
"Failed to open files at `/_wasmer/dev/fb0`: \"{}\". These are \
non-standard files. If you're using Wasmer, please ensure that \
you've updated to version 0.13.1 and are using the \
`--enable-experimental-io-devices` flag.",
e.to_string()
)
})
}
fn new_inner(x: u32, y: u32) -> io::Result<Self> {
let frame_buffer_handle = fs::OpenOptions::new()
.read(true)
.write(true)
.open("/_wasmer/dev/fb0/fb")?;
let mut resolution_handle = fs::OpenOptions::new()
.read(true)
.write(true)
.open("/_wasmer/dev/fb0/virtual_size")?;
let index_handle = fs::OpenOptions::new()
.read(true)
.write(true)
.open("/_wasmer/dev/fb0/draw")?;
let input_handle = fs::OpenOptions::new()
.read(true)
.open("/_wasmer/dev/fb0/input")?;
resolution_handle.write(format!("{}x{}", x, y).as_bytes())?;
Ok(Self {
frame_buffer_handle,
resolution_handle,
index_handle,
input_handle,
resolution: (x, y),
})
}
pub fn get_input(&mut self) -> Option<InputIter> {
let mut input_vec = vec![];
self.input_handle.read_to_end(&mut input_vec).ok()?;
Some(InputIter {
idx: 0,
bytes: input_vec,
})
}
pub fn set_resolution(&mut self, x: u32, y: u32) -> Option<()> {
self.resolution_handle
.write(format!("{}x{}", x, y).as_bytes())
.ok()?;
self.resolution.0 = x;
self.resolution.1 = y;
Some(())
}
pub fn draw(&mut self) -> Option<()> {
self.index_handle.write(&[b'0']).ok()?;
Some(())
}
pub fn update_pixels<I>(&mut self, x: u32, y: u32, pixels: I) -> Option<u32>
where
I: Iterator<Item = RGBA>,
{
self.frame_buffer_handle
.seek(SeekFrom::Start((self.resolution.0 * y + x) as u64 * 4))
.ok()?;
let mut bytes_written = 0;
for pixel in pixels {
bytes_written += self.frame_buffer_handle.write(&pixel.as_bytes()).ok()?;
}
Some(bytes_written as u32)
}
}
const KEY_PRESS: u8 = 1;
const MOUSE_MOVE: u8 = 2;
const KEY_RELEASE: u8 = 3;
const MOUSE_PRESS_LEFT: u8 = 4;
const MOUSE_PRESS_RIGHT: u8 = 5;
const MOUSE_PRESS_MIDDLE: u8 = 7;
const WINDOW_CLOSED: u8 = 8;
pub struct InputIter {
idx: usize,
bytes: Vec<u8>,
}
impl Iterator for InputIter {
type Item = InputEvent;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.bytes.len() {
return None;
}
match self.bytes[self.idx] {
KEY_PRESS => {
if self.bytes.len() >= self.idx + 2 {
if let Some(key) = FromPrimitive::from_u8(self.bytes[self.idx + 1]) {
self.idx += 2;
return Some(InputEvent::KeyPress(key));
}
}
}
KEY_RELEASE => {
if self.bytes.len() >= self.idx + 2 {
if let Some(key) = FromPrimitive::from_u8(self.bytes[self.idx + 1]) {
self.idx += 2;
return Some(InputEvent::KeyRelease(key));
}
}
}
MOUSE_MOVE | MOUSE_PRESS_LEFT | MOUSE_PRESS_RIGHT | MOUSE_PRESS_MIDDLE => {
if self.bytes.len() >= self.idx + 9 {
let mut byte_array_x = [0u8; 4];
let mut byte_array_y = [0u8; 4];
for i in 0..4 {
byte_array_x[i] = self.bytes[self.idx + 1 + i];
byte_array_y[i] = self.bytes[self.idx + 1 + 4 + i];
}
let x = u32::from_le_bytes(byte_array_x);
let y = u32::from_le_bytes(byte_array_y);
let event_type = match self.bytes[self.idx] {
MOUSE_MOVE => MouseEventType::Move,
MOUSE_PRESS_LEFT => MouseEventType::LeftClick,
MOUSE_PRESS_RIGHT => MouseEventType::RightClick,
MOUSE_PRESS_MIDDLE => MouseEventType::MiddleClick,
_ => unreachable!("Fatal internal logic error in input event parsing"),
};
self.idx += 9;
return Some(InputEvent::MouseEvent(x, y, event_type));
}
}
WINDOW_CLOSED => {
return Some(InputEvent::WindowClosed);
}
_ => {
return None;
}
}
None
}
}
#[derive(Debug, FromPrimitive, ToPrimitive)]
#[repr(u8)]
pub enum Key {
Backspace = 8,
Tab = 9,
Enter = 13,
Shift = 16,
Ctrl = 17,
Alt = 18,
Pause = 19,
CapsLock = 20,
Escape = 27,
Space = 32,
PageUp = 33,
PageDown = 34,
End = 35,
Home = 36,
Left = 37,
Up = 38,
Right = 39,
Down = 40,
Insert = 45,
Delete = 46,
Key0 = 48,
Key1 = 49,
Key2 = 50,
Key3 = 51,
Key4 = 52,
Key5 = 53,
Key6 = 54,
Key7 = 55,
Key8 = 56,
Key9 = 57,
A = b'A',
B = b'B',
C = b'C',
D = b'D',
E = b'E',
F = b'F',
G = b'G',
H = b'H',
I = b'I',
J = b'J',
K = b'K',
L = b'L',
M = b'M',
N = b'N',
O = b'O',
P = b'P',
Q = b'Q',
R = b'R',
S = b'S',
T = b'T',
U = b'U',
V = b'V',
W = b'W',
X = b'X',
Y = b'Y',
Z = b'Z',
LeftSuper = 91,
RightSuper = 92,
NumPad0 = 96,
NumPad1 = 97,
NumPad2 = 98,
NumPad3 = 99,
NumPad4 = 100,
NumPad5 = 101,
NumPad6 = 102,
NumPad7 = 103,
NumPad8 = 104,
NumPad9 = 105,
NumPadAsterisk = 106,
NumPadPlus = 107,
NumPadMinus = 109,
NumPadDot = 110,
NumPadSlash = 111,
F1 = 112,
F2 = 113,
F3 = 114,
F4 = 115,
F5 = 116,
F6 = 117,
F7 = 118,
F8 = 119,
F9 = 120,
F10 = 121,
F11 = 122,
F12 = 123,
NumLock = 144,
ScrollLock = 145,
Semicolon = 186,
Equal = 187,
Comma = 188,
Minus = 189,
Period = 190,
Slash = 191,
Backquote = 192,
Backslash = 220,
Apostrophe = 222,
LeftBracket = 219,
RightBracket = 221,
Unknown = 255,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MouseEventType {
LeftClick,
RightClick,
MiddleClick,
Move,
}
#[derive(Debug)]
pub enum InputEvent {
KeyPress(Key),
KeyRelease(Key),
MouseEvent(u32, u32, MouseEventType),
WindowClosed,
}