diff --git a/examples/audio-whitenoise.rs b/examples/audio-whitenoise.rs index dee47f77..7d2ba305 100644 --- a/examples/audio-whitenoise.rs +++ b/examples/audio-whitenoise.rs @@ -21,7 +21,7 @@ impl AudioCallback for MyCallback { } fn main() { - sdl2::init(sdl2::INIT_AUDIO); + let _sdl_context = sdl2::init(sdl2::INIT_AUDIO).unwrap(); let desired_spec = AudioSpecDesired { freq: 44100, diff --git a/examples/demo.rs b/examples/demo.rs index d27daa86..cf7dfff4 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -3,12 +3,10 @@ extern crate sdl2; use sdl2::video::{Window, WindowPos, OPENGL}; use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer}; use sdl2::pixels::Color; -use sdl2::event::poll_event; -use sdl2::event::Event::{Quit, KeyDown}; use sdl2::keycode::KeyCode; pub fn main() { - sdl2::init(sdl2::INIT_VIDEO); + let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap(); let window = match Window::new("rust-sdl2 demo: Video", WindowPos::PosCentered, WindowPos::PosCentered, 800, 600, OPENGL) { Ok(window) => window, @@ -25,17 +23,20 @@ pub fn main() { drawer.clear(); drawer.present(); - loop { - match poll_event() { - Quit{..} => break, - KeyDown { keycode: key, .. } => { - if key == KeyCode::Escape { - break; - } - } - _ => {} - } - } + let mut running = true; + let mut event_pump = sdl_context.event_pump(); - sdl2::quit(); + while running { + for event in event_pump.poll_iter() { + use sdl2::event::Event; + + match event { + Event::Quit {..} | Event::KeyDown { keycode: KeyCode::Escape, .. } => { + running = false + }, + _ => {} + } + } + // The rest of the game loop goes here... + } } diff --git a/examples/game_controller.rs b/examples/game_controller.rs index 6a5799c5..8b402e7d 100644 --- a/examples/game_controller.rs +++ b/examples/game_controller.rs @@ -1,16 +1,13 @@ -#![feature(old_io, std_misc, core)] +#![feature(core)] extern crate sdl2; use sdl2::{joystick, controller}; -use sdl2::event::Event; use sdl2::controller::GameController; -use std::old_io::timer::sleep; -use std::time::duration::Duration; use std::num::SignedInt; fn main() { - sdl2::init(sdl2::INIT_GAME_CONTROLLER); + let sdl_context = sdl2::init(sdl2::INIT_GAME_CONTROLLER).unwrap(); let available = match joystick::num_joysticks() { @@ -44,7 +41,7 @@ fn main() { } } - let controller = + let controller = match controller { Some(c) => c, None => panic!("Couldn't open any controller"), @@ -52,8 +49,12 @@ fn main() { println!("Controller mapping: {}", controller.mapping()); - loop { - match sdl2::event::poll_event() { + let mut event_pump = sdl_context.event_pump(); + + for event in event_pump.wait_iter() { + use sdl2::event::Event; + + match event { Event::ControllerAxisMotion{ axis, value: val, .. } => { // Axis motion is an absolute value in the range // [-32768, 32767]. Let's simulate a very rough dead @@ -67,12 +68,7 @@ fn main() { Event::ControllerButtonUp{ button, .. } => println!("Button {:?} up", button), Event::Quit{..} => break, - Event::None => - // Don't hog the CPU while waiting for events - sleep(Duration::milliseconds(100)), _ => (), } } - - sdl2::quit(); } diff --git a/examples/joystick.rs b/examples/joystick.rs index 9af49f20..c257781b 100644 --- a/examples/joystick.rs +++ b/examples/joystick.rs @@ -1,15 +1,12 @@ -#![feature(old_io, std_misc, core)] +#![feature(core)] extern crate sdl2; use sdl2::joystick::{Joystick, num_joysticks}; -use sdl2::event::Event; -use std::old_io::timer::sleep; -use std::time::duration::Duration; use std::num::SignedInt; fn main() { - sdl2::init(sdl2::INIT_JOYSTICK); + let sdl_context = sdl2::init(sdl2::INIT_JOYSTICK).unwrap(); let available = match num_joysticks() { @@ -34,12 +31,16 @@ fn main() { } } - if joystick.is_none() { + if joystick.is_none() { panic!("Couldn't open any joystick"); }; - loop { - match sdl2::event::poll_event() { + let mut event_pump = sdl_context.event_pump(); + + for event in event_pump.wait_iter() { + use sdl2::event::Event; + + match event { Event::JoyAxisMotion{ axis_idx, value: val, .. } => { // Axis motion is an absolute value in the range // [-32768, 32767]. Let's simulate a very rough dead @@ -55,12 +56,7 @@ fn main() { Event::JoyHatMotion{ hat_idx, state, .. } => println!("Hat {} moved to {:?}", hat_idx, state), Event::Quit{..} => break, - Event::None => - // Don't hog the CPU while waiting for events - sleep(Duration::milliseconds(100)), _ => (), } } - - sdl2::quit(); } diff --git a/examples/renderer-texture.rs b/examples/renderer-texture.rs index fab31229..6951a9e6 100644 --- a/examples/renderer-texture.rs +++ b/examples/renderer-texture.rs @@ -4,12 +4,10 @@ use sdl2::video::{Window, WindowPos, OPENGL}; use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer}; use sdl2::pixels::PixelFormatEnum; use sdl2::rect::Rect; -use sdl2::event::poll_event; -use sdl2::event::Event::{Quit, KeyDown}; use sdl2::keycode::KeyCode; pub fn main() { - sdl2::init(sdl2::INIT_VIDEO); + let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap(); let window = match Window::new("rust-sdl2 demo: Renderer + Texture", WindowPos::PosCentered, WindowPos::PosCentered, 800, 600, OPENGL) { Ok(window) => window, @@ -40,17 +38,20 @@ pub fn main() { drawer.copy_ex(&texture, None, Some(Rect::new(450, 100, 256, 256)), 30.0, None, (false, false)); drawer.present(); - loop { - match poll_event() { - Quit{..} => break, - KeyDown { keycode: key, .. } => { - if key == KeyCode::Escape { - break; - } - } - _ => {} - } - } + let mut running = true; + let mut event_pump = sdl_context.event_pump(); - sdl2::quit(); + while running { + for event in event_pump.poll_iter() { + use sdl2::event::Event; + + match event { + Event::Quit {..} | Event::KeyDown { keycode: KeyCode::Escape, .. } => { + running = false + }, + _ => {} + } + } + // The rest of the game loop goes here... + } } diff --git a/examples/renderer-yuv.rs b/examples/renderer-yuv.rs index 838ed930..9ed23084 100644 --- a/examples/renderer-yuv.rs +++ b/examples/renderer-yuv.rs @@ -4,12 +4,10 @@ use sdl2::video::{Window, WindowPos, SHOWN}; use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer}; use sdl2::pixels::PixelFormatEnum; use sdl2::rect::Rect; -use sdl2::event::poll_event; -use sdl2::event::Event::{Quit, KeyDown}; use sdl2::keycode::KeyCode; pub fn main() { - sdl2::init(sdl2::INIT_VIDEO); + let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap(); let window = match Window::new("rust-sdl2 demo: YUV", WindowPos::PosCentered, WindowPos::PosCentered, 800, 600, SHOWN) { Ok(window) => window, @@ -56,17 +54,20 @@ pub fn main() { drawer.copy(&texture, None, Some(Rect::new(100, 100, 256, 256))); drawer.present(); - loop { - match poll_event() { - Quit{..} => break, - KeyDown { keycode: key, .. } => { - if key == KeyCode::Escape { - break; - } - } - _ => {} - } - } + let mut running = true; + let mut event_pump = sdl_context.event_pump(); - sdl2::quit(); + while running { + for event in event_pump.poll_iter() { + use sdl2::event::Event; + + match event { + Event::Quit {..} | Event::KeyDown { keycode: KeyCode::Escape, .. } => { + running = false + }, + _ => {} + } + } + // The rest of the game loop goes here... + } } diff --git a/sdl2-sys/src/event.rs b/sdl2-sys/src/event.rs index 5dd05ab4..29ddb180 100644 --- a/sdl2-sys/src/event.rs +++ b/sdl2-sys/src/event.rs @@ -444,9 +444,9 @@ pub type SDL_EventFilter = extern "C" { pub fn SDL_free(mem: *const c_void); pub fn SDL_PumpEvents(); - /*pub fn SDL_PeepEvents(events: &[SDL_Event], numevents: c_int, - action: SDL_eventaction, minType: uint32_t, - maxType: uint32_t) -> c_int;*/ + pub fn SDL_PeepEvents(events: *mut SDL_Event, numevents: c_int, + action: SDL_eventaction, + minType: uint32_t, maxType: uint32_t) -> c_int; pub fn SDL_HasEvent(_type: uint32_t) -> SDL_bool; pub fn SDL_HasEvents(minType: uint32_t, maxType: uint32_t) -> SDL_bool; diff --git a/src/sdl2/event.rs b/src/sdl2/event.rs index 16f96aaa..e6e4e80b 100644 --- a/src/sdl2/event.rs +++ b/src/sdl2/event.rs @@ -8,6 +8,8 @@ use libc::{c_int, c_void, uint32_t}; use std::num::FromPrimitive; use std::ptr; use std::borrow::ToOwned; +use std::iter::FromIterator; +use std::marker::{NoCopy, PhantomData}; use controller; use controller::{Axis, Button}; @@ -125,9 +127,6 @@ impl WindowEventId { /// Different event types. pub enum Event { - None, - - Quit { timestamp: u32 }, AppTerminating { timestamp: u32 }, AppLowMemory { timestamp: u32 }, @@ -361,12 +360,16 @@ pub enum Event { _type: u32, code: i32 }, + + Unknown { + timestamp: u32, + type_: u32 + } } impl ::std::fmt::Debug for Event { fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { out.write_str(match *self { - Event::None => "Event::None", Event::Quit{..} => "Event::Quit", Event::AppTerminating{..} => "Event::AppTerminating", Event::AppLowMemory{..} => "Event::AppLowMemory", @@ -405,6 +408,7 @@ impl ::std::fmt::Debug for Event { Event::ClipboardUpdate{..} => "Event::ClipboardUpdate", Event::DropFile{..} => "Event::DropFile", Event::User{..} => "Event::User", + Event::Unknown{..} => "Event::Unknown", }) } } @@ -412,7 +416,7 @@ impl ::std::fmt::Debug for Event { // TODO: Remove this when from_utf8 is updated in Rust impl Event { fn to_ll(self) -> Option { - let ret = null_event(); + let ret = unsafe { mem::uninitialized() }; match self { // just ignore timestamp Event::User { window_id, _type, code, .. } => { @@ -439,7 +443,7 @@ impl Event { fn from_ll(raw: &ll::SDL_Event) -> Event { let raw_type = raw._type(); let raw_type = if raw_type.is_null() { - return Event::None; + panic!("Event payload is null") } else { unsafe { *raw_type } }; @@ -809,142 +813,233 @@ impl Event { } } - EventType::First | EventType::Last => Event::None, + EventType::First => panic!("Unused event, EventType::First, was encountered"), + EventType::Last => panic!("Unusable event, EventType::Last, was encountered"), // If we have no other match and the event type is >= 32768 // this is a user event EventType::User => { if raw_type < 32768 { - return Event::None; - } + // The type is unknown to us. + // It's a newer SDL2 type. + let ref event = *raw.common(); - let ref event = *raw.user(); + Event::Unknown { + timestamp: event.timestamp, + type_: event._type + } + } else { + let ref event = *raw.user(); - Event::User { - timestamp: event.timestamp, - window_id: event.windowID, - _type: raw_type, - code: event.code + Event::User { + timestamp: event.timestamp, + window_id: event.windowID, + _type: raw_type, + code: event.code + } } } }} // close unsafe & match - - } } -fn null_event() -> ll::SDL_Event { - ll::SDL_Event { data: [0; 56] } +/// A thread-safe type that encapsulates SDL event-pumping functions. +pub struct EventPump<'sdl> { + _marker: NoCopy, + _sdl: PhantomData<&'sdl ()> } -/// Pump the event loop, gathering events from the input devices. -pub fn pump_events() { - unsafe { ll::SDL_PumpEvents(); } -} +/// Prevents the event pump from moving to other threads. +/// SDL events can only be pumped on the main thread. +impl<'sdl> !Send for EventPump<'sdl> {} -/// Check for the existence of certain event types in the event queue. -pub fn has_event(_type: EventType) -> bool { - unsafe { ll::SDL_HasEvent(_type as uint32_t ) == 1 } -} +impl<'sdl> EventPump<'sdl> { + /// Polls for currently pending events. + /// + /// If no events are pending, `None` is returned. + pub fn poll_event(&mut self) -> Option { + let raw = unsafe { mem::uninitialized() }; + let has_pending = unsafe { ll::SDL_PollEvent(&raw) == 1 as c_int }; -/// Check for the existence of a range of event types in the event queue. -pub fn has_events(min: EventType, max: EventType) -> bool { - unsafe { ll::SDL_HasEvents(min as uint32_t, max as uint32_t) == 1 } -} + if has_pending { Some(Event::from_ll(&raw)) } + else { None } + } -/// Clear events from the event queue. -pub fn flush_event(_type: EventType) { - unsafe { ll::SDL_FlushEvent(_type as uint32_t) } -} + /// Returns a polling iterator that calls `poll_event()`. + /// The iterator will terminate once there are no more pending events. + /// + /// # Example + /// ```no_run + /// let mut sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap(); + /// + /// let mut event_pump = sdl_context.event_pump(); + /// for event in event_pump.poll_iter() { + /// use sdl2::event::Event; + /// match event { + /// Event::KeyDown {..} => { /*...*/ }, + /// _ => () + /// } + /// } + /// ``` + pub fn poll_iter(&mut self) -> EventPollIterator { + EventPollIterator { + event_pump: unsafe { EventPump::_unchecked_new() } + } + } -/// Clear events from the event queue of a range of event types. -pub fn flush_events(min: EventType, max: EventType) { - unsafe { ll::SDL_FlushEvents(min as uint32_t, max as uint32_t) } -} + /// Pumps the event loop, gathering events from the input devices. + pub fn pump_events(&mut self) { + unsafe { ll::SDL_PumpEvents(); }; + } -/// Poll for currently pending events. -pub fn poll_event() -> Event { - pump_events(); + /// Waits indefinitely for the next available event. + pub fn wait_event(&mut self) -> Event { + unsafe { + let raw = mem::uninitialized(); + let success = ll::SDL_WaitEvent(&raw) == 1; - let raw = null_event(); - let success = unsafe { ll::SDL_PollEvent(&raw) == 1 as c_int }; + if success { Event::from_ll(&raw) } + else { panic!(get_error()) } + } + } - if success { Event::from_ll(&raw) } - else { Event::None } -} + /// Waits until the specified timeout (in milliseconds) for the next available event. + pub fn wait_event_timeout(&mut self, timeout: u32) -> Option { + unsafe { + let raw = mem::uninitialized(); + let success = ll::SDL_WaitEventTimeout(&raw, timeout as c_int) == 1; -/// Wait indefinitely for the next available event. -pub fn wait_event() -> SdlResult { - let raw = null_event(); - let success = unsafe { ll::SDL_WaitEvent(&raw) == 1 as c_int }; + if success { Some(Event::from_ll(&raw)) } + else { None } + } + } - if success { Ok(Event::from_ll(&raw)) } - else { Err(get_error()) } -} + /// Returns a waiting iterator that calls `wait_event()`. + /// + /// Note: The iterator will never terminate. + pub fn wait_iter(&mut self) -> EventWaitIterator { + EventWaitIterator { + event_pump: unsafe { EventPump::_unchecked_new() } + } + } -/// Wait until the specified timeout (in milliseconds) for the next available event. -pub fn wait_event_timeout(timeout: i32) -> SdlResult { - let raw = null_event(); - let success = unsafe { ll::SDL_WaitEventTimeout(&raw, timeout as c_int) == - 1 as c_int }; + /// Returns a waiting iterator that calls `wait_event_timeout()`. + /// + /// Note: The iterator will never terminate, unless waiting for an event + /// exceeds the specified timeout. + pub fn wait_timeout_iter(&mut self, timeout: u32) -> EventWaitTimeoutIterator { + EventWaitTimeoutIterator { + event_pump: unsafe { EventPump::_unchecked_new() }, + timeout: timeout + } + } - if success { Ok(Event::from_ll(&raw)) } - else { Err(get_error()) } -} - -extern "C" fn event_filter_wrapper(userdata: *const c_void, event: *const ll::SDL_Event) -> c_int { - let filter: extern fn(event: Event) -> bool = unsafe { mem::transmute(userdata) }; - if event.is_null() { 1 } - else { filter(Event::from_ll(unsafe { &*event })) as c_int } -} - -/// Set up a filter to process all events before they change internal state and are posted to the internal event queue. -pub fn set_event_filter(filter_func: extern fn(event: Event) -> bool) { - unsafe { ll::SDL_SetEventFilter(event_filter_wrapper, - filter_func as *const _) } -} - -/// Add a callback to be triggered when an event is added to the event queue. -pub fn add_event_watch(filter_func: extern fn(event: Event) -> bool) { - unsafe { ll::SDL_AddEventWatch(event_filter_wrapper, - filter_func as *const _) } -} - -/// Remove an event watch callback added. -pub fn delete_event_watch(filter_func: extern fn(event: Event) -> bool) { - unsafe { ll::SDL_DelEventWatch(event_filter_wrapper, - filter_func as *const _) } -} - -/// Run a specific filter function on the current event queue, removing any events for which the filter returns 0. -pub fn filter_events(filter_func: extern fn(event: Event) -> bool) { - unsafe { ll::SDL_FilterEvents(event_filter_wrapper, - filter_func as *const _) } -} - -/// Set the state of processing events. -pub fn set_event_state(_type: EventType, state: bool) { - unsafe { ll::SDL_EventState(_type as uint32_t, - state as ll::SDL_EventState); } -} - -/// Get the state of processing events. -pub fn get_event_state(_type: EventType) -> bool { - unsafe { ll::SDL_EventState(_type as uint32_t, ll::SDL_QUERY) - == ll::SDL_ENABLE } -} - -/// allocate a set of user-defined events, and return the beginning event number for that set of events -pub fn register_events(num_events: i32) -> Option { - let ret = unsafe { ll::SDL_RegisterEvents(num_events as c_int) }; - if ret == (-1 as uint32_t) { - None - } else { - Some(ret as u32) + /// Internal use only; used by `sdl2::Sdl::event_pump()` + #[doc(hidden)] + pub unsafe fn _unchecked_new<'a>() -> EventPump<'a> { + EventPump { + _marker: NoCopy, + _sdl: PhantomData + } } } -/// add an event to the event queue +/// An iterator that calls `EventPump::poll_event()`. +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct EventPollIterator<'a> { + event_pump: EventPump<'a> +} + +impl<'a> Iterator for EventPollIterator<'a> { + pub type Item = Event; + + fn next(&mut self) -> Option { + self.event_pump.poll_event() + } +} + +/// An iterator that calls `EventPump::wait_event()`. +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct EventWaitIterator<'a> { + event_pump: EventPump<'a> +} + +impl<'a> Iterator for EventWaitIterator<'a> { + pub type Item = Event; + fn next(&mut self) -> Option { Some(self.event_pump.wait_event()) } +} + +/// An iterator that calls `EventPump::wait_event_timeout()`. +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct EventWaitTimeoutIterator<'a> { + event_pump: EventPump<'a>, + timeout: u32 +} + +impl<'a> Iterator for EventWaitTimeoutIterator<'a> { + pub type Item = Event; + fn next(&mut self) -> Option { self.event_pump.wait_event_timeout(self.timeout) } +} + +/// Removes all events in the event queue that match the specified event type. +pub fn flush_event(event_type: EventType) { + unsafe { ll::SDL_FlushEvent(event_type as uint32_t) }; +} + +/// Removes all events in the event queue that match the specified type range. +pub fn flush_events(min_type: u32, max_type: u32) { + unsafe { ll::SDL_FlushEvents(min_type, max_type) }; +} + +/// Reads the events at the front of the event queue, until the maximum amount +/// of events is read. +/// +/// The events will _not_ be removed from the queue. +/// +/// # Example +/// ```no_run +/// use sdl2::event::{Event, peek_events}; +/// +/// // Read up to 1024 events +/// let events: Vec = peek_events(1024); +/// +/// // Print each one +/// for event in events { +/// println!("{:?}", event); +/// } +/// ``` +pub fn peek_events(max_amount: u32) -> B +where B: FromIterator +{ + unsafe { + let mut events = Vec::with_capacity(max_amount as usize); + + let result = { + let events_ptr = events.as_mut_slice().as_mut_ptr(); + + ll::SDL_PeepEvents( + events_ptr, + max_amount as c_int, + ll::SDL_PEEKEVENT, + ll::SDL_FIRSTEVENT, + ll::SDL_LASTEVENT + ) + }; + + if result < 0 { + // The only error possible is "Couldn't lock event queue" + panic!(get_error()); + } else { + events.set_len(max_amount as usize); + + events.iter().map(|event_raw| { + Event::from_ll(event_raw) + }).collect() + } + } +} + +/// Pushes an event to the event queue. pub fn push_event(event: Event) -> SdlResult<()> { match event.to_ll() { Some(raw_event) => { @@ -953,7 +1048,7 @@ pub fn push_event(event: Event) -> SdlResult<()> { else { Err(get_error()) } }, None => { - Err("Unsupport event type to push back to queue.".to_owned()) + Err(format!("Cannot push unsupported event type to the queue")) } } } diff --git a/src/sdl2/sdl.rs b/src/sdl2/sdl.rs index 9e754d01..90fd8a6f 100644 --- a/src/sdl2/sdl.rs +++ b/src/sdl2/sdl.rs @@ -1,6 +1,8 @@ use std::ffi::{CStr, CString}; +use std::marker::{NoCopy, PhantomData}; use sys::sdl as ll; +use event::EventPump; bitflags! { flags InitFlag: u32 { @@ -27,30 +29,123 @@ pub enum Error { pub type SdlResult = Result; -pub fn init(flags: InitFlag) -> bool { - unsafe { - ll::SDL_Init(flags.bits()) == 0 +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; +/// Only one Sdl context can be alive at a time. +/// Set to false by default (not alive). +static IS_SDL_CONTEXT_ALIVE: AtomicBool = ATOMIC_BOOL_INIT; + +/// The SDL context type. Initialize with `sdl2::init()`. +/// +/// From a thread-safety perspective, `Sdl` represents the main thread. +/// Only one instance of `Sdl` is allowed per process, and cannot be moved or +/// used across non-main threads. +/// +/// As such, `Sdl` is a useful type for ensuring that SDL types that can only +/// be used on the main thread are initialized that way. +/// +/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be +/// called on the main thread. +/// All functionality that calls `SDL_PumpEvents()` is thus put into an +/// `EventPump` type, which can only be obtained through `Sdl`. +/// This guarantees that the only way to call event-pumping functions is on +/// the main thread. +pub struct Sdl { + _marker: NoCopy +} + +impl !Send for Sdl {} +impl !Sync for Sdl {} + +impl Sdl { + /// Initializes specific SDL subsystems. + pub fn init_subsystem(&self, flags: InitFlag) -> SdlResult { + unsafe { + if ll::SDL_InitSubSystem(flags.bits()) == 0 { + Ok(Subsystem { + flags: flags, + _marker: PhantomData + }) + } else { + Err(get_error()) + } + } + } + + /// Returns the mask of the specified subsystems which have previously been initialized. + pub fn was_init(&self, flags: InitFlag) -> InitFlag { + unsafe { + let raw = ll::SDL_WasInit(flags.bits()); + flags & InitFlag::from_bits(raw).unwrap() + } + } + + /// Obtains the SDL event pump. + pub fn event_pump(&self) -> EventPump { + unsafe { EventPump::_unchecked_new() } } } -pub fn init_subsystem(flags: InitFlag) -> bool { - unsafe { - ll::SDL_InitSubSystem(flags.bits()) == 0 +impl Drop for Sdl { + fn drop(&mut self) { + use std::sync::atomic::Ordering; + + let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed); + assert!(was_alive); + + unsafe { ll::SDL_Quit(); } } } -pub fn quit_subsystem(flags: InitFlag) { - unsafe { ll::SDL_QuitSubSystem(flags.bits()); } +/// A RAII value representing initalized SDL subsystems. See `sdl2::Sdl::init_subsystem()`. +/// +/// Subsystem initialization is ref-counted. Once `Subsystem::drop()` is called, +/// the specified subsystems' ref-counts are decremented via `SDL_QuitSubSystem`. +pub struct Subsystem<'sdl> { + flags: InitFlag, + _marker: PhantomData<&'sdl Sdl> } -pub fn quit() { - unsafe { ll::SDL_Quit(); } +#[unsafe_destructor] +impl<'sdl> Drop for Subsystem<'sdl> { + fn drop(&mut self) { + unsafe { ll::SDL_QuitSubSystem(self.flags.bits()); } + } } -pub fn was_inited(flags: InitFlag) -> InitFlag { +/// Initializes the SDL library. +/// This must be called before using any other SDL function. +/// +/// # Example +/// ```no_run +/// let sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap(); +/// +/// let mut event_pump = sdl_context.event_pump(); +/// for event in event_pump.poll_iter() { +/// // ... +/// } +/// +/// // SDL_Quit() is called here as `sdl_context` is dropped. +/// ``` +pub fn init(flags: InitFlag) -> SdlResult { unsafe { - let raw = ll::SDL_WasInit(flags.bits()); - flags & InitFlag::from_bits(raw).unwrap() + use std::sync::atomic::Ordering; + + // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true + let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed); + + if was_alive { + IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed); + Err(format!("Cannot have more than one `Sdl` in use at the same time")) + } else { + if ll::SDL_Init(flags.bits()) == 0 { + Ok(Sdl { + _marker: NoCopy + }) + } else { + IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed); + Err(get_error()) + } + } } } diff --git a/src/sdl2/timer.rs b/src/sdl2/timer.rs index dd4bae6d..12f46485 100644 --- a/src/sdl2/timer.rs +++ b/src/sdl2/timer.rs @@ -79,7 +79,7 @@ extern "C" fn c_timer_callback(_interval: u32, param: *const c_void) -> uint32_t fn test_timer_runs_multiple_times() { use std::sync::{Arc, Mutex}; let _running = TIMER_INIT_LOCK.lock().unwrap(); - ::sdl::init(::sdl::INIT_TIMER); + ::sdl::init(::sdl::INIT_TIMER).unwrap(); let local_num = Arc::new(Mutex::new(0)); let timer_num = local_num.clone(); @@ -104,7 +104,7 @@ fn test_timer_runs_multiple_times() { fn test_timer_runs_at_least_once() { use std::sync::{Arc, Mutex}; let _running = TIMER_INIT_LOCK.lock().unwrap(); - ::sdl::init(::sdl::INIT_TIMER); + ::sdl::init(::sdl::INIT_TIMER).unwrap(); let local_flag = Arc::new(Mutex::new(false)); let timer_flag = local_flag.clone(); @@ -123,7 +123,7 @@ fn test_timer_runs_at_least_once() { fn test_timer_can_be_recreated() { use std::sync::{Arc, Mutex}; let _running = TIMER_INIT_LOCK.lock().unwrap(); - ::sdl::init(::sdl::INIT_TIMER); + ::sdl::init(::sdl::INIT_TIMER).unwrap(); let local_num = Arc::new(Mutex::new(0)); let timer_num = local_num.clone(); diff --git a/tests/lib.rs b/tests/lib.rs index dac18d61..106e776b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -4,8 +4,6 @@ extern crate sdl2; #[test] fn audio_spec_wav() { - sdl2::init(sdl2::INIT_AUDIO); - let wav = sdl2::audio::AudioSpecWAV::load_wav(&Path::new("./tests/sine.wav")).unwrap(); assert_eq!(wav.freq, 22050); @@ -14,6 +12,4 @@ fn audio_spec_wav() { let buffer = wav.get_buffer(); assert_eq!(buffer.len(), 4410); - - sdl2::quit(); }