ark-api-ffi 0.16.0

Ark low-level Wasm FFI API
Documentation
define_api_id!(0xbb0f_0dfe_ff53_2a60, "applet-v5");

use crate::TransparentPad;
use bytemuck::CheckedBitPattern;
use bytemuck::NoUninit;
use num_enum::IntoPrimitive;
use num_enum::TryFromPrimitive;

pub use super::applet_v0::CursorMode;
pub use super::applet_v0::EventType;
pub use super::applet_v0::KeyEventType;
pub use super::applet_v0::KeyInput;
pub use super::applet_v0::MouseButton;
pub use super::applet_v0::MouseEventType;
pub use super::applet_v0::TouchEventType;
pub use super::applet_v0::VirtualKeyCode;
pub use super::applet_v0::WindowState;
pub use super::applet_v3::TimestepMode;
pub use super::applet_v4::CursorShape;
pub use super::applet_v4::PlayerIdRepr;

/// Axis input event axis enumeration.
#[repr(u32)]
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[non_exhaustive]
pub enum Axis {
    LeftStickX = 0, // range -1 to 1
    LeftStickY = 1,
    RightStickX = 2,
    RightStickY = 3,
    DpadX = 4, // On Android the DPAD is often exposed as an axis. So let's reserve values for that, but maybe convert to key events in the future.
    DpadY = 5,
    LeftShoulder = 6, // range 0 to 1
    RightShoulder = 7,
}

/// Describes a button of a gamepad controller.
#[repr(u32)]
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[non_exhaustive]
pub enum GamepadButton {
    North = 0,
    East = 1,
    South = 2,
    West = 3,
    Select = 4,
    Mode = 5,
    Start = 6,
    LeftBumper = 7,
    RightBumper = 8,
    LeftStick = 9,
    RightStick = 10,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct MouseInput {
    pub event_type: MouseEventType,
    /// If event_type == `ButtonPress` or `ButtonRelease`, indicates which button was pressed.
    pub button: MouseButton,
    /// Position, wheel or relative movement on the X axis. For positions, values are in logical pixels.
    pub x: f32,
    /// Position, wheel or relative movement on the Y axis. For positions, values are in logical pixels.
    pub y: f32,
    /// Only for [`MouseEventType::RelativeMove`].
    /// Rotation in radians, taking into account mouse sensitivity and optional invert y axis.
    pub delta_angle: [f32; 2],
    /// Ray origin (world space). Applicable for moves and button presses.
    pub ray_origin: [f32; 3],
    /// Ray direction (world space). Applicable for moves and button presses.
    pub ray_dir: [f32; 3],
}

crate::impl_checked_bit_pattern_for_transparent_pad!(MouseInput);

impl MouseInput {
    pub fn to_mouse_input_v0(self) -> super::applet_v0::MouseInput {
        super::applet_v0::MouseInput {
            event_type: self.event_type,
            button: self.button,
            x: self.x,
            y: self.y,
            ray_origin: self.ray_origin,
            ray_dir: self.ray_dir,
        }
    }
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct TouchInput {
    /// Represents a touch event
    pub event_type: TouchEventType,
    /// Unique identifier of a finger.
    pub id: u32,
    /// Position or relative movement on the X axis. For positions, values are in logical pixels.
    pub x: f32,
    /// Position or relative movement on the Y axis. For positions, values are in logical pixels.
    pub y: f32,
    /// Only for [`TouchEventType::RelativeMove`].
    /// Rotation in radians, taking into account mouse sensitivity and optional invert y axis.
    pub delta_angle: [f32; 2],
    /// Ray origin (world space). Applicable for moves and button presses.
    pub ray_origin: [f32; 3],
    /// Ray direction (world space). Applicable for moves and button presses.
    pub ray_dir: [f32; 3],
}

crate::impl_checked_bit_pattern_for_transparent_pad!(TouchInput);

impl TouchInput {
    pub fn to_touch_input_v0(self) -> super::applet_v0::TouchInput {
        super::applet_v0::TouchInput {
            event_type: self.event_type,
            id: self.id,
            x: self.x,
            y: self.y,
            ray_origin: self.ray_origin,
            ray_dir: self.ray_dir,
        }
    }
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct AxisInput {
    /// The axis that was moved.
    pub axis: Axis,
    /// The new value of the axis.
    pub value: f32,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(AxisInput);

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, NoUninit, CheckedBitPattern)]
pub struct GamepadButtonInput {
    /// Which gamepad button was pressed or released
    pub button: GamepadButton,
    /// True = pressed, false = released
    pub is_pressed: bool,
    pub _pad: [u8; 3],
}

crate::impl_checked_bit_pattern_for_transparent_pad!(GamepadButtonInput);

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, NoUninit, CheckedBitPattern)]
pub struct RawMidiInput {
    // TODO: Having padding problems with u64 in unions
    pub timestamp_lo: u32,
    pub timestamp_hi: u32,
    pub device_id: u32,
    pub len: u32,
    pub data: [u8; 8],
}

impl RawMidiInput {
    pub fn timestamp(&self) -> u64 {
        u64::from(self.timestamp_lo) | (u64::from(self.timestamp_hi) << 32)
    }
}

crate::impl_checked_bit_pattern_for_transparent_pad!(RawMidiInput);

