diff --git a/Cargo.toml b/Cargo.toml index 3225cbfe..8dcc1229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ num = "0.1" bitflags = "0.3" libc = "0.2" rand = "0.3" +lazy_static="0.1" [dependencies.sdl2-sys] diff --git a/src/sdl2/event.rs b/src/sdl2/event.rs index ea4675f3..3ee7ce95 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}; @@ -28,6 +30,24 @@ use ErrorMessage; 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) { @@ -104,6 +124,7 @@ impl ::EventSubsystem { } } + /// Register a custom SDL event. /// /// When pushing a user event, you must make sure that the ``type_`` field is set to a @@ -111,14 +132,14 @@ impl ::EventSubsystem { /// /// 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. + /// 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 = ev.register_event().unwrap(); + /// let custom_event_type_id = unsafe { ev.register_event().unwrap() }; /// let event = sdl2::event::Event::User { /// timestamp: 0, /// window_id: 0, @@ -131,14 +152,15 @@ impl ::EventSubsystem { /// ev.push_event(event); /// /// ``` - pub fn register_event(&self) -> SdlResult { + #[inline(always)] + pub unsafe fn register_event(&self) -> SdlResult { Ok(*try!(self.register_events(1)).first().unwrap()) } /// Registers custom SDL events. /// - /// See (register_event)[#method.register_event] for the usage details. - pub fn register_events(&self, nr: u32) -> SdlResult> { + /// 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; @@ -150,6 +172,84 @@ impl ::EventSubsystem { } } } + + /// 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. @@ -1025,6 +1125,38 @@ impl Event { } }} // 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 { @@ -1177,3 +1309,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 d9345d72..2fc08fc9 100644 --- a/src/sdl2/lib.rs +++ b/src/sdl2/lib.rs @@ -5,6 +5,9 @@ extern crate num; 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 index 5fd5d7da..4ec2e4b7 100644 --- a/tests/events.rs +++ b/tests/events.rs @@ -8,17 +8,19 @@ fn main() { test1(&ev); test2(&ev, &mut ep); + + test3(&ev); + test4(&ev, &mut ep); } fn test1(ev: &sdl2::EventSubsystem) { - let user_event1_id = ev.register_event().unwrap(); - let user_event2_id = ev.register_event().unwrap(); - + 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 = ev.register_event().unwrap(); + let user_event_id = unsafe { ev.register_event().unwrap() }; let event = event::Event::User { timestamp: 0, @@ -33,11 +35,8 @@ fn test2(ev: &sdl2::EventSubsystem, ep: &mut sdl2::EventPump) { 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, .. } => { @@ -48,3 +47,33 @@ fn test2(ev: &sdl2::EventSubsystem, ep: &mut sdl2::EventPump) { 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); + } +}