void IProvidesInputActions.AddActionListeners(IUsesInputActions user, InputAction action, ActionEventCallback onStart, ActionEventCallback onPerformed, ActionEventCallback onCancel)
        {
            if (!m_ActionListeners.TryGetValue(action, out var listeners))
            {
                listeners      = new ActionListenerCollection();
                listeners.user = user;
                m_ActionListeners.Add(action, listeners);

                action.started   += OnActionStartedEvent;
                action.performed += OnActionPerformedEvent;
                action.canceled  += OnActionCanceledEvent;
            }

            if (user != listeners.user)
            {
                Debug.LogError("Cannot add action listeners because another user is already listening to this action.");
                return;
            }

            if (onStart != null)
            {
                listeners.startedCallbacks.Add(onStart);
            }

            if (onPerformed != null)
            {
                listeners.performedCallbacks.Add(onPerformed);
            }

            if (onCancel != null)
            {
                listeners.cancelledCallbacks.Add(onCancel);
            }
        }
        void IProvidesInputActions.ConsumeControl(InputControl control, IUsesInputActions consumer)
        {
            if (control == null)
            {
                return;
            }

            if (m_LockedControls.ContainsKey(control)) //TODO handle multiple things trying to lock the same control
            {
                return;
            }

            m_LockedControls.Add(control, consumer);
            foreach (var inputProcessor in m_InputProcessors)
            {
                var input = inputProcessor.inputActions;
                if (input == null)
                {
                    continue;
                }

                if (inputProcessor.user != consumer)
                {
                    DisableActionsForControl(control, inputProcessor);
                }
            }
        }
        InputActionAsset IProvidesInputActions.CreateActions(IUsesInputActions user, InputDevice device)
        {
            var actionsAsset = user.inputActionsAsset;

            if (actionsAsset == null)
            {
                return(null);
            }

            var inputActions = Instantiate(actionsAsset);

            if (inputActions == null)
            {
                return(null);
            }

            if (device != null)
            {
                inputActions.devices = new[] { device }
            }
            ;

            inputActions.Enable();

            var order = 0;

            var attributes            = user.GetType().GetCustomAttributes(typeof(ProcessInputAttribute), true);
            var processInputAttribute = attributes.Length > 0 ? (ProcessInputAttribute)attributes[0] : null;

            if (processInputAttribute != null)
            {
                order = processInputAttribute.order;
            }

            var inputActionsProcessor = new InputActionsProcessor {
                user = user, inputActions = inputActions, order = order, actionsAsset = actionsAsset
            };

            m_InputProcessors.Add(inputActionsProcessor);
            m_InputProcessors.Sort((a, b) => a.order.CompareTo(b.order)); //TODO Implicit ordering tool stack priority

            // New action map needs to disable any actions bound to currently locked controls
            foreach (var lockedControl in m_LockedControls)
            {
                DisableActionsForControl(lockedControl.Key, inputActionsProcessor);
            }

            user.OnActionsCreated(inputActions);

            return(inputActions);
        }
        /// <inheritdoc />
        public void RemoveActions(IUsesInputActions user)
        {
            var actionsAsset = user.inputActionsAsset;

            m_RemoveInputProcessorsCopy.Clear();
            m_RemoveInputProcessorsCopy.AddRange(m_InputProcessors);
            foreach (var processor in m_RemoveInputProcessorsCopy)
            {
                if (processor.user != user || processor.actionsAsset != actionsAsset)
                {
                    continue;
                }

                m_InputProcessors.Remove(processor);
                var input = processor.inputActions;

                foreach (var action in input)
                {
                    var actionActiveControl = action.activeControl;
                    if (actionActiveControl == null || !m_LockedControls.Remove(actionActiveControl))
                    {
                        continue;
                    }

                    if (m_ConsumedActions.ContainsKey(actionActiveControl))
                    {
                        foreach (var otherAction in m_ConsumedActions[actionActiveControl])
                        {
                            otherAction.Enable();
                        }

                        m_ConsumedActions.Remove(actionActiveControl);
                    }
                }

                UnityObjectUtils.Destroy(input);
            }
        }
        void ExecuteActionListeners(EventContext context, List <ActionEventCallback> callbacks)
        {
            IUsesInputActions owner = null;

            if (context.control != null)
            {
                m_LockedControls.TryGetValue(context.control, out owner);
            }

            foreach (var listener in callbacks)
            {
                if (context.action == null)
                {
                    Debug.LogError("Context action is null when executing action listeners.");
                    continue;
                }

                var listenerTarget = listener.Target as IUsesInputActions; // TODO maybe allow for listeners to be methods that are not part of an IUsesDeviceInput

                // If the control has an owner that is not this listener, "consume" and do not invoke listener
                if (owner != null && listenerTarget != owner)
                {
                    continue;
                }

                // Event is not consumed, so try to invoke. Catch exceptions to prevent one listener from interrupting all other actions.
                try
                {
                    listener.Invoke();
                }
                catch (Exception exception)
                {
                    Debug.LogException(exception);
                }
            }
        }
 /// <summary>
 /// Removes Actions of this input user from the Input System.  This user will no longer get <see cref="IUsesInputActions.ProcessInput"/> and no action listeners will trigger.
 /// If the user calls <see cref="CreateActions"/> again, they will need to re-register action listeners, and the <see cref="InputActionAsset"/> returned from <see cref="CreateActions"/> is no longer useful.
 /// </summary>
 /// <param name="user">The functionality user.</param>
 public static void RemoveActions(this IUsesInputActions user)
 {
     user.provider.RemoveActions(user);
 }
 /// <summary>
 /// Creates an <see cref="InputActionAsset"/> that is enabled and can be used to read <see cref="InputAction"/> values from. This is a clone of the input action user's <see cref="IUsesInputActions.inputActionsAsset"/>.
 /// </summary>
 /// <param name="user">The functionality user.</param>
 /// <param name="device">The device to associate these InputActions to. This will mean that only bindings valid on the passed in device will report input. If null, or unset, will allow this functionality user to listen to all input devices.</param>
 /// <returns>The cloned, enabled InputActionAsset. This is the same asset returned to <see cref="IUsesInputActions.OnActionsCreated"/></returns>
 public static InputActionAsset CreateActions(this IUsesInputActions user, InputDevice device = null)
 {
     return(user.provider.CreateActions(user, device));
 }
 /// <summary>
 /// Adds a set of callbacks for a specified input action. These callbacks also exist on <see cref="InputAction"/>, however using this API allows other input users to block calls by using <see cref="ConsumeControl"/>.
 /// </summary>
 /// <param name="user">The functionality user.</param>
 /// <param name="action">The input action to listen for changes on.</param>
 /// <param name="onStarted">A call when the input action leaves it's default state. See <see cref="InputAction.started"/></param>
 /// <param name="onPerformed">A call when the input action passes a specified threshold and is 'triggered'. See <see cref="InputAction.performed"/></param>
 /// <param name="onCanceled">A call when the input action returns to it's default state. See <see cref="InputAction.canceled"/></param>
 public static void AddActionListeners(this IUsesInputActions user, InputAction action, ActionEventCallback onStarted = null, ActionEventCallback onPerformed = null, ActionEventCallback onCanceled = null)
 {
     user.provider.AddActionListeners(user, action, onStarted, onPerformed, onCanceled);
 }
 /// <summary>
 /// Consuming a control prevents it from calling to any other registered action callbacks until it reverts back it's default state.
 /// </summary>
 /// <param name="user">The functionality user.</param>
 /// <param name="control">The control to consume. This will affect all other InputActions that have bindings pointing to this InputControl, regardless of the binding path used.</param>
 public static void ConsumeControl(this IUsesInputActions user, InputControl control)
 {
     user.provider.ConsumeControl(control, user);
 }