Implement type safe custom events

This commit is contained in:
Maarten Hoogendoorn
2016-02-04 02:50:36 +01:00
parent a9a6314deb
commit c2a4d53660
4 changed files with 178 additions and 12 deletions
+1
View File
@@ -24,6 +24,7 @@ num = "0.1"
bitflags = "0.3"
libc = "0.2"
rand = "0.3"
lazy_static="0.1"
[dependencies.sdl2-sys]
+138 -5
View File
@@ -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<u32, ::std::any::TypeId>,
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<CustomEventTypeMaps> = { 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<u32> {
#[inline(always)]
pub unsafe fn register_event(&self) -> SdlResult<u32> {
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<Vec<u32>> {
/// Returns an error, if no more user events can be created.
pub unsafe fn register_events(&self, nr: u32) -> SdlResult<Vec<u32>> {
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<T: ::std::any::Any>(&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::<Box<T>>();
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::<SomeCustomEvent>().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::<SomeCustomEvent>().unwrap();
/// assert_eq!(e2.a, 42);
/// }
/// ```
pub fn push_custom_event<T: ::std::any::Any>(&self, event:T) -> SdlResult<()> {
use ::std::any::TypeId;
let cet = CUSTOM_EVENT_TYPES.lock().unwrap();
let type_id = TypeId::of::<Box<T>>();
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<T: ::std::any::Any>(&self) -> Option<T> {
use ::std::any::TypeId;
let type_id = TypeId::of::<Box<T>>();
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<T> = unsafe { Box::from_raw(event_box_ptr as *mut T) };
Some(*event_box)
}
}
unsafe fn poll_event() -> Option<Event> {
@@ -1177,3 +1309,4 @@ impl<'a> Iterator for EventWaitTimeoutIterator<'a> {
type Item = Event;
fn next(&mut self) -> Option<Event> { unsafe { wait_event_timeout(self.timeout) } }
}
+3
View File
@@ -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;
+36 -7
View File
@@ -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::<SomeEventType_test3>().unwrap();
ev.register_custom_event::<SomeOtherEventType_test3>().unwrap();
assert!(ev.register_custom_event::<SomeEventType_test3>().is_err());
}
struct SomeEventType_test4 {
a: u32
}
fn test4(ev: &sdl2::EventSubsystem, ep: &mut sdl2::EventPump) {
ev.register_custom_event::<SomeEventType_test4>().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::<SomeEventType_test4>().unwrap();
assert_eq!(e2.a, 42);
}
}