// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; using UnityEngine.EventSystems; namespace Microsoft.MixedReality.Toolkit.Input { /// /// The Mixed Reality Toolkit's specific implementation of the /// [HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/input/overview")] public class MixedRealityInputSystem : BaseDataProviderAccessCoreSystem, IMixedRealityInputSystem, IMixedRealityCapabilityCheck { /// /// Constructor. /// /// The instance that loaded the service. /// The configuration profile for the service. [Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")] public MixedRealityInputSystem( IMixedRealityServiceRegistrar registrar, MixedRealityInputSystemProfile profile) : this(profile) { Registrar = registrar; } /// /// Constructor. /// /// The configuration profile for the service. public MixedRealityInputSystem( MixedRealityInputSystemProfile profile) : base(profile) { } private static readonly ProfilerMarker ExecuteHierarchyPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem - Calling EventSystems.ExecuteEvents.ExecuteHierarchy"); /// public override string Name { get; protected set; } = "Mixed Reality Input System"; /// public event Action InputEnabled; /// public event Action InputDisabled; /// public HashSet DetectedInputSources { get; } = new HashSet(); /// public HashSet DetectedControllers { get; } = new HashSet(); private MixedRealityInputSystemProfile inputSystemProfile = null; /// public MixedRealityInputSystemProfile InputSystemProfile { get { if (inputSystemProfile == null) { inputSystemProfile = ConfigurationProfile as MixedRealityInputSystemProfile; } return inputSystemProfile; } } /// public IMixedRealityFocusProvider FocusProvider => focusProvider != null ? focusProvider : focusProvider = CoreServices.FocusProvider; private IMixedRealityFocusProvider focusProvider = null; /// public IMixedRealityRaycastProvider RaycastProvider => raycastProvider != null ? raycastProvider : raycastProvider = CoreServices.RaycastProvider; private IMixedRealityRaycastProvider raycastProvider = null; /// public IMixedRealityGazeProvider GazeProvider { get; private set; } /// public IMixedRealityEyeGazeProvider EyeGazeProvider { get; private set; } private readonly Stack modalInputStack = new Stack(); private readonly Stack fallbackInputStack = new Stack(); /// public bool IsInputEnabled => disabledRefCount <= 0; private int disabledRefCount; private bool isInputModuleAdded = false; private SourceStateEventData sourceStateEventData; private SourcePoseEventData sourceTrackingEventData; private SourcePoseEventData sourceVector2EventData; private SourcePoseEventData sourcePositionEventData; private SourcePoseEventData sourceRotationEventData; private SourcePoseEventData sourcePoseEventData; private FocusEventData focusEventData; private InputEventData inputEventData; private MixedRealityPointerEventData pointerEventData; private InputEventData floatInputEventData; private InputEventData vector2InputEventData; private InputEventData positionInputEventData; private InputEventData rotationInputEventData; private InputEventData poseInputEventData; private InputEventData> jointPoseInputEventData; private InputEventData handMeshInputEventData; private SpeechEventData speechEventData; private DictationEventData dictationEventData; private HandTrackingInputEventData handTrackingInputEventData; private MixedRealityInputActionRulesProfile CurrentInputActionRulesProfile { get; set; } private bool inputModuleChecked = false; private MixedRealityInputModule inputModule; #region IMixedRealityCapabilityCheck Implementation /// public bool CheckCapability(MixedRealityCapability capability) { foreach (var deviceManager in GetDataProviders()) { IMixedRealityCapabilityCheck capabilityChecker = deviceManager as IMixedRealityCapabilityCheck; // If one of the running data providers supports the requested capability, // the application has the needed support to leverage the desired functionality. if (capabilityChecker?.CheckCapability(capability) == true) { return true; } } // Check GazeProvider directly since not populated in data provider list but life-cycle is managed by InputSystem var gazeProvider_CapabilityCheck = GazeProvider as IMixedRealityCapabilityCheck; if (gazeProvider_CapabilityCheck?.CheckCapability(capability) == true) { return true; } return false; } #endregion IMixedRealityCapabilityCheck Implementation #region IMixedRealityService Implementation /// /// /// Input system is critical, so should be processed before all other managers /// public override uint Priority => 1; /// public override void Initialize() { MixedRealityInputSystemProfile profile = ConfigurationProfile as MixedRealityInputSystemProfile; if (profile == null) { Debug.LogError("The Input system is missing the required Input System Profile!"); return; } BaseInputModule[] inputModules = UnityEngine.Object.FindObjectsOfType(); if (inputModules.Length == 0) { DebugUtilities.LogVerbose("MixedRealityInputModule added to main camera"); // There is no input module attached to the camera, add one. inputModule = CameraCache.Main.gameObject.AddComponent(); isInputModuleAdded = true; } else if ((inputModules.Length == 1) && (inputModules[0] is MixedRealityInputModule)) { inputModule = inputModules[0] as MixedRealityInputModule; } else { Debug.LogError($"For Mixed Reality Toolkit input to work properly, please remove your other input module(s) and add a {typeof(MixedRealityInputModule).Name} to your main camera.", inputModules[0]); } if (InputSystemProfile == null) { Debug.LogError("The Input system is missing the required Input System Profile!"); return; } if (profile.InputActionRulesProfile != null) { CurrentInputActionRulesProfile = profile.InputActionRulesProfile; } else { Debug.LogError("The Input system is missing the required Input Action Rules Profile!"); return; } if (profile.PointerProfile != null) { InstantiateGazeProvider(profile.PointerProfile); } else { Debug.LogError("The Input system is missing the required Pointer Profile!"); return; } sourceStateEventData = new SourceStateEventData(EventSystem.current); sourceTrackingEventData = new SourcePoseEventData(EventSystem.current); sourceVector2EventData = new SourcePoseEventData(EventSystem.current); sourcePositionEventData = new SourcePoseEventData(EventSystem.current); sourceRotationEventData = new SourcePoseEventData(EventSystem.current); sourcePoseEventData = new SourcePoseEventData(EventSystem.current); focusEventData = new FocusEventData(EventSystem.current); inputEventData = new InputEventData(EventSystem.current); pointerEventData = new MixedRealityPointerEventData(EventSystem.current); floatInputEventData = new InputEventData(EventSystem.current); vector2InputEventData = new InputEventData(EventSystem.current); positionInputEventData = new InputEventData(EventSystem.current); rotationInputEventData = new InputEventData(EventSystem.current); poseInputEventData = new InputEventData(EventSystem.current); jointPoseInputEventData = new InputEventData>(EventSystem.current); handMeshInputEventData = new InputEventData(EventSystem.current); speechEventData = new SpeechEventData(EventSystem.current); dictationEventData = new DictationEventData(EventSystem.current); handTrackingInputEventData = new HandTrackingInputEventData(EventSystem.current); CreateDataProviders(); // Call the base after initialization to ensure any early exits do not // artificially declare the service as initialized. base.Initialize(); } /// public override void Enable() { CreateDataProviders(); // Ensure data providers are enabled (performed by the base class) base.Enable(); InputEnabled?.Invoke(); } public override void LateUpdate() { // Check whether manual initialization of input module is needed. // The check is only required once after input system is created. if (!isInputModuleAdded && !inputModuleChecked) { if (inputModule.ManualInitializationRequired) { inputModule.Initialize(); } inputModuleChecked = true; } base.LateUpdate(); } private void CreateDataProviders() { MixedRealityInputSystemProfile profile = ConfigurationProfile as MixedRealityInputSystemProfile; // If the system gets disabled, the gaze provider is destroyed. // Ensure that it gets recreated on when re-enabled. if (GazeProvider == null && profile != null) { InstantiateGazeProvider(profile.PointerProfile); } if ((GetDataProviders().Count == 0) && (profile != null)) { // Register the input device managers. for (int i = 0; i < profile.DataProviderConfigurations.Length; i++) { MixedRealityInputDataProviderConfiguration configuration = profile.DataProviderConfigurations[i]; object[] args = { this, configuration.ComponentName, configuration.Priority, configuration.Profile }; DebugUtilities.LogVerboseFormat( "Attempting to register input system data provider {0}, {1}, {2}", configuration.ComponentType.Type, configuration.ComponentName, configuration.RuntimePlatform); RegisterDataProvider( configuration.ComponentType.Type, configuration.ComponentName, configuration.RuntimePlatform, args); } } } private void InstantiateGazeProvider(MixedRealityPointerProfile pointerProfile) { if (pointerProfile != null && pointerProfile.GazeProviderType?.Type != null && CameraCache.Main != null) { IMixedRealityGazeProvider existingGazeProvider = CameraCache.Main.gameObject.GetComponent(); if (existingGazeProvider != null && existingGazeProvider.GetType() != pointerProfile.GazeProviderType.Type) { UnityEngine.Object.DestroyImmediate((UnityEngine.Object)existingGazeProvider); } GazeProvider = CameraCache.Main.gameObject.EnsureComponent(pointerProfile.GazeProviderType.Type) as IMixedRealityGazeProvider; GazeProvider.GazeCursorPrefab = pointerProfile.GazeCursorPrefab; DebugUtilities.LogVerboseFormat("Initialized a gaze provider of type {0}", pointerProfile.GazeProviderType.Type); // Current default implementation implements both provider types in one concrete class. EyeGazeProvider = GazeProvider as IMixedRealityEyeGazeProvider; if (EyeGazeProvider != null) { EyeGazeProvider.IsEyeTrackingEnabled = pointerProfile.IsEyeTrackingEnabled; DebugUtilities.LogVerboseFormat("Gaze Provider supports IMixedRealityEyeGazeProvider, IsEyeTrackingEnabled set to {0}", EyeGazeProvider.IsEyeTrackingEnabled); } if (GazeProvider is IMixedRealityGazeProviderHeadOverride gazeProviderHeadOverride) { gazeProviderHeadOverride.UseHeadGazeOverride = pointerProfile.UseHeadGazeOverride; DebugUtilities.LogVerboseFormat("Gaze Provider supports IMixedRealityGazeProviderHeadOverride, UseHeadGazeOverride set to {0}", gazeProviderHeadOverride.UseHeadGazeOverride); } } else { Debug.LogError("The input system is missing the required GazeProviderType!"); } } /// public override void Reset() { base.Reset(); Disable(); Initialize(); Enable(); } /// public override void Disable() { base.Disable(); // Input System adds a gaze provider component on the main camera, which needs to be removed when the input system is disabled/removed. // Otherwise the component would keep references to dead objects. // Unity's way to remove component is to destroy it. if (GazeProvider != null) { if (Application.isPlaying) { GazeProvider.GazePointer.BaseCursor.Destroy(); DebugUtilities.LogVerbose("Application was playing, destroyed the gaze pointer's BaseCursor"); } UnityObjectExtensions.DestroyObject(GazeProvider as Component); GazeProvider = null; DebugUtilities.LogVerbose("Destroyed the GazeProvider in MixedRealityInputSystem"); } foreach (var provider in GetDataProviders()) { if (provider != null) { DebugUtilities.LogVerboseFormat("Unregistering input data provider {0}", provider); UnregisterDataProvider(provider); } } InputDisabled?.Invoke(); } public override void Destroy() { if (isInputModuleAdded) { if (inputModule) { if (Application.isPlaying) { inputModule.DeactivateModule(); } UnityObjectExtensions.DestroyObject(inputModule); } } // If the MRTK profile is being switched and there is an input module in the scene in the beginning else if (Application.isPlaying && inputModule != null) { inputModule.Suspend(); } inputModule = null; base.Destroy(); } #endregion IMixedRealityService Implementation #region IMixedRealityDataProviderAccess Implementation /// public override IReadOnlyList GetDataProviders() { if (!typeof(IMixedRealityInputDeviceManager).IsAssignableFrom(typeof(T))) { return null; } return base.GetDataProviders(); } /// public override T GetDataProvider(string name = null) { if (!typeof(IMixedRealityInputDeviceManager).IsAssignableFrom(typeof(T))) { return default(T); } return base.GetDataProvider(name); } #endregion IMixedRealityDataProviderAccess Implementation #region IMixedRealityEventSystem Implementation private static readonly ProfilerMarker HandleEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.HandleEvent"); /// public override void HandleEvent(BaseEventData eventData, ExecuteEvents.EventFunction eventHandler) { using (HandleEventPerfMarker.Auto()) { if (disabledRefCount > 0) { return; } Debug.Assert(eventData != null); Debug.Assert(!(eventData is MixedRealityPointerEventData), "HandleEvent called with a pointer event. All events raised by pointer should call HandlePointerEvent"); var baseInputEventData = ExecuteEvents.ValidateEventData(eventData); DispatchEventToGlobalListeners(baseInputEventData, eventHandler); if (baseInputEventData.used) { // All global listeners get a chance to see the event, // but if any of them marked it used, // we stop the event from going any further. return; } if (baseInputEventData.InputSource.Pointers == null) { Debug.LogError($"InputSource {baseInputEventData.InputSource.SourceName} doesn't have any registered pointers! Input Sources without pointers should use the GazeProvider's pointer as a default fallback."); } var modalEventHandled = false; // Get the focused object for each pointer of the event source for (int i = 0; i < baseInputEventData.InputSource.Pointers.Length && !baseInputEventData.used; i++) { modalEventHandled = DispatchEventToObjectFocusedByPointer(baseInputEventData.InputSource.Pointers[i], baseInputEventData, modalEventHandled, eventHandler); } if (!baseInputEventData.used) { DispatchEventToFallbackHandlers(baseInputEventData, eventHandler); } } } private static readonly ProfilerMarker HandleFocusChangedEventsPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.HandleFocusChangedEvents"); /// /// Handles focus changed events /// We send all focus events to all global listeners and the actual focus change receivers. the use flag is completely ignored to avoid any interception. /// private void HandleFocusChangedEvents(FocusEventData focusEventData, ExecuteEvents.EventFunction eventHandler) { using (HandleFocusChangedEventsPerfMarker.Auto()) { Debug.Assert(focusEventData != null); DispatchEventToGlobalListeners(focusEventData, eventHandler); // Raise Focus Events on the old and new focused objects. if (focusEventData.OldFocusedObject != null) { using (ExecuteHierarchyPerfMarker.Auto()) { ExecuteEvents.ExecuteHierarchy(focusEventData.OldFocusedObject, focusEventData, eventHandler); } } if (focusEventData.NewFocusedObject != null) { using (ExecuteHierarchyPerfMarker.Auto()) { ExecuteEvents.ExecuteHierarchy(focusEventData.NewFocusedObject, focusEventData, eventHandler); } } // Raise Focus Events on the pointers cursor if it has one. if (focusEventData.Pointer != null && focusEventData.Pointer.BaseCursor != null) { try { using (ExecuteHierarchyPerfMarker.Auto()) { // When shutting down a game, we can sometime get old references to game objects that have been cleaned up. // We'll ignore when this happens. ExecuteEvents.ExecuteHierarchy(focusEventData.Pointer.BaseCursor.GameObjectReference, focusEventData, eventHandler); } } catch (Exception) { // ignored. } } } } private static readonly ProfilerMarker HandleFocusEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.HandleFocusEvent"); /// /// Handles focus enter and exit /// We send the focus event to all global listeners and the actual focus change receiver. the use flag is completely ignored to avoid any interception. /// private void HandleFocusEvent(GameObject eventTarget, FocusEventData focusEventData, ExecuteEvents.EventFunction eventHandler) { using (HandleFocusEventPerfMarker.Auto()) { Debug.Assert(focusEventData != null); DispatchEventToGlobalListeners(focusEventData, eventHandler); using (ExecuteHierarchyPerfMarker.Auto()) { ExecuteEvents.ExecuteHierarchy(eventTarget, focusEventData, eventHandler); } } } private static readonly ProfilerMarker HandlePointerEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.HandlePointerEvent"); /// /// Handles a pointer event /// Assumption: We only send pointer events to the objects that pointers are focusing, except for global event listeners (which listen to everything) /// In contract, all other events get sent to all other pointers attached to a given input source /// private void HandlePointerEvent(BaseEventData eventData, ExecuteEvents.EventFunction eventHandler) where T : IMixedRealityPointerHandler { using (HandlePointerEventPerfMarker.Auto()) { if (disabledRefCount > 0) { return; } Debug.Assert(eventData != null); var baseInputEventData = ExecuteEvents.ValidateEventData(eventData); DispatchEventToGlobalListeners(baseInputEventData, eventHandler); if (baseInputEventData.used) { // All global listeners get a chance to see the event, // but if any of them marked it used, // we stop the event from going any further. return; } Debug.Assert(pointerEventData.Pointer != null, "Trying to dispatch event on pointer but pointerEventData is null"); DispatchEventToObjectFocusedByPointer(pointerEventData.Pointer, baseInputEventData, false, eventHandler); if (!baseInputEventData.used) { DispatchEventToFallbackHandlers(baseInputEventData, eventHandler); } } } private static readonly ProfilerMarker DispatchEventToGlobalListenersPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.DispatchEventToGlobalListeners"); /// /// Dispatch an input event to all global event listeners /// Return true if the event has been handled by a global event listener /// private void DispatchEventToGlobalListeners(BaseInputEventData baseInputEventData, ExecuteEvents.EventFunction eventHandler) where T : IEventSystemHandler { using (DispatchEventToGlobalListenersPerfMarker.Auto()) { Debug.Assert(baseInputEventData != null); Debug.Assert(!baseInputEventData.used); if (baseInputEventData.InputSource == null) { Debug.Assert(baseInputEventData.InputSource != null, $"Failed to find an input source for {baseInputEventData}"); } // Send the event to global listeners base.HandleEvent(baseInputEventData, eventHandler); } } /// /// Dispatch a focus event to all global event listeners /// private void DispatchEventToGlobalListeners(FocusEventData focusEventData, ExecuteEvents.EventFunction eventHandler) where T : IEventSystemHandler { using (DispatchEventToGlobalListenersPerfMarker.Auto()) { Debug.Assert(focusEventData != null); Debug.Assert(!focusEventData.used); // Send the event to global listeners base.HandleEvent(focusEventData, eventHandler); } } private static readonly ProfilerMarker DispatchEventToFallbackHandlersPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.DispatchEventToFallbackHandlers"); private void DispatchEventToFallbackHandlers(BaseInputEventData baseInputEventData, ExecuteEvents.EventFunction eventHandler) where T : IEventSystemHandler { using (DispatchEventToFallbackHandlersPerfMarker.Auto()) { // If event was not handled by the focused object, pass it on to any fallback handlers if (!baseInputEventData.used && fallbackInputStack.Count > 0) { GameObject fallbackInput = fallbackInputStack.Peek(); using (ExecuteHierarchyPerfMarker.Auto()) { ExecuteEvents.ExecuteHierarchy(fallbackInput, baseInputEventData, eventHandler); } } } } private static readonly ProfilerMarker DispatchEventToObjectFocusedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.DispatchEventToObjectFocusedByPointer"); private static readonly ProfilerMarker DispatchModalEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.DispatchEventToObjectFocusedByPointer - Modal event handling"); /// /// Dispatch an input event to the object focused by the given IMixedRealityPointer. /// If a modal dialog is active, dispatch the pointer event to that modal dialog /// Returns true if the event was handled by a modal handler /// private bool DispatchEventToObjectFocusedByPointer(IMixedRealityPointer mixedRealityPointer, BaseInputEventData baseInputEventData, bool modalEventHandled, ExecuteEvents.EventFunction eventHandler) where T : IEventSystemHandler { using (DispatchEventToObjectFocusedPerfMarker.Auto()) { GameObject focusedObject = FocusProvider?.GetFocusedObject(mixedRealityPointer); using (DispatchModalEventPerfMarker.Auto()) { // Handle modal input if one exists if (modalInputStack.Count > 0 && !modalEventHandled) { GameObject modalInput = modalInputStack.Peek(); if (modalInput != null) { // If there is a focused object in the hierarchy of the modal handler, start the event bubble there if (focusedObject != null && focusedObject.transform.IsChildOf(modalInput.transform)) { using (ExecuteHierarchyPerfMarker.Auto()) { if (ExecuteEvents.ExecuteHierarchy(focusedObject, baseInputEventData, eventHandler) && baseInputEventData.used) { return true; } } } // Otherwise, just invoke the event on the modal handler itself else { using (ExecuteHierarchyPerfMarker.Auto()) { if (ExecuteEvents.ExecuteHierarchy(modalInput, baseInputEventData, eventHandler) && baseInputEventData.used) { return true; } } } } else { Debug.LogError("ModalInput GameObject reference was null!\nDid this GameObject get destroyed?"); } } } // If event was not handled by modal, pass it on to the current focused object if (focusedObject != null) { using (ExecuteHierarchyPerfMarker.Auto()) { ExecuteEvents.ExecuteHierarchy(focusedObject, baseInputEventData, eventHandler); } } return modalEventHandled; } } /// /// Register a GameObject to listen to events that will receive all input events, regardless /// of which other GameObjects might have handled the event beforehand. /// /// Useful for listening to events when the GameObject is currently not being raycasted against by the . /// Listener to add. public override void Register(GameObject listener) { base.Register(listener); } /// /// Unregister a GameObject from listening to input events. /// public override void Unregister(GameObject listener) { base.Unregister(listener); } #endregion IMixedRealityEventSystem Implementation #region Input Disabled Options /// /// Push a disabled input state onto the input manager. /// While input is disabled no events will be sent out and the cursor displays /// a waiting animation. /// public void PushInputDisable() { ++disabledRefCount; if (disabledRefCount == 1) { InputDisabled?.Invoke(); if (GazeProvider != null) { GazeProvider.Enabled = false; } } } /// /// Pop disabled input state. When the last disabled state is /// popped off the stack input will be re-enabled. /// public void PopInputDisable() { --disabledRefCount; Debug.Assert(disabledRefCount >= 0, "Tried to pop more input disable than the amount pushed."); if (disabledRefCount == 0) { InputEnabled?.Invoke(); if (GazeProvider != null) { GazeProvider.Enabled = true; } } } /// /// Clear the input disable stack, which will immediately re-enable input. /// public void ClearInputDisableStack() { bool wasInputDisabled = disabledRefCount > 0; disabledRefCount = 0; if (wasInputDisabled) { InputEnabled?.Invoke(); if (GazeProvider != null) { GazeProvider.Enabled = true; } } } #endregion Input Disabled Options #region Modal Input Options /// /// Push a game object into the modal input stack. Any input handlers /// on the game object are given priority to input events before any focused objects. /// /// The input handler to push public void PushModalInputHandler(GameObject inputHandler) { modalInputStack.Push(inputHandler); } /// /// Remove the last game object from the modal input stack. /// public void PopModalInputHandler() { if (modalInputStack.Count > 0) { modalInputStack.Pop(); } } /// /// Clear all modal input handlers off the stack. /// public void ClearModalInputStack() { modalInputStack.Clear(); } #endregion Modal Input Options #region Fallback Input Handler Options /// /// Push a game object into the fallback input stack. Any input handlers on /// the game object are given input events when no modal or focused objects consume the event. /// /// The input handler to push public void PushFallbackInputHandler(GameObject inputHandler) { fallbackInputStack.Push(inputHandler); } /// /// Remove the last game object from the fallback input stack. /// public void PopFallbackInputHandler() { fallbackInputStack.Pop(); } /// /// Clear all fallback input handlers off the stack. /// public void ClearFallbackInputStack() { fallbackInputStack.Clear(); } #endregion Fallback Input Handler Options #region Input Events #region Input Source Events /// public uint GenerateNewSourceId() { var newId = (uint)UnityEngine.Random.Range(1, int.MaxValue); foreach (var inputSource in DetectedInputSources) { if (inputSource.SourceId == newId) { return GenerateNewSourceId(); } } return newId; } /// public IMixedRealityInputSource RequestNewGenericInputSource(string name, IMixedRealityPointer[] pointers = null, InputSourceType sourceType = InputSourceType.Other) { return new BaseGenericInputSource(name, pointers, sourceType); } /// public BaseGlobalInputSource RequestNewGlobalInputSource(string name, IMixedRealityFocusProvider focusProvider = null, InputSourceType sourceType = InputSourceType.Other) { var inputSourceFocusProvider = focusProvider.IsNull() ? FocusProvider : focusProvider; return new BaseGlobalInputSource(name, inputSourceFocusProvider, sourceType); } #region Input Source State Events private static readonly ProfilerMarker RaiseSourceDetectedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSourceDetected"); /// public void RaiseSourceDetected(IMixedRealityInputSource source, IMixedRealityController controller = null) { using (RaiseSourceDetectedPerfMarker.Auto()) { if (DetectedInputSources.Contains(source)) { Debug.LogWarning($"[MRTK Issue] {source.SourceName} has already been registered with the Input Manager!"); return; } // Create input event sourceStateEventData.Initialize(source, controller); DetectedInputSources.Add(source); if (controller != null) { DetectedControllers.Add(controller); } DebugUtilities.LogVerboseFormat("RaiseSourceDetected: Source ID: {0}, Source Type: {1}", source.SourceId, source.SourceType); FocusProvider?.OnSourceDetected(sourceStateEventData); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourceStateEventData, OnSourceDetectedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourceDetectedEventHandler = delegate (IMixedRealitySourceStateHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnSourceDetected(casted); }; private static readonly ProfilerMarker RaiseSourceLostPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSourceLost"); /// public void RaiseSourceLost(IMixedRealityInputSource source, IMixedRealityController controller = null) { using (RaiseSourceLostPerfMarker.Auto()) { if (!DetectedInputSources.Contains(source)) { Debug.LogWarning($"[MRTK Issue] {source.SourceName} was never registered with the Input Manager!"); return; } // Create input event sourceStateEventData.Initialize(source, controller); DetectedInputSources.Remove(source); DebugUtilities.LogVerboseFormat("RaiseSourceLost: Source ID: {0}, Source Type: {1}", source.SourceId, source.SourceType); if (controller != null) { DetectedControllers.Remove(controller); } // Pass handler through HandleEvent to perform modal/fallback logic // Events have to be handled before FocusProvider.OnSourceLost since they won't be passed on without a focused object HandleEvent(sourceStateEventData, OnSourceLostEventHandler); FocusProvider?.OnSourceLost(sourceStateEventData); } } internal static readonly ExecuteEvents.EventFunction OnSourceLostEventHandler = delegate (IMixedRealitySourceStateHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnSourceLost(casted); }; #endregion Input Source State Events #region Input Source Pose Events private static readonly ProfilerMarker RaiseSourceTrackingStateChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseTrackingStateChanged"); /// public void RaiseSourceTrackingStateChanged(IMixedRealityInputSource source, IMixedRealityController controller, TrackingState state) { using (RaiseSourceTrackingStateChangedPerfMarker.Auto()) { // Create input event sourceTrackingEventData.Initialize(source, controller, state); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourceTrackingEventData, OnSourceTrackingChangedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourceTrackingChangedEventHandler = delegate (IMixedRealitySourcePoseHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnSourcePoseChanged(casted); }; private static readonly ProfilerMarker RaiseSourcePositionChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSourcePositionChanged"); /// public void RaiseSourcePositionChanged(IMixedRealityInputSource source, IMixedRealityController controller, Vector2 position) { using (RaiseSourcePositionChangedPerfMarker.Auto()) { // Create input event sourceVector2EventData.Initialize(source, controller, position); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourceVector2EventData, OnSourcePoseVector2ChangedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourcePoseVector2ChangedEventHandler = delegate (IMixedRealitySourcePoseHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnSourcePoseChanged(casted); }; /// public void RaiseSourcePositionChanged(IMixedRealityInputSource source, IMixedRealityController controller, Vector3 position) { using (RaiseSourcePositionChangedPerfMarker.Auto()) { // Create input event sourcePositionEventData.Initialize(source, controller, position); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourcePositionEventData, OnSourcePositionChangedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourcePositionChangedEventHandler = delegate (IMixedRealitySourcePoseHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnSourcePoseChanged(casted); }; private static readonly ProfilerMarker RaiseSourceRotationChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSourceRotationChanged"); /// public void RaiseSourceRotationChanged(IMixedRealityInputSource source, IMixedRealityController controller, Quaternion rotation) { using (RaiseSourceRotationChangedPerfMarker.Auto()) { // Create input event sourceRotationEventData.Initialize(source, controller, rotation); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourceRotationEventData, OnSourceRotationChangedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourceRotationChangedEventHandler = delegate (IMixedRealitySourcePoseHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnSourcePoseChanged(casted); }; private static readonly ProfilerMarker RaiseSourcePoseChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSourcePoseChanged"); /// public void RaiseSourcePoseChanged(IMixedRealityInputSource source, IMixedRealityController controller, MixedRealityPose position) { using (RaiseSourcePoseChangedPerfMarker.Auto()) { // Create input event sourcePoseEventData.Initialize(source, controller, position); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(sourcePoseEventData, OnSourcePoseChangedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnSourcePoseChangedEventHandler = delegate (IMixedRealitySourcePoseHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnSourcePoseChanged(casted); }; #endregion Input Source Pose Events #endregion Input Source Events #region Focus Events private static readonly ProfilerMarker RaisePreFocusChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePreFocusChanged"); /// public void RaisePreFocusChanged(IMixedRealityPointer pointer, GameObject oldFocusedObject, GameObject newFocusedObject) { using (RaisePreFocusChangedPerfMarker.Auto()) { focusEventData.Initialize(pointer, oldFocusedObject, newFocusedObject); HandleFocusChangedEvents(focusEventData, OnPreFocusChangedHandler); } } internal static readonly ExecuteEvents.EventFunction OnPreFocusChangedHandler = delegate (IMixedRealityFocusChangedHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnBeforeFocusChange(casted); }; private static readonly ProfilerMarker RaiseFocusChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseFocusChanged"); /// public void RaiseFocusChanged(IMixedRealityPointer pointer, GameObject oldFocusedObject, GameObject newFocusedObject) { using (RaiseFocusChangedPerfMarker.Auto()) { focusEventData.Initialize(pointer, oldFocusedObject, newFocusedObject); HandleFocusChangedEvents(focusEventData, OnFocusChangedHandler); } } internal static readonly ExecuteEvents.EventFunction OnFocusChangedHandler = delegate (IMixedRealityFocusChangedHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnFocusChanged(casted); }; private static readonly ProfilerMarker RaiseFocusEnterPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseFocusEnter"); /// public void RaiseFocusEnter(IMixedRealityPointer pointer, GameObject focusedObject) { using (RaiseFocusEnterPerfMarker.Auto()) { focusEventData.Initialize(pointer); HandleFocusEvent(focusedObject, focusEventData, OnFocusEnterEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnFocusEnterEventHandler = delegate (IMixedRealityFocusHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnFocusEnter(casted); }; private static readonly ProfilerMarker RaiseFocusExitPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseFocusExit"); /// public void RaiseFocusExit(IMixedRealityPointer pointer, GameObject unfocusedObject) { using (RaiseFocusExitPerfMarker.Auto()) { focusEventData.Initialize(pointer); HandleFocusEvent(unfocusedObject, focusEventData, OnFocusExitEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnFocusExitEventHandler = delegate (IMixedRealityFocusHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnFocusExit(casted); }; #endregion Focus Events #region Pointers #region Pointer Down internal static readonly ExecuteEvents.EventFunction OnPointerDownEventHandler = delegate (IMixedRealityPointerHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnPointerDown(casted); }; private static readonly ProfilerMarker RaisePointerDownPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePointerDown"); /// public void RaisePointerDown(IMixedRealityPointer pointer, MixedRealityInputAction inputAction, Handedness handedness = Handedness.None, IMixedRealityInputSource inputSource = null) { using (RaisePointerDownPerfMarker.Auto()) { // Only lock the object if there is a grabbable above in the hierarchy Transform currentObject = null; GameObject currentGameObject = pointer.Result?.Details.Object; if (currentGameObject != null) { currentObject = currentGameObject.transform; } IMixedRealityPointerHandler ancestorPointerHandler = null; while (currentObject != null && ancestorPointerHandler == null) { foreach (IMixedRealityPointerHandler handler in currentObject.GetComponents()) { if (handler is MonoBehaviour behavior && !behavior.enabled) { continue; } ancestorPointerHandler = handler; break; } currentObject = currentObject.transform.parent; } pointer.IsFocusLocked = ancestorPointerHandler != null; pointerEventData.Initialize(pointer, inputAction, handedness, inputSource); HandlePointerEvent(pointerEventData, OnPointerDownEventHandler); } } #endregion Pointer Down #region Pointer Dragged internal static readonly ExecuteEvents.EventFunction OnPointerDraggedEventHandler = delegate (IMixedRealityPointerHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnPointerDragged(casted); }; private static readonly ProfilerMarker RaisePointerDraggedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePointerDragged"); /// public void RaisePointerDragged(IMixedRealityPointer pointer, MixedRealityInputAction inputAction, Handedness handedness = Handedness.None, IMixedRealityInputSource inputSource = null) { using (RaisePointerDraggedPerfMarker.Auto()) { pointerEventData.Initialize(pointer, inputAction, handedness, inputSource); HandlePointerEvent(pointerEventData, OnPointerDraggedEventHandler); } } #endregion Pointer Dragged #region Pointer Click internal static readonly ExecuteEvents.EventFunction OnInputClickedEventHandler = delegate (IMixedRealityPointerHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnPointerClicked(casted); }; /// private static readonly ProfilerMarker RaisePointerClickedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePointerClicked"); public void RaisePointerClicked(IMixedRealityPointer pointer, MixedRealityInputAction inputAction, int count, Handedness handedness = Handedness.None, IMixedRealityInputSource inputSource = null) { using (RaisePointerClickedPerfMarker.Auto()) { // Create input event pointerEventData.Initialize(pointer, inputAction, handedness, inputSource, count); HandleClick(); } } private void HandleClick() { // Pass handler through HandleEvent to perform modal/fallback logic HandlePointerEvent(pointerEventData, OnInputClickedEventHandler); // NOTE: In Unity UI, a "click" happens on every pointer up, so we have RaisePointerUp call the PointerHandler. } #endregion Pointer Click #region Pointer Up internal static readonly ExecuteEvents.EventFunction OnPointerUpEventHandler = delegate (IMixedRealityPointerHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnPointerUp(casted); }; private static readonly ProfilerMarker RaisePointerUpPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePointerUp"); /// public void RaisePointerUp(IMixedRealityPointer pointer, MixedRealityInputAction inputAction, Handedness handedness = Handedness.None, IMixedRealityInputSource inputSource = null) { using (RaisePointerUpPerfMarker.Auto()) { pointerEventData.Initialize(pointer, inputAction, handedness, inputSource); HandlePointerEvent(pointerEventData, OnPointerUpEventHandler); pointer.IsFocusLocked = false; } } #endregion Pointer Up #endregion Pointers #region Generic Input Events #region Input Down internal static readonly ExecuteEvents.EventFunction OnInputDownEventHandler = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnInputDown(casted); }; internal static readonly ExecuteEvents.EventFunction OnInputDownWithActionEventHandler = delegate (IMixedRealityBaseInputHandler handler, BaseEventData eventData) { var inputData = ExecuteEvents.ValidateEventData(eventData); Debug.Assert(inputData.MixedRealityInputAction != MixedRealityInputAction.None); if (handler is IMixedRealityInputHandler inputHandler && !inputHandler.IsNull()) { inputHandler.OnInputDown(inputData); } if (handler is IMixedRealityInputActionHandler actionHandler && !actionHandler.IsNull()) { actionHandler.OnActionStarted(inputData); } }; private static readonly ProfilerMarker RaiseOnInputDownPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseOnInputDown"); /// public void RaiseOnInputDown(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction) { using (RaiseOnInputDownPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, true); // Create input event inputEventData.Initialize(source, handedness, inputAction); // Pass handler through HandleEvent to perform modal/fallback logic if (inputEventData.MixedRealityInputAction == MixedRealityInputAction.None) { HandleEvent(inputEventData, OnInputDownEventHandler); } else { HandleEvent(inputEventData, OnInputDownWithActionEventHandler); } } } #endregion Input Down #region Input Up internal static readonly ExecuteEvents.EventFunction OnInputUpEventHandler = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnInputUp(casted); }; internal static readonly ExecuteEvents.EventFunction OnInputUpWithActionEventHandler = delegate (IMixedRealityBaseInputHandler handler, BaseEventData eventData) { var inputData = ExecuteEvents.ValidateEventData(eventData); Debug.Assert(inputData.MixedRealityInputAction != MixedRealityInputAction.None); if (handler is IMixedRealityInputHandler inputHandler && !inputHandler.IsNull()) { inputHandler.OnInputUp(inputData); } if (handler is IMixedRealityInputActionHandler actionHandler && !actionHandler.IsNull()) { actionHandler.OnActionEnded(inputData); } }; private static readonly ProfilerMarker RaiseOnInputUpPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseOnInputUp"); /// public void RaiseOnInputUp(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction) { using (RaiseOnInputUpPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, false); // Create input event inputEventData.Initialize(source, handedness, inputAction); // Pass handler through HandleEvent to perform modal/fallback logic if (inputEventData.MixedRealityInputAction == MixedRealityInputAction.None) { HandleEvent(inputEventData, OnInputUpEventHandler); } else { HandleEvent(inputEventData, OnInputUpWithActionEventHandler); } } } #endregion Input Up #region Float Input Changed internal static readonly ExecuteEvents.EventFunction> OnFloatInputChanged = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnInputChanged(casted); }; private static readonly ProfilerMarker RaiseFloatInputChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseFloatInputChanged"); /// public void RaiseFloatInputChanged(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction, float inputValue) { using (RaiseFloatInputChangedPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, inputValue); // Create input event floatInputEventData.Initialize(source, handedness, inputAction, inputValue); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(floatInputEventData, OnFloatInputChanged); } } #endregion Float Input Changed #region Input Position Changed internal static readonly ExecuteEvents.EventFunction> OnTwoDoFInputChanged = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnInputChanged(casted); }; private static readonly ProfilerMarker RaisePositionInputChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePositionInputChanged"); /// public void RaisePositionInputChanged(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction, Vector2 inputPosition) { using (RaisePositionInputChangedPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, inputPosition); // Create input event vector2InputEventData.Initialize(source, handedness, inputAction, inputPosition); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(vector2InputEventData, OnTwoDoFInputChanged); } } internal static readonly ExecuteEvents.EventFunction> OnPositionInputChanged = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnInputChanged(casted); }; /// public void RaisePositionInputChanged(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction, Vector3 position) { using (RaisePositionInputChangedPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, position); // Create input event positionInputEventData.Initialize(source, handedness, inputAction, position); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(positionInputEventData, OnPositionInputChanged); } } #endregion Input Position Changed #region Input Rotation Changed internal static readonly ExecuteEvents.EventFunction> OnRotationInputChanged = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnInputChanged(casted); }; private static readonly ProfilerMarker RaiseRotationInputChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseRotationInputChanged"); /// public void RaiseRotationInputChanged(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction, Quaternion rotation) { using (RaiseRotationInputChangedPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, rotation); // Create input event rotationInputEventData.Initialize(source, handedness, inputAction, rotation); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(positionInputEventData, OnRotationInputChanged); } } #endregion Input Rotation Changed #region Input Pose Changed internal static readonly ExecuteEvents.EventFunction> OnPoseInputChanged = delegate (IMixedRealityInputHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnInputChanged(casted); }; private static readonly ProfilerMarker RaisePoseInputChangedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaisePoseInputChanged"); /// public void RaisePoseInputChanged(IMixedRealityInputSource source, Handedness handedness, MixedRealityInputAction inputAction, MixedRealityPose inputData) { using (RaisePoseInputChangedPerfMarker.Auto()) { inputAction = ProcessRules(inputAction, inputData); // Create input event poseInputEventData.Initialize(source, handedness, inputAction, inputData); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(poseInputEventData, OnPoseInputChanged); } } #endregion Input Pose Changed #endregion Generic Input Events #region Gesture Events internal static readonly ExecuteEvents.EventFunction OnGestureStarted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnGestureStarted(casted); }; internal static readonly ExecuteEvents.EventFunction OnGestureStartedWithAction = delegate (IMixedRealityBaseInputHandler handler, BaseEventData eventData) { var inputData = ExecuteEvents.ValidateEventData(eventData); Debug.Assert(inputData.MixedRealityInputAction != MixedRealityInputAction.None); if (handler is IMixedRealityGestureHandler gestureHandler && !gestureHandler.IsNull()) { gestureHandler.OnGestureStarted(inputData); } if (handler is IMixedRealityInputActionHandler actionHandler && !actionHandler.IsNull()) { actionHandler.OnActionStarted(inputData); } }; private static readonly ProfilerMarker RaiseGestureStartedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseGestureStarted"); /// public void RaiseGestureStarted(IMixedRealityController controller, MixedRealityInputAction action) { using (RaiseGestureStartedPerfMarker.Auto()) { action = ProcessRules(action, true); inputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action); if (action == MixedRealityInputAction.None) { HandleEvent(inputEventData, OnGestureStarted); } else { HandleEvent(inputEventData, OnGestureStartedWithAction); } } } internal static readonly ExecuteEvents.EventFunction OnGestureUpdated = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnGestureUpdated(casted); }; private static readonly ProfilerMarker RaiseGestureUpdatedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseGestureUpdated"); /// public void RaiseGestureUpdated(IMixedRealityController controller, MixedRealityInputAction action) { action = ProcessRules(action, true); inputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action); HandleEvent(inputEventData, OnGestureUpdated); } internal static readonly ExecuteEvents.EventFunction> OnGestureVector2PositionUpdated = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureUpdated(casted); }; /// public void RaiseGestureUpdated(IMixedRealityController controller, MixedRealityInputAction action, Vector2 inputData) { using (RaiseGestureUpdatedPerfMarker.Auto()) { action = ProcessRules(action, inputData); vector2InputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(vector2InputEventData, OnGestureVector2PositionUpdated); } } internal static readonly ExecuteEvents.EventFunction> OnGesturePositionUpdated = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureUpdated(casted); }; /// public void RaiseGestureUpdated(IMixedRealityController controller, MixedRealityInputAction action, Vector3 inputData) { using (RaiseGestureUpdatedPerfMarker.Auto()) { action = ProcessRules(action, inputData); positionInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(positionInputEventData, OnGesturePositionUpdated); } } internal static readonly ExecuteEvents.EventFunction> OnGestureRotationUpdated = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureUpdated(casted); }; /// public void RaiseGestureUpdated(IMixedRealityController controller, MixedRealityInputAction action, Quaternion inputData) { using (RaiseGestureUpdatedPerfMarker.Auto()) { action = ProcessRules(action, inputData); rotationInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(rotationInputEventData, OnGestureRotationUpdated); } } internal static readonly ExecuteEvents.EventFunction> OnGesturePoseUpdated = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureUpdated(casted); }; /// public void RaiseGestureUpdated(IMixedRealityController controller, MixedRealityInputAction action, MixedRealityPose inputData) { using (RaiseGestureUpdatedPerfMarker.Auto()) { action = ProcessRules(action, inputData); poseInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(poseInputEventData, OnGesturePoseUpdated); } } internal static readonly ExecuteEvents.EventFunction OnGestureCompleted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnGestureCompleted(casted); }; internal static readonly ExecuteEvents.EventFunction OnGestureCompletedWithAction = delegate (IMixedRealityBaseInputHandler handler, BaseEventData eventData) { var inputData = ExecuteEvents.ValidateEventData(eventData); Debug.Assert(inputData.MixedRealityInputAction != MixedRealityInputAction.None); if (handler is IMixedRealityGestureHandler gestureHandler && !gestureHandler.IsNull()) { gestureHandler.OnGestureCompleted(inputData); } if (handler is IMixedRealityInputActionHandler actionHandler && !actionHandler.IsNull()) { actionHandler.OnActionEnded(inputData); } }; private static readonly ProfilerMarker RaiseGestureCompletedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseGestureCompleted"); /// public void RaiseGestureCompleted(IMixedRealityController controller, MixedRealityInputAction action) { using (RaiseGestureCompletedPerfMarker.Auto()) { action = ProcessRules(action, false); inputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action); if (action == MixedRealityInputAction.None) { HandleEvent(inputEventData, OnGestureCompleted); } else { HandleEvent(inputEventData, OnGestureCompletedWithAction); } } } internal static readonly ExecuteEvents.EventFunction> OnGestureVector2PositionCompleted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureCompleted(casted); }; /// public void RaiseGestureCompleted(IMixedRealityController controller, MixedRealityInputAction action, Vector2 inputData) { using (RaiseGestureCompletedPerfMarker.Auto()) { action = ProcessRules(action, inputData); vector2InputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(vector2InputEventData, OnGestureVector2PositionCompleted); } } internal static readonly ExecuteEvents.EventFunction> OnGesturePositionCompleted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureCompleted(casted); }; /// public void RaiseGestureCompleted(IMixedRealityController controller, MixedRealityInputAction action, Vector3 inputData) { using (RaiseGestureCompletedPerfMarker.Auto()) { action = ProcessRules(action, inputData); positionInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(positionInputEventData, OnGesturePositionCompleted); } } internal static readonly ExecuteEvents.EventFunction> OnGestureRotationCompleted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureCompleted(casted); }; /// public void RaiseGestureCompleted(IMixedRealityController controller, MixedRealityInputAction action, Quaternion inputData) { using (RaiseGestureCompletedPerfMarker.Auto()) { action = ProcessRules(action, inputData); rotationInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(rotationInputEventData, OnGestureRotationCompleted); } } internal static readonly ExecuteEvents.EventFunction> OnGesturePoseCompleted = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnGestureCompleted(casted); }; /// public void RaiseGestureCompleted(IMixedRealityController controller, MixedRealityInputAction action, MixedRealityPose inputData) { using (RaiseGestureCompletedPerfMarker.Auto()) { action = ProcessRules(action, inputData); poseInputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action, inputData); HandleEvent(poseInputEventData, OnGesturePoseCompleted); } } internal static readonly ExecuteEvents.EventFunction OnGestureCanceled = delegate (IMixedRealityGestureHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnGestureCanceled(casted); }; private static readonly ProfilerMarker RaiseGestureCanceledPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseGestureCanceled"); /// public void RaiseGestureCanceled(IMixedRealityController controller, MixedRealityInputAction action) { using (RaiseGestureCanceledPerfMarker.Auto()) { action = ProcessRules(action, false); inputEventData.Initialize(controller.InputSource, controller.ControllerHandedness, action); HandleEvent(inputEventData, OnGestureCanceled); } } #endregion Gesture Events #region Speech Keyword Events internal static readonly ExecuteEvents.EventFunction OnSpeechKeywordRecognizedEventHandler = delegate (IMixedRealitySpeechHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnSpeechKeywordRecognized(casted); }; internal static readonly ExecuteEvents.EventFunction OnSpeechKeywordRecognizedWithActionEventHandler = delegate (IMixedRealityBaseInputHandler handler, BaseEventData eventData) { var speechData = ExecuteEvents.ValidateEventData(eventData); Debug.Assert(speechData.MixedRealityInputAction != MixedRealityInputAction.None); if (handler is IMixedRealitySpeechHandler speechHandler && !speechHandler.IsNull()) { speechHandler.OnSpeechKeywordRecognized(speechData); } if (handler is IMixedRealityInputActionHandler actionHandler && !actionHandler.IsNull()) { actionHandler.OnActionStarted(speechData); actionHandler.OnActionEnded(speechData); } }; private static readonly ProfilerMarker RaiseSpeechCommandRecognizedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseSpeechCommandRecognized"); /// public void RaiseSpeechCommandRecognized(IMixedRealityInputSource source, RecognitionConfidenceLevel confidence, TimeSpan phraseDuration, DateTime phraseStartTime, SpeechCommands command) { using (RaiseSpeechCommandRecognizedPerfMarker.Auto()) { // Create input event speechEventData.Initialize(source, confidence, phraseDuration, phraseStartTime, command); FocusProvider?.OnSpeechKeywordRecognized(speechEventData); // Pass handler through HandleEvent to perform modal/fallback logic if (command.Action == MixedRealityInputAction.None) { HandleEvent(speechEventData, OnSpeechKeywordRecognizedEventHandler); } else { HandleEvent(speechEventData, OnSpeechKeywordRecognizedWithActionEventHandler); } } } #endregion Speech Keyword Events #region Dictation Events internal static readonly ExecuteEvents.EventFunction OnDictationHypothesisEventHandler = delegate (IMixedRealityDictationHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnDictationHypothesis(casted); }; private static readonly ProfilerMarker RaiseDictationHypothesisPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseDictationHypothesis"); /// public void RaiseDictationHypothesis(IMixedRealityInputSource source, string dictationHypothesis, AudioClip dictationAudioClip = null) { using (RaiseDictationHypothesisPerfMarker.Auto()) { // Create input event dictationEventData.Initialize(source, dictationHypothesis, dictationAudioClip); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(dictationEventData, OnDictationHypothesisEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnDictationResultEventHandler = delegate (IMixedRealityDictationHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnDictationResult(casted); }; private static readonly ProfilerMarker RaiseDictationResultPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseDictationResult"); /// public void RaiseDictationResult(IMixedRealityInputSource source, string dictationResult, AudioClip dictationAudioClip = null) { using (RaiseDictationResultPerfMarker.Auto()) { // Create input event dictationEventData.Initialize(source, dictationResult, dictationAudioClip); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(dictationEventData, OnDictationResultEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnDictationCompleteEventHandler = delegate (IMixedRealityDictationHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnDictationComplete(casted); }; private static readonly ProfilerMarker RaiseDictationCompletePerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseDictationComplete"); /// public void RaiseDictationComplete(IMixedRealityInputSource source, string dictationResult, AudioClip dictationAudioClip) { using (RaiseDictationCompletePerfMarker.Auto()) { // Create input event dictationEventData.Initialize(source, dictationResult, dictationAudioClip); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(dictationEventData, OnDictationCompleteEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnDictationErrorEventHandler = delegate (IMixedRealityDictationHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnDictationError(casted); }; private static readonly ProfilerMarker RaiseDictationErrorPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseDictationError"); /// public void RaiseDictationError(IMixedRealityInputSource source, string dictationResult, AudioClip dictationAudioClip = null) { using (RaiseDictationErrorPerfMarker.Auto()) { // Create input event dictationEventData.Initialize(source, dictationResult, dictationAudioClip); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(dictationEventData, OnDictationErrorEventHandler); } } #endregion Dictation Events #region Hand Events internal static readonly ExecuteEvents.EventFunction OnHandJointsUpdatedEventHandler = delegate (IMixedRealityHandJointHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>>(eventData); handler.OnHandJointsUpdated(casted); }; private static readonly ProfilerMarker RaiseHandJointsUpdatedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseHandJointsUpdated"); public void RaiseHandJointsUpdated(IMixedRealityInputSource source, Handedness handedness, IDictionary jointPoses) { using (RaiseHandJointsUpdatedPerfMarker.Auto()) { // Create input event jointPoseInputEventData.Initialize(source, handedness, MixedRealityInputAction.None, jointPoses); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(jointPoseInputEventData, OnHandJointsUpdatedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnHandMeshUpdatedEventHandler = delegate (IMixedRealityHandMeshHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData>(eventData); handler.OnHandMeshUpdated(casted); }; private static readonly ProfilerMarker RaiseHandMeshUpdatedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseHandMeshUpdated"); public void RaiseHandMeshUpdated(IMixedRealityInputSource source, Handedness handedness, HandMeshInfo handMeshInfo) { using (RaiseHandMeshUpdatedPerfMarker.Auto()) { // Create input event handMeshInputEventData.Initialize(source, handedness, MixedRealityInputAction.None, handMeshInfo); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(handMeshInputEventData, OnHandMeshUpdatedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnTouchStartedEventHandler = delegate (IMixedRealityTouchHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnTouchStarted(casted); }; private static readonly ProfilerMarker RaiseOnTouchStartedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseOnTouchStarted"); /// public void RaiseOnTouchStarted(IMixedRealityInputSource source, IMixedRealityController controller, Handedness handedness, Vector3 touchPoint) { using (RaiseOnTouchStartedPerfMarker.Auto()) { // Create input event handTrackingInputEventData.Initialize(source, controller, handedness, touchPoint); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(handTrackingInputEventData, OnTouchStartedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnTouchCompletedEventHandler = delegate (IMixedRealityTouchHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnTouchCompleted(casted); }; private static readonly ProfilerMarker RaiseOnTouchCompletedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseOnTouchCompleted"); /// public void RaiseOnTouchCompleted(IMixedRealityInputSource source, IMixedRealityController controller, Handedness handedness, Vector3 touchPoint) { using (RaiseOnTouchCompletedPerfMarker.Auto()) { // Create input event handTrackingInputEventData.Initialize(source, controller, handedness, touchPoint); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(handTrackingInputEventData, OnTouchCompletedEventHandler); } } internal static readonly ExecuteEvents.EventFunction OnTouchUpdatedEventHandler = delegate (IMixedRealityTouchHandler handler, BaseEventData eventData) { var casted = ExecuteEvents.ValidateEventData(eventData); handler.OnTouchUpdated(casted); }; private static readonly ProfilerMarker RaiseOnTouchUpdatedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.RaiseOnTouchUpdated"); /// public void RaiseOnTouchUpdated(IMixedRealityInputSource source, IMixedRealityController controller, Handedness handedness, Vector3 touchPoint) { using (RaiseOnTouchUpdatedPerfMarker.Auto()) { // Create input event handTrackingInputEventData.Initialize(source, controller, handedness, touchPoint); // Pass handler through HandleEvent to perform modal/fallback logic HandleEvent(handTrackingInputEventData, OnTouchUpdatedEventHandler); } } #endregion Hand Events #endregion Input Events #region Rules private static readonly ProfilerMarker ProcessRulesInternalPerfMarker = new ProfilerMarker("[MRTK] MixedRealityInputSystem.ProcessRules_Internal"); private static MixedRealityInputAction ProcessRules_Internal(MixedRealityInputAction inputAction, T1[] inputActionRules, T2 criteria) where T1 : struct, IInputActionRule { using (ProcessRulesInternalPerfMarker.Auto()) { for (int i = 0; i < inputActionRules.Length; i++) { if (inputActionRules[i].BaseAction == inputAction && inputActionRules[i].Criteria.Equals(criteria)) { if (inputActionRules[i].RuleAction == inputAction) { Debug.LogError("Input Action Rule cannot be the same as the rule's Base Action!"); return inputAction; } if (inputActionRules[i].BaseAction.AxisConstraint != inputActionRules[i].RuleAction.AxisConstraint) { Debug.LogError("Input Action Rule doesn't have the same Axis Constraint as the Base Action!"); return inputAction; } return inputActionRules[i].RuleAction; } } return inputAction; } } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, bool criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesDigital?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesDigital, criteria); } return inputAction; } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, float criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesSingleAxis?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesSingleAxis, criteria); } return inputAction; } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, Vector2 criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesDualAxis?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesDualAxis, criteria); } return inputAction; } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, Vector3 criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesVectorAxis?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesVectorAxis, criteria); } return inputAction; } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, Quaternion criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesQuaternionAxis?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesQuaternionAxis, criteria); } return inputAction; } private MixedRealityInputAction ProcessRules(MixedRealityInputAction inputAction, MixedRealityPose criteria) { if (CurrentInputActionRulesProfile != null && CurrentInputActionRulesProfile.InputActionRulesPoseAxis?.Length > 0) { return ProcessRules_Internal(inputAction, CurrentInputActionRulesProfile.InputActionRulesPoseAxis, criteria); } return inputAction; } #endregion Rules } }