Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,16 @@
</defaults>
</action>

<action id="org.shadowblip.Output.ForceFeedback.Scale">
<description>Set the scale for force feedback intensity</description>
<message>Authentication is required to set the rumble intensity</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
</action>

<action id="org.shadowblip.Output.ForceFeedback.Rumble">
<description>Allow sending force feedback rumble events</description>
<message>Authentication is required to send rumble events</message>
Expand Down
36 changes: 34 additions & 2 deletions src/dbus/interface/force_feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub trait ForceFeedbacker {
&mut self,
enabled: bool,
) -> impl Future<Output = Result<(), Box<dyn Error>>> + Send;
fn get_scale(&self) -> impl Future<Output = Result<f64, Box<dyn Error>>> + Send;
fn set_scale(&mut self, scale: f64) -> impl Future<Output = Result<(), Box<dyn Error>>> + Send;
fn rumble(&mut self, value: f64) -> impl Future<Output = Result<(), Box<dyn Error>>> + Send;
fn stop(&mut self) -> impl Future<Output = Result<(), Box<dyn Error>>> + Send;
}
Expand All @@ -33,6 +35,15 @@ impl ForceFeedbacker for CompositeDeviceClient {
Ok(())
}

async fn get_scale(&self) -> Result<f64, Box<dyn Error>> {
Ok(self.get_ff_scale().await?)
}

async fn set_scale(&mut self, scale: f64) -> Result<(), Box<dyn Error>> {
self.set_ff_scale(scale).await?;
Ok(())
}

async fn rumble(&mut self, value: f64) -> Result<(), Box<dyn Error>> {
let value = value.min(1.0);
let value = value.max(0.0);
Expand Down Expand Up @@ -99,8 +110,29 @@ where
self.device
.set_enabled(enabled)
.await
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
Ok(())
.map_err(|err| fdo::Error::Failed(err.to_string()))
}

/// Set the scale for force feedback intensity
#[zbus(property)]
async fn scale(&self) -> fdo::Result<f64> {
self.device
.get_scale()
.await
.map_err(|err| fdo::Error::Failed(err.to_string()))
}
#[zbus(property)]
async fn set_scale(
&mut self,
scale: f64,
#[zbus(connection)] conn: &Connection,
#[zbus(header)] hdr: Option<Header<'_>>,
) -> fdo::Result<()> {
check_polkit(conn, hdr, "org.shadowblip.Output.ForceFeedback.Scale").await?;
self.device
.set_scale(scale)
.await
.map_err(|err| fdo::Error::Failed(err.to_string()))
}

/// Send a simple rumble event
Expand Down
20 changes: 20 additions & 0 deletions src/input/composite_device/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,24 @@ impl CompositeDeviceClient {
.await?;
Ok(())
}

/// Returns the intensity scale of force feedback where 1.0 is maximum intensity
/// and 0.0 is minimum intensity.
pub async fn get_ff_scale(&self) -> Result<f64, ClientError> {
let (tx, rx) = channel(1);
self.send(CompositeCommand::GetForceFeedbackScale(tx))
.await?;
if let Some(result) = Self::recv(rx).await {
return Ok(result);
}
Err(ClientError::ChannelClosed)
}

/// Sets the force feedback intensity to the given value where 1.0 is maximum
/// intensity and 0.0 is minimum intensity.
pub async fn set_ff_scale(&self, scale: f64) -> Result<(), ClientError> {
self.send(CompositeCommand::SetForceFeedbackScale(scale))
.await?;
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/input/composite_device/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub enum CompositeCommand {
GetFilterableEvents(mpsc::Sender<HashMap<String, Vec<Capability>>>),
GetForceFeedbackEnabled(mpsc::Sender<bool>),
SetForceFeedbackEnabled(bool),
GetForceFeedbackScale(mpsc::Sender<f64>),
SetForceFeedbackScale(f64),
SourceDeviceAdded(DeviceInfo),
SourceDeviceRemoved(DeviceInfo),
SourceDeviceStopped(DeviceInfo),
Expand Down
33 changes: 27 additions & 6 deletions src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct CompositeDevice {
/// Whether or not force feedback output events should be routed to
/// supported source devices.
ff_enabled: bool,
/// Set the scale for the force feedback intensity.
ff_scale: f64,
/// Set of available Force Feedback effect IDs that are not in use
/// TODO: Just use the keys from ff_effect_id_source_map to determine next id
ff_effect_ids: BTreeSet<i16>,
Expand Down Expand Up @@ -207,6 +209,7 @@ impl CompositeDevice {
source_devices_used: Vec::new(),
targets: CompositeDeviceTargets::new(conn, dbus_path, tx.into(), manager),
ff_enabled: true,
ff_scale: 1.0,
ff_effect_ids: (0..64).collect(),
ff_effect_id_source_map: HashMap::new(),
intercept_activation_caps: vec![Capability::Gamepad(Gamepad::Button(
Expand Down Expand Up @@ -509,6 +512,16 @@ impl CompositeDevice {
log::info!("Setting force feedback enabled: {enabled:?}");
self.ff_enabled = enabled;
}
CompositeCommand::GetForceFeedbackScale(sender) => {
if let Err(e) = sender.send(self.ff_scale).await {
log::error!("Failed to send force feedback scale status: {e}");
}
}
CompositeCommand::SetForceFeedbackScale(scale) => {
let scale = scale.clamp(0.0, 1.0);
log::info!("Setting force feedback scale: {scale}");
self.ff_scale = scale;
}
CompositeCommand::Stop => {
log::debug!(
"Got STOP signal. Stopping CompositeDevice: {}",
Expand Down Expand Up @@ -743,7 +756,7 @@ impl CompositeDevice {
}

/// Process a single output event from a target device.
async fn process_output_event(&mut self, event: OutputEvent) -> Result<(), Box<dyn Error>> {
async fn process_output_event(&mut self, mut event: OutputEvent) -> Result<(), Box<dyn Error>> {
log::trace!("Received output event: {:?}", event);

// Handle any output events that need to upload FF effect data
Expand Down Expand Up @@ -833,11 +846,19 @@ impl CompositeDevice {
return Ok(());
}

// If force feedback is disabled at the composite device level, don't
// forward any other FF events to source devices.
if !self.ff_enabled && event.is_force_feedback() {
log::trace!("Force feedback not enabled. Nothing to do.");
return Ok(());
// Process force feedback events
if event.is_force_feedback() {
// If force feedback is disabled at the composite device level, don't
// forward any other FF events to source devices.
if !self.ff_enabled {
log::trace!("Force feedback not enabled. Nothing to do.");
return Ok(());
}

// Scale the force feedback event if needed
if self.ff_scale != 1.0 {
event.scale(self.ff_scale);
}
}

// Write the event to devices that are capable of handling it
Expand Down
65 changes: 64 additions & 1 deletion src/input/output_event/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::mpsc::Sender;

use ::evdev::{FFEffectData, InputEvent};
use ::evdev::{FFEffectData, FFEffectKind, InputEvent};
use packed_struct::types::{Integer, SizedInteger};

use crate::drivers::{
dualsense::hid_report::SetStatePackedOutputData,
Expand Down Expand Up @@ -87,6 +88,68 @@ impl OutputEvent {
OutputEvent::SteamDeckRumble(_) => true,
}
}

/// Scale the intensity value of the output event. A value of 0.0 is the
/// minimum intensity. A value of 1.0 is the maximum intensity.
pub fn scale(&mut self, scale: f64) {
let scale = scale.clamp(0.0, 1.0);
match self {
Self::Evdev(_) => (),
Self::Uinput(event) => match event {
UinputOutputEvent::FFUpload(_, data, _) => match data.kind {
FFEffectKind::Damper => (),
FFEffectKind::Inertia => (),
FFEffectKind::Constant {
level: _,
envelope: _,
} => (),
FFEffectKind::Ramp {
start_level: _,
end_level: _,
envelope: _,
} => (),
FFEffectKind::Periodic {
waveform: _,
period: _,
magnitude: _,
offset: _,
phase: _,
envelope: _,
} => (),
FFEffectKind::Spring { condition: _ } => (),
FFEffectKind::Friction { condition: _ } => (),
FFEffectKind::Rumble {
ref mut strong_magnitude,
ref mut weak_magnitude,
} => {
let scaled_strong = *strong_magnitude as f64 * scale;
*strong_magnitude = scaled_strong as u16;
let scaled_weak = *weak_magnitude as f64 * scale;
*weak_magnitude = scaled_weak as u16;
}
},
UinputOutputEvent::FFErase(_) => (),
},
Self::DualSense(report) => {
if report.use_rumble_not_haptics {
let scaled_left = report.rumble_emulation_left as f64 * scale;
report.rumble_emulation_left = scaled_left as u8;
let scaled_right = report.rumble_emulation_right as f64 * scale;
report.rumble_emulation_right = scaled_right as u8;
}
}
Self::SteamDeckHaptics(data) => {
data.gain = (data.gain as f64 * scale) as i8;
}
Self::SteamDeckRumble(data) => {
data.intensity = (data.intensity as f64 * scale) as u8;
let scaled_left_speed = data.left_speed.to_primitive() as f64 * scale;
data.left_speed = Integer::from_primitive(scaled_left_speed as u16);
let scaled_right_speed = data.right_speed.to_primitive() as f64 * scale;
data.right_speed = Integer::from_primitive(scaled_right_speed as u16);
}
}
}
}

#[derive(Debug, Clone)]
Expand Down