use crate::utils::gilrs::GamepadResource;
use evdev::{Device, FFEffectData};
use log::{error, warn};
use std::collections::HashMap;
pub struct PhysicalFFDev {
pub resource: GamepadResource,
effects: HashMap<i16, evdev::FFEffect>,
}
impl PhysicalFFDev {
pub fn new(resource: GamepadResource) -> Self {
Self {
resource,
effects: HashMap::new(),
}
}
pub fn upload_effect(
&mut self,
virt_id: i16,
effect_data: FFEffectData,
) -> std::io::Result<()> {
let ff_effect = self.resource.device.upload_ff_effect(effect_data)?;
self.effects.insert(virt_id, ff_effect);
Ok(())
}
pub fn erase_effect(&mut self, virt_id: i16) -> std::io::Result<()> {
if let Some(mut effect) = self.effects.remove(&virt_id) {
effect.stop()?;
}
Ok(())
}
pub fn control_effect(&mut self, virt_id: i16, is_playing: bool) -> std::io::Result<()> {
if let Some(effect) = self.effects.get_mut(&virt_id) {
if is_playing {
effect.play(1)
} else {
effect.stop()
}
} else {
Ok(()) }
}
pub fn sync_effects(&mut self, manager: &EffectManager) -> Vec<(i16, std::io::Error)> {
let mut errors = Vec::new();
for (virt_id, effect_data) in manager.get_effects() {
if !self.effects.contains_key(&virt_id)
&& let Err(e) = self.upload_effect(virt_id, effect_data)
{
errors.push((virt_id, e));
}
}
for virt_id in manager.get_playing() {
if let Err(e) = self.control_effect(virt_id, true) {
errors.push((virt_id, e));
}
}
errors
}
pub fn recover(&mut self, manager: &EffectManager) -> std::io::Result<()> {
let path = self.resource.path.clone();
let new_device = Device::open(&path)?;
self.resource.device = new_device;
warn!("FF device reopened after disconnect: {}", path.display());
self.effects.clear();
let errors = self.sync_effects(manager);
if !errors.is_empty() {
error!(
"Encountered {} errors while re-syncing effects after recovery for {}",
errors.len(),
path.display()
);
for (virt_id, err) in errors {
error!(" - Effect {}: {}", virt_id, err);
}
}
Ok(())
}
}
pub struct EffectManager {
effects: HashMap<i16, FFEffectData>,
playing: HashMap<i16, bool>,
}
impl EffectManager {
pub fn new() -> Self {
Self {
effects: HashMap::new(),
playing: HashMap::new(),
}
}
pub fn upload(&mut self, virt_id: i16, effect_data: FFEffectData) {
self.effects.insert(virt_id, effect_data);
self.playing.insert(virt_id, false);
}
pub fn erase(&mut self, virt_id: i16) {
self.effects.remove(&virt_id);
self.playing.remove(&virt_id);
}
pub fn set_playing(&mut self, virt_id: i16, is_playing: bool) {
self.playing.insert(virt_id, is_playing);
}
pub fn get_effects(&self) -> impl Iterator<Item = (i16, FFEffectData)> + '_ {
self.effects.iter().map(|(&id, &data)| (id, data))
}
pub fn get_playing(&self) -> impl Iterator<Item = i16> + '_ {
self.playing
.iter()
.filter(|&(_, &is_playing)| is_playing)
.map(|(&id, _)| id)
}
}