diff --git a/Cargo.toml b/Cargo.toml index d160fba2..76ec3f6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "sdl2" description = "SDL2 bindings for Rust" repository = "https://github.com/AngryLawyer/rust-sdl2" documentation = "http://angrylawyer.github.io/rust-sdl2/sdl2/" -version = "0.13.0" +version = "0.14.0" license = "MIT" authors = [ "Tony Aldridge " ] keywords = ["SDL", "windowing", "graphics"] @@ -14,11 +14,17 @@ keywords = ["SDL", "windowing", "graphics"] name = "sdl2" path = "src/sdl2/lib.rs" + +[[test]] +name="events" +harness=false + [dependencies] num = "0.1" bitflags = "0.3" libc = "0.2" rand = "0.3" +lazy_static="0.1" [dependencies.sdl2-sys] diff --git a/README.md b/README.md index 8be777ba..c69b8319 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ download through Crates.io: ```toml [dependencies] - sdl2 = "0.12" + sdl2 = "0.14" ``` Alternatively, pull it from GitHub diff --git a/src/sdl2/event.rs b/src/sdl2/event.rs index 695c1cb2..0106fbd5 100644 --- a/src/sdl2/event.rs +++ b/src/sdl2/event.rs @@ -10,6 +10,8 @@ use std::ptr; use std::borrow::ToOwned; use std::iter::FromIterator; use std::marker::PhantomData; +use std::collections::HashMap; +use std::sync::Mutex; use controller; use controller::{Axis, Button}; @@ -26,6 +28,24 @@ use get_error; use sys::event as ll; +struct CustomEventTypeMaps { + sdl_id_to_type_id: HashMap, + type_id_to_sdl_id: HashMap<::std::any::TypeId, u32> +} + +impl CustomEventTypeMaps { + fn new() -> Self { + CustomEventTypeMaps { + sdl_id_to_type_id: HashMap::new(), + type_id_to_sdl_id: HashMap::new() + } + } +} + +lazy_static! { + static ref CUSTOM_EVENT_TYPES : Mutex = { Mutex::new(CustomEventTypeMaps::new()) }; +} + impl ::EventSubsystem { /// Removes all events in the event queue that match the specified event type. pub fn flush_event(&self, event_type: EventType) { @@ -101,6 +121,133 @@ impl ::EventSubsystem { } } } + + + /// Register a custom SDL event. + /// + /// When pushing a user event, you must make sure that the ``type_`` field is set to a + /// registered SDL event number. + /// + /// The ``code``, ``data1``, and ``data2`` fields can be used to store user defined data. + /// + /// See the [SDL documentation](https://wiki.libsdl.org/SDL_UserEvent) for more information. + /// + /// # Example + /// ``` + /// let sdl = sdl2::init().unwrap(); + /// let ev = sdl.event().unwrap(); + /// + /// let custom_event_type_id = unsafe { ev.register_event().unwrap() }; + /// let event = sdl2::event::Event::User { + /// timestamp: 0, + /// window_id: 0, + /// type_: custom_event_type_id, + /// code: 456, + /// data1: 0x1234 as *mut ::sdl2::libc::c_void, + /// data2: 0x5678 as *mut ::sdl2::libc::c_void, + /// }; + /// + /// ev.push_event(event); + /// + /// ``` + #[inline(always)] + pub unsafe fn register_event(&self) -> SdlResult { + Ok(*try!(self.register_events(1)).first().unwrap()) + } + + /// Registers custom SDL events. + /// + /// Returns an error, if no more user events can be created. + pub unsafe fn register_events(&self, nr: u32) -> SdlResult> { + let result = unsafe { ll::SDL_RegisterEvents(nr as ::libc::c_int) }; + const ERR_NR:u32 = ::std::u32::MAX - 1; + + match result { + ERR_NR => { Err(ErrorMessage("No more user events can be created; SDL_LASTEVENT reached".to_owned())) }, + _ => { + let event_ids = (result..(result+nr)).collect(); + Ok(event_ids) + } + } + } + + /// Register a custom event + /// + /// It returns an error when the same type is registered twice. + /// + /// # Example + /// See [push_custom_event](#method.push_custom_event) + #[inline(always)] + pub fn register_custom_event(&self) -> SdlResult<()> { + use ::std::any::TypeId; + let event_id = *try!(unsafe { self.register_events(1) }).first().unwrap(); + let mut cet = CUSTOM_EVENT_TYPES.lock().unwrap(); + let type_id = TypeId::of::>(); + + if cet.type_id_to_sdl_id.contains_key(&type_id) { + return Err(ErrorMessage("Can not register the same event type twice!".into())); + } + + cet.sdl_id_to_type_id.insert(event_id, type_id); + cet.type_id_to_sdl_id.insert(type_id, event_id); + + Ok(()) + } + + /// Push a custom event + /// + /// If the event type ``T`` was not registered using + /// [register_custom_event](#method.register_custom_event), + /// this method will panic. + /// + /// # Example: pushing and receiving a custom event + /// ``` + /// struct SomeCustomEvent { + /// a: i32 + /// } + /// + /// let sdl = sdl2::init().unwrap(); + /// let ev = sdl.event().unwrap(); + /// let mut ep = sdl.event_pump().unwrap(); + /// + /// ev.register_custom_event::().unwrap(); + /// + /// let event = SomeCustomEvent { a: 42 }; + /// + /// ev.push_custom_event(event); + /// + /// let received = ep.poll_event().unwrap(); // or within a for event in ep.poll_iter() + /// if received.is_user_event() { + /// let e2 = received.as_user_event_type::().unwrap(); + /// assert_eq!(e2.a, 42); + /// } + /// ``` + pub fn push_custom_event(&self, event:T) -> SdlResult<()> { + use ::std::any::TypeId; + let cet = CUSTOM_EVENT_TYPES.lock().unwrap(); + let type_id = TypeId::of::>(); + + let user_event_id = *match cet.type_id_to_sdl_id.get(&type_id) { + Some(id) => id, + None => { return Err(ErrorMessage("Type is not registered as a custom event type!".into())); } + }; + + let event_box = Box::new(event); + let type_id_box = Box::new(type_id); + + let event = Event::User { + timestamp: 0, + window_id: 0, + type_: user_event_id, + code: 0, + data1: Box::into_raw(event_box) as *mut ::libc::c_void, + data2: ::std::ptr::null_mut() + }; + + self.push_event(event); + + Ok(()) + } } /// Types of events that can be delivered. @@ -496,7 +643,9 @@ pub enum Event { timestamp: u32, window_id: u32, type_: u32, - code: i32 + code: i32, + data1: *mut c_void, + data2: *mut c_void }, Unknown { @@ -556,15 +705,14 @@ impl Event { fn to_ll(self) -> Option { let mut ret = unsafe { mem::uninitialized() }; match self { - // just ignore timestamp - Event::User { window_id, type_, code, .. } => { + Event::User { window_id, type_, code, data1, data2, timestamp} => { let event = ll::SDL_UserEvent { type_: type_ as uint32_t, - timestamp: 0, + timestamp: timestamp, windowID: window_id, code: code as i32, - data1: ptr::null_mut(), - data2: ptr::null_mut(), + data1: data1, + data2: data2 }; unsafe { ptr::copy(&event, &mut ret as *mut ll::SDL_Event as *mut ll::SDL_UserEvent, 1); @@ -967,12 +1115,46 @@ impl Event { timestamp: event.timestamp, window_id: event.windowID, type_: raw_type, - code: event.code + code: event.code, + data1: event.data1, + data2: event.data2 } } } }} // close unsafe & match } + + pub fn is_user_event(&self) -> bool { + match self { + &Event::User { .. } => true, + _ => false + } + } + + pub fn as_user_event_type(&self) -> Option { + use ::std::any::TypeId; + let type_id = TypeId::of::>(); + + let (event_id, event_box_ptr) = match self { + &Event::User { type_, data1, .. } => { (type_, data1) }, + _ => { return None } + }; + + let mut cet = CUSTOM_EVENT_TYPES.lock().unwrap(); + + let event_type_id = match cet.sdl_id_to_type_id.get(&event_id) { + Some(id) => id, + None => { panic!("internal error; could not find typeid") } + }; + + if &type_id != event_type_id { + return None; + } + + let event_box : Box = unsafe { Box::from_raw(event_box_ptr as *mut T) }; + + Some(*event_box) + } } unsafe fn poll_event() -> Option { @@ -1125,3 +1307,4 @@ impl<'a> Iterator for EventWaitTimeoutIterator<'a> { type Item = Event; fn next(&mut self) -> Option { unsafe { wait_event_timeout(self.timeout) } } } + diff --git a/src/sdl2/lib.rs b/src/sdl2/lib.rs index cd266d8c..1e4218fe 100644 --- a/src/sdl2/lib.rs +++ b/src/sdl2/lib.rs @@ -2,7 +2,12 @@ #![crate_type = "lib"] extern crate num; -extern crate libc; +extern crate libc as libc_crate; +pub use libc_crate as libc; + +#[macro_use] +extern crate lazy_static; + #[macro_use] extern crate bitflags; extern crate sdl2_sys as sys; diff --git a/tests/events.rs b/tests/events.rs new file mode 100644 index 00000000..4ec2e4b7 --- /dev/null +++ b/tests/events.rs @@ -0,0 +1,79 @@ +extern crate sdl2; +use sdl2::event; + +fn main() { + let sdl = sdl2::init().unwrap(); + let ev = sdl.event().unwrap(); + let mut ep = sdl.event_pump().unwrap(); + + test1(&ev); + test2(&ev, &mut ep); + + test3(&ev); + test4(&ev, &mut ep); +} + +fn test1(ev: &sdl2::EventSubsystem) { + let user_event1_id = unsafe { ev.register_event().unwrap() }; + let user_event2_id = unsafe { ev.register_event().unwrap() }; + assert!(user_event1_id != user_event2_id); +} + +fn test2(ev: &sdl2::EventSubsystem, ep: &mut sdl2::EventPump) { + let user_event_id = unsafe { ev.register_event().unwrap() }; + + let event = event::Event::User { + timestamp: 0, + window_id: 0, + type_: user_event_id, + code: 456, + data1: 0x1234 as *mut ::sdl2::libc::c_void, + data2: 0x5678 as *mut ::sdl2::libc::c_void, + }; + + let (t1, a1, a2) = match event { + event::Event::User { type_: t1, data1: a1, data2: a2, .. } => { (t1, a1, a2) } + _ => { panic!("expected user event") } + }; + ev.push_event(event.clone()).unwrap(); + let received = ep.poll_event().unwrap(); + assert_eq!(&event, &received); + match &received { + &event::Event::User { type_: t2, data1: b1, data2: b2, .. } => { + assert_eq!(t1, t2); + assert_eq!(a1, b1); + assert_eq!(a2, b2); + } + other => { panic!("Received non User event: {:?}", other) } + } +} + +struct SomeEventType_test3 { + a: u32 +} +struct SomeOtherEventType_test3 { + b: u32 +} + +fn test3(ev: &sdl2::EventSubsystem) { + ev.register_custom_event::().unwrap(); + ev.register_custom_event::().unwrap(); + + assert!(ev.register_custom_event::().is_err()); +} + +struct SomeEventType_test4 { + a: u32 +} + +fn test4(ev: &sdl2::EventSubsystem, ep: &mut sdl2::EventPump) { + ev.register_custom_event::().unwrap(); + let event = SomeEventType_test4 { a: 42 }; + ev.push_custom_event(event); + + let received = ep.poll_event().unwrap(); + if received.is_user_event() { + let e2 = received.as_user_event_type::().unwrap(); + assert_eq!(e2.a, 42); + } +}