Implemented most buttons and threaded force feedback
parent
4b87e6c013
commit
fe162b60ff
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/logs
|
110
logs/latest.log
110
logs/latest.log
|
@ -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,
|
||||
),
|
||||
)
|
196
src/app.rs
196
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))
|
||||
|
|
|
@ -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<Option<GamepadId>>) {
|
||||
pub fn set_active_gamepad(&mut self, id: impl Into<Option<GamepadId>>) {
|
||||
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<Gamepad<'_>> {
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,4 +62,4 @@ fn init_tracing() {
|
|||
);
|
||||
|
||||
tracing::subscriber::set_global_default(sub).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue