1use libc::c_char;
2use std::cell::Cell;
3use std::error;
4use std::ffi::{CStr, CString, NulError};
5use std::fmt;
6use std::marker::PhantomData;
7use std::mem::transmute;
8use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
9
10use crate::sys;
11
12#[repr(i32)]
13#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
14pub enum Error {
15 NoMemError = sys::SDL_errorcode::SDL_ENOMEM as i32,
16 ReadError = sys::SDL_errorcode::SDL_EFREAD as i32,
17 WriteError = sys::SDL_errorcode::SDL_EFWRITE as i32,
18 SeekError = sys::SDL_errorcode::SDL_EFSEEK as i32,
19 UnsupportedError = sys::SDL_errorcode::SDL_UNSUPPORTED as i32,
20}
21
22impl fmt::Display for Error {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 use self::Error::*;
25
26 match *self {
27 NoMemError => write!(f, "Out of memory"),
28 ReadError => write!(f, "Error reading from datastream"),
29 WriteError => write!(f, "Error writing to datastream"),
30 SeekError => write!(f, "Error seeking in datastream"),
31 UnsupportedError => write!(f, "Unknown SDL error"),
32 }
33 }
34}
35
36impl error::Error for Error {}
37
38static IS_MAIN_THREAD_DECLARED: AtomicBool = AtomicBool::new(false);
41
42static SDL_COUNT: AtomicU32 = AtomicU32::new(0);
44
45thread_local! {
46 static IS_MAIN_THREAD: Cell<bool> = const { Cell::new(false) };
48}
49
50#[derive(Clone)]
63pub struct Sdl {
64 sdldrop: SdlDrop,
65}
66
67impl Sdl {
68 #[inline]
69 #[doc(alias = "SDL_Init")]
70 fn new() -> Result<Sdl, String> {
71 let was_main_thread_declared = IS_MAIN_THREAD_DECLARED.swap(true, Ordering::SeqCst);
73
74 IS_MAIN_THREAD.with(|is_main_thread| {
75 if was_main_thread_declared {
76 if !is_main_thread.get() {
77 return Err("Cannot initialize `Sdl` from more than one thread.".to_owned());
78 }
79 } else {
80 is_main_thread.set(true);
81 }
82 Ok(())
83 })?;
84
85 if SDL_COUNT.fetch_add(1, Ordering::Relaxed) == 0 {
87 let result;
88
89 unsafe {
90 result = sys::SDL_Init(0);
91 }
92
93 if result != 0 {
94 SDL_COUNT.store(0, Ordering::Relaxed);
95 return Err(get_error());
96 }
97 }
98
99 Ok(Sdl {
100 sdldrop: SdlDrop {
101 marker: PhantomData,
102 },
103 })
104 }
105
106 #[inline]
108 pub fn audio(&self) -> Result<AudioSubsystem, String> {
109 AudioSubsystem::new(self)
110 }
111
112 #[inline]
114 pub fn event(&self) -> Result<EventSubsystem, String> {
115 EventSubsystem::new(self)
116 }
117
118 #[inline]
120 pub fn joystick(&self) -> Result<JoystickSubsystem, String> {
121 JoystickSubsystem::new(self)
122 }
123
124 #[inline]
126 pub fn haptic(&self) -> Result<HapticSubsystem, String> {
127 HapticSubsystem::new(self)
128 }
129
130 #[inline]
132 pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> {
133 GameControllerSubsystem::new(self)
134 }
135
136 #[inline]
138 pub fn sensor(&self) -> Result<SensorSubsystem, String> {
139 SensorSubsystem::new(self)
140 }
141
142 #[inline]
144 pub fn timer(&self) -> Result<TimerSubsystem, String> {
145 TimerSubsystem::new(self)
146 }
147
148 #[inline]
150 pub fn video(&self) -> Result<VideoSubsystem, String> {
151 VideoSubsystem::new(self)
152 }
153
154 #[inline]
160 pub fn event_pump(&self) -> Result<EventPump, String> {
161 EventPump::new(self)
162 }
163
164 #[inline]
165 #[doc(hidden)]
166 pub fn sdldrop(&self) -> SdlDrop {
167 self.sdldrop.clone()
168 }
169}
170
171#[doc(hidden)]
173#[derive(Debug)]
174pub struct SdlDrop {
175 marker: PhantomData<*mut ()>,
178}
179
180impl Clone for SdlDrop {
181 fn clone(&self) -> SdlDrop {
182 let prev_count = SDL_COUNT.fetch_add(1, Ordering::Relaxed);
183 assert!(prev_count > 0);
184 SdlDrop {
185 marker: PhantomData,
186 }
187 }
188}
189
190impl Drop for SdlDrop {
191 #[inline]
192 #[doc(alias = "SDL_Quit")]
193 fn drop(&mut self) {
194 let prev_count = SDL_COUNT.fetch_sub(1, Ordering::Relaxed);
195 assert!(prev_count > 0);
196 if prev_count == 1 {
197 unsafe {
198 sys::SDL_Quit();
199 }
200 IS_MAIN_THREAD_DECLARED.store(false, Ordering::SeqCst);
201 }
202 }
203}
204
205macro_rules! subsystem {
211 ($name:ident, $flag:expr, $counter:ident, nosync) => {
212 static $counter: AtomicU32 = AtomicU32::new(0);
213
214 #[derive(Debug, Clone)]
215 pub struct $name {
216 _subsystem_drop: SubsystemDrop,
219 }
220
221 impl $name {
222 #[inline]
223 #[doc(alias = "SDL_InitSubSystem")]
224 fn new(sdl: &Sdl) -> Result<$name, String> {
225 if $counter.fetch_add(1, Ordering::Relaxed) == 0 {
226 let result;
227
228 unsafe {
229 result = sys::SDL_InitSubSystem($flag);
230 }
231
232 if result != 0 {
233 $counter.store(0, Ordering::Relaxed);
234 return Err(get_error());
235 }
236 }
237
238 Ok($name {
239 _subsystem_drop: SubsystemDrop {
240 _sdldrop: sdl.sdldrop.clone(),
241 counter: &$counter,
242 flag: $flag,
243 },
244 })
245 }
246
247 #[inline]
249 pub fn sdl(&self) -> Sdl {
250 Sdl {
251 sdldrop: self._subsystem_drop._sdldrop.clone(),
252 }
253 }
254 }
255 };
256 ($name:ident, $flag:expr, $counter:ident, sync) => {
257 subsystem!($name, $flag, $counter, nosync);
258 unsafe impl Sync for $name {}
259 };
260}
261
262#[derive(Debug)]
265struct SubsystemDrop {
266 _sdldrop: SdlDrop,
267 counter: &'static AtomicU32,
268 flag: u32,
269}
270
271impl Clone for SubsystemDrop {
272 fn clone(&self) -> SubsystemDrop {
273 let prev_count = self.counter.fetch_add(1, Ordering::Relaxed);
274 assert!(prev_count > 0);
275 SubsystemDrop {
276 _sdldrop: self._sdldrop.clone(),
277 counter: self.counter,
278 flag: self.flag,
279 }
280 }
281}
282
283impl Drop for SubsystemDrop {
284 #[inline]
285 #[doc(alias = "SDL_QuitSubSystem")]
286 fn drop(&mut self) {
287 let prev_count = self.counter.fetch_sub(1, Ordering::Relaxed);
288 assert!(prev_count > 0);
289 if prev_count == 1 {
290 unsafe {
291 sys::SDL_QuitSubSystem(self.flag);
292 }
293 }
294 }
295}
296
297subsystem!(AudioSubsystem, sys::SDL_INIT_AUDIO, AUDIO_COUNT, nosync);
298subsystem!(
299 GameControllerSubsystem,
300 sys::SDL_INIT_GAMECONTROLLER,
301 GAMECONTROLLER_COUNT,
302 nosync
303);
304subsystem!(HapticSubsystem, sys::SDL_INIT_HAPTIC, HAPTIC_COUNT, nosync);
305subsystem!(
306 JoystickSubsystem,
307 sys::SDL_INIT_JOYSTICK,
308 JOYSTICK_COUNT,
309 nosync
310);
311subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, VIDEO_COUNT, nosync);
312subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, TIMER_COUNT, sync);
314subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, EVENTS_COUNT, sync);
316subsystem!(SensorSubsystem, sys::SDL_INIT_SENSOR, SENSOR_COUNT, sync);
317
318static IS_EVENT_PUMP_ALIVE: AtomicBool = AtomicBool::new(false);
319
320pub struct EventPump {
322 _event_subsystem: EventSubsystem,
323}
324
325impl EventPump {
326 #[inline]
328 #[doc(alias = "SDL_InitSubSystem")]
329 fn new(sdl: &Sdl) -> Result<EventPump, String> {
330 if IS_EVENT_PUMP_ALIVE.load(Ordering::Relaxed) {
332 Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
333 } else {
334 let _event_subsystem = sdl.event()?;
335 IS_EVENT_PUMP_ALIVE.store(true, Ordering::Relaxed);
336 Ok(EventPump { _event_subsystem })
337 }
338 }
339}
340
341impl Drop for EventPump {
342 #[inline]
343 #[doc(alias = "SDL_QuitSubSystem")]
344 fn drop(&mut self) {
345 assert!(IS_EVENT_PUMP_ALIVE.load(Ordering::Relaxed));
347 IS_EVENT_PUMP_ALIVE.store(false, Ordering::Relaxed);
348 }
349}
350
351#[inline]
353#[doc(alias = "SDL_GetPlatform")]
354pub fn get_platform() -> &'static str {
355 unsafe { CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap() }
356}
357
358#[inline]
373#[doc(alias = "SDL_GetError")]
374pub fn init() -> Result<Sdl, String> {
375 Sdl::new()
376}
377
378pub fn get_error() -> String {
379 unsafe {
380 let err = sys::SDL_GetError();
381 CStr::from_ptr(err).to_str().unwrap().to_owned()
382 }
383}
384
385#[doc(alias = "SDL_SetError")]
386pub fn set_error(err: &str) -> Result<(), NulError> {
387 let c_string = CString::new(err)?;
388 unsafe {
389 sys::SDL_SetError(b"%s\0".as_ptr() as *const c_char, c_string.as_ptr());
390 }
391 Ok(())
392}
393
394#[doc(alias = "SDL_Error")]
395pub fn set_error_from_code(err: Error) {
396 unsafe {
397 sys::SDL_Error(transmute::<Error, sys::SDL_errorcode>(err));
398 }
399}
400
401#[doc(alias = "SDL_ClearError")]
402pub fn clear_error() {
403 unsafe {
404 sys::SDL_ClearError();
405 }
406}