engine/presentation/src/input/eventsystem.rs
2025-03-27 12:30:14 +01:00

383 lines
14 KiB
Rust

use sdl3::{
self, EventPump, EventSubsystem, GamepadSubsystem, JoystickSubsystem, Sdl,
event::{Event as SdlEvent, WindowEvent},
gamepad::{Axis, Button},
keyboard::Keycode,
mouse::{MouseButton as SdlMouseButton, MouseUtil, MouseWheelDirection},
};
use ui::prelude::*;
use std::collections::HashMap;
use crate::Result;
use anyhow::anyhow;
use super::controller::{Controller, ControllerDeadzones};
use super::joystick::Joystick;
fn convert_button(button: Button) -> ControllerButton {
match button {
Button::South => ControllerButton::A,
Button::East => ControllerButton::B,
Button::North => ControllerButton::Y,
Button::West => ControllerButton::X,
Button::Start => ControllerButton::Start,
Button::Back => ControllerButton::Select,
Button::RightShoulder => ControllerButton::RightButton,
Button::LeftShoulder => ControllerButton::LeftButton,
Button::DPadUp => ControllerButton::DPadUp,
Button::DPadDown => ControllerButton::DPadDown,
Button::DPadRight => ControllerButton::DPadRight,
Button::DPadLeft => ControllerButton::DPadLeft,
Button::Guide => ControllerButton::Guide,
Button::LeftStick => ControllerButton::LeftStick,
Button::RightStick => ControllerButton::RightStick,
Button::Misc1 => ControllerButton::Misc,
Button::RightPaddle1 => ControllerButton::Paddle1,
Button::RightPaddle2 => ControllerButton::Paddle2,
Button::LeftPaddle1 => ControllerButton::Paddle3,
Button::LeftPaddle2 => ControllerButton::Paddle4,
Button::Touchpad => ControllerButton::Touchpad,
}
}
#[derive(Debug)]
pub enum Event<'a> {
// mouse events
MouseMotion(u32, u32),
MouseButtonDown(MouseButton),
MouseButtonUp(MouseButton),
MouseWheel(i32, i32, MouseWheelDirection),
// keyboard events
KeyDown(Keycode),
KeyUp(Keycode),
TextInput(String),
// controller events
ControllerAxis(&'a Controller),
ControllerButtonDown(&'a Controller, ControllerButton),
ControllerButtonUp(&'a Controller, ControllerButton),
ControllerAdded(&'a Controller),
ControllerRemoved(&'a Controller),
// joystick events
JoystickAxis(&'a Joystick, u8, i16),
JoystickButtonDown(&'a Joystick),
JoystickButtonUp(&'a Joystick),
JoystickAdded(&'a Joystick),
JoystickRemoved(&'a Joystick),
// drag'n'drop
FileDrop(String),
}
pub struct EventSystem {
event_pump: EventPump,
mouse: MouseUtil,
controller_subsystem: GamepadSubsystem,
joystick_subsystem: JoystickSubsystem,
event_subsystem: EventSubsystem,
controller_deadzones: ControllerDeadzones,
connected_controllers: HashMap<u32, Controller>,
connected_joysticks: HashMap<u32, Joystick>,
}
impl EventSystem {
pub fn new(sdl2_context: &Sdl) -> Result<EventSystem> {
let mut event_system = EventSystem {
event_pump: sdl2_context
.event_pump()
.map_err(|s| anyhow::Error::msg(s))?,
mouse: sdl2_context.mouse(),
controller_subsystem: sdl2_context.gamepad().map_err(|s| anyhow::Error::msg(s))?,
joystick_subsystem: sdl2_context.joystick().map_err(|s| anyhow::Error::msg(s))?,
event_subsystem: sdl2_context.event().map_err(|s| anyhow::Error::msg(s))?,
controller_deadzones: ControllerDeadzones::default(),
connected_controllers: HashMap::new(),
connected_joysticks: HashMap::new(),
};
event_system.connected_joysticks = event_system
.joystick_subsystem
.joysticks()
.map_err(|s| anyhow::Error::msg(s))?
.into_iter()
.map(|instance| {
Ok((
instance.id,
Joystick::new(&event_system.joystick_subsystem, instance)?,
))
})
.collect::<Result<HashMap<_, _>>>()?;
event_system.connected_controllers = (0..event_system
.controller_subsystem
.num_gamepads()
.map_err(|s| anyhow::Error::msg(s))?)
.into_iter()
.filter(|i| event_system.controller_subsystem.is_game_controller(*i))
.map(|i| {
Ok((
i,
Controller::new(
&event_system.controller_subsystem,
i,
event_system.controller_deadzones.clone(),
)?,
))
})
.collect::<Result<HashMap<_, _>>>()?;
event_system.disable_mouse();
Ok(event_system)
}
pub fn enable_mouse(&mut self) {
self.mouse.show_cursor(true);
}
pub fn disable_mouse(&mut self) {
self.mouse.show_cursor(false);
}
pub fn set_controller_axis_enable_deadzone(&mut self, deadzone: f32) {
self.controller_deadzones.axis_enable_deadzone = deadzone;
}
pub fn set_controller_axis_disable_deadzone(&mut self, deadzone: f32) {
self.controller_deadzones.axis_disable_deadzone = deadzone;
}
pub fn set_controller_trigger_enable_deadzone(&mut self, deadzone: f32) {
self.controller_deadzones.trigger_enable_deadzone = deadzone;
}
pub fn set_controller_trigger_disable_deadzone(&mut self, deadzone: f32) {
self.controller_deadzones.trigger_disable_deadzone = deadzone;
}
pub fn quit(&self) -> Result<()> {
Ok(self
.event_subsystem
.push_event(SdlEvent::Quit { timestamp: 0 })
.map_err(|s| anyhow::Error::msg(s))?)
}
pub fn poll_events<F, R>(&mut self, mut event_callback: F, mut resize: R) -> Result<bool>
where
F: FnMut(Event<'_>) -> Result<()>,
R: FnMut(u32, u32) -> Result<()>,
{
for event in self.event_pump.poll_iter() {
match event {
SdlEvent::Window { win_event, .. } => match win_event {
WindowEvent::Resized(w, h) => {
resize(w as u32, h as u32)?;
}
_ => (),
},
SdlEvent::Quit { .. } => return Ok(false),
// ----------------- Mouse Events ---------------------
SdlEvent::MouseMotion { x, y, .. } => {
event_callback(Event::MouseMotion(x as u32, y as u32))?;
}
SdlEvent::MouseButtonDown { mouse_btn, .. } => {
let mouse_button = match mouse_btn {
SdlMouseButton::Left => MouseButton::Left,
SdlMouseButton::Right => MouseButton::Right,
SdlMouseButton::Middle => MouseButton::Middle,
SdlMouseButton::X1 => MouseButton::Forward,
SdlMouseButton::X2 => MouseButton::Backward,
SdlMouseButton::Unknown => continue,
};
event_callback(Event::MouseButtonDown(mouse_button))?;
}
SdlEvent::MouseButtonUp { mouse_btn, .. } => {
let mouse_button = match mouse_btn {
SdlMouseButton::Left => MouseButton::Left,
SdlMouseButton::Right => MouseButton::Right,
SdlMouseButton::Middle => MouseButton::Middle,
SdlMouseButton::X1 => MouseButton::Forward,
SdlMouseButton::X2 => MouseButton::Backward,
SdlMouseButton::Unknown => continue,
};
event_callback(Event::MouseButtonUp(mouse_button))?;
}
SdlEvent::MouseWheel {
x, y, direction, ..
} => {
event_callback(Event::MouseWheel(x as i32, y as i32, direction))?;
}
// ------------------- Key Events ---------------------
SdlEvent::KeyDown {
keycode, repeat, ..
} => {
if repeat {
continue;
}
if let Some(keycode) = keycode {
event_callback(Event::KeyDown(keycode))?;
}
}
SdlEvent::KeyUp {
keycode, repeat, ..
} => {
if repeat {
continue;
}
if let Some(keycode) = keycode {
event_callback(Event::KeyUp(keycode))?;
}
}
SdlEvent::TextInput { text, .. } => {
event_callback(Event::TextInput(text))?;
}
// --------------- Controller Events -------------------
SdlEvent::ControllerDeviceAdded { which, .. } => {
if cfg!(debug_assertions) {
println!("New Device: {}", which);
}
if let Ok(controller) = Controller::new(
&self.controller_subsystem,
which as u32,
self.controller_deadzones.clone(),
) {
if cfg!(debug_assertions) {
println!(
"Controller added: {}({})",
controller.name(),
controller.id()
);
}
event_callback(Event::ControllerAdded(&controller))?;
self.connected_controllers
.insert(controller.id(), controller);
}
}
SdlEvent::ControllerDeviceRemoved { which, .. } => {
if let Some(controller) = self.connected_controllers.remove(&which) {
if cfg!(debug_assertions) {
println!(
"Controller removed: {}({})",
controller.name(),
controller.id()
);
}
event_callback(Event::ControllerRemoved(&controller))?;
}
}
SdlEvent::ControllerButtonDown { button, which, .. } => {
event_callback(Event::ControllerButtonDown(
self.connected_controllers.get(&which).unwrap(),
convert_button(button),
))?;
}
SdlEvent::ControllerButtonUp { button, which, .. } => {
event_callback(Event::ControllerButtonUp(
self.connected_controllers.get(&which).unwrap(),
convert_button(button),
))?;
}
SdlEvent::ControllerAxisMotion {
axis, value, which, ..
} => {
let controller = self.connected_controllers.get_mut(&which).unwrap();
let normalized = value as f32 * 0.000_030_518;
match axis {
Axis::LeftX => {
controller.set_left_x(normalized);
}
Axis::RightX => {
controller.set_right_x(normalized);
}
Axis::LeftY => {
controller.set_left_y(-normalized);
}
Axis::RightY => {
controller.set_right_y(normalized);
}
Axis::TriggerLeft => {
controller.set_left_trigger(normalized);
}
Axis::TriggerRight => {
controller.set_right_trigger(normalized);
}
}
event_callback(Event::ControllerAxis(&*controller))?;
}
SdlEvent::JoyDeviceAdded { which, .. } => {
if let Some(joystick_instance) = self
.joystick_subsystem
.joysticks()
.map_err(|_| anyhow!("failed querying joysticks"))?
.into_iter()
.find(|instance| instance.id == which)
{
let joystick = Joystick::new(&self.joystick_subsystem, joystick_instance)?;
event_callback(Event::JoystickAdded(&joystick))?;
self.connected_joysticks.insert(joystick.id(), joystick);
}
}
SdlEvent::JoyDeviceRemoved { which, .. } => {
if let Some(joysticks) = self.connected_joysticks.remove(&which) {
event_callback(Event::JoystickRemoved(&joysticks))?;
}
}
SdlEvent::JoyAxisMotion {
which,
axis_idx,
value,
..
} => {
if let Some(joysticks) = self.connected_joysticks.get(&which) {
event_callback(Event::JoystickAxis(&joysticks, axis_idx, value))?;
}
}
SdlEvent::DropFile { filename, .. } => {
event_callback(Event::FileDrop(filename))?;
}
_ => (),
}
}
Ok(true)
}
pub fn controllers(&self) -> impl Iterator<Item = &Controller> {
self.connected_controllers.values()
}
pub fn joysticks(&self) -> impl Iterator<Item = &Joystick> {
self.connected_joysticks.values()
}
}
unsafe impl Send for EventSystem {}
unsafe impl Sync for EventSystem {}