use bitflags::bitflags;
use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr::NonNull};
use crate::{bind, event::joystick::Joystick, Result, Sdl, SdlError};
pub mod direction;
pub mod effect;
mod joystick;
mod mouse;
mod playing;
pub mod rumble;
pub use joystick::*;
pub use mouse::*;
pub use playing::*;
use self::effect::HapticEffect;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HapticProperty: u32 {
const CONSTANT = 1 << 0;
const SINE = 1 << 1;
const LEFT_RIGHT = 1 << 2;
const TRIANGLE = 1 << 3;
const SAW_TOOTH_UP = 1 << 4;
const SAW_TOOTH_DOWN = 1 << 5;
const RAMP = 1 << 6;
const CUSTOM = 1 << 11;
const GAIN = 1 << 12;
const AUTO_CENTER = 1 << 13;
const STATUS = 1 << 14;
const PAUSE = 1 << 15;
}
}
pub struct Haptic {
ptr: NonNull<bind::SDL_Haptic>,
}
impl std::fmt::Debug for Haptic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Haptic")
.field("name", &self.name())
.finish_non_exhaustive()
}
}
impl Haptic {
#[must_use]
pub fn name(&self) -> String {
let index = unsafe { bind::SDL_HapticIndex(self.ptr.as_ptr()) };
let cstr = unsafe { CStr::from_ptr(bind::SDL_HapticName(index)) };
cstr.to_string_lossy().to_string()
}
#[must_use]
pub fn num_axes(&self) -> u32 {
unsafe { bind::SDL_HapticNumAxes(self.ptr.as_ptr()) as u32 }
}
#[must_use]
pub fn is_effect_supported(&self, effect: &HapticEffect) -> bool {
let mut raw = effect.clone().into_raw();
unsafe {
bind::SDL_HapticEffectSupported(self.ptr.as_ptr(), &mut raw) as bind::SDL_bool
== bind::SDL_TRUE
}
}
pub fn new_effect(&self, effect: &HapticEffect) -> Result<PendingEffect> {
if !self.is_effect_supported(effect) {
return Err(SdlError::UnsupportedFeature);
}
let mut raw = effect.clone().into_raw();
let ret = unsafe { bind::SDL_HapticNewEffect(self.ptr.as_ptr(), &mut raw) };
if ret < 0 {
Err(SdlError::Others { msg: Sdl::error() })
} else {
Ok(PendingEffect::new(ret, self))
}
}
#[must_use]
pub fn effects_creation_capacity(&self) -> usize {
unsafe { bind::SDL_HapticNumEffects(self.ptr.as_ptr()) as usize }
}
#[must_use]
pub fn effects_playing_capacity(&self) -> usize {
unsafe { bind::SDL_HapticNumEffectsPlaying(self.ptr.as_ptr()) as usize }
}
pub fn stop_all_effect(&self) {
unsafe {
bind::SDL_HapticStopAll(self.ptr.as_ptr());
}
}
pub fn set_gain(&self, gain: u32) {
if !self.property().contains(HapticProperty::GAIN) {
return;
}
let ret = unsafe { bind::SDL_HapticSetGain(self.ptr.as_ptr(), gain.min(100) as c_int) };
if ret < 0 {
eprintln!("{}", Sdl::error());
}
}
pub fn set_auto_center(&self, auto_center: u32) {
if !self.property().contains(HapticProperty::AUTO_CENTER) {
return;
}
let ret = unsafe {
bind::SDL_HapticSetAutocenter(self.ptr.as_ptr(), auto_center.min(100) as c_int)
};
if ret < 0 {
eprintln!("{}", Sdl::error());
}
}
#[must_use]
pub fn property(&self) -> HapticProperty {
let bits = unsafe { bind::SDL_HapticQuery(self.ptr.as_ptr()) };
HapticProperty::from_bits(bits).unwrap()
}
#[must_use]
pub fn pause(self) -> PausedHaptic {
unsafe {
bind::SDL_HapticPause(self.ptr.as_ptr());
}
PausedHaptic { haptic: self }
}
}
pub struct PausedHaptic {
haptic: Haptic,
}
impl PausedHaptic {
#[must_use]
pub fn unpause(self) -> Haptic {
unsafe {
bind::SDL_HapticUnpause(self.haptic.ptr.as_ptr());
}
self.haptic
}
}
pub struct HapticSet(Vec<Haptic>);
impl HapticSet {
#[must_use]
pub fn new() -> Self {
let num_haptics = unsafe {
bind::SDL_InitSubSystem(bind::SDL_INIT_HAPTIC);
bind::SDL_NumHaptics()
};
Self(
(0..num_haptics)
.filter_map(|index| {
let ptr = unsafe { bind::SDL_HapticOpen(index) };
NonNull::new(ptr).map(|ptr| Haptic { ptr })
})
.collect(),
)
}
#[must_use]
pub fn haptics(&self) -> &[Haptic] {
&self.0
}
}
impl Default for HapticSet {
fn default() -> Self {
Self::new()
}
}
impl Drop for HapticSet {
fn drop(&mut self) {
for haptic in &self.0 {
unsafe { bind::SDL_HapticClose(haptic.ptr.as_ptr()) }
}
}
}