A game about forced loneliness, made by TACStudios
1using System.Runtime.InteropServices; 2using UnityEngine.InputSystem.Controls; 3using UnityEngine.InputSystem.Layouts; 4using UnityEngine.InputSystem.LowLevel; 5using UnityEngine.InputSystem.Utilities; 6using UnityEngine.Scripting; 7 8////TODO: add capabilities indicating whether pressure is supported 9 10////REVIEW: is there an opportunity to collapse "press" and "pressure" into one? after all, if there's any pressure, isn't the pointer pressed? 11 12////REVIEW: should "displayIndex" be called "windowIndex"? or be part of a better thought-out multi-display API altogether? 13 14////REVIEW: add click and clickCount controls directly to Pointer? 15//// (I gave this a look but in my initial try, found it somewhat difficult to add click detection at the Pointer level due 16//// to the extra state it involves) 17 18////REVIEW: should we put lock state directly on Pointer? 19 20////REVIEW: should pointer IDs be required to be globally unique across pointing devices? 21////REVIEW: should we create new devices instead of using pointer IDs? 22 23////FIXME: pointer deltas in EditorWindows need to be Y *down* 24 25////REVIEW: kill EditorWindowSpace processor and add GetPositionInEditorWindowSpace() and GetDeltaInEditorWindowSpace()? 26//// (if we do this, every touch control has to get this, too) 27 28namespace UnityEngine.InputSystem.LowLevel 29{ 30 /// <summary> 31 /// Default state structure for pointer devices. 32 /// </summary> 33 [StructLayout(LayoutKind.Sequential)] 34 internal struct PointerState : IInputStateTypeInfo 35 { 36 public static FourCC kFormat => new FourCC('P', 'T', 'R'); 37 38 uint pointerId; 39 40 /// <summary> 41 /// Position of the pointer in screen space. 42 /// </summary> 43#if UNITY_EDITOR 44 [InputControl(layout = "Vector2", displayName = "Position", usage = "Point", processors = "AutoWindowSpace", dontReset = true)] 45#else 46 [InputControl(layout = "Vector2", displayName = "Position", usage = "Point", dontReset = true)] 47#endif 48 public Vector2 position; 49 50 ////REVIEW: if we have Secondary2DMotion on this, seems like this should be normalized 51 [InputControl(layout = "Delta", displayName = "Delta", usage = "Secondary2DMotion")] 52 public Vector2 delta; 53 54 [InputControl(layout = "Analog", displayName = "Pressure", usage = "Pressure", defaultState = 1f)] 55 public float pressure; 56 57 [InputControl(layout = "Vector2", displayName = "Radius", usage = "Radius")] 58 public Vector2 radius; 59 60 [InputControl(name = "press", displayName = "Press", layout = "Button", format = "BIT", bit = 0)] 61 public ushort buttons; 62 63 [InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")] 64 public ushort displayIndex; 65 66 public FourCC format => kFormat; 67 } 68} 69 70namespace UnityEngine.InputSystem 71{ 72 /// <summary> 73 /// Base class for pointer-style devices moving on a 2D screen. 74 /// </summary> 75 /// <remarks> 76 /// This class abstracts over general "pointing" behavior where a pointer is moved across a 2D 77 /// surface. Operating at the <c>Pointer</c> level allows treating <see>Mouse</see>, <see>Pen</see>, 78 /// and <see>Touchscreen</see> all as pointers with a set of shared behaviors. 79 /// 80 /// Note that a pointer may have "multi-point" ability as is the case with multi-touch where 81 /// multiple touches represent multiple concurrent "pointers". However, for any pointer device 82 /// with multiple pointers, only one pointer is considered "primary" and drives the pointer 83 /// controls present on the base class. 84 /// </remarks> 85 /// <seealso cref="Mouse"/> 86 /// <seealso cref="Pen"/> 87 /// <seealso cref="Touchscreen"/> 88 [InputControlLayout(stateType = typeof(PointerState), isGenericTypeOfDevice = true)] 89 public class Pointer : InputDevice, IInputStateCallbackReceiver 90 { 91 ////REVIEW: shouldn't this be done for every touch position, too? 92 /// <summary> 93 /// The current pointer coordinates in window space. 94 /// </summary> 95 /// <value>Control representing the current position of the pointer on screen.</value> 96 /// <remarks> 97 /// Within player code, the coordinates are in the coordinate space of Unity's <c>Display</c>. 98 /// 99 /// Within editor code, the coordinates are in the coordinate space of the current <c>EditorWindow</c> 100 /// This means that if you query the <see cref="Mouse"/> <see cref="position"/> in <c>EditorWindow.OnGUI</c>, for example, 101 /// the returned 2D vector will be in the coordinate space of your local GUI (same as 102 /// <c>Event.mousePosition</c>). 103 /// </remarks> 104 public Vector2Control position { get; protected set; } 105 106 /// <summary> 107 /// The current window-space motion delta of the pointer. 108 /// </summary> 109 /// <value>Control representing the motion delta of the pointer.</value> 110 /// <remarks> 111 /// Every time a pointer is moved, it generates a motion delta. This control represents 112 /// this motion. 113 /// 114 /// Note that some pointers have the ability to generate motion deltas <em>without</em> 115 /// actually changing the position of the pointer. This is the case for <see cref="Mouse"/> 116 /// which even when, for example, bumping up against the edges of the screen or when being 117 /// locked in place, can generate motion. This means that activity on <c>delta</c> is not 118 /// necessarily correlated with activity on <see cref="position"/>. 119 /// 120 /// Deltas have two special behaviors attached to them that makes them quite unique 121 /// among input controls. 122 /// 123 /// For one, deltas will automatically reset to <c>(0,0)</c> between frames. If, for example, 124 /// the current delta value is <c>(12,8)</c>, then after the next <see cref="InputSystem.Update"/>, 125 /// the delta is automatically set to <c>(0,0)</c>. More precisely, deltas will reset as part 126 /// of <see cref="InputSystem.onBeforeUpdate"/>. This happens every time regardless of whether 127 /// there are pending motion events for the pointer or not. But because it happens in 128 /// <see cref="InputSystem.onBeforeUpdate"/> (that is, <em>before</em> events are processed), 129 /// subsequent motion deltas are incorporated normally. 130 /// 131 /// Note that the resetting is visible to <see cref="InputAction"/>s. This means that when 132 /// binding to a delta control from an action that is not using <see cref="InputActionType.PassThrough"/>, 133 /// you will see the action getting cancelled at the start of every frame. With a <c>PassThrough</c> 134 /// actions, you will instead see it perform one extra time with a zero value. 135 /// 136 /// The other special behavior of deltas is accumulation. When receiving more than one 137 /// motion update in a frame, deltas will not simply switch from one value to the other 138 /// but instead accumulate them. For example, if two events are received for a pointer 139 /// in a frame and one has a motion delta of <c>(1,1)</c> and the other has a motion delta 140 /// of <c>(2,2)</c>, then once <see cref="InputSystem.Update"/> has finished processing 141 /// events, the value of the delta control will be <c>(3,3)</c> and not <c>(2,2)</c>. 142 /// 143 /// Note that just like resetting, accumulation is also visible to <see cref="InputAction"/>s. 144 /// This means that because the delta control changes value twice, the action will trigger 145 /// twice but the value when it is triggered the second time will be <c>(3,3)</c> and 146 /// not <c>(2,2)</c> even though that's the value received from the event. 147 /// </remarks> 148 /// <seealso cref="InputControlExtensions.AccumulateValueInEvent"/> 149 public DeltaControl delta { get; protected set; } 150 151 ////REVIEW: move this down to only TouchScreen? 152 /// <summary> 153 /// Window-space radius of the pointer contact with the surface. 154 /// </summary> 155 /// <value>Control representing the horizontal and vertical extents of the pointer contact.</value> 156 /// <remarks> 157 /// Usually, only touch input has radius detection. 158 /// </remarks> 159 /// <seealso cref="TouchControl.radius"/> 160 public Vector2Control radius { get; protected set; } 161 162 /// <summary> 163 /// Normalized pressure with which the pointer is currently pressed while in contact with the pointer surface. 164 /// </summary> 165 /// <value>Control representing the pressure with which the pointer is pressed down.</value> 166 /// <remarks> 167 /// This is only meaningful for pointing devices that support pressure. Mice do not, pens usually do, and touch 168 /// usually does on mobile platforms. 169 /// 170 /// Note that it is possible for the value to go above 1 even though it is considered normalized. The reason is 171 /// that calibration on the system can put the maximum pressure point below the physically supported maximum value. 172 /// </remarks> 173 public AxisControl pressure { get; protected set; } 174 175 /// <summary> 176 /// Whether the pointer is pressed down. 177 /// </summary> 178 /// <remarks> 179 /// What this means exactly depends on the nature of the pointer. For mice (<see cref="Mouse"/>), it means 180 /// that the left button is pressed. For pens (<see cref="Pen"/>), it means that the pen tip is touching 181 /// the screen/tablet surface. For touchscreens (<see cref="Touchscreen"/>), it means that there is at least 182 /// one finger touching the screen. 183 /// </remarks> 184 public ButtonControl press { get; protected set; } 185 186 /// <summary> 187 /// The index of the display the Pointer is currently on. This is useful for multiple screen setups. 188 /// This may not be supported on all platforms. When unsupported, this will always produce the index of the primary display i.e. zero. 189 /// <see href="https://docs.unity3d.com/ScriptReference/Display.html"/> 190 /// </summary> 191 public IntegerControl displayIndex { get; protected set; } 192 193 /// <summary> 194 /// The pointer that was added or used last by the user or <c>null</c> if there is no pointer 195 /// device connected to the system. 196 /// </summary> 197 /// <value>Currently active <c>Pointer</c> or <c>null</c>.</value> 198 public static Pointer current { get; internal set; } 199 200 /// <inheritdoc /> 201 public override void MakeCurrent() 202 { 203 base.MakeCurrent(); 204 current = this; 205 } 206 207 /// <inheritdoc /> 208 protected override void OnRemoved() 209 { 210 base.OnRemoved(); 211 if (current == this) 212 current = null; 213 } 214 215 /// <inheritdoc /> 216 protected override void FinishSetup() 217 { 218 position = GetChildControl<Vector2Control>("position"); 219 delta = GetChildControl<DeltaControl>("delta"); 220 radius = GetChildControl<Vector2Control>("radius"); 221 pressure = GetChildControl<AxisControl>("pressure"); 222 press = GetChildControl<ButtonControl>("press"); 223 displayIndex = GetChildControl<IntegerControl>("displayIndex"); 224 225 base.FinishSetup(); 226 } 227 228 /// <summary> 229 /// Called whenever the input system advances by one frame. 230 /// </summary> 231 /// <seealso cref="InputSystem.Update"/> 232 protected void OnNextUpdate() 233 { 234 InputState.Change(delta, Vector2.zero); 235 } 236 237 /// <summary> 238 /// Called when the pointer receives a state event. 239 /// </summary> 240 /// <param name="eventPtr">The input event.</param> 241 protected unsafe void OnStateEvent(InputEventPtr eventPtr) 242 { 243 ////FIXME: This stuff makes pointer events too expensive; find a better way. 244 delta.AccumulateValueInEvent(currentStatePtr, eventPtr); 245 InputState.Change(this, eventPtr); 246 } 247 248 void IInputStateCallbackReceiver.OnNextUpdate() 249 { 250 OnNextUpdate(); 251 } 252 253 void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr) 254 { 255 OnStateEvent(eventPtr); 256 } 257 258 bool IInputStateCallbackReceiver.GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref uint offset) 259 { 260 return false; 261 } 262 } 263}