From 455a1b6e44dd5d394cf28dfba846dad2cbed8a74 Mon Sep 17 00:00:00 2001 From: Berghopper <12167073+Berghopper@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:33:46 +0300 Subject: [PATCH 1/3] add: cli and refactor stuff -> borrowing and lifetime hell --- README.md | 4 + src/controller_abs/mod.rs | 23 ++- src/controller_in/mod.rs | 409 +++++++++++++++++--------------------- src/main.rs | 189 +++++++++++++++--- 4 files changed, 358 insertions(+), 267 deletions(-) diff --git a/README.md b/README.md index beb3606..0b8cf61 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,7 @@ Input controllers: - Axis to button mapping? - Rumble support - Other 'fancy' settings (leds etc.) + +## Known issues + +- xwiimote not linked properly => try to compile with `XWIIMOTE_SYS_STATIC=1` diff --git a/src/controller_abs/mod.rs b/src/controller_abs/mod.rs index 23ecc05..b6c6f01 100644 --- a/src/controller_abs/mod.rs +++ b/src/controller_abs/mod.rs @@ -293,8 +293,14 @@ impl Default for Axis { #[test] fn test_axis() { - assert_eq!(Axis::new::(127, u8::MIN, u8::MAX).convert_into::(false), 127); - assert_eq!(Axis::new::(50, 0, 100).convert_into::(false), 127); + assert_eq!( + Axis::new::(127, u8::MIN, u8::MAX).convert_into::(false), + 127 + ); + assert_eq!( + Axis::new::(50, 0, 100).convert_into::(false), + 127 + ); assert_eq!(Axis::new(0.0, -1.0, 1.0).convert_into::(false), 127); } @@ -357,7 +363,7 @@ impl JoystickState { } // Generic gamepad -#[derive(EnumIter, Hash, Eq, PartialEq, Clone)] +#[derive(EnumIter, Hash, Eq, PartialEq, Clone, Debug)] pub enum GamepadButton { North, East, @@ -376,7 +382,7 @@ pub enum GamepadButton { DPadRight, } -#[derive(EnumIter, PartialEq, Eq, Hash, Clone)] +#[derive(EnumIter, PartialEq, Eq, Hash, Clone, Debug)] pub enum GamepadAxis { LeftTrigger, RightTrigger, @@ -436,10 +442,15 @@ where pub output: OutputMapping, } +pub trait ControllerRetriever { + type ControllerType<'a> + where + Self: 'a; + + fn discover_all<'a>(&'a self) -> Box> + 'a>; +} pub trait ControllerInput { type ControllerType; fn to_gamepad<'a>(&'a mut self) -> &'a Gamepad; - fn discover_all() -> Vec; fn prep_for_input_events(&mut self); - async fn get_next_inputs(&mut self) -> Result; } diff --git a/src/controller_in/mod.rs b/src/controller_in/mod.rs index b61f02c..0a718a0 100644 --- a/src/controller_in/mod.rs +++ b/src/controller_in/mod.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{borrow::{Borrow, BorrowMut}, cell::Ref, time::{Duration, SystemTime}}; use futures::TryStreamExt; use futures_util::StreamExt; @@ -9,16 +9,21 @@ use xwiimote::{ }; use crate::controller_abs::{ - Axis, ControllerInput, ControllerMapping, Gamepad, GamepadAxis, GamepadButton, OutputMapping, + Axis, ControllerInput, ControllerMapping, ControllerRetriever, Gamepad, GamepadAxis, + GamepadButton, OutputMapping, }; use futures::executor::block_on; use gilrs::{ - Axis as GilAxis, Button as GilButton, Event as GilEvent, Gamepad as GilGamepad, - GamepadId as GilGamepadId, Gilrs, + Axis as GilAxis, Button as GilButton, Event as GilEvent, EventType as GilEventType, + Gamepad as GilGamepad, GamepadId as GilGamepadId, Gilrs, }; use gilrs::ev::state::AxisData as GilAxisData; use gilrs::ev::Code as GilCode; +use std::collections::{HashMap, VecDeque}; +use std::cell::RefCell; +use std::ops::Deref; +use std::rc::Rc; // TODO: use actix? @@ -47,6 +52,14 @@ impl PartialEq for XWiiEvent { } } +pub struct XWiiHandler {} + +impl XWiiHandler { + pub fn new() -> XWiiHandler { + XWiiHandler {} + } +} + pub struct XWiiInput { device: Device, gamepad: Gamepad, @@ -197,34 +210,9 @@ impl XWiiInput { } } } -} - -impl ControllerInput for XWiiInput { - type ControllerType = XWiiInput; - fn to_gamepad<'a>(&'a mut self) -> &'a Gamepad { - return &self.gamepad; - } - - fn discover_all() -> Vec { - let monitor = Monitor::enumerate().unwrap(); - - let addresses: Vec<_> = block_on(async { monitor.collect().await }); - - let mut inps: Vec = vec![]; - for address in addresses { - inps.push(Self::ControllerType::new(&address.unwrap())); - } - - return inps; - } - - fn prep_for_input_events(&mut self) { - // TODO: better decice handling with disconnects etc. - self.device - .open(Channels::from_bits(self.channels.bits()).unwrap(), true) - .unwrap(); - println!("XWiiInput connected: {}", self.device.kind().unwrap()); + pub fn get_device(&self) -> &Device { + &self.device } async fn get_next_inputs(&mut self) -> Result { @@ -254,237 +242,200 @@ impl ControllerInput for XWiiInput { } } -pub struct GilRsInput { - gamepad: Gamepad, - gil_rs: Gilrs, - gil_rs_device_id: GilGamepadId, - deadzone_percentage: f64, -} +impl ControllerRetriever for XWiiHandler { + type ControllerType<'a> = XWiiInput; -impl GilRsInput { - pub fn new(gil_rs: Gilrs, gil_rs_device_id: GilGamepadId) -> GilRsInput { - GilRsInput { - gamepad: Gamepad::new(), - gil_rs, - gil_rs_device_id, - deadzone_percentage: 0.05, // 5% + fn discover_all<'a>(&'a self) -> Box> + 'a> { + let monitor = Monitor::enumerate().unwrap(); + + let addresses: Vec<_> = block_on(async { monitor.collect().await }); + + let mut inps: Vec> = vec![]; + for address in addresses { + inps.push(Self::ControllerType::new(&address.unwrap())); } + + Box::new(inps.into_iter()) } +} - fn get_gilrs_gamepad(&self) -> GilGamepad { - self.gil_rs.gamepad(self.gil_rs_device_id) +impl ControllerInput for XWiiInput { + type ControllerType = XWiiInput; + + fn to_gamepad<'a>(&'a mut self) -> &'a Gamepad { + return &self.gamepad; } - fn map_gilrs_to_gamepad(&mut self) { - let buttons = [ - GilButton::South, - GilButton::East, - GilButton::North, - GilButton::West, - // GilButton::C, - // GilButton::Z, - GilButton::LeftTrigger, - // GilButton::LeftTrigger2, - GilButton::RightTrigger, - // GilButton::RightTrigger2, - GilButton::Select, - GilButton::Start, - GilButton::Mode, - GilButton::LeftThumb, - GilButton::RightThumb, - GilButton::DPadUp, - GilButton::DPadDown, - GilButton::DPadLeft, - GilButton::DPadRight, - ]; - // We also NEED to consume events here, otherwise data is not filled properly on the gamepad - while let Some(GilEvent { id, event, time }) = self.gil_rs.next_event() { - // FIXME: gamepad state seems inconsistent?/mappings might be weird... - println!("{:?} New event from {}: {:?}", time, id, event); - } + fn prep_for_input_events(&mut self) { + // TODO: better decice handling with disconnects etc. + self.device + .open(Channels::from_bits(self.channels.bits()).unwrap(), true) + .unwrap(); + println!("XWiiInput connected: {}", self.device.kind().unwrap()); + } +} - for button in buttons.iter() { - let gilrs_gamepad = self.get_gilrs_gamepad(); - - let is_pressed = gilrs_gamepad.is_pressed(button.clone()); - - // Button to button - match button { - GilButton::South => self.gamepad.set_button(GamepadButton::South, is_pressed), - GilButton::East => self.gamepad.set_button(GamepadButton::East, is_pressed), - GilButton::North => self.gamepad.set_button(GamepadButton::North, is_pressed), - GilButton::West => self.gamepad.set_button(GamepadButton::West, is_pressed), - GilButton::LeftTrigger => self - .gamepad - .set_button(GamepadButton::LeftShoulderButton, is_pressed), - GilButton::RightTrigger => self - .gamepad - .set_button(GamepadButton::RightShoulderButton, is_pressed), - GilButton::Select => self.gamepad.set_button(GamepadButton::Select, is_pressed), - GilButton::Start => self.gamepad.set_button(GamepadButton::Start, is_pressed), - GilButton::Mode => self.gamepad.set_button(GamepadButton::Mode, is_pressed), - GilButton::LeftThumb => self - .gamepad - .set_button(GamepadButton::LeftThumb, is_pressed), - GilButton::RightThumb => self - .gamepad - .set_button(GamepadButton::RightThumb, is_pressed), - GilButton::DPadUp => self.gamepad.set_button(GamepadButton::DPadUp, is_pressed), - GilButton::DPadDown => self.gamepad.set_button(GamepadButton::DPadDown, is_pressed), - GilButton::DPadLeft => self.gamepad.set_button(GamepadButton::DPadLeft, is_pressed), - GilButton::DPadRight => self - .gamepad - .set_button(GamepadButton::DPadRight, is_pressed), - _ => (), - }; - } - // Axis to axis - let axes = [ - GilAxis::LeftStickX, - GilAxis::LeftStickY, - GilAxis::RightStickX, - GilAxis::RightStickY, - GilAxis::RightZ, - GilAxis::LeftZ, - ]; - - let mut current_state: Vec<(GilCode, GilAxisData)> = vec![]; - { - for (code, axis) in self.get_gilrs_gamepad().state().axes() { - current_state.push((code.clone(), axis.clone())); - } +// --- GILRS --- +pub struct GilRsHandler { + pub gilrs: Gilrs, + event_queue: HashMap>, +} + +impl GilRsHandler { + pub fn new() -> Self { + Self { + gilrs: Gilrs::new().unwrap(), + event_queue: HashMap::new(), } + } - // Axis mapping is pretty weird... - for (code, axis) in current_state { - let in_axis = Axis::new(axis.value(), -1.0, 1.0); - let mut known_axis = false; - for ax in axes { - if (self.get_gilrs_gamepad().axis_code(ax)) != Some(code) { - break; - } + pub fn process_events(&mut self) { + while let Some(GilEvent { id, event, time }) = self.gilrs.next_event() { + println!("{:?} New event from {:?}: {:?}", time, id, event); - match ax { - GilAxis::LeftStickX => { - known_axis = true; - self.gamepad.get_axis_ref(GamepadAxis::LeftJoystickX).value = - in_axis.convert_into(false); - } - GilAxis::LeftStickY => { - known_axis = true; - self.gamepad.get_axis_ref(GamepadAxis::LeftJoystickY).value = - in_axis.convert_into(false); - } - GilAxis::RightStickX => { - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickX).value = - in_axis.convert_into(false); - } - GilAxis::RightStickY => { - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickY).value = - in_axis.convert_into(false); - } - GilAxis::LeftZ => { - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickX).value = - in_axis.convert_into(false); - } - GilAxis::RightZ => { - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickY).value = - in_axis.invert().convert_into(false); - } + // Format `GamepadId` as a string and then parse it into a `usize` + let gamepad_id_str = id.to_string(); + let gamepad_id: u8 = gamepad_id_str.parse().unwrap(); - _ => {} - } - } - if !known_axis { - match format!("{}", code).as_str() { - // Right trigger - "ABS(9)" => { - self.gamepad.get_axis_ref(GamepadAxis::RightTrigger).value = - in_axis.convert_into(false); - } - // Left trigger - "ABS(10)" => { - self.gamepad.get_axis_ref(GamepadAxis::LeftTrigger).value = - in_axis.convert_into(false); - } - // Left stick - "ABS(0)" => { - self.gamepad.get_axis_ref(GamepadAxis::LeftJoystickX).value = - in_axis.convert_into(false); - } - "ABS(1)" => { - self.gamepad.get_axis_ref(GamepadAxis::LeftJoystickY).value = - in_axis.convert_into(false); - } - // Right stick - "ABS(2)" => { - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickX).value = - in_axis.convert_into(false); - } - "ABS(5)" => { - // For some reason this is inverted.. - self.gamepad.get_axis_ref(GamepadAxis::RightJoystickY).value = - in_axis.invert().convert_into(false); - } - _ => { - // FIXME: turned off for spam, some axes seem duplicated?... - println!("Unknown axis!: {}/{}", code, axis.value()); - } - } - } + // Insert the event into the queue for this gamepad ID + let queue = self + .event_queue + .entry(gamepad_id) + .or_insert_with(VecDeque::new); + + // Add the event and time to the queue + queue.push_back((time, event)); } } -} -impl ControllerInput for GilRsInput { - type ControllerType = GilRsInput; + pub fn dequeue_event_queue(&mut self, id: GilGamepadId) -> Vec<(SystemTime, GilEventType)> { + let gamepad_id_str = id.to_string(); + let gamepad_id: u8 = gamepad_id_str.parse().unwrap(); - fn to_gamepad<'b>(&'b mut self) -> &'b Gamepad { - return &self.gamepad; + if let Some(queue) = self.event_queue.get_mut(&gamepad_id) { + let events: Vec<(SystemTime, GilEventType)> = queue.drain(..).collect(); + events + } else { + Vec::new() // Return an empty Vec if no events exist for the given ID + } } - fn discover_all() -> Vec { - let gilrs = Gilrs::new().unwrap(); - - let mut inps: Vec = vec![]; + pub fn discover_all<'a>(&'a self, self_ref: Rc>) -> Box> + 'a> { + let mut inps = Vec::new(); macro_rules! ignore_controller { ($gamepad:expr, $s:expr) => { if $gamepad.name().contains($s) | $gamepad.os_name().contains($s) { continue; } - } + }; } - for (_id, gamepad) in gilrs.gamepads() { - let gilrs_current_gamepad = Gilrs::new().unwrap(); - + for (_id, gamepad) in self.gilrs.gamepads() { // e.g. Nintendo Wii Remote Nunchuk ignore_controller!(gamepad, "Wii"); ignore_controller!(gamepad, "Nunchuk"); - - - inps.push(Self::ControllerType::new( - gilrs_current_gamepad, - gamepad.id(), - )); - println!( - "Detected!: {}/{}/{}", - gamepad.id(), - gamepad.name(), - gamepad.os_name() - ); + + inps.push(GilRsInput::new(Rc::clone(&self_ref), gamepad.id())); + // println!( + // "Detected!: {}/{}/{}", + // gamepad.id(), + // gamepad.name(), + // gamepad.os_name() + // ); } - return inps; + Box::new(inps.into_iter()) } +} - fn prep_for_input_events(&mut self) { - println!("GilRsInput connected: {}", self.get_gilrs_gamepad().name()); +// struct GilGamepadGuard<'c> { +// guard: Ref<'c, GilGamepad<'c>>, +// } + +// impl<'c> Deref for GilGamepadGuard<'c> { +// type Target = GilGamepad<'c>; + +// fn deref(&self) -> &GilGamepad<'c> { +// &self.guard +// } +// } + +pub struct GilRsInput<'a> { + gilrs_handler: Rc>, + gamepad: Gamepad, + pub id: GilGamepadId, + deadzone_percentage: f64, +} + +impl<'a> GilRsInput<'a> { + pub fn new(gilrs_handler: Rc>, id: GilGamepadId) -> GilRsInput<'a> { + GilRsInput { + gilrs_handler, + gamepad: Gamepad::new(), + id, + deadzone_percentage: 0.05, // 5% + } } - async fn get_next_inputs(&mut self) -> Result { - self.map_gilrs_to_gamepad(); - return Ok(true); + fn get_inputs_for_mapping(&mut self) { + let y = self.gilrs_handler.get_mut(); + let x = y.dequeue_event_queue(self.id); + // FIXME / WIP + } + + pub fn get_gilrs(&self) -> Ref<'_, Gilrs> { + Ref::map(self.gilrs_handler.borrow(), |x| &x.gilrs) + } +} + +// impl ControllerRetriever for GilRsHandler { +// type ControllerType<'a> = GilRsInput<'a> where Self: 'a; + +// fn discover_all<'a>(&'a self) -> Box> + 'a> { +// let mut inps = Vec::new(); + +// macro_rules! ignore_controller { +// ($gamepad:expr, $s:expr) => { +// if $gamepad.name().contains($s) | $gamepad.os_name().contains($s) { +// continue; +// } +// }; +// } +// let selfref: RefCell<&mut GilRsHandler> = RefCell::new(self); +// let y; +// { +// let borrowed_self = selfref.borrow(); +// } + +// let y = borrowed_self.gilrs.gamepads(); + +// for (_id, gamepad) in y { +// // e.g. Nintendo Wii Remote Nunchuk +// ignore_controller!(gamepad, "Wii"); +// ignore_controller!(gamepad, "Nunchuk"); + +// inps.push(Self::ControllerType::new(selfr, gamepad.id())); +// // println!( +// // "Detected!: {}/{}/{}", +// // gamepad.id(), +// // gamepad.name(), +// // gamepad.os_name() +// // ); +// } + +// Box::new(inps.into_iter()) +// } +// } + +impl<'a> ControllerInput for GilRsInput<'a> { + type ControllerType = GilRsInput<'a> where Self: 'a; + + fn to_gamepad<'b>(&'b mut self) -> &'b Gamepad { + return &self.gamepad; + } + + fn prep_for_input_events(&mut self) { + println!("GilRsInput connected: {}", self.get_gilrs().gamepad(self.id).name()); } } diff --git a/src/main.rs b/src/main.rs index 0d8ba30..036d497 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,14 @@ -use controller_abs::{ControllerInput, GamepadAxis, OutputMapping}; +use controller_abs::{ControllerInput, ControllerRetriever, Gamepad, GamepadAxis, OutputMapping}; use futures_util::TryStreamExt; +use std::io::{self, Write}; use std::thread::sleep; use std::time::Duration; +use strum::IntoEnumIterator; use tokio; use xwiimote::events::{Event, Key, KeyState, NunchukKey}; use xwiimote::{Monitor, Result}; +use std::cell::RefCell; +use std::rc::Rc; #[allow(dead_code)] mod controller_abs; @@ -14,7 +18,7 @@ mod controller_in; mod controller_out; use controller_abs::GamepadButton; -use controller_in::{GilRsInput, XWiiInput}; +use controller_in::{GilRsHandler, GilRsInput, XWiiHandler, XWiiInput}; use controller_out::x360::XboxControllerState; @@ -59,6 +63,157 @@ fn example_loop() { // close_360_gadget_c(fd); } +fn get_user_input(prompt: &str) -> String { + print!("{} (q to quit): ", prompt); + io::stdout().flush().unwrap(); + + let mut input = String::new(); + + // Read input from stdin (standard input) + io::stdin() + .read_line(&mut input) + .expect("Failed to read user input"); + + let retval = input.trim().to_string(); + + if retval.to_lowercase() == "q" { + std::process::exit(0); + } + + retval +} + +fn cli_map_user_input(inp: &InputType, outputmapping: OutputMapping) { + // First await input + match &inp { + &InputType::WiiInput(_wii_inp) => { + // FIXME + } + &InputType::GilInput(gil_inp) => { + // FIXME + + } + } +} + +fn cli_gamepad_settings(inp: &InputType) { + loop { + // List all output mapping options + println!("The following options are available to map to (not your input controller!):"); + println!("\n\nButtons:"); + for (index, button) in GamepadButton::iter().enumerate() { + println!(" {}: {:?}", index + 1, button); + } + + // Calculate offset for axes numbering + let button_count = GamepadButton::iter().count(); + + // List all Gamepad Axes + println!("\nAxes:"); + for (index, axis) in GamepadAxis::iter().enumerate() { + println!(" {}: {:?}", button_count + index + 1, axis); + } + + let userinp = get_user_input("\n\nSelect which button/axis you want to map to:"); + let optional_choice = userinp.parse::(); + + if optional_choice.is_err() { + println!("Invalid input, try again"); + } + let choice = optional_choice.unwrap(); + + if choice <= button_count { + // Button selected + let selected_button = GamepadButton::iter().nth(choice - 1).unwrap(); + println!("You selected button: {:?}", selected_button); + + // Await user input + cli_map_user_input(inp, OutputMapping::Button(selected_button)); + } + } +} + +fn cli_gamepads_overview(inps: Vec) { + loop { + println!("Detected {} controller(s):\n", &inps.len()); + + let mut n_gamepads: u8 = 0; + + for inp in &inps { + n_gamepads += 1; + match inp { + InputType::GilInput(gil_inp) => { + let gilrs = gil_inp.get_gilrs(); + let gil_gampead = gilrs.gamepad(gil_inp.id); + if gil_gampead.os_name().to_lowercase() != gil_gampead.name().to_lowercase() { + println!( + "{}. {} ({})", + n_gamepads, + gil_gampead.name(), + gil_gampead.os_name() + ); + } else { + println!("{}. {}", n_gamepads, gil_gampead.name()); + } + } + InputType::WiiInput(_wii_inp) => { + println!("Wii mote #{}", n_gamepads); + } + } + } + + let choice: usize; + + loop { + let inp = get_user_input("Select which controller you would like to set up"); + + let gamepad_id = inp.parse::(); + if gamepad_id.is_err() + || gamepad_id.as_ref().unwrap() < &1 + || gamepad_id.as_ref().unwrap() > &n_gamepads.into() + { + println!("Invalid input, try again"); + } else { + choice = gamepad_id.unwrap(); + break; + } + } + cli_gamepad_settings(&inps[choice - 1]) + } +} + +enum InputType<'a> { + WiiInput(XWiiInput), + GilInput(GilRsInput<'a>), +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let mut inps = vec![]; + let wii_handler = XWiiHandler::new(); + let mut gilrs_handler = Rc::new(RefCell::new(GilRsHandler::new())); + + for inp in wii_handler.discover_all() { + inps.push(InputType::WiiInput(inp)); + } + + let gilrs_handler_b = gilrs_handler.borrow(); + for inp in gilrs_handler_b.discover_all(Rc::clone(gilrs_handler)) { + inps.push(InputType::GilInput(inp)); + } + + if inps.len() == 0 { + println!("You have no connected controllers!"); + std::process::exit(1); + } + + cli_gamepads_overview(inps); + + Ok(()) +} + +// OLD + // #[tokio::main(flavor = "current_thread")] // async fn main() -> Result<()> { // // Create a monitor to enumerate connected Wii Remotes @@ -151,33 +306,3 @@ fn example_loop() { // Ok(()) // } - - - - -#[tokio::main(flavor = "current_thread")] -async fn main() -> Result<()> { - let fd = init_360_gadget_c(true, 1); - let mut controller_state = XboxControllerState::new(); - - let mut gil_inps = GilRsInput::discover_all(); - gil_inps[0].prep_for_input_events(); - - loop { - // println!("Getting inputs..."); - let _res = gil_inps[0].get_next_inputs().await; - // println!("Updating state..."); - controller_state.update_from_gamepad(gil_inps[0].to_gamepad()); - // println!("A button: {}", controller_state.buttons.a.value); - - let success = send_to_ep_c(fd, 0, controller_state.to_packet().as_ptr(), 20); - if !success { - // Probably crashed? - break; - } - // After sending state, sleep 1ms. - tokio::time::sleep(Duration::from_micros(900)).await; - } - - Ok(()) -} From 97ba018460758fc65e9762b8f5f7ed3ccd87f337 Mon Sep 17 00:00:00 2001 From: Berghopper <12167073+Berghopper@users.noreply.github.com> Date: Sun, 22 Sep 2024 00:15:55 +0300 Subject: [PATCH 2/3] rc + refcell ftw! --- src/controller_in/mod.rs | 38 ++++++++++++++++++++------------------ src/main.rs | 11 ++++++----- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/controller_in/mod.rs b/src/controller_in/mod.rs index 0a718a0..363154e 100644 --- a/src/controller_in/mod.rs +++ b/src/controller_in/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::{Borrow, BorrowMut}, cell::Ref, time::{Duration, SystemTime}}; +use std::{cell::Ref, time::{Duration, SystemTime}}; use futures::TryStreamExt; use futures_util::StreamExt; @@ -320,7 +320,7 @@ impl GilRsHandler { } } - pub fn discover_all<'a>(&'a self, self_ref: Rc>) -> Box> + 'a> { + pub fn discover_all(&self, self_ref: Rc>) -> Box> { let mut inps = Vec::new(); macro_rules! ignore_controller { @@ -361,15 +361,15 @@ impl GilRsHandler { // } // } -pub struct GilRsInput<'a> { - gilrs_handler: Rc>, +pub struct GilRsInput { + gilrs_handler: Rc>, gamepad: Gamepad, pub id: GilGamepadId, deadzone_percentage: f64, } -impl<'a> GilRsInput<'a> { - pub fn new(gilrs_handler: Rc>, id: GilGamepadId) -> GilRsInput<'a> { +impl GilRsInput { + pub fn new(gilrs_handler: Rc>, id: GilGamepadId) -> GilRsInput { GilRsInput { gilrs_handler, gamepad: Gamepad::new(), @@ -379,9 +379,11 @@ impl<'a> GilRsInput<'a> { } fn get_inputs_for_mapping(&mut self) { - let y = self.gilrs_handler.get_mut(); - let x = y.dequeue_event_queue(self.id); - // FIXME / WIP + + let mut a = self.gilrs_handler.borrow_mut(); + + a.dequeue_event_queue(self.id); + } pub fn get_gilrs(&self) -> Ref<'_, Gilrs> { @@ -428,14 +430,14 @@ impl<'a> GilRsInput<'a> { // } // } -impl<'a> ControllerInput for GilRsInput<'a> { - type ControllerType = GilRsInput<'a> where Self: 'a; +// impl<'a> ControllerInput for GilRsInput<'a> { +// type ControllerType = GilRsInput<'a> where Self: 'a; - fn to_gamepad<'b>(&'b mut self) -> &'b Gamepad { - return &self.gamepad; - } +// fn to_gamepad<'b>(&'b mut self) -> &'b Gamepad { +// return &self.gamepad; +// } - fn prep_for_input_events(&mut self) { - println!("GilRsInput connected: {}", self.get_gilrs().gamepad(self.id).name()); - } -} +// fn prep_for_input_events(&mut self) { +// println!("GilRsInput connected: {}", self.get_gilrs().gamepad(self.id).name()); +// } +// } diff --git a/src/main.rs b/src/main.rs index 036d497..c9e55ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,23 +182,24 @@ fn cli_gamepads_overview(inps: Vec) { } } -enum InputType<'a> { +enum InputType { WiiInput(XWiiInput), - GilInput(GilRsInput<'a>), + GilInput(GilRsInput), } #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let mut inps = vec![]; let wii_handler = XWiiHandler::new(); - let mut gilrs_handler = Rc::new(RefCell::new(GilRsHandler::new())); + let gilrs_handler = Rc::new(RefCell::new(GilRsHandler::new())); for inp in wii_handler.discover_all() { inps.push(InputType::WiiInput(inp)); } - let gilrs_handler_b = gilrs_handler.borrow(); - for inp in gilrs_handler_b.discover_all(Rc::clone(gilrs_handler)) { + let gilrs_handler_b = gilrs_handler.borrow_mut(); + + for inp in gilrs_handler_b.discover_all(Rc::clone(&gilrs_handler)) { inps.push(InputType::GilInput(inp)); } From 0fbdfd4ad4b83c0e5683ba7aa3aee2ce9e734a7e Mon Sep 17 00:00:00 2001 From: Berghopper <12167073+Berghopper@users.noreply.github.com> Date: Sun, 22 Sep 2024 15:15:57 +0300 Subject: [PATCH 3/3] add: more cli/event code --- rustfmt.toml | 1 + src/controller_abs/mod.rs | 13 +-- src/controller_in/mod.rs | 212 ++++++++++++++++++++------------------ src/main.rs | 97 ++++++++++++----- 4 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..4e727a0 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +reorder_imports = true \ No newline at end of file diff --git a/src/controller_abs/mod.rs b/src/controller_abs/mod.rs index b6c6f01..b3e70c1 100644 --- a/src/controller_abs/mod.rs +++ b/src/controller_abs/mod.rs @@ -111,6 +111,7 @@ fn test_normalization() { } // Values in axis are all u64, most likely controllers will have smaller sizes, so more easily convertible. +#[derive(Clone, Debug)] pub struct Axis { pub value: u64, min: u64, @@ -429,11 +430,12 @@ pub enum InputType { } // Mappings -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum OutputMapping { Button(GamepadButton), Axis(GamepadAxis), } + pub struct ControllerMapping where T: Clone, @@ -442,15 +444,8 @@ where pub output: OutputMapping, } -pub trait ControllerRetriever { - type ControllerType<'a> - where - Self: 'a; - - fn discover_all<'a>(&'a self) -> Box> + 'a>; -} pub trait ControllerInput { type ControllerType; - fn to_gamepad<'a>(&'a mut self) -> &'a Gamepad; + fn to_gamepad(&mut self) -> &Gamepad; fn prep_for_input_events(&mut self); } diff --git a/src/controller_in/mod.rs b/src/controller_in/mod.rs index 363154e..02a62c4 100644 --- a/src/controller_in/mod.rs +++ b/src/controller_in/mod.rs @@ -1,16 +1,19 @@ -use std::{cell::Ref, time::{Duration, SystemTime}}; +use std::{ + cell::Ref, + time::{Duration, SystemTime}, + vec, +}; use futures::TryStreamExt; use futures_util::StreamExt; use num_traits::ToPrimitive; use xwiimote::{ - events::{Event, KeyState}, + events::{Event as WiiEvent, KeyState}, Address, Channels, Device, Monitor, }; use crate::controller_abs::{ - Axis, ControllerInput, ControllerMapping, ControllerRetriever, Gamepad, GamepadAxis, - GamepadButton, OutputMapping, + Axis, ControllerInput, ControllerMapping, Gamepad, GamepadAxis, GamepadButton, OutputMapping, }; use futures::executor::block_on; use gilrs::{ @@ -20,18 +23,17 @@ use gilrs::{ use gilrs::ev::state::AxisData as GilAxisData; use gilrs::ev::Code as GilCode; -use std::collections::{HashMap, VecDeque}; use std::cell::RefCell; -use std::ops::Deref; +use std::collections::{HashMap, VecDeque}; use std::rc::Rc; // TODO: use actix? -struct XWiiEvent(Event); +struct XWiiEvent(WiiEvent); impl XWiiEvent { // Constructor to wrap an Event into MyEvent - fn new(event: xwiimote::events::Event) -> Self { + fn new(event: WiiEvent) -> Self { XWiiEvent(event) } } @@ -39,32 +41,24 @@ impl XWiiEvent { impl PartialEq for XWiiEvent { fn eq(&self, other: &Self) -> bool { match (&self.0, &other.0) { - (Event::Key(key1, _), Event::Key(key2, _)) => { + (WiiEvent::Key(key1, _), WiiEvent::Key(key2, _)) => { std::mem::discriminant(key1) == std::mem::discriminant(key2) } - (Event::NunchukKey(key1, _), Event::NunchukKey(key2, _)) => { + (WiiEvent::NunchukKey(key1, _), WiiEvent::NunchukKey(key2, _)) => { std::mem::discriminant(key1) == std::mem::discriminant(key2) } - (Event::NunchukMove { .. }, Event::NunchukMove { .. }) => true, + (WiiEvent::NunchukMove { .. }, WiiEvent::NunchukMove { .. }) => true, // FIXME: Add others... _ => false, } } } -pub struct XWiiHandler {} - -impl XWiiHandler { - pub fn new() -> XWiiHandler { - XWiiHandler {} - } -} - pub struct XWiiInput { device: Device, gamepad: Gamepad, channels: Channels, - mappings: Vec>, + mappings: Vec>, nunchuck_x_min: i32, nunchuck_x_max: i32, nunchuck_y_min: i32, @@ -88,14 +82,14 @@ impl XWiiInput { } } - pub fn map_event(&mut self, event: Event, to_mapping: OutputMapping) { + pub fn map_event(&mut self, event: WiiEvent, to_mapping: OutputMapping) { self.mappings.push(ControllerMapping { input: event, output: to_mapping.clone(), }); } - fn map_event_to_gamepad(&mut self, event: Event) { + fn map_event_to_gamepad(&mut self, event: WiiEvent) { macro_rules! button_to_gamepad { ($self:expr, $controller_mapping_output:expr, $key_state:expr) => { let button_down = !matches!($key_state, KeyState::Up); @@ -125,13 +119,13 @@ impl XWiiInput { // If we have found our input key, we still need to do some basic matching to ensure correct mapping. // E.g. button -> Axis is a little weird. match event { - Event::Key(_key, key_state) => { + WiiEvent::Key(_key, key_state) => { button_to_gamepad!(self, &controller_mapping.output, key_state); } - Event::NunchukKey(_key, key_state) => { + WiiEvent::NunchukKey(_key, key_state) => { button_to_gamepad!(self, &controller_mapping.output, key_state); } - Event::NunchukMove { + WiiEvent::NunchukMove { x, y, x_acceleration: _, @@ -240,19 +234,15 @@ impl XWiiInput { self.map_event_to_gamepad(event); return Ok(true); } -} - -impl ControllerRetriever for XWiiHandler { - type ControllerType<'a> = XWiiInput; - fn discover_all<'a>(&'a self) -> Box> + 'a> { + pub fn discover_all() -> Box> { let monitor = Monitor::enumerate().unwrap(); let addresses: Vec<_> = block_on(async { monitor.collect().await }); - let mut inps: Vec> = vec![]; + let mut inps: Vec = vec![]; for address in addresses { - inps.push(Self::ControllerType::new(&address.unwrap())); + inps.push(Self::new(&address.unwrap())); } Box::new(inps.into_iter()) @@ -320,7 +310,10 @@ impl GilRsHandler { } } - pub fn discover_all(&self, self_ref: Rc>) -> Box> { + pub fn discover_all( + &self, + self_ref: Rc>, + ) -> Box> { let mut inps = Vec::new(); macro_rules! ignore_controller { @@ -349,22 +342,17 @@ impl GilRsHandler { } } -// struct GilGamepadGuard<'c> { -// guard: Ref<'c, GilGamepad<'c>>, -// } - -// impl<'c> Deref for GilGamepadGuard<'c> { -// type Target = GilGamepad<'c>; - -// fn deref(&self) -> &GilGamepad<'c> { -// &self.guard -// } -// } +#[derive(Copy, Clone, Debug)] +pub enum GilInputs { + Axis((GilAxis, GilCode)), + Button((GilButton, GilCode)), +} pub struct GilRsInput { gilrs_handler: Rc>, gamepad: Gamepad, pub id: GilGamepadId, + mappings: Vec>, deadzone_percentage: f64, } @@ -374,16 +362,78 @@ impl GilRsInput { gilrs_handler, gamepad: Gamepad::new(), id, + mappings: vec![], deadzone_percentage: 0.05, // 5% } } - fn get_inputs_for_mapping(&mut self) { - - let mut a = self.gilrs_handler.borrow_mut(); - - a.dequeue_event_queue(self.id); + pub fn get_inputs_for_mapping>, O2: Into>>>( + &self, + duration_secs: u64, + listen_for_gyro_opt: O, + ignore_axes_opt: O2, + ) -> Vec { + let listen_for_gyro = listen_for_gyro_opt.into().unwrap_or(false); + let ignore_axes: Vec = ignore_axes_opt.into().unwrap_or(vec![]); + let mut gilrs_handler_ref = self.gilrs_handler.borrow_mut(); + let outputmappings = vec![]; + + let mut tracky = 0; + while tracky <= duration_secs { + let mut dequeued_evs = gilrs_handler_ref.dequeue_event_queue(self.id); + dequeued_evs.sort_by_key(|k| k.0); + + let mut outputmappings = vec![]; + 'ev_loop: for ev in dequeued_evs { + match ev.1 { + GilEventType::AxisChanged(gil_axis, _val, code) => { + for ignore_axis in &ignore_axes { + match ignore_axis { + &GilInputs::Axis(ignore) => { + if gil_axis == ignore.0 && code == ignore.1 { + // gyro! ignore. + // FIXME: add support. + continue 'ev_loop; + } + } + _ => {} + } + } + // Axis is always -1.0 - 1.0 + outputmappings.push(GilInputs::Axis((gil_axis, code))); + } + GilEventType::ButtonPressed(button, code) + | GilEventType::ButtonRepeated(button, code) + | GilEventType::ButtonReleased(button, code) => { + if listen_for_gyro { + // ignore + break; + } + outputmappings.push(GilInputs::Button((button, code))); + } + GilEventType::ButtonChanged(button, _val, code) => { + // Potentially an axis?... Unsure what do with that, for now let's just map this as a regular button... + if listen_for_gyro { + // ignore + break; + } + outputmappings.push(GilInputs::Button((button, code))); + } + _ => { + // TODO: disconnects + } + } + } + if listen_for_gyro { + println!("Listening for accelerometer/gyro data, move your controller around gently ({}s)", tracky); + } else { + println!("Waiting for input ({}s)", tracky); + } + std::thread::sleep(Duration::from_secs(1)); + tracky += 1; + } + outputmappings } pub fn get_gilrs(&self) -> Ref<'_, Gilrs> { @@ -391,53 +441,17 @@ impl GilRsInput { } } -// impl ControllerRetriever for GilRsHandler { -// type ControllerType<'a> = GilRsInput<'a> where Self: 'a; - -// fn discover_all<'a>(&'a self) -> Box> + 'a> { -// let mut inps = Vec::new(); - -// macro_rules! ignore_controller { -// ($gamepad:expr, $s:expr) => { -// if $gamepad.name().contains($s) | $gamepad.os_name().contains($s) { -// continue; -// } -// }; -// } -// let selfref: RefCell<&mut GilRsHandler> = RefCell::new(self); -// let y; -// { -// let borrowed_self = selfref.borrow(); -// } - -// let y = borrowed_self.gilrs.gamepads(); - -// for (_id, gamepad) in y { -// // e.g. Nintendo Wii Remote Nunchuk -// ignore_controller!(gamepad, "Wii"); -// ignore_controller!(gamepad, "Nunchuk"); - -// inps.push(Self::ControllerType::new(selfr, gamepad.id())); -// // println!( -// // "Detected!: {}/{}/{}", -// // gamepad.id(), -// // gamepad.name(), -// // gamepad.os_name() -// // ); -// } - -// Box::new(inps.into_iter()) -// } -// } - -// impl<'a> ControllerInput for GilRsInput<'a> { -// type ControllerType = GilRsInput<'a> where Self: 'a; - -// fn to_gamepad<'b>(&'b mut self) -> &'b Gamepad { -// return &self.gamepad; -// } - -// fn prep_for_input_events(&mut self) { -// println!("GilRsInput connected: {}", self.get_gilrs().gamepad(self.id).name()); -// } -// } +impl ControllerInput for GilRsInput { + type ControllerType = GilRsInput; + + fn to_gamepad(&mut self) -> &Gamepad { + return &self.gamepad; + } + + fn prep_for_input_events(&mut self) { + println!( + "GilRsInput connected: {}", + self.get_gilrs().gamepad(self.id).name() + ); + } +} diff --git a/src/main.rs b/src/main.rs index c9e55ef..a7909f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,14 @@ -use controller_abs::{ControllerInput, ControllerRetriever, Gamepad, GamepadAxis, OutputMapping}; +use controller_abs::{ControllerInput, Gamepad, GamepadAxis, OutputMapping}; use futures_util::TryStreamExt; +use std::cell::RefCell; use std::io::{self, Write}; +use std::rc::Rc; use std::thread::sleep; use std::time::Duration; use strum::IntoEnumIterator; use tokio; use xwiimote::events::{Event, Key, KeyState, NunchukKey}; use xwiimote::{Monitor, Result}; -use std::cell::RefCell; -use std::rc::Rc; #[allow(dead_code)] mod controller_abs; @@ -18,7 +18,7 @@ mod controller_in; mod controller_out; use controller_abs::GamepadButton; -use controller_in::{GilRsHandler, GilRsInput, XWiiHandler, XWiiInput}; +use controller_in::{GilInputs, GilRsHandler, GilRsInput, XWiiInput}; use controller_out::x360::XboxControllerState; @@ -83,20 +83,60 @@ fn get_user_input(prompt: &str) -> String { retval } -fn cli_map_user_input(inp: &InputType, outputmapping: OutputMapping) { +fn cli_map_user_input(inp: &mut InputType, outputmapping: OutputMapping) { // First await input - match &inp { - &InputType::WiiInput(_wii_inp) => { + match inp { + InputType::WiiInput(_wii_inp) => { // FIXME } - &InputType::GilInput(gil_inp) => { - // FIXME + InputType::GilInput(gil_inp) => { + // We have no idea which axis's might be gyro, so let's capture it seperately. + let gyro_inputs = gil_inp.get_inputs_for_mapping(10, true, None); + let inps = gil_inp.get_inputs_for_mapping(10, false, gyro_inputs); + + loop { + let choice; + if inps.len() < 1 { + println!("No input was captured!"); + return; + } else if inps.len() == 1 { + choice = &inps[0]; + } else { + println!( + "Captured multiple! Select the input you want to use (to map to {:?}):", + outputmapping + ); + for (index, inp) in inps.iter().enumerate() { + match inp { + GilInputs::Axis(x) => { + println!("{}. Axis: {:?}, code: {}", index + 1, x.0, x.1) + } + GilInputs::Button(x) => { + println!("{}. Button: {:?}, code: {}", index + 1, x.0, x.1) + } + } + } + let userinp = get_user_input("input"); + let optional_choice = userinp.parse::(); + if optional_choice.is_err() { + println!("Invalid input, try again"); + continue; + } + + let choice = optional_choice.unwrap(); + if choice < 1 || choice > inps.len() { + println!("Invalid input, try again"); + continue; + } + let x = inps[choice - 1]; + } + } } } } -fn cli_gamepad_settings(inp: &InputType) { +fn cli_gamepad_settings(inp: &mut InputType) { loop { // List all output mapping options println!("The following options are available to map to (not your input controller!):"); @@ -117,29 +157,32 @@ fn cli_gamepad_settings(inp: &InputType) { let userinp = get_user_input("\n\nSelect which button/axis you want to map to:"); let optional_choice = userinp.parse::(); - if optional_choice.is_err() { + if optional_choice.is_err() + || optional_choice.as_ref().unwrap() < &1 + || optional_choice.as_ref().unwrap() > &GamepadButton::iter().len() + { println!("Invalid input, try again"); + continue; } let choice = optional_choice.unwrap(); - if choice <= button_count { - // Button selected - let selected_button = GamepadButton::iter().nth(choice - 1).unwrap(); - println!("You selected button: {:?}", selected_button); + // Button selected + let selected_button = GamepadButton::iter().nth(choice - 1).unwrap(); + println!("You selected button: {:?}", selected_button); - // Await user input - cli_map_user_input(inp, OutputMapping::Button(selected_button)); - } + // Await user input + cli_map_user_input(inp, OutputMapping::Button(selected_button)); } } -fn cli_gamepads_overview(inps: Vec) { +fn cli_gamepads_overview(inps: &mut Vec) { + let n_controllers = inps.len(); loop { - println!("Detected {} controller(s):\n", &inps.len()); + println!("Detected {} controller(s):\n", n_controllers); let mut n_gamepads: u8 = 0; - for inp in &inps { + for inp in inps { n_gamepads += 1; match inp { InputType::GilInput(gil_inp) => { @@ -178,7 +221,8 @@ fn cli_gamepads_overview(inps: Vec) { break; } } - cli_gamepad_settings(&inps[choice - 1]) + // wip fix + cli_gamepad_settings(&mut inps[choice - 1]) } } @@ -190,15 +234,16 @@ enum InputType { #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let mut inps = vec![]; - let wii_handler = XWiiHandler::new(); + // Need to keep gilrs_handler in scope at all times, as it handles the overarching event loop. + // It's passed as a ref to other input objs. let gilrs_handler = Rc::new(RefCell::new(GilRsHandler::new())); - for inp in wii_handler.discover_all() { + for inp in XWiiInput::discover_all() { inps.push(InputType::WiiInput(inp)); } let gilrs_handler_b = gilrs_handler.borrow_mut(); - + for inp in gilrs_handler_b.discover_all(Rc::clone(&gilrs_handler)) { inps.push(InputType::GilInput(inp)); } @@ -208,7 +253,7 @@ async fn main() -> Result<()> { std::process::exit(1); } - cli_gamepads_overview(inps); + cli_gamepads_overview(&mut inps); Ok(()) }