use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::rc::Rc;
use libc::{c_char, uint32_t};
use std::mem::transmute;
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]
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 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]
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]
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 $name {
#[inline]
pub fn clone(&self) -> $name {
$name {
_subsystem_drop: self._subsystem_drop.clone()
}
}
#[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: uint32_t
}
impl Drop for SubsystemDrop {
#[inline]
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);
static mut IS_EVENT_PUMP_ALIVE: bool = false;
pub struct EventPump {
_sdldrop: Rc<SdlDrop>
}
impl EventPump {
#[inline]
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]
fn drop(&mut self) {
unsafe {
assert!(IS_EVENT_PUMP_ALIVE);
sys::SDL_QuitSubSystem(sys::SDL_INIT_EVENTS);
IS_EVENT_PUMP_ALIVE = false;
}
}
}
#[inline]
pub fn get_platform() -> &'static str {
unsafe {
CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap()
}
}
#[inline]
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()
}
}
pub fn set_error(err: &str) -> Result<(), NulError> {
let c_string = r#try!(CString::new(err));
Ok(unsafe {
sys::SDL_SetError(c_string.as_ptr() as *const c_char);
})
}
pub fn set_error_from_code(err: Error) {
unsafe { sys::SDL_Error(transmute(err)); }
}
pub fn clear_error() {
unsafe { sys::SDL_ClearError(); }
}