@@ -14,14 +14,17 @@ use std::{
1414use evdev:: InputEvent ;
1515use targets:: CompositeDeviceTargets ;
1616use tokio:: { sync:: mpsc, task:: JoinSet , time:: Duration } ;
17- use zbus:: Connection ;
17+ use zbus:: { object_server :: Interface , Connection } ;
1818
1919use crate :: {
2020 config:: {
2121 capability_map:: CapabilityMapConfig , path:: get_profiles_path, CompositeDeviceConfig ,
2222 DeviceProfile , ProfileMapping ,
2323 } ,
24- dbus:: interface:: { composite_device:: CompositeDeviceInterface , DBusInterfaceManager } ,
24+ dbus:: interface:: {
25+ composite_device:: CompositeDeviceInterface , force_feedback:: ForceFeedbackInterface ,
26+ DBusInterfaceManager ,
27+ } ,
2528 input:: {
2629 capability:: { Capability , Gamepad , GamepadButton , Mouse } ,
2730 event:: {
@@ -77,8 +80,12 @@ pub struct CompositeDevice {
7780 name : String ,
7881 /// Capabilities describe all input capabilities from all source devices
7982 capabilities : HashSet < Capability > ,
83+ /// Capabilities sorted by source device id
84+ capabilities_by_source : HashMap < String , HashSet < Capability > > ,
8085 /// Output capabilities describe all output capabilities from all source devices
8186 output_capabilities : HashSet < OutputCapability > ,
87+ /// Output capabilities sorted by source device id.
88+ output_capabilities_by_source : HashMap < String , HashSet < OutputCapability > > ,
8289 /// Capability mapping for the CompositeDevice
8390 capability_map : Option < CapabilityMapConfig > ,
8491 /// Currently loaded [DeviceProfile] for the [CompositeDevice]. The [DeviceProfile]
@@ -127,6 +134,9 @@ pub struct CompositeDevice {
127134 source_devices_used : Vec < String > ,
128135 /// State of target devices attached to the composite device
129136 targets : CompositeDeviceTargets ,
137+ /// Whether or not force feedback output events should be routed to
138+ /// supported source devices.
139+ ff_enabled : bool ,
130140 /// Set of available Force Feedback effect IDs that are not in use
131141 /// TODO: Just use the keys from ff_effect_id_source_map to determine next id
132142 ff_effect_ids : BTreeSet < i16 > ,
@@ -169,7 +179,9 @@ impl CompositeDevice {
169179 config,
170180 name,
171181 capabilities : HashSet :: new ( ) ,
182+ capabilities_by_source : HashMap :: new ( ) ,
172183 output_capabilities : HashSet :: new ( ) ,
184+ output_capabilities_by_source : HashMap :: new ( ) ,
173185 capability_map,
174186 device_profile : None ,
175187 device_profile_path : None ,
@@ -189,6 +201,7 @@ impl CompositeDevice {
189201 source_device_tasks : JoinSet :: new ( ) ,
190202 source_devices_used : Vec :: new ( ) ,
191203 targets : CompositeDeviceTargets :: new ( conn, dbus_path, tx. into ( ) , manager) ,
204+ ff_enabled : true ,
192205 ff_effect_ids : ( 0 ..64 ) . collect ( ) ,
193206 ff_effect_id_source_map : HashMap :: new ( ) ,
194207 intercept_activation_caps : vec ! [ Capability :: Gamepad ( Gamepad :: Button (
@@ -484,6 +497,15 @@ impl CompositeDevice {
484497 CompositeCommand :: SetInterceptActivation ( activation_caps, target_cap) => {
485498 self . set_intercept_activation ( activation_caps, target_cap)
486499 }
500+ CompositeCommand :: GetForceFeedbackEnabled ( sender) => {
501+ if let Err ( e) = sender. send ( self . ff_enabled ) . await {
502+ log:: error!( "Failed to send force feedback status: {e}" ) ;
503+ }
504+ }
505+ CompositeCommand :: SetForceFeedbackEnabled ( enabled) => {
506+ log:: info!( "Setting force feedback enabled: {enabled:?}" ) ;
507+ self . ff_enabled = enabled;
508+ }
487509 CompositeCommand :: Stop => {
488510 log:: debug!(
489511 "Got STOP signal. Stopping CompositeDevice: {}" ,
@@ -785,6 +807,12 @@ impl CompositeDevice {
785807 return Ok ( ( ) ) ;
786808 }
787809
810+ // If force feedback is disabled at the composite device level, don't
811+ // forward any other FF events to source devices.
812+ if !self . ff_enabled && event. is_force_feedback ( ) {
813+ return Ok ( ( ) ) ;
814+ }
815+
788816 // TODO: Only write the event to devices that are capabile of handling it
789817 for ( source_id, source) in self . source_devices . iter ( ) {
790818 // If this is a force feedback event, translate the effect id into
@@ -1513,6 +1541,51 @@ impl CompositeDevice {
15131541 self . ff_effect_ids . insert ( effect_id) ;
15141542 }
15151543
1544+ // Remove tracked input capabilities
1545+ self . capabilities_by_source . remove ( & id) ;
1546+ let mut capabilities_to_remove = vec ! [ ] ;
1547+ for capability in self . capabilities . iter ( ) {
1548+ // Check if any surviving source devices use this capability
1549+ let capability_in_use = self
1550+ . capabilities_by_source
1551+ . iter ( )
1552+ . any ( |( _, capabilities) | capabilities. contains ( capability) ) ;
1553+ if capability_in_use {
1554+ continue ;
1555+ }
1556+ capabilities_to_remove. push ( capability. clone ( ) ) ;
1557+ }
1558+ for capability in capabilities_to_remove {
1559+ self . capabilities . remove ( & capability) ;
1560+ }
1561+
1562+ // Remove tracked output capabilities
1563+ self . output_capabilities_by_source . remove ( & id) ;
1564+ let mut capabilities_to_remove = vec ! [ ] ;
1565+ for capability in self . output_capabilities . iter ( ) {
1566+ // Check if any surviving source devices use this capability
1567+ let capability_in_use = self
1568+ . output_capabilities_by_source
1569+ . iter ( )
1570+ . any ( |( _, capabilities) | capabilities. contains ( capability) ) ;
1571+ if capability_in_use {
1572+ continue ;
1573+ }
1574+ capabilities_to_remove. push ( capability. clone ( ) ) ;
1575+ }
1576+ for capability in capabilities_to_remove {
1577+ self . output_capabilities . remove ( & capability) ;
1578+ }
1579+
1580+ // Remove any interfaces that are no longer required
1581+ let ff_iface_name = ForceFeedbackInterface :: < CompositeDeviceClient > :: name ( ) ;
1582+ let supports_ff = self
1583+ . output_capabilities
1584+ . contains ( & OutputCapability :: ForceFeedback ) ;
1585+ if !supports_ff && self . dbus . has_interface ( & ff_iface_name) {
1586+ self . dbus . unregister ( & ff_iface_name) ;
1587+ }
1588+
15161589 if let Some ( idx) = self . source_device_paths . iter ( ) . position ( |str| str == & path) {
15171590 self . source_device_paths . remove ( idx) ;
15181591 } ;
@@ -1610,25 +1683,44 @@ impl CompositeDevice {
16101683 } ;
16111684
16121685 // Get the capabilities of the source device.
1613- // TODO: When we *remove* a source device, we also need to remove
1614- // capabilities
1686+ let id = source_device. get_id ( ) ;
16151687 if !is_blocked {
1616- let capabilities = source_device. get_capabilities ( ) ?;
1617- for cap in capabilities {
1618- if self . translatable_capabilities . contains ( & cap) {
1688+ // Get the input capabilities of the source device and keep track
1689+ // of them.
1690+ let capabilities: HashSet < Capability > =
1691+ source_device. get_capabilities ( ) ?. into_iter ( ) . collect ( ) ;
1692+ for cap in capabilities. iter ( ) {
1693+ if self . translatable_capabilities . contains ( cap) {
16191694 continue ;
16201695 }
1621- self . capabilities . insert ( cap) ;
1696+ self . capabilities . insert ( cap. clone ( ) ) ;
16221697 }
1698+ self . capabilities_by_source . insert ( id. clone ( ) , capabilities) ;
16231699
1624- let output_capabilities = source_device. get_output_capabilities ( ) ?;
1625- for cap in output_capabilities {
1626- self . output_capabilities . insert ( cap) ;
1700+ // Get the output capabilities of the source device and keep track
1701+ // of them.
1702+ let output_capabilities: HashSet < OutputCapability > = source_device
1703+ . get_output_capabilities ( ) ?
1704+ . into_iter ( )
1705+ . collect ( ) ;
1706+ for cap in output_capabilities. iter ( ) {
1707+ self . output_capabilities . insert ( cap. clone ( ) ) ;
1708+ }
1709+ self . output_capabilities_by_source
1710+ . insert ( id. clone ( ) , output_capabilities) ;
1711+
1712+ // Determine if the FF dbus interface should be created
1713+ let supports_ff = self
1714+ . output_capabilities
1715+ . contains ( & OutputCapability :: ForceFeedback ) ;
1716+ let ff_iface_name = ForceFeedbackInterface :: < CompositeDeviceClient > :: name ( ) ;
1717+ if supports_ff && !self . dbus . has_interface ( & ff_iface_name) {
1718+ let iface = ForceFeedbackInterface :: new ( self . client ( ) ) ;
1719+ self . dbus . register ( iface) ;
16271720 }
16281721 }
16291722
16301723 // Check if this device should be blocked from sending events to target devices.
1631- let id = source_device. get_id ( ) ;
16321724 if let Some ( device_config) = self
16331725 . config
16341726 . get_matching_device ( & source_device. get_device_ref ( ) . to_owned ( ) )
0 commit comments