public unsafe void AddActionMap(InputActionMap map) { Debug.Assert(map != null, "Received null map"); var actionsInThisMap = map.m_Actions; var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap?.Length ?? 0; var actionCountInThisMap = actionsInThisMap?.Length ?? 0; var mapIndex = totalMapCount; // Keep track of indices for this map. var actionStartIndex = totalActionCount; var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; // Allocate an initial block of memory. We probably will have to re-allocate once // at the end to accommodate interactions and controls added from the map. var newMemory = new InputActionState.UnmanagedMemory(); newMemory.Allocate( mapCount: totalMapCount + 1, actionCount: totalActionCount + actionCountInThisMap, bindingCount: totalBindingCount + bindingCountInThisMap, // We reallocate for the following once we know the final count. interactionCount: totalInteractionCount, compositeCount: totalCompositeCount, controlCount: totalControlCount); if (memory.isAllocated) { newMemory.CopyDataFrom(memory); } ////TODO: make sure composite objects get all the bindings they need ////TODO: handle case where we have bindings resolving to the same control //// (not so clear cut what to do there; each binding may have a different interaction setup, for example) var currentCompositeBindingIndex = InputActionState.kInvalidIndex; var currentCompositeIndex = InputActionState.kInvalidIndex; var currentCompositePartCount = 0; var currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; InputAction currentCompositeAction = null; var bindingMaskOnThisMap = map.m_BindingMask; var devicesForThisMap = map.devices; var isSingletonAction = map.m_SingletonAction != null; // Can't use `using` as we need to use it with `ref`. var resolvedControls = new InputControlList <InputControl>(Allocator.Temp); // We gather all controls in temporary memory and then move them over into newMemory once // we're done resolving. try { for (var n = 0; n < bindingCountInThisMap; ++n) { var bindingStatesPtr = newMemory.bindingStates; ref var unresolvedBinding = ref bindingsInThisMap[n]; var bindingIndex = bindingStartIndex + n; var isComposite = unresolvedBinding.isComposite; var isPartOfComposite = !isComposite && unresolvedBinding.isPartOfComposite; var bindingState = &bindingStatesPtr[bindingIndex]; try { ////TODO: if it's a composite, check if any of the children matches our binding masks (if any) and skip composite if none do var firstControlIndex = 0; // numControls dictates whether this is a valid index or not. var firstInteractionIndex = InputActionState.kInvalidIndex; var firstProcessorIndex = InputActionState.kInvalidIndex; var actionIndexForBinding = InputActionState.kInvalidIndex; var partIndex = InputActionState.kInvalidIndex; var numControls = 0; var numInteractions = 0; var numProcessors = 0; // Make sure that if it's part of a composite, we are actually part of a composite. if (isPartOfComposite && currentCompositeBindingIndex == InputActionState.kInvalidIndex) { throw new InvalidOperationException( $"Binding '{unresolvedBinding}' is marked as being part of a composite but the preceding binding is not a composite"); } // Try to find action. // // NOTE: We ignore actions on bindings that are part of composites. We only allow // actions to be triggered from the composite itself. var actionIndexInMap = InputActionState.kInvalidIndex; var actionName = unresolvedBinding.action; InputAction action = null; if (!isPartOfComposite) { if (isSingletonAction) { // Singleton actions always ignore names. actionIndexInMap = 0; } else if (!string.IsNullOrEmpty(actionName)) { ////REVIEW: should we fail here if we don't manage to find the action actionIndexInMap = map.FindActionIndex(actionName); } if (actionIndexInMap != InputActionState.kInvalidIndex) { action = actionsInThisMap[actionIndexInMap]; } } else { actionIndexInMap = currentCompositeActionIndexInMap; action = currentCompositeAction; } // If it's a composite, start a chain. if (isComposite) { currentCompositeBindingIndex = bindingIndex; currentCompositeAction = action; currentCompositeActionIndexInMap = actionIndexInMap; } // Determine if the binding is disabled. // Disabled if path is empty. var path = unresolvedBinding.effectivePath; var bindingIsDisabled = string.IsNullOrEmpty(path) // Also, if we can't find the action to trigger for the binding, we just go and disable // the binding. || action == null // Also, disabled if binding doesn't match with our binding mask (might be empty). || (!isComposite && bindingMask != null && !bindingMask.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)) // Also, disabled if binding doesn't match the binding mask on the map (might be empty). || (!isComposite && bindingMaskOnThisMap != null && !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)) // Finally, also disabled if binding doesn't match the binding mask on the action (might be empty). || (!isComposite && action?.m_BindingMask != null && !action.m_BindingMask.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)); // If the binding isn't disabled, look up controls now. We do this first as we may still disable the // binding if it doesn't resolve to any controls or resolves only to controls already bound to by // other bindings. // // NOTE: We continuously add controls here to `resolvedControls`. Once we've completed our // pass over the bindings in the map, `resolvedControls` will have all the controls for // the current map. if (!bindingIsDisabled && !isComposite) { firstControlIndex = memory.controlCount + resolvedControls.Count; if (devicesForThisMap != null) { // Search in devices for only this map. var list = devicesForThisMap.Value; for (var i = 0; i < list.Count; ++i) { var device = list[i]; if (!device.added) { continue; // Skip devices that have been removed. } numControls += InputControlPath.TryFindControls(device, path, 0, ref resolvedControls); } } else { // Search globally. numControls = InputSystem.FindControls(path, ref resolvedControls); } // Disable binding if it doesn't resolve to any controls. // NOTE: This also happens to bindings that got all their resolved controls removed because other bindings from the same // action already grabbed them. if (numControls == 0) { bindingIsDisabled = true; } } // If the binding isn't disabled, resolve its controls, processors, and interactions. if (!bindingIsDisabled) { // Instantiate processors. var processorString = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processorString)) { // Add processors from binding. firstProcessorIndex = ResolveProcessors(processorString); if (firstProcessorIndex != InputActionState.kInvalidIndex) { numProcessors = totalProcessorCount - firstProcessorIndex; } } if (!string.IsNullOrEmpty(action.m_Processors)) { // Add processors from action. var index = ResolveProcessors(action.m_Processors); if (index != InputActionState.kInvalidIndex) { if (firstProcessorIndex == InputActionState.kInvalidIndex) { firstProcessorIndex = index; } numProcessors += totalProcessorCount - index; } } // Instantiate interactions. var interactionString = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactionString)) { // Add interactions from binding. firstInteractionIndex = ResolveInteractions(interactionString); if (firstInteractionIndex != InputActionState.kInvalidIndex) { numInteractions = totalInteractionCount - firstInteractionIndex; } } if (!string.IsNullOrEmpty(action.m_Interactions)) { // Add interactions from action. var index = ResolveInteractions(action.m_Interactions); if (index != InputActionState.kInvalidIndex) { if (firstInteractionIndex == InputActionState.kInvalidIndex) { firstInteractionIndex = index; } numInteractions += totalInteractionCount - index; } } // If it's the start of a composite chain, create the composite. Otherwise, go and // resolve controls for the binding. if (isComposite) { // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); // Record where the controls for parts of the composite start. firstControlIndex = memory.controlCount + resolvedControls.Count; } else { // If we've reached the end of a composite chain, finish // off the current composite. if (!isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex) { currentCompositePartCount = 0; currentCompositeBindingIndex = InputActionState.kInvalidIndex; currentCompositeIndex = InputActionState.kInvalidIndex; currentCompositeAction = null; currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; } } } // If the binding is part of a composite, pass the resolved controls // on to the composite. if (isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex && numControls > 0) { // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new InvalidOperationException( $"Binding '{unresolvedBinding}' that is part of composite '{composites[currentCompositeIndex]}' is missing a name"); } // Assign an index to the current part of the composite which // can be used by the composite to read input from this part. partIndex = AssignCompositePartIndex(composites[currentCompositeIndex], unresolvedBinding.name, ref currentCompositePartCount); // Keep track of total number of controls bound in the composite. bindingStatesPtr[currentCompositeBindingIndex].controlCount += numControls; // Force action index on part binding to be same as that of composite. actionIndexForBinding = bindingStatesPtr[currentCompositeBindingIndex].actionIndex; } else if (actionIndexInMap != InputActionState.kInvalidIndex) { actionIndexForBinding = actionStartIndex + actionIndexInMap; } // Store resolved binding. *bindingState = new InputActionState.BindingState { controlStartIndex = firstControlIndex, // For composites, this will be adjusted as we add each part. controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isComposite = isComposite, isPartOfComposite = unresolvedBinding.isPartOfComposite, partIndex = partIndex, actionIndex = actionIndexForBinding, compositeOrCompositeBindingIndex = isComposite ? currentCompositeIndex : currentCompositeBindingIndex, mapIndex = totalMapCount, wantsInitialStateCheck = action?.wantsInitialStateCheck ?? false }; } catch (Exception exception) { Debug.LogError( $"{exception.GetType().Name} while resolving binding '{unresolvedBinding}' in action map '{map}'"); Debug.LogException(exception); // Don't swallow exceptions that indicate something is wrong in the code rather than // in the data. if (exception.IsExceptionIndicatingBugInCode()) { throw; } } } // Re-allocate memory to accommodate controls and interaction states. The count for those // we only know once we've completed all resolution. var controlCountInThisMap = resolvedControls.Count; var newTotalControlCount = memory.controlCount + controlCountInThisMap; if (newMemory.interactionCount != totalInteractionCount || newMemory.compositeCount != totalCompositeCount || newMemory.controlCount != newTotalControlCount) { var finalMemory = new InputActionState.UnmanagedMemory(); finalMemory.Allocate( mapCount: newMemory.mapCount, actionCount: newMemory.actionCount, bindingCount: newMemory.bindingCount, controlCount: newTotalControlCount, interactionCount: totalInteractionCount, compositeCount: totalCompositeCount); finalMemory.CopyDataFrom(newMemory); newMemory.Dispose(); newMemory = finalMemory; } // Add controls to array. var controlCountInArray = memory.controlCount; ArrayHelpers.AppendListWithCapacity(ref controls, ref controlCountInArray, resolvedControls); Debug.Assert(controlCountInArray == newTotalControlCount, "Control array should have combined count of old and new controls"); // Set up control to binding index mapping. for (var i = 0; i < bindingCountInThisMap; ++i) { var bindingStatesPtr = newMemory.bindingStates; var bindingState = &bindingStatesPtr[bindingStartIndex + i]; var numControls = bindingState->controlCount; var startIndex = bindingState->controlStartIndex; for (var n = 0; n < numControls; ++n) { newMemory.controlIndexToBindingIndex[startIndex + n] = bindingStartIndex + i; } } // Initialize initial interaction states. for (var i = memory.interactionCount; i < newMemory.interactionCount; ++i) { newMemory.interactionStates[i].phase = InputActionPhase.Waiting; } // Initialize action data. var runningIndexInBindingIndices = memory.bindingCount; for (var i = 0; i < actionCountInThisMap; ++i) { var action = actionsInThisMap[i]; var actionIndex = actionStartIndex + i; // Correlate action with its trigger state. action.m_ActionIndexInState = actionIndex; // Collect bindings for action. var bindingStartIndexForAction = runningIndexInBindingIndices; var bindingCountForAction = 0; var numPossibleConcurrentActuations = 0; for (var n = 0; n < bindingCountInThisMap; ++n) { var bindingIndex = bindingStartIndex + n; var bindingState = &newMemory.bindingStates[bindingIndex]; if (bindingState->actionIndex != actionIndex) { continue; } if (bindingState->isPartOfComposite) { continue; } Debug.Assert(bindingIndex <= ushort.MaxValue, "Binding index exceeds limit"); newMemory.actionBindingIndices[runningIndexInBindingIndices] = (ushort)bindingIndex; ++runningIndexInBindingIndices; ++bindingCountForAction; // Keep track of how many concurrent actuations we may be seeing on the action so that // we know whether we need to enable conflict resolution or not. if (bindingState->isComposite) { // Composite binding. Actuates as a whole. Check if the composite has successfully // resolved any controls. If so, it adds one possible actuation. if (bindingState->controlCount > 0) { ++numPossibleConcurrentActuations; } } else { // Normal binding. Every successfully resolved control results in one possible actuation. numPossibleConcurrentActuations += bindingState->controlCount; } } Debug.Assert(bindingStartIndexForAction < ushort.MaxValue, "Binding start index on action exceeds limit"); Debug.Assert(bindingCountForAction < ushort.MaxValue, "Binding count on action exceeds limit"); newMemory.actionBindingIndicesAndCounts[actionIndex * 2] = (ushort)bindingStartIndexForAction; newMemory.actionBindingIndicesAndCounts[actionIndex * 2 + 1] = (ushort)bindingCountForAction; // See if we may need conflict resolution on this action. Never needed for pass-through actions. // Otherwise, if we have more than one bound control or have several bindings and one of them // is a composite, we enable it. var isPassThroughAction = action.type == InputActionType.PassThrough; var isButtonAction = action.type == InputActionType.Button; var mayNeedConflictResolution = !isPassThroughAction && numPossibleConcurrentActuations > 1; // Initialize initial trigger state. newMemory.actionStates[actionIndex] = new InputActionState.TriggerState { phase = InputActionPhase.Disabled, mapIndex = mapIndex, controlIndex = InputActionState.kInvalidIndex, interactionIndex = InputActionState.kInvalidIndex, isPassThrough = isPassThroughAction, isButton = isButtonAction, mayNeedConflictResolution = mayNeedConflictResolution, }; } // Store indices for map. newMemory.mapIndices[mapIndex] = new InputActionState.ActionMapIndices { actionStartIndex = actionStartIndex, actionCount = actionCountInThisMap, controlStartIndex = controlStartIndex, controlCount = controlCountInThisMap, bindingStartIndex = bindingStartIndex, bindingCount = bindingCountInThisMap, interactionStartIndex = interactionStartIndex, interactionCount = totalInteractionCount - interactionStartIndex, processorStartIndex = processorStartIndex, processorCount = totalProcessorCount - processorStartIndex, compositeStartIndex = compositeStartIndex, compositeCount = totalCompositeCount - compositeStartIndex, }; map.m_MapIndexInState = mapIndex; var finalActionMapCount = memory.mapCount; ArrayHelpers.AppendWithCapacity(ref maps, ref finalActionMapCount, map, capacityIncrement: 4); Debug.Assert(finalActionMapCount == newMemory.mapCount, "Final action map count should match old action map count plus one"); // As a final act, swap the new memory in. memory.Dispose(); memory = newMemory; }
public Enumerator(InputControlList <TControl> list) { m_Count = list.m_Count; m_Current = -1; m_Indices = m_Count > 0 ? (ulong *)list.m_Indices.GetUnsafeReadOnlyPtr() : null; }
private static TControl MatchByUsageAtDeviceRootRecursive <TControl>(InputDevice device, string path, int indexInPath, ref InputControlList <TControl> matches, bool matchMultiple) where TControl : InputControl { var usages = device.m_UsagesForEachControl; if (usages == null) { return(null); } var usageCount = usages.Length; var startIndex = indexInPath + 1; var pathCanMatchMultiple = PathComponentCanYieldMultipleMatches(path, indexInPath); var pathLength = path.Length; Debug.Assert(path[indexInPath] == '{'); ++indexInPath; if (indexInPath == pathLength) { throw new Exception($"Invalid path spec '{path}'; trailing '{{'"); } TControl lastMatch = null; for (var i = 0; i < usageCount; ++i) { var usage = usages[i]; // Match usage against path. var usageIsMatch = MatchPathComponent(usage, path, ref indexInPath, PathComponentType.Usage); // If it isn't a match, go to next usage. if (!usageIsMatch) { indexInPath = startIndex; continue; } var controlMatchedByUsage = device.m_UsageToControl[i]; // If there's more to go in the path, dive into the children of the control. if (indexInPath < pathLength && path[indexInPath] == '/') { lastMatch = MatchChildrenRecursive(controlMatchedByUsage, path, indexInPath + 1, ref matches, matchMultiple); // We can stop going through usages if we matched something and the // path component covering usage does not contain wildcards. if (lastMatch != null && !pathCanMatchMultiple) { break; } // We can stop going through usages if we have a match and are only // looking for a single one. if (lastMatch != null && !matchMultiple) { break; } } else { lastMatch = controlMatchedByUsage as TControl; if (lastMatch != null) { if (matchMultiple) { matches.Add(lastMatch); } else { // Only looking for single match and we have one. break; } } } } return(lastMatch); }
////TODO: refactor this to use the new PathParser /// <summary> /// Recursively match path elements in <paramref name="path"/>. /// </summary> /// <param name="control">Current control we're at.</param> /// <param name="path">Control path we are matching against.</param> /// <param name="indexInPath">Index of current component in <paramref name="path"/>.</param> /// <param name="matches"></param> /// <param name="matchMultiple"></param> /// <typeparam name="TControl"></typeparam> /// <returns></returns> private static TControl MatchControlsRecursive <TControl>(InputControl control, string path, int indexInPath, ref InputControlList <TControl> matches, bool matchMultiple) where TControl : InputControl { var pathLength = path.Length; // Try to get a match. A path spec has three components: // "<layout>{usage}name" // All are optional but at least one component must be present. // Names can be aliases, too. // We don't tap InputControl.path strings of controls so as to not create a // bunch of string objects while feeling our way down the hierarchy. var controlIsMatch = true; // Match by layout. if (path[indexInPath] == '<') { ++indexInPath; controlIsMatch = MatchPathComponent(control.layout, path, ref indexInPath, PathComponentType.Layout); // If the layout isn't a match, walk up the base layout // chain and match each base layout. if (!controlIsMatch) { var baseLayout = control.m_Layout; while (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(baseLayout, out baseLayout)) { controlIsMatch = MatchPathComponent(baseLayout, path, ref indexInPath, PathComponentType.Layout); if (controlIsMatch) { break; } } } } // Match by usage. if (indexInPath < pathLength && path[indexInPath] == '{' && controlIsMatch) { ++indexInPath; for (var i = 0; i < control.usages.Count; ++i) { controlIsMatch = MatchPathComponent(control.usages[i], path, ref indexInPath, PathComponentType.Usage); if (controlIsMatch) { break; } } } // Match by display name. if (indexInPath < pathLength - 1 && controlIsMatch && path[indexInPath] == '#' && path[indexInPath + 1] == '(') { indexInPath += 2; controlIsMatch = MatchPathComponent(control.displayName, path, ref indexInPath, PathComponentType.DisplayName); } // Match by name. if (indexInPath < pathLength && controlIsMatch && path[indexInPath] != '/') { // Normal name match. controlIsMatch = MatchPathComponent(control.name, path, ref indexInPath, PathComponentType.Name); // Alternative match by alias. if (!controlIsMatch) { for (var i = 0; i < control.aliases.Count && !controlIsMatch; ++i) { controlIsMatch = MatchPathComponent(control.aliases[i], path, ref indexInPath, PathComponentType.Name); } } } // If we have a match, return it or, if there's children, recurse into them. if (controlIsMatch) { // If we ended up on a wildcard, we've successfully matched it. if (indexInPath < pathLength && path[indexInPath] == '*') { ++indexInPath; } // If we've reached the end of the path, we have a match. if (indexInPath == pathLength) { // Check type. if (!(control is TControl match)) { return(null); } if (matchMultiple) { matches.Add(match); } return(match); } // If we've reached a separator, dive into our children. if (path[indexInPath] == '/') { ++indexInPath; // Silently accept trailing slashes. if (indexInPath == pathLength) { // Check type. if (!(control is TControl match)) { return(null); } if (matchMultiple) { matches.Add(match); } return(match); } // See if we want to match children by usage or by name. TControl lastMatch; if (path[indexInPath] == '{') { ////TODO: support scavenging a subhierarchy for usages if (!ReferenceEquals(control.device, control)) { throw new NotImplementedException( "Matching usages inside subcontrols instead of at device root"); } // Usages are kind of like entry points that can route to anywhere else // on a device's control hierarchy and then we keep going from that re-routed // point. lastMatch = MatchByUsageAtDeviceRootRecursive(control.device, path, indexInPath, ref matches, matchMultiple); } else { // Go through children and see what we can match. lastMatch = MatchChildrenRecursive(control, path, indexInPath, ref matches, matchMultiple); } return(lastMatch); } } return(null); }
public static int TryFindControls(InputControl control, string path, ref InputControlList <InputControl> matches, int indexInPath = 0) { return(TryFindControls(control, path, indexInPath, ref matches)); }
public unsafe void AddActionMap(InputActionMap map) { Debug.Assert(map != null, "Received null map"); var actionsInThisMap = map.m_Actions; var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap?.Length ?? 0; var actionCountInThisMap = actionsInThisMap?.Length ?? 0; var mapIndex = totalMapCount; // Keep track of indices for this map. var actionStartIndex = totalActionCount; var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; // Allocate an initial block of memory. We probably will have to re-allocate once // at the end to accommodate interactions and controls added from the map. var newMemory = new InputActionState.UnmanagedMemory(); newMemory.Allocate( mapCount: totalMapCount + 1, actionCount: totalActionCount + actionCountInThisMap, bindingCount: totalBindingCount + bindingCountInThisMap, // We reallocate for the following once we know the final count. interactionCount: totalInteractionCount, compositeCount: totalCompositeCount, controlCount: totalControlCount); if (memory.isAllocated) { newMemory.CopyDataFrom(memory); } ////TODO: make sure composite objects get all the bindings they need ////TODO: handle case where we have bindings resolving to the same control //// (not so clear cut what to do there; each binding may have a different interaction setup, for example) var currentCompositeBindingIndex = InputActionState.kInvalidIndex; var currentCompositeIndex = InputActionState.kInvalidIndex; var currentCompositePartCount = 0; var currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; InputAction currentCompositeAction = null; var bindingMaskOnThisMap = map.m_BindingMask; var devicesForThisMap = map.devices; // Can't use `using` as we need to use it with `ref`. var resolvedControls = new InputControlList <InputControl>(Allocator.Temp); // We gather all controls in temporary memory and then move them over into newMemory once // we're done resolving. try { for (var n = 0; n < bindingCountInThisMap; ++n) { var bindingStatesPtr = newMemory.bindingStates; ref var unresolvedBinding = ref bindingsInThisMap[n]; var bindingIndex = bindingStartIndex + n; var isComposite = unresolvedBinding.isComposite; var isPartOfComposite = !isComposite && unresolvedBinding.isPartOfComposite; var bindingState = &bindingStatesPtr[bindingIndex]; try { ////TODO: if it's a composite, check if any of the children matches our binding masks (if any) and skip composite if none do var firstControlIndex = 0; // numControls dictates whether this is a valid index or not. var firstInteractionIndex = InputActionState.kInvalidIndex; var firstProcessorIndex = InputActionState.kInvalidIndex; var actionIndexForBinding = InputActionState.kInvalidIndex; var partIndex = InputActionState.kInvalidIndex; var numControls = 0; var numInteractions = 0; var numProcessors = 0; // Make sure that if it's part of a composite, we are actually part of a composite. if (isPartOfComposite && currentCompositeBindingIndex == InputActionState.kInvalidIndex) { throw new InvalidOperationException( $"Binding '{unresolvedBinding}' is marked as being part of a composite but the preceding binding is not a composite"); } // Try to find action. // // NOTE: We ignore actions on bindings that are part of composites. We only allow // actions to be triggered from the composite itself. var actionIndexInMap = InputActionState.kInvalidIndex; var actionName = unresolvedBinding.action; InputAction action = null; if (!isPartOfComposite) { if (!string.IsNullOrEmpty(actionName)) { ////REVIEW: should we fail here if we don't manage to find the action actionIndexInMap = map.FindActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndexInMap = 0; } if (actionIndexInMap != InputActionState.kInvalidIndex) { action = actionsInThisMap[actionIndexInMap]; } } else { actionIndexInMap = currentCompositeActionIndexInMap; action = currentCompositeAction; } // If it's a composite, start a chain. if (isComposite) { currentCompositeBindingIndex = bindingIndex; currentCompositeAction = action; currentCompositeActionIndexInMap = actionIndexInMap; } // Determine if the binding is disabled. // Disabled if path is empty. var path = unresolvedBinding.effectivePath; var bindingIsDisabled = string.IsNullOrEmpty(path) // Also, if we can't find the action to trigger for the binding, we just go and disable // the binding. || action == null // Also, disabled if binding doesn't match with our binding mask (might be empty). || (!isComposite && bindingMask != null && !bindingMask.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)) // Also, disabled if binding doesn't match the binding mask on the map (might be empty). || (!isComposite && bindingMaskOnThisMap != null && !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)) // Finally, also disabled if binding doesn't match the binding mask on the action (might be empty). || (!isComposite && action?.m_BindingMask != null && !action.m_BindingMask.Value.Matches(ref unresolvedBinding, InputBinding.MatchOptions.EmptyGroupMatchesAny)); // If the binding isn't disabled, look up controls now. We do this first as we may still disable the // binding if it doesn't resolve to any controls or resolves only to controls already bound to by // other bindings. // // NOTE: We continuously add controls here to `resolvedControls`. Once we've completed our // pass over the bindings in the map, `resolvedControls` will have all the controls for // the current map. if (!bindingIsDisabled && !isComposite) { firstControlIndex = memory.controlCount + resolvedControls.Count; if (devicesForThisMap != null) { // Search in devices for only this map. var list = devicesForThisMap.Value; for (var i = 0; i < list.Count; ++i) { var device = list[i]; if (!device.added) { continue; // Skip devices that have been removed. } numControls += InputControlPath.TryFindControls(device, path, 0, ref resolvedControls); } } else { // Search globally. numControls = InputSystem.FindControls(path, ref resolvedControls); } // Check for controls that are already bound to the action through other // bindings. The first binding that grabs a specific control gets to "own" it. if (numControls > 0) { for (var i = 0; i < n; ++i) { ref var otherBindingState = ref bindingStatesPtr[bindingStartIndex + i]; // Skip if binding has no controls. if (otherBindingState.controlCount == 0) { continue; } // Skip if binding isn't from same action. if (otherBindingState.actionIndex != actionStartIndex + actionIndexInMap) { continue; } // Check for controls in the set that we just resolved that are also on the other // binding. Each such control we find, we kick out of the list. for (var k = 0; k < numControls; ++k) { var controlOnCurrentBinding = resolvedControls[firstControlIndex + k - memory.controlCount]; var controlIndexOnOtherBinding = resolvedControls.IndexOf(controlOnCurrentBinding, otherBindingState.controlStartIndex - memory.controlCount, otherBindingState.controlCount); if (controlIndexOnOtherBinding != -1) { // Control is bound to a previous binding. Remove it from the current binding. resolvedControls.RemoveAt(firstControlIndex + k - memory.controlCount); --numControls; --k; } } } } // Disable binding if it doesn't resolve to any controls. // NOTE: This also happens to bindings that got all their resolved controls removed because other bindings from the same // action already grabbed them. if (numControls == 0) { bindingIsDisabled = true; } } // If the binding isn't disabled, resolve its controls, processors, and interactions. if (!bindingIsDisabled) { // Instantiate processors. var processorString = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processorString)) { // Add processors from binding. firstProcessorIndex = ResolveProcessors(processorString); if (firstProcessorIndex != InputActionState.kInvalidIndex) { numProcessors = totalProcessorCount - firstProcessorIndex; } } if (!string.IsNullOrEmpty(action.m_Processors)) { // Add processors from action. var index = ResolveProcessors(action.m_Processors); if (index != InputActionState.kInvalidIndex) { if (firstProcessorIndex == InputActionState.kInvalidIndex) { firstProcessorIndex = index; } numProcessors += totalProcessorCount - index; } } // Instantiate interactions. var interactionString = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactionString)) { // Add interactions from binding. firstInteractionIndex = ResolveInteractions(interactionString); if (firstInteractionIndex != InputActionState.kInvalidIndex) { numInteractions = totalInteractionCount - firstInteractionIndex; } } if (!string.IsNullOrEmpty(action.m_Interactions)) { // Add interactions from action. var index = ResolveInteractions(action.m_Interactions); if (index != InputActionState.kInvalidIndex) { if (firstInteractionIndex == InputActionState.kInvalidIndex) { firstInteractionIndex = index; } numInteractions += totalInteractionCount - index; } } // If it's the start of a composite chain, create the composite. Otherwise, go and // resolve controls for the binding. if (isComposite) { // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); // Record where the controls for parts of the composite start. firstControlIndex = memory.controlCount + resolvedControls.Count; } else { // If we've reached the end of a composite chain, finish // off the current composite. if (!isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex) { currentCompositePartCount = 0; currentCompositeBindingIndex = InputActionState.kInvalidIndex; currentCompositeIndex = InputActionState.kInvalidIndex; currentCompositeAction = null; currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; } } } // If the binding is part of a composite, pass the resolved controls // on to the composite. if (isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex && numControls > 0) { // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new InvalidOperationException( $"Binding '{unresolvedBinding}' that is part of composite '{composites[currentCompositeIndex]}' is missing a name"); } // Give a part index for the partIndex = AssignCompositePartIndex(composites[currentCompositeIndex], unresolvedBinding.name, ref currentCompositePartCount); // Keep track of total number of controls bound in the composite. bindingStatesPtr[currentCompositeBindingIndex].controlCount += numControls; // Force action index on part binding to be same as that of composite. actionIndexForBinding = bindingStatesPtr[currentCompositeBindingIndex].actionIndex; } else if (actionIndexInMap != InputActionState.kInvalidIndex) { actionIndexForBinding = actionStartIndex + actionIndexInMap; } // Store resolved binding. *bindingState = new InputActionState.BindingState { controlStartIndex = firstControlIndex, // For composites, this will be adjusted as we add each part. controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isComposite = isComposite, isPartOfComposite = unresolvedBinding.isPartOfComposite, partIndex = partIndex, actionIndex = actionIndexForBinding, compositeOrCompositeBindingIndex = isComposite ? currentCompositeIndex : currentCompositeBindingIndex, mapIndex = totalMapCount, wantsInitialStateCheck = action?.wantsInitialStateCheck ?? false }; } catch (Exception exception) { Debug.LogError( $"{exception.GetType().Name} while resolving binding '{unresolvedBinding}' in action map '{map}'"); Debug.LogException(exception); // Don't swallow exceptions that indicate something is wrong in the code rather than // in the data. if (exception.IsExceptionIndicatingBugInCode()) { throw; } } }
public InputControlListDebugView(InputControlList <TControl> list) { m_Controls = list.ToArray(); }