summaryrefslogtreecommitdiff
path: root/src/script/wasm.rs
blob: 96190f6f3fca80f660731d5dff60438f9ba32993 (plain)
use std::time::UNIX_EPOCH;

use crossterm::{event::KeyCode, style::ContentStyle};
use wasmi::{Caller, Engine, Linker};

use crate::{buffer::Cell, script::ScriptContext};

fn add_cell(mut caller: Caller<&mut ScriptContext>, character: u32, location: u32, z: u32) {
	caller.data_mut().buffer.cells.push(Cell {
		character: char::from_u32(character).unwrap_or_default(),
		style: ContentStyle::new(),
		x: (location >> 16) as u16,
		y: (location & 0xffff) as u16,
		z: z as u8,
	});
}

fn window_size(caller: Caller<&mut ScriptContext>) -> u32 {
	let window_size = &caller.data().buffer.window_size;
	((window_size.rows as u32) << 16) | (window_size.columns as u32)
}

fn is_key_pressed_this_frame(caller: Caller<&mut ScriptContext>, key_code: u32) -> u32 {
	const BACKSPACE: u32 = 1 << 24;
	const ENTER: u32 = 2 << 24;
	const LEFT: u32 = 3 << 24;
	const RIGHT: u32 = 4 << 24;
	const UP: u32 = 5 << 24;
	const DOWN: u32 = 6 << 24;
	const HOME: u32 = 7 << 24;
	const END: u32 = 8 << 24;
	const PAGE_UP: u32 = 9 << 24;
	const PAGE_DOWN: u32 = 10 << 24;
	const TAB: u32 = 11 << 24;
	const BACK_TAB: u32 = 12 << 24;
	const DELETE: u32 = 13 << 24;
	const INSERT: u32 = 14 << 24;
	const F: u32 = 15 << 24;
	const CHAR: u32 = 16 << 24;
	const ESC: u32 = 17 << 24;
	let key_code = match key_code {
		0 => KeyCode::Null,
		BACKSPACE => KeyCode::Backspace,
		ENTER => KeyCode::Enter,
		LEFT => KeyCode::Left,
		RIGHT => KeyCode::Right,
		UP => KeyCode::Up,
		DOWN => KeyCode::Down,
		HOME => KeyCode::Home,
		END => KeyCode::End,
		PAGE_UP => KeyCode::PageUp,
		PAGE_DOWN => KeyCode::PageDown,
		TAB => KeyCode::Tab,
		BACK_TAB => KeyCode::BackTab,
		DELETE => KeyCode::Delete,
		INSERT => KeyCode::Insert,
		F..CHAR => KeyCode::F((key_code & 0xff) as u8),
		CHAR..ESC => KeyCode::Char(char::from_u32(key_code & 0xffffff).unwrap_or_default()),
		ESC => KeyCode::Esc,
		_ => KeyCode::Null,
	};

	if caller.data().input.is_key_pressed_this_frame(key_code) {
		1
	} else {
		0
	}
}

fn exit(mut caller: Caller<&mut ScriptContext>) {
	let _ = caller.data_mut().buffer.finish();
	std::process::exit(0);
}

fn random_u64() -> u64 {
	getrandom::u64().unwrap_or(4)
}

fn system_now(caller: Caller<&mut ScriptContext>) -> u64 {
	caller
		.data()
		.time
		.system_now()
		.duration_since(UNIX_EPOCH)
		.unwrap()
		.as_nanos() as u64
}

fn monotonic_now(caller: Caller<&mut ScriptContext>) -> u64 {
	caller.data().time.monotonic_now().as_nanos() as u64
}

fn delta_time(caller: Caller<&mut ScriptContext>) -> u64 {
	caller.data().time.delta_time().as_nanos() as u64
}

pub fn linker<'ctx>(
	engine: &Engine,
) -> Result<wasmi::Linker<&'ctx mut ScriptContext<'ctx>>, wasmi::Error> {
	macro_rules! add_fns {
		($linker: expr, [$($func: ident),*]) => {
			$($linker.func_wrap("tortuise", stringify!($func), $func)?;)*
		};
	}

	let mut linker = Linker::new(engine);
	add_fns!(
		linker,
		[
			add_cell,
			window_size,
			is_key_pressed_this_frame,
			exit,
			random_u64,
			system_now,
			monotonic_now,
			delta_time
		]
	);
	Ok(linker)
}