feat(input): implement dual-motor force feedback and trigger rumble manager
This commit is contained in:
parent
98328f6014
commit
52ac39072f
3 changed files with 85 additions and 0 deletions
63
crates/molotov-core/src/input/haptics.rs
Normal file
63
crates/molotov-core/src/input/haptics.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct RumbleState {
|
||||
pub low_freq_motor: u16,
|
||||
pub high_freq_motor: u16,
|
||||
pub left_trigger: u16,
|
||||
pub right_trigger: u16,
|
||||
pub duration_ms: u32,
|
||||
}
|
||||
|
||||
pub struct RumbleManager {
|
||||
states: HashMap<u32, RumbleState>,
|
||||
}
|
||||
|
||||
impl Default for RumbleManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl RumbleManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rumble(&mut self, gamepad_id: u32, low_freq: f32, high_freq: f32, duration_ms: u32) {
|
||||
let low = (low_freq.clamp(0.0, 1.0) * 65535.0) as u16;
|
||||
let high = (high_freq.clamp(0.0, 1.0) * 65535.0) as u16;
|
||||
|
||||
let entry = self.states.entry(gamepad_id).or_default();
|
||||
entry.low_freq_motor = low;
|
||||
entry.high_freq_motor = high;
|
||||
entry.duration_ms = duration_ms;
|
||||
}
|
||||
|
||||
pub fn rumble_triggers(&mut self, gamepad_id: u32, left: f32, right: f32, duration_ms: u32) {
|
||||
let l = (left.clamp(0.0, 1.0) * 65535.0) as u16;
|
||||
let r = (right.clamp(0.0, 1.0) * 65535.0) as u16;
|
||||
|
||||
let entry = self.states.entry(gamepad_id).or_default();
|
||||
entry.left_trigger = l;
|
||||
entry.right_trigger = r;
|
||||
entry.duration_ms = duration_ms;
|
||||
}
|
||||
|
||||
pub fn stop(&mut self, gamepad_id: u32) {
|
||||
if let Some(entry) = self.states.get_mut(&gamepad_id) {
|
||||
entry.low_freq_motor = 0;
|
||||
entry.high_freq_motor = 0;
|
||||
entry.left_trigger = 0;
|
||||
entry.right_trigger = 0;
|
||||
entry.duration_ms = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_state(&self, gamepad_id: u32) -> Option<RumbleState> {
|
||||
self.states.get(&gamepad_id).copied()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub mod gamepad;
|
||||
pub mod haptics;
|
||||
pub mod ime;
|
||||
|
|
|
|||
21
crates/molotov-core/tests/haptics_test.rs
Normal file
21
crates/molotov-core/tests/haptics_test.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use molotov_core::input::haptics::RumbleManager;
|
||||
|
||||
#[test]
|
||||
fn test_rumble_actuation_and_clamping() {
|
||||
let mut manager = RumbleManager::new();
|
||||
let pad_id = 1;
|
||||
|
||||
// Actuate rumble: low_freq = 0.5 (32767), high_freq = 1.0 (65535), duration = 250ms
|
||||
manager.rumble(pad_id, 0.5, 1.0, 250);
|
||||
let state = manager.get_state(pad_id).expect("Should have rumble state");
|
||||
assert_eq!(state.low_freq_motor, 32767);
|
||||
assert_eq!(state.high_freq_motor, 65535);
|
||||
assert_eq!(state.duration_ms, 250);
|
||||
|
||||
// Stop rumble
|
||||
manager.stop(pad_id);
|
||||
let stopped = manager.get_state(pad_id).expect("Should have rumble state");
|
||||
assert_eq!(stopped.low_freq_motor, 0);
|
||||
assert_eq!(stopped.high_freq_motor, 0);
|
||||
}
|
||||
Reference in a new issue