Pen.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. using System.ComponentModel;
  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. ////TODO: expose whether pen actually has eraser and which barrel buttons it has
  8. ////TODO: hook up pointerId in backend to allow identifying different pens
  9. ////REVIEW: have surface distance property to detect how far pen is when hovering?
  10. ////REVIEW: does it make sense to have orientation support for pen, too?
  11. namespace UnityEngine.InputSystem.LowLevel
  12. {
  13. /// <summary>
  14. /// Default state layout for pen devices.
  15. /// </summary>
  16. // IMPORTANT: Must match with PenInputState in native.
  17. [StructLayout(LayoutKind.Explicit, Size = 36)]
  18. public struct PenState : IInputStateTypeInfo
  19. {
  20. /// <summary>
  21. /// Format code for PenState.
  22. /// </summary>
  23. /// <value>Returns "PEN ".</value>
  24. /// <seealso cref="InputStateBlock.format"/>
  25. public static FourCC Format => new FourCC('P', 'E', 'N');
  26. /// <summary>
  27. /// Current screen-space position of the pen.
  28. /// </summary>
  29. /// <value>Screen-space position.</value>
  30. /// <seealso cref="Pointer.position"/>
  31. [InputControl(usage = "Point")]
  32. [FieldOffset(0)]
  33. public Vector2 position;
  34. /// <summary>
  35. /// Screen-space motion delta.
  36. /// </summary>
  37. /// <value>Screen-space motion delta.</value>
  38. /// <seealso cref="Pointer.delta"/>
  39. [InputControl(usage = "Secondary2DMotion")]
  40. [FieldOffset(8)]
  41. public Vector2 delta;
  42. /// <summary>
  43. /// The way the pen is leaned over perpendicular to the tablet surface. X goes [-1..1] left to right
  44. /// (with -1 and 1 being completely flush to the surface) and Y goes [-1..1] bottom to top.
  45. /// </summary>
  46. /// <value>Amount pen is leaning over.</value>
  47. /// <seealso cref="Pen.tilt"/>
  48. [InputControl(layout = "Vector2", displayName = "Tilt", usage = "Tilt")]
  49. [FieldOffset(16)]
  50. public Vector2 tilt;
  51. /// <summary>
  52. /// Pressure with which the pen is pressed against the surface. 0 is none, 1 is full pressure.
  53. /// </summary>
  54. /// <value>Pressure with which the pen is pressed.</value>
  55. /// <remarks>
  56. /// May go beyond 1 depending on pressure calibration on the system. The maximum pressure point
  57. /// may be set to less than the physical maximum pressure point determined by the hardware.
  58. /// </remarks>
  59. /// <seealso cref="Pointer.pressure"/>
  60. [InputControl(layout = "Analog", usage = "Pressure", defaultState = 0.0f)]
  61. [FieldOffset(24)]
  62. public float pressure;
  63. /// <summary>
  64. /// Amount by which the pen is rotated around itself.
  65. /// </summary>
  66. /// <value>Rotation of the pen around itself.</value>
  67. /// <seealso cref="Pen.twist"/>
  68. [InputControl(layout = "Axis", displayName = "Twist", usage = "Twist")]
  69. [FieldOffset(28)]
  70. public float twist;
  71. /// <summary>
  72. /// Button mask for which buttons on the pen are active.
  73. /// </summary>
  74. /// <value>Bitmask for buttons on the pen.</value>
  75. [InputControl(name = "tip", displayName = "Tip", layout = "Button", bit = (int)PenButton.Tip, usage = "PrimaryAction")]
  76. [InputControl(name = "press", useStateFrom = "tip", synthetic = true, usages = new string[0])]
  77. [InputControl(name = "eraser", displayName = "Eraser", layout = "Button", bit = (int)PenButton.Eraser)]
  78. [InputControl(name = "inRange", displayName = "In Range?", layout = "Button", bit = (int)PenButton.InRange, synthetic = true)]
  79. [InputControl(name = "barrel1", displayName = "Barrel Button #1", layout = "Button", bit = (int)PenButton.BarrelFirst, alias = "barrelFirst", usage = "SecondaryAction")]
  80. [InputControl(name = "barrel2", displayName = "Barrel Button #2", layout = "Button", bit = (int)PenButton.BarrelSecond, alias = "barrelSecond")]
  81. [InputControl(name = "barrel3", displayName = "Barrel Button #3", layout = "Button", bit = (int)PenButton.BarrelThird, alias = "barrelThird")]
  82. [InputControl(name = "barrel4", displayName = "Barrel Button #4", layout = "Button", bit = (int)PenButton.BarrelFourth, alias = "barrelFourth")]
  83. // "Park" unused controls.
  84. [InputControl(name = "radius", layout = "Vector2", format = "VEC2", sizeInBits = 64, usage = "Radius", offset = InputStateBlock.AutomaticOffset)]
  85. [InputControl(name = "pointerId", layout = "Digital", format = "UINT", sizeInBits = 32, offset = InputStateBlock.AutomaticOffset)] ////TODO: this should be used
  86. [FieldOffset(32)]
  87. public ushort buttons;
  88. // Not currently used, but still needed in this struct for padding,
  89. // as il2cpp does not implement FieldOffset.
  90. [FieldOffset(34)]
  91. ushort displayIndex;
  92. /// <summary>
  93. /// Set or unset the bit in <see cref="buttons"/> for the given <paramref name="button"/>.
  94. /// </summary>
  95. /// <param name="button">Button whose state to set.</param>
  96. /// <param name="state">Whether the button is on or off.</param>
  97. /// <returns>Same PenState with an updated <see cref="buttons"/> mask.</returns>
  98. public PenState WithButton(PenButton button, bool state = true)
  99. {
  100. if (state)
  101. buttons |= (ushort)(1 << (int)button);
  102. else
  103. buttons &= (ushort)~(1 << (int)button);
  104. return this;
  105. }
  106. /// <inheritdoc />
  107. public FourCC format => Format;
  108. }
  109. }
  110. namespace UnityEngine.InputSystem
  111. {
  112. /// <summary>
  113. /// Enumeration of buttons on a <see cref="Pen"/>.
  114. /// </summary>
  115. public enum PenButton
  116. {
  117. /// <summary>
  118. /// Button at the tip of a pen.
  119. /// </summary>
  120. /// <seealso cref="Pen.tip"/>
  121. Tip,
  122. /// <summary>
  123. /// Button located end of pen opposite to <see cref="Tip"/>.
  124. /// </summary>
  125. /// <remarks>
  126. /// Pens do not necessarily have an eraser. If a pen doesn't, the respective button
  127. /// does nothing and will always be unpressed.
  128. /// </remarks>
  129. /// <seealso cref="Pen.eraser"/>
  130. Eraser,
  131. /// <summary>
  132. /// First button on the side of the pen.
  133. /// </summary>
  134. /// <see cref="Pen.firstBarrelButton"/>
  135. BarrelFirst,
  136. /// <summary>
  137. /// Second button on the side of the pen.
  138. /// </summary>
  139. /// <seealso cref="Pen.secondBarrelButton"/>
  140. BarrelSecond,
  141. /// <summary>
  142. /// Artificial button that indicates whether the pen is in detection range or not.
  143. /// </summary>
  144. /// <remarks>
  145. /// Range detection may not be supported by a pen/tablet.
  146. /// </remarks>
  147. /// <seealso cref="Pen.inRange"/>
  148. InRange,
  149. /// <summary>
  150. /// Third button on the side of the pen.
  151. /// </summary>
  152. /// <seealso cref="Pen.thirdBarrelButton"/>
  153. BarrelThird,
  154. /// <summary>
  155. /// Fourth button on the side of the pen.
  156. /// </summary>
  157. /// <see cref="Pen.fourthBarrelButton"/>
  158. BarrelFourth,
  159. /// <summary>
  160. /// Synonym for <see cref="BarrelFirst"/>.
  161. /// </summary>
  162. Barrel1 = BarrelFirst,
  163. /// <summary>
  164. /// Synonym for <see cref="BarrelSecond"/>.
  165. /// </summary>
  166. Barrel2 = BarrelSecond,
  167. /// <summary>
  168. /// Synonym for <see cref="BarrelThird"/>.
  169. /// </summary>
  170. Barrel3 = BarrelThird,
  171. /// <summary>
  172. /// Synonym for <see cref="BarrelFourth"/>.
  173. /// </summary>
  174. Barrel4 = BarrelFourth,
  175. }
  176. /// <summary>
  177. /// Represents a pen/stylus input device.
  178. /// </summary>
  179. /// <remarks>
  180. /// Unlike mice but like touch, pens are absolute pointing devices moving across a fixed
  181. /// surface area.
  182. ///
  183. /// The <see cref="tip"/> acts as a button that is considered pressed as long as the pen is in contact with the
  184. /// tablet surface.
  185. /// </remarks>
  186. [InputControlLayout(stateType = typeof(PenState), isGenericTypeOfDevice = true)]
  187. [Scripting.Preserve]
  188. public class Pen : Pointer
  189. {
  190. ////TODO: give the tip and eraser a very low press point
  191. /// <summary>
  192. /// The tip button of the pen.
  193. /// </summary>
  194. /// <value>Control representing the tip button.</value>
  195. /// <seealso cref="PenButton.Tip"/>
  196. public ButtonControl tip { get; private set; }
  197. /// <summary>
  198. /// The eraser button of the pen, i.e. the button on the end opposite to the tip.
  199. /// </summary>
  200. /// <value>Control representing the eraser button.</value>
  201. /// <remarks>
  202. /// If the pen does not have an eraser button, this control will still be present
  203. /// but will not trigger.
  204. /// </remarks>
  205. /// <seealso cref="PenButton.Eraser"/>
  206. public ButtonControl eraser { get; private set; }
  207. /// <summary>
  208. /// The button on the side of the pen barrel and located closer to the tip of the pen.
  209. /// </summary>
  210. /// <value>Control representing the first side button.</value>
  211. /// <remarks>
  212. /// If the pen does not have barrel buttons, this control will still be present
  213. /// but will not trigger.
  214. /// </remarks>
  215. /// <seealso cref="PenButton.BarrelFirst"/>
  216. public ButtonControl firstBarrelButton { get; private set; }
  217. /// <summary>
  218. /// The button on the side of the pen barrel and located closer to the eraser end of the pen.
  219. /// </summary>
  220. /// <value>Control representing the second side button.</value>
  221. /// <remarks>
  222. /// If the pen does not have barrel buttons, this control will still be present
  223. /// but will not trigger.
  224. /// </remarks>
  225. /// <seealso cref="PenButton.BarrelSecond"/>
  226. public ButtonControl secondBarrelButton { get; private set; }
  227. /// <summary>
  228. /// Third button the side of the pen barrel.
  229. /// </summary>
  230. /// <value>Control representing the third side button.</value>
  231. /// <remarks>
  232. /// If the pen does not have a third barrel buttons, this control will still be present
  233. /// but will not trigger.
  234. /// </remarks>
  235. /// <seealso cref="PenButton.BarrelThird"/>
  236. public ButtonControl thirdBarrelButton { get; private set; }
  237. /// <summary>
  238. /// Fourth button the side of the pen barrel.
  239. /// </summary>
  240. /// <value>Control representing the fourth side button.</value>
  241. /// <remarks>
  242. /// If the pen does not have a fourth barrel buttons, this control will still be present
  243. /// but will not trigger.
  244. /// </remarks>
  245. /// <seealso cref="PenButton.BarrelFourth"/>
  246. public ButtonControl fourthBarrelButton { get; private set; }
  247. /// <summary>
  248. /// Button control that indicates whether the pen is in range of the tablet surface or not.
  249. /// </summary>
  250. /// <remarks>
  251. /// This is a synthetic control (<see cref="InputControl.synthetic"/>).
  252. ///
  253. /// If range detection is not supported by the pen, this button will always be "pressed".
  254. /// </remarks>
  255. /// <seealso cref="PenButton.InRange"/>
  256. public ButtonControl inRange { get; private set; }
  257. /// <summary>
  258. /// Orientation of the pen relative to the tablet surface, i.e. the amount by which it is leaning
  259. /// over along the X and Y axis.
  260. /// </summary>
  261. /// <value>Control presenting the amount the pen is leaning over.</value>
  262. /// <remarks>
  263. /// X axis goes from [-1..1] left to right with -1 and 1 meaning the pen is flush with the tablet surface. Y axis
  264. /// goes from [-1..1] bottom to top.
  265. /// </remarks>
  266. public Vector2Control tilt { get; private set; }
  267. /// <summary>
  268. /// Rotation of the pointer around its own axis. 0 means the pointer is facing away from the user (12 'o clock position)
  269. /// and ~1 means the pointer has been rotated clockwise almost one full rotation.
  270. /// </summary>
  271. /// <value>Control representing the twist of the pen around itself.</value>
  272. /// <remarks>
  273. /// Twist is generally only supported by pens and even among pens, twist support is rare. An example product that
  274. /// supports twist is the Wacom Art Pen.
  275. ///
  276. /// The axis of rotation is the vector facing away from the pointer surface when the pointer is facing straight up
  277. /// (i.e. the surface normal of the pointer surface). When the pointer is tilted, the rotation axis is tilted along
  278. /// with it.
  279. /// </remarks>
  280. public AxisControl twist { get; private set; }
  281. /// <summary>
  282. /// The pen that was active or connected last or <c>null</c> if there is no pen.
  283. /// </summary>
  284. public new static Pen current { get; internal set; }
  285. /// <summary>
  286. /// Return the given pen button.
  287. /// </summary>
  288. /// <param name="button">Pen button to return.</param>
  289. /// <exception cref="InvalidEnumArgumentException"><paramref name="button"/> is not a valid pen button.</exception>
  290. public ButtonControl this[PenButton button]
  291. {
  292. get
  293. {
  294. switch (button)
  295. {
  296. case PenButton.Tip: return tip;
  297. case PenButton.Eraser: return eraser;
  298. case PenButton.BarrelFirst: return firstBarrelButton;
  299. case PenButton.BarrelSecond: return secondBarrelButton;
  300. case PenButton.BarrelThird: return thirdBarrelButton;
  301. case PenButton.BarrelFourth: return fourthBarrelButton;
  302. case PenButton.InRange: return inRange;
  303. default:
  304. throw new InvalidEnumArgumentException(nameof(button), (int)button, typeof(PenButton));
  305. }
  306. }
  307. }
  308. /// <summary>
  309. /// Make this the last used pen, i.e. <see cref="current"/>.
  310. /// </summary>
  311. /// <remarks>
  312. /// This is called automatically by the system when a pen is added or receives
  313. /// input.
  314. /// </remarks>
  315. public override void MakeCurrent()
  316. {
  317. base.MakeCurrent();
  318. current = this;
  319. }
  320. /// <summary>
  321. /// Called when the pen is removed from the system.
  322. /// </summary>
  323. protected override void OnRemoved()
  324. {
  325. base.OnRemoved();
  326. if (current == this)
  327. current = null;
  328. }
  329. /// <inheritdoc />
  330. protected override void FinishSetup()
  331. {
  332. tip = GetChildControl<ButtonControl>("tip");
  333. eraser = GetChildControl<ButtonControl>("eraser");
  334. firstBarrelButton = GetChildControl<ButtonControl>("barrel1");
  335. secondBarrelButton = GetChildControl<ButtonControl>("barrel2");
  336. thirdBarrelButton = GetChildControl<ButtonControl>("barrel3");
  337. fourthBarrelButton = GetChildControl<ButtonControl>("barrel4");
  338. inRange = GetChildControl<ButtonControl>("inRange");
  339. tilt = GetChildControl<Vector2Control>("tilt");
  340. twist = GetChildControl<AxisControl>("twist");
  341. base.FinishSetup();
  342. }
  343. }
  344. }