use serde::{Deserialize, Serialize}; use crate::ScriptContext; mod tortuise { #[link(wasm_import_module = "tortuise")] unsafe extern "C" { pub safe fn is_key_pressed_this_frame(context: u64, keycode: u32) -> u32; pub safe fn is_mouse_clicked_this_frame(context: u64) -> u32; pub safe fn mouse_location(context: u64) -> u32; pub safe fn scroll_direction_this_frame(context: u64) -> u32; pub safe fn is_button_pressed_this_frame(context: u64, gamepad_id: u32, button: u32) -> u32; pub safe fn is_button_released_this_frame( context: u64, gamepad_id: u32, button: u32, ) -> u32; pub safe fn is_button_down(context: u64, gamepad_id: u32, button: u32) -> u32; pub safe fn axis_value(context: u64, gamepad_id: u32, axis: u32) -> f32; pub safe fn is_gamepad_connected_this_frame(context: u64) -> u32; pub safe fn is_gamepad_connected(context: u64, gamepad_id: u32) -> u32; pub safe fn gamepad_power_info(context: u64, gamepad_id: u32) -> u32; pub safe fn is_focused(context: u64) -> u32; pub safe fn was_focused_this_frame(context: u64) -> u32; pub safe fn was_unfocused_this_frame(context: u64) -> u32; } } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct Position { pub x: u16, pub y: u16, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Direction { Up = 1, Down, Left, Right, } /// Represents a key. #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash, Serialize, Deserialize)] pub enum KeyCode { /// Backspace key (Delete on macOS, Backspace on other platforms). Backspace, /// Enter key. Enter, /// Left arrow key. Left, /// Right arrow key. Right, /// Up arrow key. Up, /// Down arrow key. Down, /// Home key. Home, /// End key. End, /// Page up key. PageUp, /// Page down key. PageDown, /// Tab key. Tab, /// Shift + Tab key. BackTab, /// Delete key. (Fn+Delete on macOS, Delete on other platforms) Delete, /// Insert key. Insert, /// F key. /// /// `KeyCode::F(1)` represents F1 key, etc. F(u8), /// A character. /// /// `KeyCode::Char('c')` represents `c` character, etc. Char(char), /// Null. Null, /// Escape key. Esc, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Button { // Action Pad South = 1, East = 2, North = 4, West = 5, C = 3, Z = 6, // Triggers LeftTrigger = 7, LeftTrigger2 = 9, RightTrigger = 8, RightTrigger2 = 10, // Menu Pad Select = 11, Start = 12, Mode = 13, // Sticks LeftThumb = 14, RightThumb = 15, // D-Pad DPadUp = 16, DPadDown = 17, DPadLeft = 18, DPadRight = 19, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Axis { LeftStickX = 1, LeftStickY = 2, LeftZ = 3, RightStickX = 4, RightStickY = 5, RightZ = 6, DPadX = 7, DPadY = 8, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum PowerInfo { Wired, Discharging(u8), Charging(u8), Charged, Unknown, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Gamepad(u32); impl From for Position { fn from(value: u32) -> Self { Self { x: (value & 0xffff) as u16, y: ((value >> 16) & 0xffff) as u16, } } } impl TryFrom for Direction { type Error = (); fn try_from(value: u32) -> Result { match value { 1 => Ok(Direction::Up), 2 => Ok(Direction::Down), 3 => Ok(Direction::Left), 4 => Ok(Direction::Right), _ => Err(()), } } } fn key_code_to_number(key_code: KeyCode) -> u32 { match key_code { KeyCode::Null => 0, KeyCode::Backspace => 1 << 24, KeyCode::Enter => 2 << 24, KeyCode::Left => 3 << 24, KeyCode::Right => 4 << 24, KeyCode::Up => 5 << 24, KeyCode::Down => 6 << 24, KeyCode::Home => 7 << 24, KeyCode::End => 8 << 24, KeyCode::PageUp => 9 << 24, KeyCode::PageDown => 10 << 24, KeyCode::Tab => 11 << 24, KeyCode::BackTab => 12 << 24, KeyCode::Delete => 13 << 24, KeyCode::Insert => 14 << 24, KeyCode::F(f) => 15 << 24 | f as u32, KeyCode::Char(c) => 16 << 24 | c as u32, KeyCode::Esc => 17 << 24, } } impl Gamepad { pub fn is_button_pressed_this_frame(&self, context: &ScriptContext, button: Button) -> bool { tortuise::is_button_pressed_this_frame(context.0, self.0, button as u32) > 0 } pub fn is_button_released_this_frame(&self, context: &ScriptContext, button: Button) -> bool { tortuise::is_button_released_this_frame(context.0, self.0, button as u32) > 0 } pub fn is_button_down(&self, context: &ScriptContext, button: Button) -> bool { tortuise::is_button_down(context.0, self.0, button as u32) > 0 } pub fn axis_value(&self, context: &ScriptContext, axis: Axis) -> f32 { tortuise::axis_value(context.0, self.0, axis as u32) } pub fn is_connected(&self, context: &ScriptContext) -> bool { tortuise::is_gamepad_connected(context.0, self.0) > 0 } pub fn power_info(&self, context: &ScriptContext) -> PowerInfo { const WIRED: u32 = 1 << 16; const DISCHARGING: u32 = 2 << 16; const CHARGING: u32 = 3 << 16; const CHARGED: u32 = 4 << 16; match tortuise::gamepad_power_info(context.0, self.0) { WIRED => PowerInfo::Wired, power @ DISCHARGING..CHARGING => PowerInfo::Discharging((power & 0xff) as u8), power @ CHARGING..CHARGED => PowerInfo::Charging((power & 0xff) as u8), CHARGED => PowerInfo::Charged, _ => PowerInfo::Unknown, } } } impl ScriptContext<'_> { pub fn is_key_pressed_this_frame(&self, key_code: KeyCode) -> bool { tortuise::is_key_pressed_this_frame(self.0, key_code_to_number(key_code)) > 0 } pub fn is_mouse_clicked_this_frame(&self) -> bool { tortuise::is_mouse_clicked_this_frame(self.0) > 0 } pub fn mouse_location(&self) -> Option { let location = tortuise::mouse_location(self.0); (location != u32::MAX).then(|| location.into()) } pub fn scroll_direction_this_frame(&self) -> Option { tortuise::scroll_direction_this_frame(self.0) .try_into() .ok() } pub fn is_gamepad_connected_this_frame(&self) -> Option { let gamepad_id = tortuise::is_gamepad_connected_this_frame(self.0); if gamepad_id == 0 { return None; } Some(Gamepad(gamepad_id)) } pub fn is_focused(&self) -> bool { tortuise::is_focused(self.0) > 0 } pub fn was_focused_this_frame(&self) -> bool { tortuise::was_focused_this_frame(self.0) > 0 } pub fn was_unfocused_this_frame(&self) -> bool { tortuise::was_unfocused_this_frame(self.0) > 0 } }