using System; using UnityEngine.InputSystem.Composites; using UnityEngine.InputSystem.LowLevel; namespace UnityEngine.InputSystem.Layouts { /// <summary> /// Mark a field or property as representing/identifying an input control in some form. /// </summary> /// <remarks> /// This attribute is used in different places for different purposes. /// /// When creating input control layouts (<see cref="InputControlLayout"/>) in C#, applying the /// attribute to fields in a state struct (see <see cref="IInputStateTypeInfo"/> or <see cref="GamepadState"/> /// for an example) or to properties in an input device (<see cref="InputDevice"/>), will cause an /// <see cref="InputControl"/> to be created from the field or property at runtime. The attribute /// can be applied multiple times to create multiple input controls (e.g. when having an int field /// that represents a bitfield where each bit is a separate button). /// /// <example> /// <code> /// public class MyDevice : InputDevice /// { /// // Adds an InputControl with name=myButton and layout=Button to the device. /// [InputControl] /// public ButtonControl myButton { get; set; } /// } /// </code> /// </example> /// /// Another use is for marking <c>string</c> type fields that represent input control paths. Applying /// the attribute to them will cause them to automatically use a custom inspector similar to the one /// found in the action editor. For this use, only the <see cref="layout"/> property is taken into /// account. /// /// <example> /// <code> /// public class MyBehavior : MonoBehaviour /// { /// // In the inspector, shows a control selector that is restricted to /// // selecting buttons. As a result, controlPath will be set to path /// // representing the control that was picked (e.g. "<Gamepad>/buttonSouth"). /// [InputControl(layout = "Button")] /// public string controlPath; /// /// protected void OnEnable() /// { /// // Find controls by path. /// var controls = InputSystem.FindControl(controlPath); /// //... /// } /// } /// </code> /// </example> /// /// Finally, the attribute is also used in composite bindings (<see cref="InputBindingComposite"/>) /// to mark fields that reference parts of the composite. An example for this is <see cref="AxisComposite.negative"/>. /// In this use, also only the <see cref="layout"/> property is taken into account while other properties /// are ignored. /// /// <example> /// <code> /// public class MyComposite : InputBindingComposite<float> /// { /// // Add a part to the composite called 'firstControl' which expects /// // AxisControls. /// [InputControl(layout = "Axis")] /// public int firstControl; /// /// // Add a part to the composite called 'secondControl' which expects /// // Vector3Controls. /// [InputControl(layout = "Vector3")] /// public int secondControl; /// /// //... /// } /// </code> /// </example> /// </remarks> /// <seealso cref="InputControlLayout"/> /// <seealso cref="InputBindingComposite"/> [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public sealed class InputControlAttribute : PropertyAttribute { /// <summary> /// Layout to use for the control. /// </summary> /// <value>Layout to use for the control.</value> /// <remarks> /// If this is not set, the system tries to infer the layout type from the value type of /// the field or property. If the value type is itself registered as a layout, that layout /// will be used (e.g. when you have a property of type <see cref="Controls.ButtonControl"/>, the layout /// will be inferred to be "Button"). Otherwise, if a layout with the same name as the type is registered, /// that layout will be used (e.g. when you have a field of type <see cref="Vector3"/>, the layout /// will be inferred to be "Vector3"). /// </remarks> /// <seealso cref="InputControlLayout"/> public string layout { get; set; } /// <summary> /// Layout variant to use for the control. /// </summary> /// <value>Layout variant to use for the control.</value> public string variants { get; set; } /// <summary> /// Name to give to the name. If null or empty, the name of the property or /// field the attribute is applied to will be used. /// </summary> /// <value>Name to give to the control.</value> /// <seealso cref="InputControl.name"/> public string name { get; set; } /// <summary> /// Storage format to use for the control. If not set, default storage format /// for the given <see cref="layout"/> is used. /// </summary> /// <value>Memory storage format to use for the control.</value> /// <seealso cref="InputStateBlock.format"/> public string format { get; set; } /// <summary> /// Usage to apply to the control. /// </summary> /// <value>Usage for the control.</value> /// <remarks> /// This property can be used in place of <see cref="usages"/> to set just a single /// usage on the control. /// </remarks> /// <seealso cref="InputControl.usages"/> /// <seealso cref="InputControlLayout.ControlItem.usages"/> /// <seealso cref="CommonUsages"/> public string usage { get; set; } /// <summary> /// Usages to apply to the control. /// </summary> /// <value>Usages for the control.</value> /// <remarks> /// This property should be used instead of <see cref="usage"/> when a control has multiple usages. /// </remarks> /// <seealso cref="InputControl.usages"/> /// <seealso cref="InputControlLayout.ControlItem.usages"/> /// <seealso cref="CommonUsages"/> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "According to MSDN, this message can be ignored for attribute parameters, as there are no better alternatives.")] public string[] usages { get; set; } /// <summary> /// Optional list of parameters to apply to the control. /// </summary> /// <value>Parameters to apply to the control.</value> /// <remarks> /// An <see cref="InputControl"/> may expose public fields which can be set as /// parameters. An example of this is <see cref="Controls.AxisControl.clamp"/>. /// /// <example> /// <code> /// public struct MyStateStruct : IInputStateTypeInfo /// { /// [InputControl(parameters = "clamp,clampMin=-0.5,clampMax=0.5")] /// public float axis; /// } /// </code> /// </example> /// </remarks> /// <seealso cref="InputControlLayout.ControlItem.parameters"/> public string parameters { get; set; } /// <summary> /// Optional list of processors to add to the control. /// </summary> /// <value>Processors to apply to the control.</value> /// <remarks> /// Each element in the list is a name of a processor (as registered with /// <see cref="InputSystem.RegisterProcessor{T}"/>) followed by an optional /// list of parameters. /// /// For example, <c>"normalize(min=0,max=256)"</c> is one element that puts /// a <c>NormalizeProcessor</c> on the control and sets its <c>min</c> field /// to 0 and its its <c>max</c> field to 256. /// /// Multiple processors can be put on a control by separating them with a comma. /// For example, <c>"normalize(max=256),scale(factor=2)"</c> puts both a <c>NormalizeProcessor</c> /// and a <c>ScaleProcessor</c> on the control. Processors are applied in the /// order they are listed. /// </remarks> /// <seealso cref="InputControlLayout.ControlItem.processors"/> /// <seealso cref="InputBinding.processors"/> public string processors { get; set; } /// <summary> /// An alternative name that can be used in place of <see cref="name"/> to find /// the control. /// </summary> /// <value>Alternative name for the control.</value> /// <remarks> /// This property can be used instead of <see cref="aliases"/> when there is only a /// single alias for the control. /// /// Aliases, like names, are case-insensitive. Any control may have arbitrary many /// aliases. /// </remarks> /// <seealso cref="InputControl.aliases"/> /// <seealso cref="InputControlLayout.ControlItem.aliases"/> public string alias { get; set; } /// <summary> /// A list of alternative names that can be used in place of <see cref="name"/> to /// find the control. /// </summary> /// <value>Alternative names for the control.</value> /// <remarks> /// This property should be used instead of <see cref="alias"/> when a control has /// multiple aliases. /// /// Aliases, like names, are case-insensitive. Any control may have arbitrary many /// aliases. /// </remarks> /// <seealso cref="InputControl.aliases"/> /// <seealso cref="InputControlLayout.ControlItem.aliases"/> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "According to MSDN, this message can be ignored for attribute parameters, as there are no better alternatives.")] public string[] aliases { get; set; } public string useStateFrom { get; set; } public uint bit { get; set; } = InputStateBlock.InvalidOffset; /// <summary> /// Offset in bytes to where the memory of the control starts. Relative to /// the offset of the parent control (which may be the device itself). /// </summary> /// <value>Byte offset of the control.</value> /// <remarks> /// If the attribute is applied to fields in an <see cref="InputControlLayout"/> and /// this property is not set, the offset of the field is used instead. /// /// <example> /// <code> /// public struct MyStateStruct : IInputStateTypeInfo /// { /// public int buttons; /// /// [InputControl] // Automatically uses the offset of 'axis'. /// public float axis; /// } /// /// [InputControlLayout(stateType = typeof(MyStateStruct))] /// public class MyDevice : InputDevice /// { /// } /// </code> /// </example> /// </remarks> /// <seealso cref="InputControlLayout.ControlItem.offset"/> public uint offset { get; set; } = InputStateBlock.InvalidOffset; /// <summary> /// Size of the memory storage for the control in bits. /// </summary> /// <value>Size of the control in bits.</value> /// <remarks> /// If the attribute is applied to fields in an <see cref="InputControlLayout"/> and /// this property is not set, the size is taken from the field. /// /// <example> /// <code> /// public struct MyStateStruct : IInputStateTypeInfo /// { /// public int buttons; /// /// [InputControl] // Automatically uses sizeof(float). /// public float axis; /// } /// /// [InputControlLayout(stateType = typeof(MyStateStruct))] /// public class MyDevice : InputDevice /// { /// } /// </code> /// </example> /// </remarks> /// <seealso cref="InputControlLayout.ControlItem.sizeInBits"/> /// <seealso cref="InputStateBlock.sizeInBits"/> public uint sizeInBits { get; set; } public int arraySize { get; set; } /// <summary> /// Display name to assign to the control. /// </summary> /// <value>Display name for the control.</value> /// <seealso cref="InputControl.displayName"/> /// <seealso cref="InputControlLayout.ControlItem.displayName"/> public string displayName { get; set; } /// <summary> /// Short display name to assign to the control. /// </summary> /// <value>Short display name for the control.</value> /// <seealso cref="InputControl.shortDisplayName"/> /// <seealso cref="InputControlLayout.ControlItem.shortDisplayName"/> public string shortDisplayName { get; set; } /// <summary> /// Whether the control is noisy. Off by default. /// </summary> /// <value>Whether control is noisy.</value> /// <seealso cref="InputControl.noisy"/> /// <seealso cref="InputControlLayout.ControlItem.isNoisy"/> public bool noisy { get; set; } /// <summary> /// Whether the control is synthetic. Off by default. /// </summary> /// <value>Whether control is synthetic.</value> /// <seealso cref="InputControl.synthetic"/> /// <seealso cref="InputControlLayout.ControlItem.isSynthetic"/> public bool synthetic { get; set; } /// <summary> /// Allows you to specify that a control should not be reset when its device is reset. /// </summary> /// <value>If true, resets of the device will leave the value of the control untouched except if a "hard" reset /// is explicitly enforced.</value> /// <seealso cref="InputSystem.ResetDevice"/> /// <seealso cref="InputControlLayout.ControlItem.dontReset"/> public bool dontReset { get; set; } /// <summary> /// Default state to write into the control's memory. /// </summary> /// <value>Default memory state for the control.</value> /// <remarks> /// This is not the default <em>value</em> but rather the default memory state, i.e. /// the raw memory value read and the processed and returned as a value. By default /// this is <c>null</c> and result in a control's memory to be initialized with all /// zeroes. /// </remarks> /// <seealso cref="InputControlLayout.ControlItem.defaultState"/> public object defaultState { get; set; } /// <summary> /// Lower limit for values of the control. /// </summary> /// <value>Lower limit for values of the control.</value> /// <remarks> /// This is null by default in which case no lower bound is applied to the TODO /// </remarks> public object minValue { get; set; } public object maxValue { get; set; } } }