use crate::{Vec2, Vec3};
mod ffi {
pub use crate::ffi::{applet_v0 as v0, applet_v0::*};
pub use crate::ffi::{applet_v1 as v1, applet_v1::*};
pub use crate::ffi::{applet_v2 as v2, applet_v2::*};
pub use crate::ffi::{applet_v3 as v3, applet_v3::*};
pub use crate::ffi::{applet_v4 as v4, applet_v4::*};
pub use crate::ffi::{applet_v5 as v5, applet_v5::*};
}
pub use ffi::PlayerIdRepr as PlayerId;
pub use ffi::{
Axis, CursorMode, CursorShape, EventType, GamepadButton, KeyEventType, MouseButton,
MouseEventType, TaggedEvent, TouchEventType, VirtualKeyCode, WindowState,
};
pub mod input;
use std::mem::size_of;
#[doc(hidden)]
pub use crate::ffi::applet_v5::API as FFI_API;
#[derive(Debug, Clone, Copy)]
pub enum TouchInput {
Started {
finger_id: u32,
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
Ended {
finger_id: u32,
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
Move {
finger_id: u32,
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
RelativeMove {
finger_id: u32,
delta_logical_pixels: Vec2,
delta_angle: Vec2,
},
}
#[derive(Debug, Clone, Copy)]
pub enum MouseInput {
Move {
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
ButtonPress {
button: MouseButton,
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
ButtonRelease {
button: MouseButton,
pos: Vec2,
ray_origin: Vec3,
ray_dir: Vec3,
},
RelativeMove {
delta_logical_pixels: Vec2,
delta_angle: Vec2,
},
Scroll {
delta: Vec2,
},
}
#[derive(Clone, Copy, Debug)]
pub enum KeyInput {
KeyPress(VirtualKeyCode),
KeyRelease(VirtualKeyCode),
Text(char),
}
#[derive(Clone, Copy, Debug)]
pub struct AxisInput {
pub axis: Axis,
pub value: f32,
}
#[derive(Clone, Copy, Debug)]
pub struct GamepadButtonInput {
pub button: GamepadButton,
pub is_pressed: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct RawMidiInput {
pub timestamp: u64,
pub device_id: u32,
pub len: u32,
pub data: [u8; 8],
}
#[derive(Copy, Clone)]
pub enum EventEnum {
Key(KeyInput),
Mouse(MouseInput),
Touch(TouchInput),
Axis(AxisInput),
GamepadButton(GamepadButtonInput),
RawMidi(RawMidiInput),
}
impl EventEnum {
fn from_tagged(event: TaggedEvent) -> Option<Self> {
match event.ty {
ffi::EventType2::Key => Some(Self::Key(convert_key_event(&event.key()?))),
ffi::EventType2::Mouse => Some(Self::Mouse(convert_mouse_event(&event.mouse()?)?)),
ffi::EventType2::Touch => Some(Self::Touch(convert_touch_event(&event.touch()?)?)),
ffi::EventType2::Axis => Some(Self::Axis(convert_axis_event(&event.axis()?))),
ffi::EventType2::GamepadButton => Some(Self::GamepadButton(
convert_gamepad_button_event(&event.gamepad_button()?),
)),
ffi::EventType2::RawMidi => {
Some(Self::RawMidi(convert_raw_midi_event(&event.raw_midi()?)))
}
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum VirtualKeyboardCommand {
Show,
Hide,
}
#[derive(Copy, Clone)]
pub struct Applet {}
impl Applet {
pub fn input_events(self) -> Vec<EventEnum> {
self.input_events_player(0)
}
pub fn input_events_player(self, player_id: PlayerId) -> Vec<EventEnum> {
let num = ffi::events_count(player_id) as usize;
let mut events = vec![unsafe { std::mem::zeroed() }; num];
ffi::events_get(player_id, &mut events);
events
.into_iter()
.filter_map(EventEnum::from_tagged)
.collect()
}
pub fn window_state(self) -> Option<WindowState> {
self.window_state_player(0)
}
pub fn window_state_player(self, player_id: PlayerId) -> Option<WindowState> {
let window_states = get_events_by_type::<WindowState>(EventType::Window, player_id);
if window_states.is_empty() {
None
} else {
Some(window_states[0])
}
}
pub fn connected_players(self) -> Vec<PlayerId> {
get_events_by_type::<PlayerId>(EventType::Players, 0) }
pub fn game_delta_time(self) -> f32 {
ffi::delta_time()
}
pub fn game_time(self) -> f64 {
ffi::game_time()
}
pub fn real_time_since_start(self) -> f64 {
ffi::real_time_since_start()
}
pub fn set_cursor_mode(self, mode: CursorMode) {
ffi::set_cursor_mode(mode);
}
pub fn set_cursor_shape(self, shape: CursorShape) {
ffi::set_cursor_shape(shape);
}
pub fn random_seed_value(self) -> u128 {
*ffi::random_seed_value()
}
pub fn set_clipboard_string(self, string: &str) {
ffi::set_clipboard_string(string);
}
pub fn set_player_clipboard_string(self, player_id: PlayerId, string: &str) {
ffi::set_player_clipboard_string(player_id, string);
}
pub fn clipboard_string(self) -> Option<String> {
let len = ffi::get_clipboard_string();
if len == 0 {
return None;
};
let mut buffer = vec![0; len as usize];
ffi::retrieve_clipboard_string(&mut buffer);
Some(String::from_utf8(buffer).unwrap())
}
pub fn launch_argument(self) -> String {
ffi::v5::get_launch_argument_string()
}
pub fn launch_applet_with_url(self, url: &str) {
ffi::launch_applet_with_url(url);
}
pub fn simulation_rate(&self) -> f64 {
ffi::simulation_rate()
}
pub fn set_simulation_rate(&self, simulation_rate: f64) {
ffi::set_simulation_rate(simulation_rate);
}
pub fn request_quit(self) {
ffi::request_quit();
}
pub fn broadcast_message(msg: &str, duration: f32) {
ffi::broadcast_message(msg, duration);
}
pub fn show_virtual_keyboard(self, player_id: PlayerId, command: VirtualKeyboardCommand) {
let show = if command == VirtualKeyboardCommand::Show {
1
} else {
0
};
ffi::show_virtual_keyboard(player_id, show);
}
}
fn get_events_by_type<T>(event_type: EventType, player: PlayerId) -> Vec<T> {
let size = ffi::v4::event_size(event_type as u32, player) as usize;
let len = size / size_of::<T>();
assert_eq!(
len * size_of::<T>(),
size,
"FFI and module-side type definitions of {:?} ({}) are out-of-sync",
event_type,
std::any::type_name::<T>()
);
let mut inputs: Vec<T> = Vec::with_capacity(len);
let raw_slice: &mut [u8] =
unsafe { std::slice::from_raw_parts_mut(inputs.as_mut_ptr().cast::<u8>(), size) };
ffi::v4::retrieve_events(event_type as u32, player, raw_slice);
unsafe {
inputs.set_len(len);
}
inputs
}
fn convert_key_event(ffi_key: &ffi::KeyInput) -> KeyInput {
match ffi_key.event_type {
KeyEventType::KeyPress => KeyInput::KeyPress(ffi_key.code),
KeyEventType::KeyRelease => KeyInput::KeyRelease(ffi_key.code),
KeyEventType::Text => KeyInput::Text(ffi_key.text),
}
}
fn convert_mouse_event(ffi_mouse: &ffi::v5::MouseInput) -> Option<MouseInput> {
#[allow(clippy::wildcard_enum_match_arm)]
match ffi_mouse.event_type {
MouseEventType::ButtonPress => Some(MouseInput::ButtonPress {
button: ffi_mouse.button,
pos: Vec2::new(ffi_mouse.x, ffi_mouse.y),
ray_origin: ffi_mouse.ray_origin.into(),
ray_dir: ffi_mouse.ray_dir.into(),
}),
MouseEventType::ButtonRelease => Some(MouseInput::ButtonRelease {
button: ffi_mouse.button,
pos: Vec2::new(ffi_mouse.x, ffi_mouse.y),
ray_origin: ffi_mouse.ray_origin.into(),
ray_dir: ffi_mouse.ray_dir.into(),
}),
MouseEventType::Move => Some(MouseInput::Move {
pos: Vec2::new(ffi_mouse.x, ffi_mouse.y),
ray_origin: ffi_mouse.ray_origin.into(),
ray_dir: ffi_mouse.ray_dir.into(),
}),
MouseEventType::Scroll => Some(MouseInput::Scroll {
delta: Vec2::new(ffi_mouse.x, ffi_mouse.y),
}),
MouseEventType::RelativeMove => Some(MouseInput::RelativeMove {
delta_logical_pixels: Vec2::new(ffi_mouse.x, ffi_mouse.y),
delta_angle: ffi_mouse.delta_angle.into(),
}),
_ => None,
}
}
fn convert_touch_event(ffi_touch: &ffi::v5::TouchInput) -> Option<TouchInput> {
match ffi_touch.event_type {
TouchEventType::Started => Some(TouchInput::Started {
finger_id: ffi_touch.id,
pos: Vec2::new(ffi_touch.x, ffi_touch.y),
ray_origin: ffi_touch.ray_origin.into(),
ray_dir: ffi_touch.ray_dir.into(),
}),
TouchEventType::Ended => Some(TouchInput::Ended {
finger_id: ffi_touch.id,
pos: Vec2::new(ffi_touch.x, ffi_touch.y),
ray_origin: ffi_touch.ray_origin.into(),
ray_dir: ffi_touch.ray_dir.into(),
}),
TouchEventType::Move => Some(TouchInput::Move {
finger_id: ffi_touch.id,
pos: Vec2::new(ffi_touch.x, ffi_touch.y),
ray_origin: ffi_touch.ray_origin.into(),
ray_dir: ffi_touch.ray_dir.into(),
}),
TouchEventType::RelativeMove => Some(TouchInput::RelativeMove {
finger_id: ffi_touch.id,
delta_logical_pixels: Vec2::new(ffi_touch.x, ffi_touch.y),
delta_angle: ffi_touch.delta_angle.into(),
}),
TouchEventType::Still => None,
}
}
fn convert_axis_event(ffi_axis: &ffi::AxisInput) -> AxisInput {
AxisInput {
axis: ffi_axis.axis,
value: ffi_axis.value,
}
}
fn convert_gamepad_button_event(ffi_button: &ffi::GamepadButtonInput) -> GamepadButtonInput {
GamepadButtonInput {
button: ffi_button.button,
is_pressed: ffi_button.is_pressed,
}
}
fn convert_raw_midi_event(ffi_raw_midi: &ffi::RawMidiInput) -> RawMidiInput {
RawMidiInput {
timestamp: ffi_raw_midi.timestamp(),
data: ffi_raw_midi.data,
len: ffi_raw_midi.len,
device_id: ffi_raw_midi.device_id,
}
}