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