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/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 23ecc05..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, @@ -293,8 +294,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 +364,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 +383,7 @@ pub enum GamepadButton { DPadRight, } -#[derive(EnumIter, PartialEq, Eq, Hash, Clone)] +#[derive(EnumIter, PartialEq, Eq, Hash, Clone, Debug)] pub enum GamepadAxis { LeftTrigger, RightTrigger, @@ -423,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, @@ -438,8 +446,6 @@ where pub trait ControllerInput { type ControllerType; - fn to_gamepad<'a>(&'a mut self) -> &'a Gamepad; - fn discover_all() -> Vec; + fn to_gamepad(&mut self) -> &Gamepad; 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..02a62c4 100644 --- a/src/controller_in/mod.rs +++ b/src/controller_in/mod.rs @@ -1,10 +1,14 @@ -use std::time::Duration; +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, }; @@ -13,20 +17,23 @@ use crate::controller_abs::{ }; 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::cell::RefCell; +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) } } @@ -34,13 +41,13 @@ 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, } @@ -51,7 +58,7 @@ 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, @@ -75,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); @@ -112,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: _, @@ -197,34 +204,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 { @@ -252,239 +234,224 @@ impl ControllerInput for XWiiInput { self.map_event_to_gamepad(event); return Ok(true); } -} -pub struct GilRsInput { - gamepad: Gamepad, - gil_rs: Gilrs, - gil_rs_device_id: GilGamepadId, - deadzone_percentage: f64, -} + pub fn discover_all() -> Box> { + let monitor = Monitor::enumerate().unwrap(); -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% + let addresses: Vec<_> = block_on(async { monitor.collect().await }); + + let mut inps: Vec = vec![]; + for address in addresses { + inps.push(Self::new(&address.unwrap())); } + + Box::new(inps.into_iter()) } +} + +impl ControllerInput for XWiiInput { + type ControllerType = XWiiInput; - fn get_gilrs_gamepad(&self) -> GilGamepad { - self.gil_rs.gamepad(self.gil_rs_device_id) + 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( + &self, + self_ref: Rc>, + ) -> Box> { + 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()); +#[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, +} + +impl GilRsInput { + pub fn new(gilrs_handler: Rc>, id: GilGamepadId) -> GilRsInput { + GilRsInput { + gilrs_handler, + gamepad: Gamepad::new(), + id, + mappings: vec![], + deadzone_percentage: 0.05, // 5% + } } - async fn get_next_inputs(&mut self) -> Result { - self.map_gilrs_to_gamepad(); - return Ok(true); + 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> { + Ref::map(self.gilrs_handler.borrow(), |x| &x.gilrs) + } +} + +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 0d8ba30..a7909f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ -use controller_abs::{ControllerInput, 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}; @@ -14,7 +18,7 @@ mod controller_in; mod controller_out; use controller_abs::GamepadButton; -use controller_in::{GilRsInput, XWiiInput}; +use controller_in::{GilInputs, GilRsHandler, GilRsInput, XWiiInput}; use controller_out::x360::XboxControllerState; @@ -59,6 +63,203 @@ 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: &mut InputType, outputmapping: OutputMapping) { + // First await input + match inp { + InputType::WiiInput(_wii_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: &mut 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() + || 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(); + + // 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: &mut Vec) { + let n_controllers = inps.len(); + loop { + println!("Detected {} controller(s):\n", n_controllers); + + 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; + } + } + // wip fix + cli_gamepad_settings(&mut inps[choice - 1]) + } +} + +enum InputType { + WiiInput(XWiiInput), + GilInput(GilRsInput), +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let mut inps = vec![]; + // 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 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)); + } + + if inps.len() == 0 { + println!("You have no connected controllers!"); + std::process::exit(1); + } + + cli_gamepads_overview(&mut inps); + + Ok(()) +} + +// OLD + // #[tokio::main(flavor = "current_thread")] // async fn main() -> Result<()> { // // Create a monitor to enumerate connected Wii Remotes @@ -151,33 +352,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(()) -}