#[repr(u32)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, NoUninit, CheckedBitPattern)]
#[non_exhaustive]
pub enum EventType2 {
    Key = 0,
    Mouse = 7,
    Touch = 8,
    Axis = 9,
    GamepadButton = 10,
    RawMidi = 11,
}

#[repr(C)]
#[derive(Copy, Clone, NoUninit, CheckedBitPattern)]
pub struct TaggedEvent {
    pub ty: EventType2,
    data: EventUnion,
}

impl TaggedEvent {
    pub fn new_key(key: KeyInput) -> Self {
        Self {
            ty: EventType2::Key,
            data: EventUnion {
                key: TransparentPad::new(key),
            },
        }
    }

    pub fn new_mouse(mouse: MouseInput) -> Self {
        Self {
            ty: EventType2::Mouse,
            data: EventUnion {
                mouse: TransparentPad::new(mouse),
            },
        }
    }

    pub fn new_touch(touch: TouchInput) -> Self {
        Self {
            ty: EventType2::Touch,
            data: EventUnion {
                touch: TransparentPad::new(touch),
            },
        }
    }

    pub fn new_axis(axis: AxisInput) -> Self {
        Self {
            ty: EventType2::Axis,
            data: EventUnion {
                axis: TransparentPad::new(axis),
            },
        }
    }

    pub fn new_gamepad_button(gamepad_button: GamepadButtonInput) -> Self {
        Self {
            ty: EventType2::GamepadButton,
            data: EventUnion {
                gamepad_button: TransparentPad::new(gamepad_button),
            },
        }
    }

    pub fn new_raw_midi(raw_midi: RawMidiInput) -> Self {
        Self {
            ty: EventType2::RawMidi,
            data: EventUnion {
                raw_midi: TransparentPad::new(raw_midi),
            },
        }
    }

    pub fn key(&self) -> Option<KeyInput> {
        if self.ty == EventType2::Key {
            Some(*self.data.as_key())
        } else {
            None
        }
    }

    pub fn mouse_v0(&self) -> Option<super::applet_v0::MouseInput> {
        self.mouse().map(MouseInput::to_mouse_input_v0)
    }

    pub fn mouse(&self) -> Option<MouseInput> {
        if self.ty == EventType2::Mouse {
            Some(*self.data.as_mouse())
        } else {
            None
        }
    }

    pub fn touch_v0(&self) -> Option<super::applet_v0::TouchInput> {
        self.touch().map(TouchInput::to_touch_input_v0)
    }

    pub fn touch(&self) -> Option<TouchInput> {
        if self.ty == EventType2::Touch {
            Some(*self.data.as_touch())
        } else {
            None
        }
    }

    pub fn axis(&self) -> Option<AxisInput> {
        if self.ty == EventType2::Axis {
            Some(*self.data.as_axis())
        } else {
            None
        }
    }

    pub fn gamepad_button(&self) -> Option<GamepadButtonInput> {
        if self.ty == EventType2::GamepadButton {
            Some(*self.data.as_gamepad_button())
        } else {
            None
        }
    }

    pub fn raw_midi(&self) -> Option<RawMidiInput> {
        if self.ty == EventType2::RawMidi {
            Some(*self.data.as_raw_midi())
        } else {
            None
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
#[ark_api_macros::ffi_union(size = 48, checked_accessors)]
pub union EventUnion {
    pub key: KeyInput,
    pub mouse: MouseInput,
    pub touch: TouchInput,
    pub axis: AxisInput,
    pub gamepad_button: GamepadButtonInput,
    pub raw_midi: RawMidiInput,
}

impl core::fmt::Debug for EventUnion {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let bytes = bytemuck::cast_ref::<Self, [u8; 48]>(self);
        write!(f, "EventUnion {{ <opaque>: {bytes:?} }}")
    }
}

#[ark_api_macros::ark_bindgen(imports = "ark-applet-v5")]
mod applet {
    use super::*;

    extern "C" {
        /// Get the module update and physics simulation frequency in Hz. Default is 60 Hz.
        #[deprecated_infallible]
        pub fn simulation_rate() -> f64;

        /// Set the module update and physics simulation frequency in Hz. Default is 60 Hz.
        #[deprecated_infallible]
        pub fn set_simulation_rate(simulation_rate: f64);

        /// Call first to know how many events to expect from `events_get`.
        #[deprecated_infallible]
        pub fn events_count(player_id: PlayerIdRepr) -> u64;

        /// Writes all events into `out_events` for a specific player (0 == local).
        ///
        /// Use `events_count` to know how many events to expect.
        #[deprecated_infallible]
        pub fn events_get(player_id: PlayerIdRepr, out_events: &mut [TaggedEvent]);

        /// Retrieves module launch argument string.
        ///
        /// This is an arbitrary string argument that can be passed in to the module from an Ark
        /// module link.
        ///
        /// note: This should really been called `launch_argument_string` (without the `get_`) prefix, should fix for a future breaking version
        #[deprecated_infallible]
        pub fn get_launch_argument_string() -> String;

        /// In multiplayer mode, broadcasts a message to all the players for the specified duration
        /// (in ms).
        pub fn broadcast_message(msg: &str, duration: f32);

        /// Request the virtual keyboard to be shown or not for the specified player
        ///
        /// - show is expected to be a bool, so either 0 or 1
        pub fn show_virtual_keyboard(player_id: PlayerIdRepr, show: u32);
    }
}

pub use applet::*;