@@ -20,16 +20,19 @@ use custom_bindings::{BoolActionData, FloatActionData};
2020use legacy:: { setup_legacy_bindings, LegacyActionData } ;
2121use log:: { debug, info, trace, warn} ;
2222use openvr:: { self as vr, space_relation_to_openvr_pose} ;
23- use openxr as xr;
23+ use openxr:: {
24+ self as xr,
25+ sys:: { self , ActiveActionSetPrioritiesEXT , ActiveActionSetPriorityEXT } ,
26+ } ;
2427use slotmap:: { new_key_type, Key , KeyData , SecondaryMap , SlotMap } ;
25- use std:: collections:: HashMap ;
2628use std:: ffi:: { c_char, CStr , CString } ;
2729use std:: mem:: ManuallyDrop ;
2830use std:: path:: PathBuf ;
2931use std:: sync:: {
3032 atomic:: { AtomicU32 , Ordering } ,
3133 Arc , Mutex , OnceLock , RwLock ,
3234} ;
35+ use std:: { collections:: HashMap , ptr} ;
3336
3437new_key_type ! {
3538 struct InputSourceKey ;
@@ -819,41 +822,111 @@ impl<C: openxr_data::Compositor> vr::IVRInput010_Interface for Input<C> {
819822 let active_sets =
820823 unsafe { std:: slice:: from_raw_parts ( active_sets, active_set_count as usize ) } ;
821824
822- if active_sets
823- . iter ( )
824- . any ( |set| set. ulRestrictedToDevice != vr:: k_ulInvalidInputValueHandle)
825- {
826- crate :: warn_once!( "Per device action set restriction is not implemented yet." ) ;
827- }
828-
829825 let data = self . openxr . session_data . get ( ) ;
830826 let Some ( actions) = data. input_data . get_loaded_actions ( ) else {
831827 return vr:: EVRInputError :: InvalidParam ;
832828 } ;
833829
834830 let set_map = self . set_map . read ( ) . unwrap ( ) ;
831+
832+ // SteamVR uses an int32, OpenXR a uint32
833+ // Not sure what the correct behavior is here, does SteamVR allow negative values there? Does -1 mean it doesn't override legacy input?
834+ let min_priority = active_sets. iter ( ) . map ( |set| set. nPriority ) . min ( ) . unwrap ( ) ;
835+
836+ // +1 to make everything override legacy input
837+ let priority_offset = if min_priority < 0 {
838+ -min_priority + 1
839+ } else {
840+ 1
841+ } ;
842+
835843 let mut sync_sets = Vec :: with_capacity ( active_sets. len ( ) + 1 ) ;
844+ let mut priorities = Vec :: with_capacity ( active_sets. len ( ) + 1 ) ;
836845 {
837846 tracy_span ! ( "UpdateActionState generate active sets" ) ;
838847 for set in active_sets {
848+ let priority = ( set. nPriority + priority_offset) as u32 ;
849+ let mut path = xr:: Path :: NULL ;
850+
851+ // Restrict to device
852+ match self . subaction_path_from_handle ( set. ulRestrictedToDevice ) {
853+ Some ( new_path) => {
854+ path = new_path;
855+ // Handle secondary action set
856+ if set. ulSecondaryActionSet != vr:: k_ulInvalidInputValueHandle {
857+ let path = if path == self . openxr . left_hand . subaction_path {
858+ self . openxr . right_hand . subaction_path
859+ } else {
860+ self . openxr . left_hand . subaction_path
861+ } ;
862+ let key =
863+ ActionSetKey :: from ( KeyData :: from_ffi ( set. ulSecondaryActionSet ) ) ;
864+ let name = set_map. get ( key) ;
865+ let Some ( set) = actions. sets . get ( key) else {
866+ debug ! (
867+ "Application passed invalid secondary action set key: {key:?} ({name:?})"
868+ ) ;
869+ return vr:: EVRInputError :: InvalidHandle ;
870+ } ;
871+ debug ! ( "Activating secondary set {}" , name. unwrap( ) ) ;
872+ sync_sets. push ( xr:: ActiveActionSet :: with_subaction ( set, path) ) ;
873+ // I assume the secondary action set must have the same priority as the set that activates it
874+ priorities. push ( ActiveActionSetPriorityEXT {
875+ action_set : set. as_raw ( ) ,
876+ priority_override : priority,
877+ } ) ;
878+ }
879+ }
880+ None => ( ) ,
881+ } ;
882+
839883 let key = ActionSetKey :: from ( KeyData :: from_ffi ( set. ulActionSet ) ) ;
840884 let name = set_map. get ( key) ;
841885 let Some ( set) = actions. sets . get ( key) else {
842886 debug ! ( "Application passed invalid action set key: {key:?} ({name:?})" ) ;
843887 return vr:: EVRInputError :: InvalidHandle ;
844888 } ;
845889 debug ! ( "Activating set {}" , name. unwrap( ) ) ;
846- sync_sets. push ( set. into ( ) ) ;
890+ sync_sets. push ( xr:: ActiveActionSet :: with_subaction ( set, path) ) ;
891+ priorities. push ( ActiveActionSetPriorityEXT {
892+ action_set : set. as_raw ( ) ,
893+ priority_override : priority,
894+ } ) ;
847895 }
848896
849897 let legacy = data. input_data . legacy_actions . get ( ) . unwrap ( ) ;
850898 sync_sets. push ( xr:: ActiveActionSet :: new ( & legacy. set ) ) ;
899+ priorities. push ( ActiveActionSetPriorityEXT {
900+ action_set : legacy. set . as_raw ( ) ,
901+ priority_override : 0 ,
902+ } ) ;
851903 self . legacy_packet_num . fetch_add ( 1 , Ordering :: Relaxed ) ;
852904 }
853905
854906 {
855907 tracy_span ! ( "xrSyncActions" ) ;
856- data. session . sync_actions ( & sync_sets) . unwrap ( ) ;
908+ let info = sys:: ActionsSyncInfo {
909+ ty : sys:: ActionsSyncInfo :: TYPE ,
910+ next : & ActiveActionSetPrioritiesEXT {
911+ ty : sys:: ActiveActionSetPrioritiesEXT :: TYPE ,
912+ next : ptr:: null ( ) ,
913+ action_set_priority_count : priorities. len ( ) as u32 ,
914+ action_set_priorities : priorities. as_ptr ( ) ,
915+ } as * const _ as * const _ ,
916+ count_active_action_sets : sync_sets. len ( ) as u32 ,
917+ active_action_sets : sync_sets. as_ptr ( ) as _ ,
918+ } ;
919+ unsafe {
920+ let result =
921+ ( & self . openxr . instance . fp ( ) . sync_actions ) ( data. session . as_raw ( ) , & info) ;
922+ if result. into_raw ( ) >= 0 {
923+ Ok ( result)
924+ } else {
925+ Err ( result)
926+ }
927+ }
928+ . unwrap ( ) ;
929+
857930 // The IPC client won't be running during tests
858931 #[ cfg( not( test) ) ]
859932 self . skeletal_input_ipc
0 commit comments