mirror of
https://github.com/encounter/rust-sdl2.git
synced 2026-07-10 21:18:41 -07:00
Implement type safe custom events
This commit is contained in:
@@ -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
@@ -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) } }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user