383 lines
14 KiB
Rust
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 {}
|