Pointer.cs 12 KB

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