//! Input handling schema written in rust. Backend agnostic, provides serializability, assignment and unified interface for working with inputs. Keyboard, mouse and controllers supported. //! //! Current backends include Piston and Gilrs with more on the way. //! //! **NOTE** prongs requires rust 1.32 or later use serde::{Deserialize, Serialize}; use std::collections::HashMap; pub mod types; // error out specifically if no backend is chosen #[cfg(not(any(feature="backend_piston", feature="backend_gilrs")))] compile_error!("No backend selected, use features= in Cargo.toml or --features when building directly."); #[cfg(feature="backend_piston")] pub mod backend_piston; #[cfg(feature="backend_gilrs")] pub mod backend_gilrs; pub use types::{InputCause, InputTypeFlags, ProcessingResult, ButtonState}; #[cfg(feature="backend_piston")] pub use backend_piston::SchemaPiston; #[cfg(feature="backend_gilrs")] pub use backend_gilrs::SchemaGilrs; #[cfg(test)] mod tests; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(transparent)] struct EventType(u128); impl EventType { fn new(rt: u64, rv: u64) -> Self { EventType { 0: u128::from(rt).rotate_left(64) + u128::from(rv) } } } /// Converts backend events to more "generic" and serializable /// event types. This usually means stripping down needless data /// and making the result small (u64). For example, for keyboard key mapping /// we only care about the event's "identifier" for the key and it's state (pressed/released) /// If there's more info in the backend event we discard that as it could be tied to the /// instance rather than the "type of input" such as event_id sequence or such. /// This should be typically implemented by the prongs-backend- sub-cargos. trait ToEventType where TControllerID: Copy + PartialEq + Serialize { fn to_raw(&self) -> Option; fn controller_id(&self) -> Option; fn filter_for_assignment(&self, iaf: InputTypeFlags) -> bool; fn get_cause(&self) -> Option; } type KeyMap = HashMap; const TYPICAL_MAPPING_SIZE: usize = 255; #[derive(Serialize, Deserialize)] struct Schema where TEventType: ToEventType, TControllerID: Copy + PartialEq + Serialize, TUserAction: Clone + Serialize, { name: String, player_id: Option, controller_id: Option, keymap: KeyMap, #[serde(skip)] _phantom: std::marker::PhantomData, } impl Schema where TEventType: ToEventType, TControllerID: Copy + PartialEq + Serialize, TUserAction: Clone + Serialize, { /// Create a new empty schema fn new(name: &str) -> Self { Schema { name: name.to_string(), keymap: KeyMap::with_capacity(TYPICAL_MAPPING_SIZE), player_id: None, controller_id: None, _phantom: std::marker::PhantomData, } } /// Assign controller to this schema. Future input events will be ignored unless /// their controller matches the assigned one. NOTE: many backends don't have controller IDs /// for keyboards and mice. fn assign_controller(&mut self, event: &TEventType, iaf: InputTypeFlags) -> bool { let event_controller_id = event.controller_id(); if event_controller_id != self.controller_id && event_controller_id.is_some() && event.filter_for_assignment(iaf) { self.controller_id = event.controller_id(); return true } false } /// Assigns input event -> user action mapping fn assign_input(&mut self, event: &TEventType, action: TUserAction, iaf: InputTypeFlags) -> bool { if event.filter_for_assignment(iaf) { if let Some(event_type) = event.to_raw() { self.keymap.insert(event_type, action); self.assign_controller(event, iaf); return true } } false } /// Removes input event -> user action mappings and assigned controller_id /// Player_id stays assigned, if you need to clear that use set_player_id() fn clear_assignments(&mut self) { self.controller_id = None; self.keymap.clear(); } /// Main processing loop. Takes input event and returns an option with processing result /// If the mapping was found result will be set, otherwise None fn process_event(&self, event: &TEventType) -> Option> { if let Some(event_type) = event.to_raw() { if let Some(action) = self.keymap.get(&event_type) { if self.controller_id.is_some() && self.controller_id != event.controller_id() { return None } return Some(ProcessingResult { action: action.clone(), player_id: self.player_id, cause: event.get_cause(), }); } } None } /// Set player_id as option, use None to clear fn set_player_id(&mut self, player_id: Option) { self.player_id = player_id; } }