feat(input): implement dual-motor force feedback and trigger rumble manager

This commit is contained in:
katR 2026-09-30 07:45:06 +05:30
parent 98328f6014
commit 52ac39072f
3 changed files with 85 additions and 0 deletions

View 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()
}
}

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod gamepad;
pub mod haptics;
pub mod ime;

View 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);
}