use libc::c_char;
use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::mem::transmute;
use std::rc::Rc;
use crate::sys;
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
NoMemError = sys::SDL_errorcode::SDL_ENOMEM as i32,
ReadError = sys::SDL_errorcode::SDL_EFREAD as i32,
WriteError = sys::SDL_errorcode::SDL_EFWRITE as i32,
SeekError = sys::SDL_errorcode::SDL_EFSEEK as i32,
UnsupportedError = sys::SDL_errorcode::SDL_UNSUPPORTED as i32,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
NoMemError => write!(f, "Out of memory"),
ReadError => write!(f, "Error reading from datastream"),
WriteError => write!(f, "Error writing to datastream"),
SeekError => write!(f, "Error seeking in datastream"),
UnsupportedError => write!(f, "Unknown SDL error"),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
use self::Error::*;
match *self {
NoMemError => "out of memory",
ReadError => "error reading from datastream",
WriteError => "error writing to datastream",
SeekError => "error seeking in datastream",
UnsupportedError => "unknown SDL error",
}
}
}
use std::sync::atomic::AtomicBool;
static IS_SDL_CONTEXT_ALIVE: AtomicBool = AtomicBool::new(false);
#[derive(Clone)]
pub struct Sdl {
sdldrop: Rc<SdlDrop>,
}
impl Sdl {
#[inline]
#[doc(alias = "SDL_Init")]
fn new() -> Result<Sdl, String> {
unsafe {
use std::sync::atomic::Ordering;
let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);
if was_alive {
Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
} else if sys::SDL_Init(0) == 0 {
Ok(Sdl {
sdldrop: Rc::new(SdlDrop),
})
} else {
IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
Err(get_error())
}
}
}
#[inline]
pub fn audio(&self) -> Result<AudioSubsystem, String> {
AudioSubsystem::new(self)
}
#[inline]
pub fn event(&self) -> Result<EventSubsystem, String> {
EventSubsystem::new(self)
}
#[inline]
pub fn joystick(&self) -> Result<JoystickSubsystem, String> {
JoystickSubsystem::new(self)
}
#[inline]
pub fn haptic(&self) -> Result<HapticSubsystem, String> {
HapticSubsystem::new(self)
}
#[inline]
pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> {
GameControllerSubsystem::new(self)
}
#[inline]
pub fn sensor(&self) -> Result<SensorSubsystem, String> {
SensorSubsystem::new(self)
}
#[inline]
pub fn timer(&self) -> Result<TimerSubsystem, String> {
TimerSubsystem::new(self)
}
#[inline]
pub fn video(&self) -> Result<VideoSubsystem, String> {
VideoSubsystem::new(self)
}
#[inline]
pub fn event_pump(&self) -> Result<EventPump, String> {
EventPump::new(self)
}
#[inline]
#[doc(hidden)]
pub fn sdldrop(&self) -> Rc<SdlDrop> {
self.sdldrop.clone()
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct SdlDrop;
impl Drop for SdlDrop {
#[inline]
#[doc(alias = "SDL_Quit")]
fn drop(&mut self) {
use std::sync::atomic::Ordering;
let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
assert!(was_alive);
unsafe {
sys::SDL_Quit();
}
}
}
macro_rules! subsystem {
($name:ident, $flag:expr) => {
impl $name {
#[inline]
#[doc(alias = "SDL_InitSubSystem")]
fn new(sdl: &Sdl) -> Result<$name, String> {
let result = unsafe { sys::SDL_InitSubSystem($flag) };
if result == 0 {
Ok($name {
_subsystem_drop: Rc::new(SubsystemDrop {
_sdldrop: sdl.sdldrop.clone(),
flag: $flag,
}),
})
} else {
Err(get_error())
}
}
}
};
($name:ident, $flag:expr, nosync) => {
#[derive(Debug, Clone)]
pub struct $name {
/// Subsystems cannot be moved or (usually) used on non-main threads.
_subsystem_drop: Rc<SubsystemDrop>,
}
impl $name {
#[inline]
pub fn sdl(&self) -> Sdl {
Sdl {
sdldrop: self._subsystem_drop._sdldrop.clone(),
}
}
}
subsystem!($name, $flag);
};
($name:ident, $flag:expr, sync) => {
pub struct $name {
/// Subsystems cannot be moved or (usually) used on non-main threads.
_subsystem_drop: Rc<SubsystemDrop>,
}
unsafe impl Sync for $name {}
impl std::clone::Clone for $name {
#[inline]
fn clone(&self) -> $name {
$name {
_subsystem_drop: self._subsystem_drop.clone(),
}
}
}
impl $name {
#[inline]
pub fn sdl(&self) -> Sdl {
Sdl {
sdldrop: self._subsystem_drop._sdldrop.clone(),
}
}
}
subsystem!($name, $flag);
};
}
#[derive(Debug, Clone)]
struct SubsystemDrop {
_sdldrop: Rc<SdlDrop>,
flag: u32,
}
impl Drop for SubsystemDrop {
#[inline]
#[doc(alias = "SDL_QuitSubSystem")]
fn drop(&mut self) {
unsafe {
sys::SDL_QuitSubSystem(self.flag);
}
}
}
subsystem!(AudioSubsystem, sys::SDL_INIT_AUDIO, nosync);
subsystem!(
GameControllerSubsystem,
sys::SDL_INIT_GAMECONTROLLER,
nosync
);
subsystem!(HapticSubsystem, sys::SDL_INIT_HAPTIC, nosync);
subsystem!(JoystickSubsystem, sys::SDL_INIT_JOYSTICK, nosync);
subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);
subsystem!(SensorSubsystem, sys::SDL_INIT_SENSOR, sync);
static mut IS_EVENT_PUMP_ALIVE: bool = false;
pub struct EventPump {
_sdldrop: Rc<SdlDrop>,
}
impl EventPump {
#[inline]
#[doc(alias = "SDL_InitSubSystem")]
fn new(sdl: &Sdl) -> Result<EventPump, String> {
unsafe {
if IS_EVENT_PUMP_ALIVE {
Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
} else {
let result = sys::SDL_InitSubSystem(sys::SDL_INIT_EVENTS);
if result == 0 {
IS_EVENT_PUMP_ALIVE = true;
Ok(EventPump {
_sdldrop: sdl.sdldrop.clone(),
})
} else {
Err(get_error())
}
}
}
}
}
impl Drop for EventPump {
#[inline]
#[doc(alias = "SDL_QuitSubSystem")]
fn drop(&mut self) {
unsafe {
assert!(IS_EVENT_PUMP_ALIVE);
sys::SDL_QuitSubSystem(sys::SDL_INIT_EVENTS);
IS_EVENT_PUMP_ALIVE = false;
}
}
}
#[inline]
#[doc(alias = "SDL_GetPlatform")]
pub fn get_platform() -> &'static str {
unsafe { CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap() }
}
#[inline]
#[doc(alias = "SDL_GetError")]
pub fn init() -> Result<Sdl, String> {
Sdl::new()
}
pub fn get_error() -> String {
unsafe {
let err = sys::SDL_GetError();
CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
}
}
#[doc(alias = "SDL_SetError")]
pub fn set_error(err: &str) -> Result<(), NulError> {
let c_string = CString::new(err)?;
unsafe {
sys::SDL_SetError(
b"%s\0".as_ptr() as *const c_char,
c_string.as_ptr() as *const c_char,
);
}
Ok(())
}
#[doc(alias = "SDL_Error")]
pub fn set_error_from_code(err: Error) {
unsafe {
sys::SDL_Error(transmute(err));
}
}
#[doc(alias = "SDL_ClearError")]
pub fn clear_error() {
unsafe {
sys::SDL_ClearError();
}
}