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}