From be5c8fdb107e5c8d1e55ff6743d7ae4efba4d805 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 31 Jul 2025 12:15:36 -0700 Subject: [PATCH 1/9] Don't provide hand joints or input viz when unfocused (outside the editor) --- .../Subsystems/MRTKLifecycleManager.cs | 4 +- .../Controllers/HandModel.cs | 41 ++++++++++++++----- .../Subsystems/Hands/HandsProvider.cs | 18 ++++++++ .../Tracking/HandPoseDriver.cs | 2 +- .../Utilities/HandDataContainer.cs | 13 +++--- .../ControllerVisualizer.cs | 1 - 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs index ed28c877d..bcc34aefb 100644 --- a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs +++ b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs @@ -27,7 +27,7 @@ public class MRTKLifecycleManager : /// /// The list of objects being managed by this class. /// - protected List ManagedSubsystems + protected List ManagedSubsystems { get => managedSubsystems; set => managedSubsystems = value; @@ -180,6 +180,7 @@ private void LateUpdate() } } +#if !UNITY_EDITOR /// /// Sent to all GameObjects when the player gets or loses focus. /// @@ -203,6 +204,7 @@ protected void OnApplicationFocus(bool focus) } } } +#endif // !UNITY_EDITOR #endregion MonoBehaviour diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs index 72d0c9548..5234ea12d 100644 --- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs +++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs @@ -60,15 +60,31 @@ public Transform ModelPrefab #endregion Associated hand select values + /// + /// See . + /// + protected virtual void Awake() + { + // Create empty container transform for the model if none specified. + // This is not strictly necessary to create since this GameObject could be used + // as the parent for the instantiated prefab, but doing so anyway for backwards compatibility. + if (modelParent == null) + { + modelParent = new GameObject($"[{gameObject.name}] Model Parent").transform; + modelParent.SetParent(transform, false); + modelParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + } + } + /// /// A Unity event function that is called on the frame when a script is enabled just before any of the update methods are called the first time. /// protected virtual void Start() { // Instantiate the model prefab if it is set - if (ModelPrefab != null) + if (modelPrefab != null) { - model = Instantiate(ModelPrefab, ModelParent); + model = Instantiate(modelPrefab, modelParent); Debug.Assert(selectInput != null, $"The Select Input reader for {name} is not set and will not be used with the instantiated hand model."); @@ -80,20 +96,23 @@ protected virtual void Start() } } +#if !UNITY_EDITOR /// - /// See . + /// Sent to all GameObjects when the player gets or loses focus. /// - protected virtual void Awake() + /// if the GameObjects have focus, else . + protected void OnApplicationFocus(bool focus) { - // Create empty container transform for the model if none specified. - // This is not strictly necessary to create since this GameObject could be used - // as the parent for the instantiated prefab, but doing so anyway for backwards compatibility. - if (modelParent == null) + // We want to ensure we're focused for input visualization, as some runtimes continue reporting "tracked" while pose updates are paused. + // This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, + // and applications should react to an inactive input action by skipping rendering of that action's input avatar + // (depictions of hands or other tracked objects controlled by the user)." + + if (modelParent != null) { - modelParent = new GameObject($"[{gameObject.name}] Model Parent").transform; - modelParent.SetParent(transform, false); - modelParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + modelParent.gameObject.SetActive(focus); } } +#endif // !UNITY_EDITOR } } diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs index 019443042..dd0570274 100644 --- a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs +++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs @@ -60,6 +60,15 @@ private void ResetHands() public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList jointPoses) { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetEntireHand query."); + +#if !UNITY_EDITOR + if (!Application.isFocused) + { + jointPoses = Array.Empty(); + return false; + } +#endif // !UNITY_EDITOR + return hands[handNode].TryGetEntireHand(out jointPoses); } @@ -67,6 +76,15 @@ public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList - /// Will be if the the hand joint query as successful. + /// Will be if the hand joint query was successful. /// public bool FullQueryValid { get; protected set; } @@ -41,7 +41,7 @@ public HandDataContainer(XRNode handNode) } /// - /// Reset the hand data query status + /// Reset the hand data query status. /// public void Reset() { @@ -49,16 +49,15 @@ public void Reset() FullQueryValid = false; } - - /// - /// Implemented in derived classes. This method gets all of the joint poses for the hand. + /// + /// Implemented in derived classes. This method gets all of the joint poses for the hand. /// /// The returned list of HandJointPoses /// if the query was successful, otherwise . public abstract bool TryGetEntireHand(out IReadOnlyList joints); - /// - /// Implemented in derived classes. This method gets the specified joint pose. + /// + /// Implemented in derived classes. This method gets the specified joint pose. /// /// The TrackedHandJoint to retrieve the post for. /// The returned HandJointPose. diff --git a/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs index 2b53c1eef..4ab036c36 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs @@ -2,7 +2,6 @@ // Licensed under the BSD 3-Clause using MixedReality.Toolkit.Input.Simulation; -using MixedReality.Toolkit.Subsystems; using System; using System.Threading.Tasks; using UnityEngine; From 35af2699715674ac116dbc28eb42442f1aef6a96 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 1 Aug 2025 13:28:54 -0700 Subject: [PATCH 2/9] Create MRTKInputFocusManager and move input action focus handling into it --- .../Subsystems/MRTKLifecycleManager.cs | 32 +------ .../Assets/Prefabs/MRTK XR Rig.prefab | 13 +++ .../MRTK.Input.asmdef | 1 + .../Utilities/MRTKInputFocusManager.cs | 90 +++++++++++++++++++ .../Utilities/MRTKInputFocusManager.cs.meta | 11 +++ 5 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs create mode 100644 org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta diff --git a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs index bcc34aefb..5f1934445 100644 --- a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs +++ b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Unity.Profiling; using UnityEngine; -using UnityEngine.InputSystem; namespace MixedReality.Toolkit.Subsystems { @@ -19,9 +18,6 @@ public class MRTKLifecycleManager : MonoBehaviour, IDisposable { - [SerializeField, Tooltip("A set of input actions to enable/disable according to the app's focus state.")] - private InputActionReference[] inputActionReferences; - private List managedSubsystems = new List(); /// @@ -115,7 +111,7 @@ private void OnDisable() /// /// A Unity event function that is called when the script component has been enabled. - /// + /// private void OnEnable() { using (OnEnableProfilerMarker.Auto()) @@ -180,32 +176,6 @@ private void LateUpdate() } } -#if !UNITY_EDITOR - /// - /// Sent to all GameObjects when the player gets or loses focus. - /// - /// if the GameObjects have focus, else . - protected void OnApplicationFocus(bool focus) - { - // We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused. - // This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, - // and applications should react to an inactive input action by skipping rendering of that action's input avatar - // (depictions of hands or other tracked objects controlled by the user)." - - foreach (InputActionReference reference in inputActionReferences) - { - if (focus) - { - reference.action.Enable(); - } - else - { - reference.action.Disable(); - } - } - } -#endif // !UNITY_EDITOR - #endregion MonoBehaviour /// diff --git a/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab b/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab index 2587371fe..46f67b566 100644 --- a/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab +++ b/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab @@ -325,6 +325,7 @@ GameObject: - component: {fileID: 2351505567455720332} - component: {fileID: 4160709927669568829} - component: {fileID: 6400715630075217958} + - component: {fileID: 1798554929582623642} m_Layer: 0 m_Name: MRTK XR Rig m_TagString: Untagged @@ -377,6 +378,18 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 1a107350295baaf4489642caa92f05de, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &1798554929582623642 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2351505567455720334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 671b97df530b06e46893c4189b3ceab4, type: 3} + m_Name: + m_EditorClassIdentifier: inputActionReferences: - {fileID: -7613329581162844239, guid: 18c412191cdc9274897f101c7fd5316f, type: 3} - {fileID: 3239510804178183174, guid: 18c412191cdc9274897f101c7fd5316f, type: 3} diff --git a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef index 75718572f..70a822a79 100644 --- a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef +++ b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef @@ -5,6 +5,7 @@ "glTFast", "Microsoft.MixedReality.OpenXR", "MixedReality.Toolkit.Core", + "Snapdragon.Spaces.Runtime", "Unity.InputSystem", "Unity.XR.CoreUtils", "Unity.XR.Hands", diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs new file mode 100644 index 000000000..d7791410c --- /dev/null +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -0,0 +1,90 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.InputSystem; + +namespace MixedReality.Toolkit.Input +{ + /// + /// Manages input based on XrSession focus. + /// + public sealed class MRTKInputFocusManager : MonoBehaviour + { + [SerializeField, Tooltip("A set of input actions to enable/disable according to the app's focus state.")] + private InputActionReference[] inputActionReferences; + + /// + /// Provides an event based on the current XrSession becoming focused or not. + /// + public static UnityEvent OnXrSessionFocus { get; } = new UnityEvent(); + + /// + /// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused. + /// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, + /// and applications should react to an inactive input action by skipping rendering of that action's input avatar + /// (depictions of hands or other tracked objects controlled by the user)." + /// + private void OnFocusChange(bool focus) + { + OnXrSessionFocus?.Invoke(focus); + + foreach (InputActionReference reference in inputActionReferences) + { + if (focus) + { + reference.action.Enable(); + } + else + { + reference.action.Disable(); + } + } + } + +#if USING_SNAPDRAGON_SPACES_SDK + private Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature spacesOpenXRFeature = null; + private int lastSessionState = -1; + + private void Update() + { + if (spacesOpenXRFeature == null) + { + spacesOpenXRFeature = UnityEngine.XR.OpenXR.OpenXRSettings.Instance.GetFeature(); + } + + // XrSessionState maps better to this behavior than OnApplicationFocus but isn't + // easily available in Unity. For now, only the Snapdragon Spaces plugin provides it. + if (spacesOpenXRFeature != null && lastSessionState != spacesOpenXRFeature.SessionState) + { + // If we've lost focus... + // XR_SESSION_STATE_FOCUSED = 5 + if (lastSessionState == 5) + { + OnFocusChange(false); + } + // ...or if we've gained focus + // XR_SESSION_STATE_FOCUSED = 5 + else if (spacesOpenXRFeature.SessionState == 5) + { + OnFocusChange(true); + } + + lastSessionState = spacesOpenXRFeature.SessionState; + } + } +#elif !UNITY_EDITOR + /// + /// Sent to all GameObjects when the player gets or loses focus. + /// + /// if the GameObjects have focus, else . + /// + /// Ideally, we'd use XrSessionState here, as it maps better to this behavior than OnApplicationFocus, but + /// it isn't easily available in Unity. For now, only the Snapdragon Spaces plugin provides it, and we use + /// OnApplicationFocus for the rest. + /// + private void OnApplicationFocus(bool focus) => OnFocusChange(focus); +#endif + } +} diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta new file mode 100644 index 000000000..ec4c2079a --- /dev/null +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 671b97df530b06e46893c4189b3ceab4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 34cea62ddce81172c3628db23844efcaf33c2b8c Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 1 Aug 2025 14:36:25 -0700 Subject: [PATCH 3/9] Migrate HandModel to use the new event --- .../Controllers/HandModel.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs index 5234ea12d..21842c674 100644 --- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs +++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs @@ -2,7 +2,6 @@ // Licensed under the BSD 3-Clause using UnityEngine; -using UnityEngine.XR; using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; namespace MixedReality.Toolkit.Input @@ -94,14 +93,20 @@ protected virtual void Start() selectInputVisualizer.SelectInput = selectInput; } } + + MRTKInputFocusManager.OnXrSessionFocus.AddListener(OnXrSessionFocus); } -#if !UNITY_EDITOR + /// + /// See . + /// + private void OnDestroy() => MRTKInputFocusManager.OnXrSessionFocus.RemoveListener(OnXrSessionFocus); + /// /// Sent to all GameObjects when the player gets or loses focus. /// /// if the GameObjects have focus, else . - protected void OnApplicationFocus(bool focus) + private void OnXrSessionFocus(bool focus) { // We want to ensure we're focused for input visualization, as some runtimes continue reporting "tracked" while pose updates are paused. // This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, @@ -113,6 +118,5 @@ protected void OnApplicationFocus(bool focus) modelParent.gameObject.SetActive(focus); } } -#endif // !UNITY_EDITOR } } From 80eeb47cfbd9814422c4820febd3e850506bc6ea Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 1 Aug 2025 14:50:05 -0700 Subject: [PATCH 4/9] Also expose current focus state for HandsProvider --- .../MRTK.Input.asmdef | 5 ++++ .../Subsystems/Hands/HandsProvider.cs | 8 ++--- .../Utilities/MRTKInputFocusManager.cs | 29 +++++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef index 70a822a79..c7c2ba0ba 100644 --- a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef +++ b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef @@ -70,6 +70,11 @@ "name": "com.unity.xr.openxr", "expression": "", "define": "UNITY_OPENXR_PRESENT" + }, + { + "name": "com.qualcomm.snapdragon.spaces", + "expression": "", + "define": "SNAPDRAGON_SPACES_PRESENT" } ], "noEngineReferences": false diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs index dd0570274..6de1246a6 100644 --- a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs +++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs @@ -61,13 +61,11 @@ public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList(); return false; } -#endif // !UNITY_EDITOR return hands[handNode].TryGetEntireHand(out jointPoses); } @@ -77,13 +75,11 @@ public override bool TryGetJoint(TrackedHandJoint joint, XRNode handNode, out Ha { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetJoint query."); -#if !UNITY_EDITOR - if (!Application.isFocused) + if (!MRTKInputFocusManager.HasFocus) { jointPose = default; return false; } -#endif // !UNITY_EDITOR return hands[handNode].TryGetJoint(joint, out jointPose); } diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs index d7791410c..08558bbb0 100644 --- a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -1,6 +1,7 @@ // Copyright (c) Mixed Reality Toolkit Contributors // Licensed under the BSD 3-Clause +using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; @@ -20,6 +21,18 @@ public sealed class MRTKInputFocusManager : MonoBehaviour /// public static UnityEvent OnXrSessionFocus { get; } = new UnityEvent(); + /// + /// Whether the current XrSession has focus or not. + /// + public static bool HasFocus => +#if UNITY_EDITOR + true; +#elif SNAPDRAGON_SPACES_PRESENT + lastSessionState == 5; +#else + Application.isFocused; +#endif + /// /// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused. /// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, @@ -43,15 +56,25 @@ private void OnFocusChange(bool focus) } } -#if USING_SNAPDRAGON_SPACES_SDK +#if SNAPDRAGON_SPACES_PRESENT + private static readonly List featureList = new(); + private static int lastSessionState = -1; private Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature spacesOpenXRFeature = null; - private int lastSessionState = -1; private void Update() { if (spacesOpenXRFeature == null) { - spacesOpenXRFeature = UnityEngine.XR.OpenXR.OpenXRSettings.Instance.GetFeature(); + int count = UnityEngine.XR.OpenXR.OpenXRSettings.Instance.GetFeatures(featureList); + for (int i = 0; i < count; i++) + { + Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature feature = featureList[i]; + if (feature != null && feature.enabled) + { + spacesOpenXRFeature = feature; + break; + } + } } // XrSessionState maps better to this behavior than OnApplicationFocus but isn't From 4a07161ccf7e3279e9a96e71bfd3f96797cc296d Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 4 Aug 2025 12:19:50 -0700 Subject: [PATCH 5/9] Update MRTKInputFocusManager.cs --- .../Utilities/MRTKInputFocusManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs index 08558bbb0..694a1b976 100644 --- a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -24,10 +24,9 @@ public sealed class MRTKInputFocusManager : MonoBehaviour /// /// Whether the current XrSession has focus or not. /// - public static bool HasFocus => -#if UNITY_EDITOR - true; -#elif SNAPDRAGON_SPACES_PRESENT + /// Always in the editor. + public static bool HasFocus => Application.isEditor || +#if SNAPDRAGON_SPACES_PRESENT lastSessionState == 5; #else Application.isFocused; From 813124df511dfaaa3e1297285d808137540358f6 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 4 Aug 2025 14:30:26 -0700 Subject: [PATCH 6/9] Convert to using IReadOnlyBindableVariable --- .../Controllers/HandModel.cs | 4 ++-- .../Subsystems/Hands/HandsProvider.cs | 4 ++-- .../Utilities/MRTKInputFocusManager.cs | 19 +++++-------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs index 21842c674..baf8a055c 100644 --- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs +++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs @@ -94,13 +94,13 @@ protected virtual void Start() } } - MRTKInputFocusManager.OnXrSessionFocus.AddListener(OnXrSessionFocus); + MRTKInputFocusManager.XrSessionHasFocus.SubscribeAndUpdate(OnXrSessionFocus); } /// /// See . /// - private void OnDestroy() => MRTKInputFocusManager.OnXrSessionFocus.RemoveListener(OnXrSessionFocus); + private void OnDestroy() => MRTKInputFocusManager.XrSessionHasFocus.Unsubscribe(OnXrSessionFocus); /// /// Sent to all GameObjects when the player gets or loses focus. diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs index 6de1246a6..4a098955c 100644 --- a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs +++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs @@ -61,7 +61,7 @@ public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList(); return false; @@ -75,7 +75,7 @@ public override bool TryGetJoint(TrackedHandJoint joint, XRNode handNode, out Ha { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetJoint query."); - if (!MRTKInputFocusManager.HasFocus) + if (!MRTKInputFocusManager.XrSessionHasFocus.Value) { jointPose = default; return false; diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs index 694a1b976..073ebfe27 100644 --- a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -2,8 +2,8 @@ // Licensed under the BSD 3-Clause using System.Collections.Generic; +using Unity.XR.CoreUtils.Bindings.Variables; using UnityEngine; -using UnityEngine.Events; using UnityEngine.InputSystem; namespace MixedReality.Toolkit.Input @@ -17,20 +17,11 @@ public sealed class MRTKInputFocusManager : MonoBehaviour private InputActionReference[] inputActionReferences; /// - /// Provides an event based on the current XrSession becoming focused or not. - /// - public static UnityEvent OnXrSessionFocus { get; } = new UnityEvent(); - - /// - /// Whether the current XrSession has focus or not. + /// Whether the current XrSession has focus or not, stored as a bindable variable that can be subscribed to for value changes. /// /// Always in the editor. - public static bool HasFocus => Application.isEditor || -#if SNAPDRAGON_SPACES_PRESENT - lastSessionState == 5; -#else - Application.isFocused; -#endif + public static IReadOnlyBindableVariable XrSessionHasFocus => xrSessionHasFocus; + private static readonly BindableVariable xrSessionHasFocus = new(Application.isEditor); /// /// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused. @@ -40,7 +31,7 @@ public sealed class MRTKInputFocusManager : MonoBehaviour /// private void OnFocusChange(bool focus) { - OnXrSessionFocus?.Invoke(focus); + xrSessionHasFocus.Value = focus; foreach (InputActionReference reference in inputActionReferences) { From 90f7fb8d3a56f0e6a07c7a4bb977cfc7476237c8 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 9 Dec 2025 10:58:02 -0800 Subject: [PATCH 7/9] Add MRTKFocusFeature --- .../XR/Settings/OpenXR Package Settings.asset | 63 +++++++++++++++++++ .../Editor/InputValidation.cs | 58 +++++++++++++++-- org.mixedrealitytoolkit.input/Features.meta | 8 +++ .../Features/MRTKFocusFeature.cs | 59 +++++++++++++++++ .../Features/MRTKFocusFeature.cs.meta | 11 ++++ 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 org.mixedrealitytoolkit.input/Features.meta create mode 100644 org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs create mode 100644 org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta diff --git a/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset b/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset index 3a4da5b8d..74ba99edb 100644 --- a/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset +++ b/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset @@ -363,6 +363,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-5000202051992085229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Android + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-4989097037534641705 MonoBehaviour: m_ObjectHideFlags: 0 @@ -394,6 +414,7 @@ MonoBehaviour: - {fileID: -5492763381520605560} - {fileID: 720212866400260749} - {fileID: -2783824018031006640} + - {fileID: -4614918767838866102} - {fileID: 4334429337943452973} - {fileID: -5448262054166454653} - {fileID: -9173909432611776730} @@ -429,6 +450,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-4614918767838866102 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Standalone + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-3765664599707111037 MonoBehaviour: m_ObjectHideFlags: 0 @@ -536,6 +577,7 @@ MonoBehaviour: - {fileID: -7229825001273466666} - {fileID: 4508051287823359615} - {fileID: 2835676197965704550} + - {fileID: -5000202051992085229} - {fileID: -7431403579802858517} - {fileID: -3273436338079595365} - {fileID: 8773911662759437579} @@ -651,6 +693,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-1685409458817180080 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Metro + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-1310172892294911860 MonoBehaviour: m_ObjectHideFlags: 0 @@ -934,6 +996,7 @@ MonoBehaviour: - {fileID: 8785654261743502636} - {fileID: 2622006578924267534} - {fileID: -6733472631637007722} + - {fileID: -1685409458817180080} - {fileID: 4575689165844638740} - {fileID: 6698335440458243644} - {fileID: -2219742231848666541} diff --git a/org.mixedrealitytoolkit.input/Editor/InputValidation.cs b/org.mixedrealitytoolkit.input/Editor/InputValidation.cs index 06af1d363..ad7c3be9f 100644 --- a/org.mixedrealitytoolkit.input/Editor/InputValidation.cs +++ b/org.mixedrealitytoolkit.input/Editor/InputValidation.cs @@ -27,29 +27,40 @@ private static void AddInputValidationRule() { foreach (var buildTargetGroup in MRTKProjectValidation.BuildTargetGroups) { - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateSpeechInteractorRule(buildTargetGroup) }, buildTargetGroup); + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateSpeechInteractorRule(buildTargetGroup) + }, buildTargetGroup); #if UNITY_OPENXR_PRESENT // Skip the standalone target as the hand subsystem rule for it is already present for all build targets if (buildTargetGroup != BuildTargetGroup.Standalone) { - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateUnityHandsRule(buildTargetGroup) }, buildTargetGroup); + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateUnityHandsRule(buildTargetGroup), + GenerateMRTKFocusFeatureRule(buildTargetGroup) + }, buildTargetGroup); } #endif } - MRTKProjectValidation.AddTargetIndependentRules(new List() { GenerateSkinWeightsRule(), GenerateGLTFastRule(), + MRTKProjectValidation.AddTargetIndependentRules(new List() { + GenerateSkinWeightsRule(), + GenerateGLTFastRule(), #if UNITY_OPENXR_PRESENT GenerateUnityHandsRule(BuildTargetGroup.Standalone), #endif }); // Only generate the KTX rule for platforms related to Meta - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateKTXRule(), + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateKTXRule(), #if UNITY_OPENXR_PRESENT GenerateAndroidHandsRule(), #endif }, BuildTargetGroup.Android); - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateKTXRule() }, BuildTargetGroup.Standalone); + + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateKTXRule() + }, BuildTargetGroup.Standalone); } private static BuildValidationRule GenerateSpeechInteractorRule(BuildTargetGroup buildTargetGroup) @@ -197,6 +208,43 @@ private static BuildValidationRule GenerateAndroidHandsRule() }; #pragma warning restore CS0618 // Type or member is obsolete } + + private static BuildValidationRule GenerateMRTKFocusFeatureRule(BuildTargetGroup buildTargetGroup) + { + return new BuildValidationRule() + { + Category = "MRTK3", + Message = $"For MRTK3 input to work correctly on OpenXR, enable {MRTKFocusFeature.FriendlyName} in the OpenXR Settings.", + CheckPredicate = () => + { + OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup); + if (settings == null) + { + return false; + } + + MRTKFocusFeature focusFeature = settings.GetFeature(); + return focusFeature != null && focusFeature.enabled; + }, + FixIt = () => + { + OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup); + if (settings == null) + { + return; + } + + MRTKFocusFeature focusFeature = settings.GetFeature(); + if (focusFeature != null) + { + focusFeature.enabled = true; + EditorUtility.SetDirty(settings); + } + }, + FixItMessage = $"Enable {nameof(MRTKFocusFeature)} in the OpenXR settings.", + Error = true + }; + } #endif } } diff --git a/org.mixedrealitytoolkit.input/Features.meta b/org.mixedrealitytoolkit.input/Features.meta new file mode 100644 index 000000000..7c4ed83ba --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8904458f727e8da428312ac577c1a981 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs new file mode 100644 index 000000000..29cd7da46 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs @@ -0,0 +1,59 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using Unity.XR.CoreUtils.Bindings.Variables; +using UnityEngine; +using UnityEngine.XR.OpenXR.Features; + +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +namespace MixedReality.Toolkit.Input +{ + /// + /// Provides focus data based on XrSession state. + /// +#if UNITY_EDITOR + [OpenXRFeature( + UiName = FriendlyName, + Desc = "Provides focus data based on XrSession state.", + Company = "Mixed Reality Toolkit Contributors", + Version = "4.0.0", + BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android }, + Category = FeatureCategory.Feature, + FeatureId = "org.mixedreality.toolkit.input.focus")] +#endif + public sealed class MRTKFocusFeature : OpenXRFeature + { + /// + /// The "friendly" name for this feature. + /// + public const string FriendlyName = "MRTK3 Session Focus"; + + /// + /// Whether the current XrSession has focus or not, stored as a bindable variable that can be subscribed to for value changes. + /// + /// Always in the editor. + public static IReadOnlyBindableVariable XrSessionFocused => xrSessionFocused; + private static readonly BindableVariable xrSessionFocused = new(Application.isEditor); + + /// + protected override void OnSessionStateChange(int oldState, int newState) + { + // If we've lost focus... + // XR_SESSION_STATE_FOCUSED = 5 + if (oldState == 5) + { + xrSessionFocused.Value = false; + } + // ...or if we've gained focus + // XR_SESSION_STATE_FOCUSED = 5 + else if (newState == 5) + { + xrSessionFocused.Value = true; + } + } + } +} diff --git a/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta new file mode 100644 index 000000000..d5b524e4c --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30587bb28b5390644ae70f801841a073 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 0c4155ef7d5b05b56a092e3e28bb15c61dc42036 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 9 Dec 2025 13:45:43 -0800 Subject: [PATCH 8/9] Update to use new feature --- .../Controllers/HandModel.cs | 8 +- .../MRTK.Input.asmdef | 6 -- .../Subsystems/Hands/HandsProvider.cs | 4 +- .../Utilities/MRTKInputFocusManager.cs | 84 ++++--------------- 4 files changed, 22 insertions(+), 80 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs index baf8a055c..8297d01e1 100644 --- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs +++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs @@ -94,18 +94,18 @@ protected virtual void Start() } } - MRTKInputFocusManager.XrSessionHasFocus.SubscribeAndUpdate(OnXrSessionFocus); + MRTKFocusFeature.XrSessionFocused.SubscribeAndUpdate(OnXrSessionFocus); } /// /// See . /// - private void OnDestroy() => MRTKInputFocusManager.XrSessionHasFocus.Unsubscribe(OnXrSessionFocus); + private void OnDestroy() => MRTKFocusFeature.XrSessionFocused.Unsubscribe(OnXrSessionFocus); /// - /// Sent to all GameObjects when the player gets or loses focus. + /// Sent when the XrSession gains or loses focus. /// - /// if the GameObjects have focus, else . + /// if the XrSession has focus, else . private void OnXrSessionFocus(bool focus) { // We want to ensure we're focused for input visualization, as some runtimes continue reporting "tracked" while pose updates are paused. diff --git a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef index c7c2ba0ba..75718572f 100644 --- a/org.mixedrealitytoolkit.input/MRTK.Input.asmdef +++ b/org.mixedrealitytoolkit.input/MRTK.Input.asmdef @@ -5,7 +5,6 @@ "glTFast", "Microsoft.MixedReality.OpenXR", "MixedReality.Toolkit.Core", - "Snapdragon.Spaces.Runtime", "Unity.InputSystem", "Unity.XR.CoreUtils", "Unity.XR.Hands", @@ -70,11 +69,6 @@ "name": "com.unity.xr.openxr", "expression": "", "define": "UNITY_OPENXR_PRESENT" - }, - { - "name": "com.qualcomm.snapdragon.spaces", - "expression": "", - "define": "SNAPDRAGON_SPACES_PRESENT" } ], "noEngineReferences": false diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs index 4a098955c..c3e35c2b1 100644 --- a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs +++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs @@ -61,7 +61,7 @@ public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList(); return false; @@ -75,7 +75,7 @@ public override bool TryGetJoint(TrackedHandJoint joint, XRNode handNode, out Ha { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetJoint query."); - if (!MRTKInputFocusManager.XrSessionHasFocus.Value) + if (!MRTKFocusFeature.XrSessionFocused.Value) { jointPose = default; return false; diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs index 073ebfe27..73c62d229 100644 --- a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -1,8 +1,6 @@ // Copyright (c) Mixed Reality Toolkit Contributors // Licensed under the BSD 3-Clause -using System.Collections.Generic; -using Unity.XR.CoreUtils.Bindings.Variables; using UnityEngine; using UnityEngine.InputSystem; @@ -16,22 +14,26 @@ public sealed class MRTKInputFocusManager : MonoBehaviour [SerializeField, Tooltip("A set of input actions to enable/disable according to the app's focus state.")] private InputActionReference[] inputActionReferences; - /// - /// Whether the current XrSession has focus or not, stored as a bindable variable that can be subscribed to for value changes. - /// - /// Always in the editor. - public static IReadOnlyBindableVariable XrSessionHasFocus => xrSessionHasFocus; - private static readonly BindableVariable xrSessionHasFocus = new(Application.isEditor); + private void OnEnable() + { + MRTKFocusFeature.XrSessionFocused.SubscribeAndUpdate(OnXrSessionFocus); + } + + private void OnDisable() + { + MRTKFocusFeature.XrSessionFocused.Unsubscribe(OnXrSessionFocus); + } /// - /// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused. - /// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, - /// and applications should react to an inactive input action by skipping rendering of that action's input avatar - /// (depictions of hands or other tracked objects controlled by the user)." + /// Sent when the XrSession gains or loses focus. /// - private void OnFocusChange(bool focus) + /// if the XrSession has focus, else . + private void OnXrSessionFocus(bool focus) { - xrSessionHasFocus.Value = focus; + // We want to ensure we're focused for input visualization, as some runtimes continue reporting "tracked" while pose updates are paused. + // This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused, + // and applications should react to an inactive input action by skipping rendering of that action's input avatar + // (depictions of hands or other tracked objects controlled by the user)." foreach (InputActionReference reference in inputActionReferences) { @@ -45,59 +47,5 @@ private void OnFocusChange(bool focus) } } } - -#if SNAPDRAGON_SPACES_PRESENT - private static readonly List featureList = new(); - private static int lastSessionState = -1; - private Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature spacesOpenXRFeature = null; - - private void Update() - { - if (spacesOpenXRFeature == null) - { - int count = UnityEngine.XR.OpenXR.OpenXRSettings.Instance.GetFeatures(featureList); - for (int i = 0; i < count; i++) - { - Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature feature = featureList[i]; - if (feature != null && feature.enabled) - { - spacesOpenXRFeature = feature; - break; - } - } - } - - // XrSessionState maps better to this behavior than OnApplicationFocus but isn't - // easily available in Unity. For now, only the Snapdragon Spaces plugin provides it. - if (spacesOpenXRFeature != null && lastSessionState != spacesOpenXRFeature.SessionState) - { - // If we've lost focus... - // XR_SESSION_STATE_FOCUSED = 5 - if (lastSessionState == 5) - { - OnFocusChange(false); - } - // ...or if we've gained focus - // XR_SESSION_STATE_FOCUSED = 5 - else if (spacesOpenXRFeature.SessionState == 5) - { - OnFocusChange(true); - } - - lastSessionState = spacesOpenXRFeature.SessionState; - } - } -#elif !UNITY_EDITOR - /// - /// Sent to all GameObjects when the player gets or loses focus. - /// - /// if the GameObjects have focus, else . - /// - /// Ideally, we'd use XrSessionState here, as it maps better to this behavior than OnApplicationFocus, but - /// it isn't easily available in Unity. For now, only the Snapdragon Spaces plugin provides it, and we use - /// OnApplicationFocus for the rest. - /// - private void OnApplicationFocus(bool focus) => OnFocusChange(focus); -#endif } } From a812c5ce62052a813437408fa9ac857b534f5840 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 9 Dec 2025 13:51:20 -0800 Subject: [PATCH 9/9] Update CHANGELOGs --- org.mixedrealitytoolkit.core/CHANGELOG.md | 6 ++++++ org.mixedrealitytoolkit.input/CHANGELOG.md | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/org.mixedrealitytoolkit.core/CHANGELOG.md b/org.mixedrealitytoolkit.core/CHANGELOG.md index ffe977dc0..2585246d7 100644 --- a/org.mixedrealitytoolkit.core/CHANGELOG.md +++ b/org.mixedrealitytoolkit.core/CHANGELOG.md @@ -2,6 +2,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## Unreleased + +### Removed + +* Removed input action focus handling from `MRTKLifecycleManager` and moved to the Input package. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) + ## [4.0.0-pre.2] - 2025-12-05 ### Added diff --git a/org.mixedrealitytoolkit.input/CHANGELOG.md b/org.mixedrealitytoolkit.input/CHANGELOG.md index bb0000f72..3eb63cd49 100644 --- a/org.mixedrealitytoolkit.input/CHANGELOG.md +++ b/org.mixedrealitytoolkit.input/CHANGELOG.md @@ -2,6 +2,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## Unreleased + +### Added + +* Added `MRTKFocusFeature` to provide XrSession focus info to MRTK components. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) +* Added input action focus handling to disable controller/hand tracked state when the XrSession goes out of focus. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) + ## [4.0.0-pre.2] - 2025-12-05 ### Changed