/// <summary> /// Add a control to the list. /// </summary> /// <param name="item">Control to add. Allowed to be <c>null</c>.</param> /// <remarks> /// If necessary, <see cref="Capacity"/> will be increased. /// /// It is allowed to add nulls to the list. This can be useful, for example, when /// specific indices in the list correlate with specific matches and a given match /// needs to be marked as "matches nothing". /// </remarks> /// <seealso cref="Remove"/> public void Add(TControl item) { var index = ToIndex(item); var allocator = m_Allocator != Allocator.Invalid ? m_Allocator : Allocator.Persistent; ArrayHelpers.AppendWithCapacity(ref m_Indices, ref m_Count, index, allocator: allocator); }
internal void AppendTo(ref TControl[] array, ref int count) { for (var i = 0; i < m_Count; ++i) { ArrayHelpers.AppendWithCapacity(ref array, ref count, this[i]); } }
private int ResolveProcessors(string processorString) { var firstProcessorIndex = totalProcessorCount; if (!InputControlLayout.ParseNameAndParameterList(processorString, ref m_Parameters)) { return(firstProcessorIndex); } for (var i = 0; i < m_Parameters.Count; ++i) { // Look up processor. var type = InputControlProcessor.s_Processors.LookupTypeRegistration(m_Parameters[i].name); if (type == null) { throw new Exception(string.Format( "No processor with name '{0}' (mentioned in '{1}') has been registered", m_Parameters[i].name, processorString)); } // Instantiate it. var processor = Activator.CreateInstance(type); // Pass parameters to it. InputDeviceBuilder.SetParameters(processor, m_Parameters[i].parameters); // Add to list. ArrayHelpers.AppendWithCapacity(ref processors, ref totalProcessorCount, processor); } return(firstProcessorIndex); }
public RebindingOperation WithControlsExcluding(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } for (var i = 0; i < m_ExcludePathCount; ++i) { if (string.Compare(m_ExcludePaths[i], path, StringComparison.InvariantCultureIgnoreCase) == 0) { return(this); } } ArrayHelpers.AppendWithCapacity(ref m_ExcludePaths, ref m_ExcludePathCount, path); return(this); }
private int ResolveInteractions(string interactionString) { ////REVIEW: We're piggybacking off the processor parsing here as the two syntaxes are identical. Might consider //// moving the logic to a shared place. //// Alternatively, may split the paths. May help in getting rid of unnecessary allocations. var firstInteractionIndex = totalInteractionCount; if (!InputControlLayout.ParseNameAndParameterList(interactionString, ref m_Parameters)) { return(firstInteractionIndex); } for (var i = 0; i < m_Parameters.Count; ++i) { // Look up interaction. var type = InputInteraction.s_Interactions.LookupTypeRegistration(m_Parameters[i].name); if (type == null) { throw new Exception(string.Format( "No interaction with name '{0}' (mentioned in '{1}') has been registered", m_Parameters[i].name, interactionString)); } // Instantiate it. var interaction = Activator.CreateInstance(type) as IInputInteraction; if (interaction == null) { throw new Exception(string.Format("Interaction '{0}' is not an IInputInteraction", m_Parameters[i].name)); } // Pass parameters to it. InputDeviceBuilder.SetParameters(interaction, m_Parameters[i].parameters); // Add to list. var interactionStateCount = totalInteractionCount; ArrayHelpers.AppendWithCapacity(ref interactionStates, ref interactionStateCount, new InputActionMapState.InteractionState { phase = InputActionPhase.Waiting }); ArrayHelpers.AppendWithCapacity(ref interactions, ref totalInteractionCount, interaction); Debug.Assert(interactionStateCount == totalInteractionCount); } return(firstInteractionIndex); }
private static bool AssignDeviceInternal(int userIndex, InputDevice device) { var deviceCount = s_AllUserData[userIndex].deviceCount; var deviceStartIndex = s_AllUserData[userIndex].deviceStartIndex; // Ignore if already assigned to user. for (var i = 0; i < deviceCount; ++i) { if (s_AllDevices[deviceStartIndex + i] == device) { return(false); } } // Move our devices to end of array. if (deviceCount > 0) { ArrayHelpers.MoveSlice(s_AllDevices, deviceStartIndex, s_AllDeviceCount - deviceCount, deviceCount); // Adjust users that have been impacted by the change. for (var i = 0; i < s_AllUserCount; ++i) { if (i == userIndex) { continue; } if (s_AllUserData[i].deviceStartIndex <= deviceStartIndex) { continue; } s_AllUserData[i].deviceStartIndex -= deviceCount; } } // Append to array. deviceStartIndex = s_AllDeviceCount - deviceCount; s_AllUserData[userIndex].deviceStartIndex = deviceStartIndex; ArrayHelpers.AppendWithCapacity(ref s_AllDevices, ref s_AllDeviceCount, device); ++s_AllUserData[userIndex].deviceCount; return(true); }
protected void AddPointer(Pointer pointer) { if (pointer == null) { throw new ArgumentNullException(nameof(pointer)); } // Ignore if already added. if (ArrayHelpers.ContainsReference(m_Sources, m_NumSources, pointer)) { return; } var numPositions = m_NumSources; ArrayHelpers.AppendWithCapacity(ref m_CurrentPositions, ref numPositions, Vector2.zero); var index = ArrayHelpers.AppendWithCapacity(ref m_Sources, ref m_NumSources, pointer); InstallStateChangeMonitors(index); }
protected void AddPointer(Pointer pointer) { if (pointer == null) { throw new ArgumentNullException(nameof(pointer)); } // Ignore if already added. if (m_Pointers.ContainsReference(m_NumPointers, pointer)) { return; } // Add to list. var numPointers = m_NumPointers; ArrayHelpers.AppendWithCapacity(ref m_Pointers, ref m_NumPointers, pointer); ArrayHelpers.AppendWithCapacity(ref m_CurrentPositions, ref numPointers, default); InputSystem.DisableDevice(pointer, keepSendingEvents: true); }
/// <summary> /// Add a new user. /// </summary> /// <param name="userName">Optional <see cref="userName"/> to assign to the newly created user.</param> /// <returns>A newly created user.</returns> /// <remarks> /// Adding a user sends a notification with <see cref="InputUserChange.Added"/> through <see cref="onChange"/>. /// /// The user will start out with no devices and no actions assigned. /// /// The user is added to <see cref="all"/>. /// </remarks> public static void Add(IInputUser user, string userName = null) { if (user == null) { throw new ArgumentNullException("user"); } var userData = new UserData { id = ++s_LastUserId, userName = userName, }; // Add to list. var userCount = s_AllUserCount; ArrayHelpers.AppendWithCapacity(ref s_AllUsers, ref userCount, user); ArrayHelpers.AppendWithCapacity(ref s_AllUserData, ref s_AllUserCount, userData); // Send notification. Notify(user, InputUserChange.Added); }
/// <summary> /// Push a single action onto the stack. /// </summary> /// <param name="action"></param> public void Push(InputAction action) { if (action == null) { throw new ArgumentNullException("action"); } if (action.enabled) { throw new ArgumentException( string.Format("Cannot add action '{0}' to stack as it is currently enabled", action), "action"); } // Add. EnsureCapacity(1); ArrayHelpers.AppendWithCapacity(ref m_Actions, ref m_ActionCount, action); // Enable action, if necessary. if (enabled) { action.Enable(); } }
private void InitializeIfNeeded(SerializedProperty property) { if (m_Initialized) { return; } // Look for action events. foreach (var child in property.GetChildren()) { // Skip properties that aren't action events. var type = child.GetFieldType(); if (type == null || !typeof(UnityEvent <InputAction.CallbackContext>).IsAssignableFrom(type)) { continue; } // Add to list. ArrayHelpers.AppendWithCapacity(ref m_ActionEvents, ref m_ActionEventCount, child.Copy()); } m_Initialized = true; }
public void AddCandidate(InputControl control, float score) { if (control == null) { throw new ArgumentNullException(nameof(control)); } // If it's already added, update score. var index = m_Candidates.IndexOf(control); if (index != -1) { m_Scores[index] = score; } else { // Otherwise, add it. var candidateCount = m_Candidates.Count; m_Candidates.Add(control); ArrayHelpers.AppendWithCapacity(ref m_Scores, ref candidateCount, score); } SortCandidatesByScore(); }
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, 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; } // Look up controls. // // 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. 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); } } } // 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; } } } // 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; }
/// <summary> /// Called when the gamepad is added to the system. /// </summary> protected override void OnAdded() { ArrayHelpers.AppendWithCapacity(ref s_Gamepads, ref s_GamepadCount, this); }
private static void UpdateControllers() { if (api == null) { return; } // Update controller state. api.RunFrame(); // Check if we have any new controllers have appeared. if (s_ConnectedControllers == null) { s_ConnectedControllers = new SteamHandle <SteamController> [STEAM_CONTROLLER_MAX_COUNT]; } var numConnectedControllers = api.GetConnectedControllers(s_ConnectedControllers); for (var i = 0; i < numConnectedControllers; ++i) { var handle = s_ConnectedControllers[i]; // See if we already have a device for this one. if (s_InputDevices != null) { SteamController existingDevice = null; for (var n = 0; n < s_InputDeviceCount; ++n) { if (s_InputDevices[n].handle == handle) { existingDevice = s_InputDevices[n]; break; } } // Yes, we do. if (existingDevice != null) { continue; } } // No, so create a new device. var controllerLayouts = InputSystem.ListLayoutsBasedOn("SteamController"); foreach (var layout in controllerLayouts) { // Rather than directly creating a device with the layout, let it go through // the usual matching process. var device = InputSystem.AddDevice(new InputDeviceDescription { interfaceName = SteamController.kSteamInterface, product = layout }); // Make sure it's a SteamController we got. var steamDevice = device as SteamController; if (steamDevice == null) { Debug.LogError(string.Format( "InputDevice created from layout '{0}' based on the 'SteamController' layout is not a SteamController", device.layout)); continue; } // Resolve the controller's actions. steamDevice.InvokeResolveActions(); // Assign it the Steam controller handle. steamDevice.handle = handle; ArrayHelpers.AppendWithCapacity(ref s_InputDevices, ref s_InputDeviceCount, steamDevice); } } // Update all controllers we have. for (var i = 0; i < s_InputDeviceCount; ++i) { var device = s_InputDevices[i]; var handle = device.handle; // Check if the device still exists. var stillExists = false; for (var n = 0; n < numConnectedControllers; ++n) { if (s_ConnectedControllers[n] == handle) { stillExists = true; break; } } // If not, remove it. if (!stillExists) { ArrayHelpers.EraseAtByMovingTail(s_InputDevices, ref s_InputDeviceCount, i); ////REVIEW: should this rather queue a device removal event? InputSystem.RemoveDevice(device); --i; continue; } ////TODO: support polling Steam controllers on an async polling thread adhering to InputSystem.pollingFrequency // Otherwise, update it. device.InvokeUpdate(); } }
/// <summary> /// Resolve and add all bindings and actions from the given map. /// </summary> /// <param name="map"></param> /// <exception cref="Exception"></exception> public void AddActionMap(InputActionMap map) { Debug.Assert(map != null); // Keep track of indices for this map. var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; var actionStartIndex = totalActionCount; // Allocate binding states. var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap != null ? bindingsInThisMap.Length : 0; totalBindingCount += bindingCountInThisMap; ArrayHelpers.GrowBy(ref bindingStates, totalBindingCount); ////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 = InputActionMapState.kInvalidIndex; var currentCompositeIndex = InputActionMapState.kInvalidIndex; var currentCompositePartIndex = 0; var bindingMaskOnThisMap = map.m_BindingMask; var actionsInThisMap = map.m_Actions; var devicesForThisMap = map.devices; var actionCountInThisMap = actionsInThisMap != null ? actionsInThisMap.Length : 0; var resolvedControls = new InputControlList <InputControl>(Allocator.Temp); try { for (var n = 0; n < bindingCountInThisMap; ++n) { var unresolvedBinding = bindingsInThisMap[n]; var bindingIndex = bindingStartIndex + n; // Set binding state to defaults. bindingStates[bindingIndex].mapIndex = totalMapCount; bindingStates[bindingIndex].compositeOrCompositeBindingIndex = InputActionMapState.kInvalidIndex; bindingStates[bindingIndex].actionIndex = InputActionMapState.kInvalidIndex; // Skip binding if it is disabled (path is empty string). var path = unresolvedBinding.effectivePath; if (unresolvedBinding.path == "") { continue; } // Skip binding if it doesn't match with our binding mask (might be empty). if (bindingMask != null && !bindingMask.Value.Matches(ref unresolvedBinding)) { continue; } // Skip binding if it doesn't match the binding mask on the map (might be empty). if (bindingMaskOnThisMap != null && !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding)) { continue; } // Try to find action. // NOTE: Technically, we allow individual bindings of composites to trigger actions independent // of the action triggered by the composite. var actionIndexInMap = InputActionMapState.kInvalidIndex; var actionName = unresolvedBinding.action; if (!string.IsNullOrEmpty(actionName)) { actionIndexInMap = map.TryGetActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndexInMap = 0; } // Skip binding if it doesn't match the binding mask on the action (might be empty). if (actionIndexInMap != InputActionMapState.kInvalidIndex) { var action = actionsInThisMap[actionIndexInMap]; if (action.m_BindingMask != null && !action.m_BindingMask.Value.Matches(ref unresolvedBinding)) { continue; } } // Instantiate processors. var firstProcessorIndex = 0; var numProcessors = 0; var processors = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processors)) { firstProcessorIndex = ResolveProcessors(processors); if (processors != null) { numProcessors = totalProcessorCount - firstProcessorIndex; } } // Instantiate interactions. var firstInteractionIndex = 0; var numInteractions = 0; var interactions = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactions)) { firstInteractionIndex = ResolveInteractions(interactions); if (interactionStates != null) { numInteractions = totalInteractionCount - firstInteractionIndex; } } ////TODO: allow specifying parameters for composite on its path (same way as parameters work for interactions) //// (Example: "Axis(min=-1,max=1)" creates an axis that goes from -1..1 instead of the default 0..1) // If it's the start of a composite chain, create the composite. if (unresolvedBinding.isComposite) { ////REVIEW: what to do about interactions on composites? // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); currentCompositeBindingIndex = bindingIndex; bindingStates[bindingIndex] = new InputActionMapState.BindingState { actionIndex = actionStartIndex + actionIndexInMap, compositeOrCompositeBindingIndex = currentCompositeIndex, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, interactionCount = numInteractions, interactionStartIndex = firstInteractionIndex, mapIndex = totalMapCount, isComposite = true, }; // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. continue; } // If we've reached the end of a composite chain, finish // off the current composite. if (!unresolvedBinding.isPartOfComposite && currentCompositeBindingIndex != InputActionMapState.kInvalidIndex) { currentCompositePartIndex = 0; currentCompositeBindingIndex = InputActionMapState.kInvalidIndex; currentCompositeIndex = InputActionMapState.kInvalidIndex; } // Look up controls. var firstControlIndex = totalControlCount; int numControls = 0; 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); } if (numControls > 0) { resolvedControls.AppendTo(ref controls, ref totalControlCount); resolvedControls.Clear(); } // If the binding is part of a composite, pass the resolved controls // on to the composite. if (unresolvedBinding.isPartOfComposite && currentCompositeBindingIndex != InputActionMapState.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 Exception(string.Format( "Binding with path '{0}' that is part of composite '{1}' is missing a name", path, composites[currentCompositeIndex])); } // Install the controls on the binding. BindControlInComposite(composites[currentCompositeIndex], unresolvedBinding.name, ref currentCompositePartIndex); } // Add entry for resolved binding. bindingStates[bindingIndex] = new InputActionMapState.BindingState { controlStartIndex = firstControlIndex, controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isPartOfComposite = unresolvedBinding.isPartOfComposite, partIndex = currentCompositePartIndex, actionIndex = actionIndexInMap, compositeOrCompositeBindingIndex = currentCompositeBindingIndex, mapIndex = totalMapCount, }; } } finally { resolvedControls.Dispose(); } // Set up control to binding index mapping. var controlCountInThisMap = totalControlCount - controlStartIndex; ArrayHelpers.GrowBy(ref controlIndexToBindingIndex, controlCountInThisMap); for (var i = 0; i < bindingCountInThisMap; ++i) { var numControls = bindingStates[bindingStartIndex + i].controlCount; var startIndex = bindingStates[bindingStartIndex + i].controlStartIndex; for (var n = 0; n < numControls; ++n) { controlIndexToBindingIndex[startIndex + n] = i; } } // Store indices for map. var numMaps = totalMapCount; var mapIndex = ArrayHelpers.AppendWithCapacity(ref maps, ref numMaps, map); ArrayHelpers.AppendWithCapacity(ref mapIndices, ref totalMapCount, new InputActionMapState.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; // Allocate action states. if (actionCountInThisMap > 0) { // Assign action indices. var actions = map.m_Actions; for (var i = 0; i < actionCountInThisMap; ++i) { actions[i].m_ActionIndex = totalActionCount + i; } ArrayHelpers.GrowBy(ref actionStates, actionCountInThisMap); totalActionCount += actionCountInThisMap; for (var i = 0; i < actionCountInThisMap; ++i) { actionStates[i].mapIndex = mapIndex; } } }
/// <summary> /// Resolve and add all bindings and actions from the given map. /// </summary> /// <param name="map"></param> /// <exception cref="Exception"></exception> public void AddActionMap(InputActionMap map) { Debug.Assert(map != null); Debug.Assert(map.m_MapIndex == InputActionMapState.kInvalidIndex); // Keep track of indices for this map. var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; var actionStartIndex = totalActionCount; // Allocate binding states. var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap != null ? bindingsInThisMap.Length : 0; totalBindingCount += bindingCountInThisMap; ArrayHelpers.GrowBy(ref bindingStates, totalBindingCount); ////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 currentCompositeIndex = InputActionMapState.kInvalidIndex; var actionsInThisMap = map.m_Actions; var actionCountInThisMap = actionsInThisMap != null ? actionsInThisMap.Length : 0; for (var n = 0; n < bindingCountInThisMap; ++n) { var unresolvedBinding = bindingsInThisMap[n]; // Skip binding if it is disabled (path is empty string). var path = unresolvedBinding.effectivePath; if (unresolvedBinding.path == "") { continue; } // Try to find action. var actionIndex = InputActionMapState.kInvalidIndex; var actionName = unresolvedBinding.action; if (!string.IsNullOrEmpty(actionName)) { actionIndex = map.TryGetActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndex = 0; } // Instantiate processors. var firstProcessorIndex = 0; var numProcessors = 0; var processors = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processors)) { firstProcessorIndex = ResolveProcessors(processors); if (processors != null) { numProcessors = totalProcessorCount - firstProcessorIndex; } } ////TODO: allow specifying parameters for composite on its path (same way as parameters work for interactions) // If it's the start of a composite chain, create the composite. if (unresolvedBinding.isComposite) { ////REVIEW: what to do about interactions on composites? // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); bindingStates[bindingStartIndex + n] = new InputActionMapState.BindingState { actionIndex = actionIndex, compositeIndex = currentCompositeIndex, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, mapIndex = totalMapCount, }; // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. continue; } // If we've reached the end of a composite chain, finish // off the current composite. if (!unresolvedBinding.isPartOfComposite && currentCompositeIndex != InputActionMapState.kInvalidIndex) { currentCompositeIndex = InputActionMapState.kInvalidIndex; } // Look up controls. var firstControlIndex = totalControlCount; if (controls == null) { controls = new InputControl[10]; } var resolvedControls = new ArrayOrListWrapper <InputControl>(controls, totalControlCount); var numControls = InputSystem.GetControls(path, ref resolvedControls); controls = resolvedControls.array; totalControlCount = resolvedControls.count; // Instantiate interactions. var firstInteractionIndex = 0; var numInteractions = 0; var interactions = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactions)) { firstInteractionIndex = ResolveInteractions(interactions); if (interactionStates != null) { numInteractions = totalInteractionCount - firstInteractionIndex; } } // Add entry for resolved binding. bindingStates[bindingStartIndex + n] = new InputActionMapState.BindingState { controlStartIndex = firstControlIndex, controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isPartOfComposite = unresolvedBinding.isPartOfComposite, actionIndex = actionIndex, compositeIndex = currentCompositeIndex, mapIndex = totalMapCount, }; // If the binding is part of a composite, pass the resolve controls // on to the composite. if (unresolvedBinding.isPartOfComposite && currentCompositeIndex != InputActionMapState.kInvalidIndex && numControls != 0) { ////REVIEW: what should we do when a single binding in a composite resolves to multiple controls? //// if the composite has more than one bindable control, it's not readily apparent how we would group them if (numControls > 1) { throw new NotImplementedException("Handling case where single binding in composite resolves to multiple controls"); } // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new Exception(string.Format( "Binding that is part of composite '{0}' is missing a name", composites[currentCompositeIndex])); } // Install the control on the binding. BindControlInComposite(composites[currentCompositeIndex], unresolvedBinding.name, controls[firstControlIndex]); } } // Set up control to binding index mapping. var controlCountInThisMap = totalControlCount - controlStartIndex; ArrayHelpers.GrowBy(ref controlIndexToBindingIndex, controlCountInThisMap); for (var i = 0; i < bindingCountInThisMap; ++i) { var numControls = bindingStates[bindingStartIndex + i].controlCount; var startIndex = bindingStates[bindingStartIndex + i].controlStartIndex; for (var n = 0; n < numControls; ++n) { controlIndexToBindingIndex[startIndex + n] = i; } } // Store indices for map. var numMaps = totalMapCount; var mapIndex = ArrayHelpers.AppendWithCapacity(ref maps, ref numMaps, map); ArrayHelpers.AppendWithCapacity(ref mapIndices, ref totalMapCount, new InputActionMapState.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_MapIndex = mapIndex; // Allocate action states. if (actionCountInThisMap > 0) { // Assign action indices. var actions = map.m_Actions; for (var i = 0; i < actionCountInThisMap; ++i) { actions[i].m_ActionIndex = totalActionCount + i; } ArrayHelpers.GrowBy(ref actionStates, actionCountInThisMap); totalActionCount += actionCountInThisMap; for (var i = 0; i < actionCountInThisMap; ++i) { actionStates[i].mapIndex = mapIndex; } } }
private unsafe void OnEvent(InputEventPtr eventPtr, InputDevice device) { if (device == null) { return; } // Ignore if not a state event. if (!eventPtr.IsA <StateEvent>() && !eventPtr.IsA <DeltaStateEvent>()) { return; } // Go through controls and see if there's anything interesting in the event. var controls = device.allControls; var controlCount = controls.Count; var haveChangedCandidates = false; for (var i = 0; i < controlCount; ++i) { var control = controls[i]; // Skip controls that have no state in the event. var statePtr = control.GetStatePtrFromStateEvent(eventPtr); if (statePtr == null) { continue; } // If the control that cancels has been actuated, abort the operation now. if (!string.IsNullOrEmpty(m_CancelBinding) && InputControlPath.Matches(m_CancelBinding, control) && !control.CheckStateIsAtDefault(statePtr) && control.HasValueChangeInState(statePtr)) { OnCancel(); break; } // Skip noisy controls. if (control.noisy && (m_Flags & Flags.DontIgnoreNoisyControls) == 0) { continue; } // If controls have to match a certain path, check if this one does. if (m_IncludePathCount > 0 && !HavePathMatch(control, m_IncludePaths, m_IncludePathCount)) { continue; } // If controls must not match certain path, make sure the control doesn't. if (m_ExcludePathCount > 0 && HavePathMatch(control, m_ExcludePaths, m_ExcludePathCount)) { continue; } // If we're expecting controls of a certain type, skip if control isn't of // the right type. if (m_ControlType != null && !m_ControlType.IsInstanceOfType(control)) { continue; } // If we're expecting controls to be based on a specific layout, skip if control // isn't based on that layout. if (!m_ExpectedLayout.IsEmpty() && m_ExpectedLayout != control.m_Layout && !InputControlLayout.s_Layouts.IsBasedOn(m_ExpectedLayout, control.m_Layout)) { continue; } // Skip controls that are in their default state. // NOTE: This is the cheapest check with respect to looking at actual state. So // do this first before looking further at the state. if (control.CheckStateIsAtDefault(statePtr)) { continue; } // Skip controls that have no effective value change. // NOTE: This will run the full processor stack and is more involved. if (!control.HasValueChangeInState(statePtr)) { continue; } // If we have a magnitude threshold, see if control passes it. var magnitude = -1f; if (m_MagnitudeThreshold >= 0f) { magnitude = control.EvaluateMagnitude(statePtr); if (magnitude >= 0 && magnitude < m_MagnitudeThreshold) { continue; // No, so skip. } } // Compute score. float score; if (m_OnComputeScore != null) { score = m_OnComputeScore(control, eventPtr); } else { score = magnitude; // We don't want synthetic controls to not be bindable at all but they should // generally cede priority to controls that aren't synthetic. So we bump all // scores of controls that aren't synthetic. if (!control.synthetic) { score += 1f; } } // Control is a candidate. // See if we already singled the control out as a potential candidate. var candidateIndex = m_Candidates.IndexOf(control); if (candidateIndex != -1) { // Yes, we did. So just check whether it became a better candidate than before. if (m_Scores[candidateIndex] < score) { haveChangedCandidates = true; m_Scores[candidateIndex] = score; if (m_WaitSecondsAfterMatch > 0) { m_LastMatchTime = InputRuntime.s_Instance.currentTime; } } } else { // No, so add it. var candidateCount = m_Candidates.Count; m_Candidates.Add(control); ArrayHelpers.AppendWithCapacity(ref m_Scores, ref candidateCount, score); haveChangedCandidates = true; if (m_WaitSecondsAfterMatch > 0) { m_LastMatchTime = InputRuntime.s_Instance.currentTime; } } } if (haveChangedCandidates && !canceled) { // If we have a callback that wants to control matching, leave it to the callback to decide // whether the rebind is complete or not. Otherwise, just complete. if (m_OnPotentialMatch != null) { SortCandidatesByScore(); m_OnPotentialMatch(this); } else if (m_WaitSecondsAfterMatch <= 0) { OnComplete(); } else { SortCandidatesByScore(); } } }
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; } } }
/// <summary> /// Called when the joystick is added to the system. /// </summary> protected override void OnAdded() { ArrayHelpers.AppendWithCapacity(ref s_Joysticks, ref s_JoystickCount, this); }
private static void UpdateControllers() { if (api == null) { return; } // Check if we have any controllers have appeared or disappeared. if (s_ConnectedControllers == null) { s_ConnectedControllers = new ulong[STEAM_CONTROLLER_MAX_COUNT]; } var numConnectedControllers = api.GetConnectedControllers(s_ConnectedControllers); for (var i = 0; i < numConnectedControllers; ++i) { var handle = s_ConnectedControllers[i]; // See if we already have a device for this one. if (s_InputDevices != null) { SteamController existingDevice = null; for (var n = 0; n < s_InputDeviceCount; ++n) { if (s_InputDevices[n].steamControllerHandle == handle) { existingDevice = s_InputDevices[n]; break; } } // Yes, we do. if (existingDevice != null) { continue; } } // No, so create a new device. var controllerLayouts = InputSystem.ListLayoutsBasedOn("SteamController"); foreach (var layout in controllerLayouts) { // Rather than directly creating a device with the layout, let it go through // the usual matching process. var device = InputSystem.AddDevice(new InputDeviceDescription { interfaceName = SteamController.kSteamInterface, product = layout }); // Make sure it's a SteamController we got. var steamDevice = device as SteamController; if (steamDevice == null) { Debug.LogError(string.Format( "InputDevice created from layout '{0}' based on the 'SteamController' layout is not a SteamController", device.layout)); continue; } // Assign it the Steam controller handle. steamDevice.steamControllerHandle = handle; ArrayHelpers.AppendWithCapacity(ref s_InputDevices, ref s_InputDeviceCount, steamDevice); } } if (s_InputDevices != null) { // Remove anything no longer there. for (var i = 0; i < s_InputDeviceCount; ++i) { var device = s_InputDevices[i]; var handle = device.steamControllerHandle; var stillExists = false; for (var n = 0; n < numConnectedControllers; ++n) { if (s_ConnectedControllers[n] == handle) { stillExists = true; break; } } if (!stillExists) { ArrayHelpers.EraseAtByMovingTail(s_InputDevices, ref s_InputDeviceCount, i); InputSystem.RemoveDevice(device); } } } }
/// <summary> /// Called when an action is triggered in one of the action maps added to the manager. Records /// relevant trigger information to surface in event list later on. /// </summary> /// <param name="context"></param> unsafe void IInputActionCallbackReceiver.OnActionTriggered(ref InputAction.CallbackContext context) { var controlIndex = context.m_ControlIndex; var control = context.control; var time = context.m_Time; // See if already have a trigger record for the control. var triggerIndex = -1; for (var i = 0; i < m_TriggerDataCount; ++i) { var otherControlIndex = m_TriggerDataBuffer[i].controlIndex; if (otherControlIndex != controlIndex) { ////REVIEW: shouldn't we make sure somehow that control indices are unique? // NOTE: We're not just comparing control indices here but rather actual control references // as the same control may appear multiple times in the list. var otherControl = m_State.controls[otherControlIndex]; if (!ReferenceEquals(otherControl, control)) { continue; } } if (!Mathf.Approximately((float)m_TriggerDataBuffer[i].time, (float)time)) { continue; } triggerIndex = i; break; } // If not, create one. if (triggerIndex == -1) { // Save the current state of the control. Copy full bytes only (means we may be grabbing some // state from other controls here but that doesn't matter). var stateSizeInBytes = control.m_StateBlock.alignedSizeInBytes; var stateOffset = control.m_StateBlock.byteOffset; var statePtr = (byte *)control.currentStatePtr.ToPointer() + stateOffset; var offsetOfSavedState = ArrayHelpers.GrowWithCapacity(ref m_StateDataBuffer, ref m_StateDataSize, (int)stateSizeInBytes, 1024); UnsafeUtility.MemCpy((byte *)m_StateDataBuffer.GetUnsafePtr() + offsetOfSavedState, statePtr, stateSizeInBytes); // Append trigger data. var triggerData = new TriggerData { controlIndex = controlIndex, time = time, stateOffset = (uint)offsetOfSavedState, stateSizeInBytes = stateSizeInBytes, }; triggerIndex = ArrayHelpers.AppendWithCapacity(ref m_TriggerDataBuffer, ref m_TriggerDataCount, triggerData); } // Add action record. var data = m_TriggerDataBuffer[triggerIndex]; ++data.actionEventCount; var actionIndex = ArrayHelpers.AppendWithCapacity(ref m_ActionDataBuffer, ref m_ActionDataCount, new ActionData { triggerIndex = triggerIndex, bindingIndex = context.m_BindingIndex, interactionIndex = context.m_InteractionIndex, phase = context.phase, }); if (data.actionEventCount == 1) { data.actionEventIndex = actionIndex; } m_TriggerDataBuffer[triggerIndex] = data; }