From fe162b60ff35a3f70beec43dc52c90d63edf5374 Mon Sep 17 00:00:00 2001 From: Wynd Date: Sat, 19 Oct 2024 14:46:46 +0300 Subject: [PATCH] Implemented most buttons and threaded force feedback --- .gitignore | 1 + logs/latest.log | 110 ----------------------- src/app.rs | 196 +++++++++++++++++++++++++++++++---------- src/gamepad_manager.rs | 101 ++++++++++++++------- src/main.rs | 2 +- 5 files changed, 219 insertions(+), 191 deletions(-) delete mode 100644 logs/latest.log diff --git a/.gitignore b/.gitignore index ea8c4bf..c0ed7d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/logs \ No newline at end of file diff --git a/logs/latest.log b/logs/latest.log deleted file mode 100644 index 522524a..0000000 --- a/logs/latest.log +++ /dev/null @@ -1,110 +0,0 @@ -2024-10-18T22:01:48.001029Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.001150Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.474890Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.474947Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.495298Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.495345Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.618136Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.659732Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.659792Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.743791Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:48.743851Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.058470Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.058527Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.099680Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.099736Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.579664Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:49.579721Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:50.259909Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:50.259973Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:50.259992Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:50.657504Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) -2024-10-18T22:01:50.657561Z INFO src/gamepad_manager.rs:33: Selected gamepad changed to: Some( - GamepadId( - 0, - ), -) diff --git a/src/app.rs b/src/app.rs index ced0eee..af1138d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,6 @@ use std::time::{Duration, Instant}; -use color_eyre::{owo_colors::OwoColorize, Result}; +use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Result}; use gilrs::{Gamepad, Gilrs}; use ratatui::{ buffer::Buffer, @@ -78,6 +78,8 @@ impl App { self.manager.scan_gamepads(); return Ok(()); } + KeyCode::Char('v') => self.manager.test_weak_ff(), + KeyCode::Char('V') => self.manager.test_strong_ff(), KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => self.quit(), _ => {} } @@ -94,33 +96,35 @@ impl App { self.state = AppState::Quitting; } - fn render_title(area: Rect, buf: &mut Buffer) { - Paragraph::new("App Example Title") - .block( - Block::bordered() - .border_type(BorderType::Rounded) - .padding(Padding::top(1)), - ) - .alignment(Alignment::Center) - .render(area, buf); - } + // fn render_title(area: Rect, buf: &mut Buffer) { + // Paragraph::new("App Example Title") + // .block( + // Block::bordered() + // .border_type(BorderType::Rounded) + // .padding(Padding::top(1)), + // ) + // .alignment(Alignment::Center) + // .render(area, buf); + // } + // + // fn render_connected_gamepad(&self, area: Rect, buf: &mut Buffer) { + // Paragraph::new(format!( + // "{} connected gamepads", + // self.manager.connected_gamepads() + // )) + // .block( + // Block::bordered() + // .border_type(BorderType::Rounded) + // .padding(Padding::top(1)), + // ) + // .alignment(Alignment::Center) + // .bold() + // .render(area, buf); + // } - fn render_connected_gamepad(&self, area: Rect, buf: &mut Buffer) { - Paragraph::new(format!( - "{} connected gamepads", - self.manager.connected_gamepads() - )) - .block( - Block::bordered() - .border_type(BorderType::Rounded) - .padding(Padding::top(1)), - ) - .alignment(Alignment::Center) - .bold() - .render(area, buf); - } + fn render_action_buttons(&self, area: Rect, buf: &mut Buffer) { + Block::bordered().render(area, buf); - fn render_right_buttons(&self, area: Rect, buf: &mut Buffer) { let north_button = self.create_action_button_ui(gilrs::Button::North); let east_button = self.create_action_button_ui(gilrs::Button::East); let south_button = self.create_action_button_ui(gilrs::Button::South); @@ -144,13 +148,113 @@ impl App { west_button.render(west, buf); } + fn render_dpad_buttons(&self, area: Rect, buf: &mut Buffer) { + Block::bordered().render(area, buf); + + let up_button = self.create_action_button_ui(gilrs::Button::DPadUp); + let right_button = self.create_action_button_ui(gilrs::Button::DPadRight); + let down_button = self.create_action_button_ui(gilrs::Button::DPadDown); + let left_button = self.create_action_button_ui(gilrs::Button::DPadLeft); + + let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]); + let [top, mid, bot] = layers.areas(area); + + let top_layer = Layout::horizontal([Fill(1), Fill(1), Fill(1)]); + let [_, up, _] = top_layer.areas(top); + + let mid_layer = Layout::horizontal([Fill(1), Fill(1), Fill(1)]); + let [left, _, right] = mid_layer.areas(mid); + + let bot_layer = Layout::horizontal([Fill(1), Fill(1), Fill(1)]); + let [_, down, _] = bot_layer.areas(bot); + + up_button.render(up, buf); + right_button.render(right, buf); + down_button.render(down, buf); + left_button.render(left, buf); + } + + fn render_setting_buttons(&self, area: Rect, buf: &mut Buffer) { + Block::bordered().render(area, buf); + + let select_button = self.create_action_button_ui(gilrs::Button::Select); + let start_button = self.create_action_button_ui(gilrs::Button::Start); + + let layers = Layout::horizontal([Fill(1), Fill(1), Fill(1), Fill(1), Fill(1)]); + let [_, select_area, _, start_area, _] = layers.areas(area); + + let select_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); + let [_, select, _, _] = select_layer.areas(select_area); + + let start_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); + let [_, start, _, _] = start_layer.areas(start_area); + + select_button.render(select, buf); + start_button.render(start, buf); + } + + fn render_trigger_buttons(&self, area: Rect, buf: &mut Buffer) { + Block::bordered().render(area, buf); + + let r1_button = self.create_action_button_ui(gilrs::Button::RightTrigger); + let r2_button = self.create_action_button_ui(gilrs::Button::RightTrigger2); + let l1_button = self.create_action_button_ui(gilrs::Button::LeftTrigger); + let l2_button = self.create_action_button_ui(gilrs::Button::LeftTrigger2); + + let layers = Layout::horizontal([ + Fill(2), + Fill(1), + Fill(1), + Fill(1), + Fill(4), + Fill(1), + Fill(1), + Fill(1), + Fill(2), + ]); + let [_, l2_area, _, l1_area, _, r1_area, _, r2_area, _] = layers.areas(area); + + let r1_layer = Layout::vertical([Fill(2), Fill(1), Fill(2)]); + let [_, r1, _] = r1_layer.areas(r1_area); + + let r2_layer = Layout::vertical([Fill(1), Fill(3), Fill(1)]); + let [_, r2, _] = r2_layer.areas(r2_area); + + let l1_layer = Layout::vertical([Fill(2), Fill(1), Fill(2)]); + let [_, l1, _] = l1_layer.areas(l1_area); + + let l2_layer = Layout::vertical([Fill(1), Fill(3), Fill(1)]); + let [_, l2, _] = l2_layer.areas(l2_area); + + r1_button.render(r1, buf); + r2_button.render(r2, buf); + l1_button.render(l1, buf); + l2_button.render(l2, buf); + + //TODO: For PS4+ controllers doesn't have dedicated trigger push force + // let gauge_percentage = match self.manager.active_gamepad() { + // Ok(gamepad) => match gamepad.axis_data(gilrs::Axis::Unknown) { + // Some(ad) => ad.value(), + // None => 0.0, + // }, + // Err(_) => 0.0, + // }; + // let gauge_percentage = 0; + + // Gauge::default() + // .gauge_style(Color::Green) + // .bg(Color::DarkGray) + // .ratio(gauge_percentage.into()) + // .render(r2, buf); + } + fn create_action_button_ui(&self, dir: gilrs::Button) -> Block { let color = match self.manager.active_gamepad() { Ok(gamepad) => match gamepad.is_pressed(dir) { true => Color::Green, - false => Color::Gray, + false => Color::DarkGray, }, - Err(_) => Color::Gray, + Err(_) => Color::DarkGray, }; Block::new().style(Style::default().bg(color)) @@ -159,29 +263,26 @@ impl App { impl Widget for &App { fn render(self, area: Rect, buf: &mut Buffer) { - let layout = Layout::vertical([Length(5), Min(0), Length(5)]); - let [header_area, inner_area, footer_area] = layout.areas(area); + let layout = Layout::vertical([Percentage(100)]); + let [inner_area] = layout.areas(area); - let header_layout = Layout::horizontal([Fill(1), Fill(4)]); - let [tabs_area, title_area] = header_layout.areas(header_area); + let gamepad_layout = Layout::vertical([Fill(2), Fill(4), Fill(3)]); + let [triggers, buttons, joysticks] = gamepad_layout.areas(inner_area); - let gamepad_layout = Layout::vertical([ - Percentage(10), - Percentage(50), - Percentage(30), - Percentage(10), - ]); - let [_, buttons, joysticks, _] = gamepad_layout.areas(inner_area); - - let buttons_layout = Layout::horizontal([Fill(1), Fill(2), Fill(2), Fill(2), Fill(1)]); - let [_, left_buttons, mid_buttons, right_buttons, _] = buttons_layout.areas(buttons); + let buttons_layout = Layout::horizontal([Fill(2), Fill(2), Fill(2)]); + let [left_buttons, mid_buttons, right_buttons] = buttons_layout.areas(buttons); // let details_layout = Layout::horizontal([Fill(2), Fill(2)]); // let [right_details_area, left_details_area] = details_layout.areas(details_area); - App::render_title(title_area, buf); - self.render_connected_gamepad(tabs_area, buf); - Block::bordered().render(inner_area, buf); + let mut title_label = Block::bordered(); + title_label = match self.manager.active_gamepad() { + Ok(gp) => title_label + .title(gp.name().to_string()) + .title_style(Color::Rgb(255, 165, 35)), + Err(_) => title_label.title("No active device".to_string()), + }; + title_label.render(inner_area, buf); // Block::bordered() // .style(Style::default().bg(tailwind::GREEN.c900)) @@ -191,7 +292,10 @@ impl Widget for &App { // .style(Style::default().bg(tailwind::YELLOW.c300)) // .render(details_area, buf); - self.render_right_buttons(right_buttons, buf); + self.render_dpad_buttons(left_buttons, buf); + self.render_setting_buttons(mid_buttons, buf); + self.render_action_buttons(right_buttons, buf); + self.render_trigger_buttons(triggers, buf); // Block::bordered() // .style(Style::default().bg(tailwind::TEAL.c300)) diff --git a/src/gamepad_manager.rs b/src/gamepad_manager.rs index 5d1339e..a55ab82 100644 --- a/src/gamepad_manager.rs +++ b/src/gamepad_manager.rs @@ -1,3 +1,5 @@ +use std::{thread, time::Duration}; + use color_eyre::{eyre::eyre, Result}; use gilrs::{ ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks}, @@ -24,13 +26,9 @@ impl GamepadManager { }) } - pub fn select_gamepad(&mut self, id: impl Into>) { + pub fn set_active_gamepad(&mut self, id: impl Into>) { self.active_gamepad = id.into(); - } - - pub fn set_active_gamepad(&mut self, id: GamepadId) { - self.active_gamepad = Some(id); - tracing::info!("Selected gamepad changed to: {:#?}", self.active_gamepad); + // tracing::info!("Selected gamepad changed to: {:#?}", self.active_gamepad); } pub fn active_gamepad(&self) -> Result> { @@ -52,7 +50,7 @@ impl GamepadManager { let mut gamepads = vec![]; // Iterate over all connected gamepads - for (id, gamepad) in self.gilrs.gamepads() { + for (id, _) in self.gilrs.gamepads() { // println!("{} is {:?}", gamepad.name(), gamepad.power_info()); // println!("Has Force Feedback Enabled ? {}", gamepad.is_ff_supported()); // if gamepad.is_ff_supported() { @@ -65,34 +63,69 @@ impl GamepadManager { self.gamepads = gamepads; } - pub fn test_ff(&mut self, gamepad_id: GamepadId) { - let gamepad = self.gilrs.gamepad(gamepad_id); + pub fn test_weak_ff(&mut self) { + let Some(id) = self.active_gamepad + else { + tracing::warn!("No active gamepad found!"); + return; + }; - let duration: u32 = 1000; - let ff_play_ticks = Ticks::from_ms(duration); - let effect = EffectBuilder::new() - .add_effect(BaseEffect { - kind: BaseEffectType::Strong { magnitude: 40_000 }, - scheduling: Replay { - play_for: ff_play_ticks, - with_delay: ff_play_ticks * 2, - ..Default::default() - }, - envelope: Default::default(), - }) - .add_effect(BaseEffect { - kind: BaseEffectType::Weak { magnitude: 40_000 }, - scheduling: Replay { - after: ff_play_ticks, - play_for: ff_play_ticks, - ..Default::default() - }, - envelope: Default::default(), - }) - .add_gamepad(&gamepad) - .finish(&mut self.gilrs) - .unwrap(); + thread::spawn(move || { + let mut gilrs = Gilrs::new().unwrap(); + let gamepad = gilrs.gamepad(id); - effect.play().unwrap(); + let duration = Ticks::from_ms(1000); + let delay = Ticks::from_ms(500); + let effect = EffectBuilder::new() + .add_effect(BaseEffect { + kind: BaseEffectType::Weak { magnitude: 40_000 }, + scheduling: Replay { + play_for: duration, + with_delay: delay, + ..Default::default() + }, + envelope: Default::default(), + }) + .add_gamepad(&gamepad) + .finish(&mut gilrs) + .unwrap(); + + effect.play().unwrap(); + + thread::sleep(Duration::from_millis(4500)); + }); + } + + pub fn test_strong_ff(&mut self) { + let Some(id) = self.active_gamepad + else { + tracing::warn!("No active gamepad found!"); + return; + }; + + thread::spawn(move || { + let mut gilrs = Gilrs::new().unwrap(); + let gamepad = gilrs.gamepad(id); + + let duration = Ticks::from_ms(1000); + let delay = Ticks::from_ms(500); + let effect = EffectBuilder::new() + .add_effect(BaseEffect { + kind: BaseEffectType::Strong { magnitude: 40_000 }, + scheduling: Replay { + play_for: duration, + with_delay: delay, + ..Default::default() + }, + envelope: Default::default(), + }) + .add_gamepad(&gamepad) + .finish(&mut gilrs) + .unwrap(); + + effect.play().unwrap(); + + thread::sleep(Duration::from_millis(4500)); + }); } } diff --git a/src/main.rs b/src/main.rs index 9e04929..594e620 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,4 +62,4 @@ fn init_tracing() { ); tracing::subscriber::set_global_default(sub).unwrap(); -} \ No newline at end of file +}