1use std::error;
2use std::ffi::{CStr, CString, NulError};
3use std::fmt;
4use std::rc::Rc;
5use libc::{c_char, uint32_t};
6use std::mem::transmute;
7
8use crate::sys;
9
10#[repr(i32)]
11#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
12pub enum Error {
13 NoMemError = sys::SDL_errorcode::SDL_ENOMEM as i32,
14 ReadError = sys::SDL_errorcode::SDL_EFREAD as i32,
15 WriteError = sys::SDL_errorcode::SDL_EFWRITE as i32,
16 SeekError = sys::SDL_errorcode::SDL_EFSEEK as i32,
17 UnsupportedError = sys::SDL_errorcode::SDL_UNSUPPORTED as i32
18}
19
20impl fmt::Display for Error {
21 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22 use self::Error::*;
23
24 match *self {
25 NoMemError => write!(f, "Out of memory"),
26 ReadError => write!(f, "Error reading from datastream"),
27 WriteError => write!(f, "Error writing to datastream"),
28 SeekError => write!(f, "Error seeking in datastream"),
29 UnsupportedError => write!(f, "Unknown SDL error")
30 }
31 }
32}
33
34impl error::Error for Error {
35 fn description(&self) -> &str {
36 use self::Error::*;
37
38 match *self {
39 NoMemError => "out of memory",
40 ReadError => "error reading from datastream",
41 WriteError => "error writing to datastream",
42 SeekError => "error seeking in datastream",
43 UnsupportedError => "unknown SDL error"
44 }
45 }
46}
47
48use std::sync::atomic::{AtomicBool};
49static IS_SDL_CONTEXT_ALIVE: AtomicBool = AtomicBool::new(false);
52
53#[derive(Clone)]
66pub struct Sdl {
67 sdldrop: Rc<SdlDrop>
68}
69
70impl Sdl {
71 #[inline]
72 fn new() -> Result<Sdl, String> {
73 unsafe {
74 use std::sync::atomic::Ordering;
75
76 let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);
78
79 if was_alive {
80 Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
81 } else if sys::SDL_Init(0) == 0 {
82 Ok(Sdl {
84 sdldrop: Rc::new(SdlDrop)
85 })
86 } else {
87 IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
88 Err(get_error())
89 }
90 }
91 }
92
93 #[inline]
95 pub fn audio(&self) -> Result<AudioSubsystem, String> { AudioSubsystem::new(self) }
96
97 #[inline]
99 pub fn event(&self) -> Result<EventSubsystem, String> { EventSubsystem::new(self) }
100
101 #[inline]
103 pub fn joystick(&self) -> Result<JoystickSubsystem, String> { JoystickSubsystem::new(self) }
104
105 #[inline]
107 pub fn haptic(&self) -> Result<HapticSubsystem, String> { HapticSubsystem::new(self) }
108
109 #[inline]
111 pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> { GameControllerSubsystem::new(self) }
112
113 #[inline]
115 pub fn timer(&self) -> Result<TimerSubsystem, String> { TimerSubsystem::new(self) }
116
117 #[inline]
119 pub fn video(&self) -> Result<VideoSubsystem, String> { VideoSubsystem::new(self) }
120
121 #[inline]
127 pub fn event_pump(&self) -> Result<EventPump, String> {
128 EventPump::new(self)
129 }
130
131 #[inline]
132 #[doc(hidden)]
133 pub fn sdldrop(&self) -> Rc<SdlDrop> {
134 self.sdldrop.clone()
135 }
136}
137
138#[doc(hidden)]
140#[derive(Debug)]
141pub struct SdlDrop;
142
143impl Drop for SdlDrop {
144 #[inline]
145 fn drop(&mut self) {
146 use std::sync::atomic::Ordering;
147
148 let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
149 assert!(was_alive);
150
151 unsafe { sys::SDL_Quit(); }
152 }
153}
154
155macro_rules! subsystem {
161 ($name:ident, $flag:expr) => (
162 impl $name {
163 #[inline]
164 fn new(sdl: &Sdl) -> Result<$name, String> {
165 let result = unsafe { sys::SDL_InitSubSystem($flag) };
166
167 if result == 0 {
168 Ok($name {
169 _subsystem_drop: Rc::new(SubsystemDrop {
170 _sdldrop: sdl.sdldrop.clone(),
171 flag: $flag
172 })
173 })
174 } else {
175 Err(get_error())
176 }
177 }
178 }
179 );
180 ($name:ident, $flag:expr, nosync) => (
181 #[derive(Debug, Clone)]
182 pub struct $name {
183 _subsystem_drop: Rc<SubsystemDrop>
186 }
187
188 impl $name {
189 #[inline]
191 pub fn sdl(&self) -> Sdl {
192 Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
193 }
194 }
195
196 subsystem!($name, $flag);
197 );
198 ($name:ident, $flag:expr, sync) => (
199 pub struct $name {
200 _subsystem_drop: Rc<SubsystemDrop>
203 }
204 unsafe impl Sync for $name {}
205
206 impl $name {
207 #[inline]
208 pub fn clone(&self) -> $name {
209 $name {
210 _subsystem_drop: self._subsystem_drop.clone()
211 }
212 }
213
214 #[inline]
216 pub fn sdl(&self) -> Sdl {
217 Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
218 }
219 }
220
221 subsystem!($name, $flag);
222 )
223}
224
225#[derive(Debug, Clone)]
228struct SubsystemDrop {
229 _sdldrop: Rc<SdlDrop>,
230 flag: uint32_t
231}
232
233impl Drop for SubsystemDrop {
234 #[inline]
235 fn drop(&mut self) {
236 unsafe { sys::SDL_QuitSubSystem(self.flag); }
237 }
238}
239
240subsystem!(AudioSubsystem, sys::SDL_INIT_AUDIO, nosync);
241subsystem!(GameControllerSubsystem, sys::SDL_INIT_GAMECONTROLLER, nosync);
242subsystem!(HapticSubsystem, sys::SDL_INIT_HAPTIC, nosync);
243subsystem!(JoystickSubsystem, sys::SDL_INIT_JOYSTICK, nosync);
244subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
245subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
247subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);
249
250static mut IS_EVENT_PUMP_ALIVE: bool = false;
251
252pub struct EventPump {
254 _sdldrop: Rc<SdlDrop>
255}
256
257impl EventPump {
258 #[inline]
260 fn new(sdl: &Sdl) -> Result<EventPump, String> {
261 unsafe {
264 if IS_EVENT_PUMP_ALIVE {
265 Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
266 } else {
267 let result = sys::SDL_InitSubSystem(sys::SDL_INIT_EVENTS);
269
270 if result == 0 {
271 IS_EVENT_PUMP_ALIVE = true;
272
273 Ok(EventPump {
274 _sdldrop: sdl.sdldrop.clone()
275 })
276 } else {
277 Err(get_error())
278 }
279 }
280 }
281 }
282}
283
284impl Drop for EventPump {
285 #[inline]
286 fn drop(&mut self) {
287 unsafe {
290 assert!(IS_EVENT_PUMP_ALIVE);
291 sys::SDL_QuitSubSystem(sys::SDL_INIT_EVENTS);
292 IS_EVENT_PUMP_ALIVE = false;
293 }
294 }
295}
296
297#[inline]
299pub fn get_platform() -> &'static str {
300 unsafe {
301 CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap()
302 }
303}
304
305#[inline]
320pub fn init() -> Result<Sdl, String> { Sdl::new() }
321
322pub fn get_error() -> String {
323 unsafe {
324 let err = sys::SDL_GetError();
325 CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
326 }
327}
328
329pub fn set_error(err: &str) -> Result<(), NulError> {
330 let c_string = r#try!(CString::new(err));
331 Ok(unsafe {
332 sys::SDL_SetError(c_string.as_ptr() as *const c_char);
333 })
334}
335
336pub fn set_error_from_code(err: Error) {
337 unsafe { sys::SDL_Error(transmute(err)); }
338}
339
340pub fn clear_error() {
341 unsafe { sys::SDL_ClearError(); }
342}