123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- using System.ComponentModel;
- using System.Runtime.InteropServices;
- using UnityEngine.InputSystem.Controls;
- using UnityEngine.InputSystem.Layouts;
- using UnityEngine.InputSystem.LowLevel;
- using UnityEngine.InputSystem.Utilities;
- ////TODO: expose whether pen actually has eraser and which barrel buttons it has
- ////TODO: hook up pointerId in backend to allow identifying different pens
- ////REVIEW: have surface distance property to detect how far pen is when hovering?
- ////REVIEW: does it make sense to have orientation support for pen, too?
- namespace UnityEngine.InputSystem.LowLevel
- {
- /// <summary>
- /// Default state layout for pen devices.
- /// </summary>
- // IMPORTANT: Must match with PenInputState in native.
- [StructLayout(LayoutKind.Explicit, Size = 36)]
- public struct PenState : IInputStateTypeInfo
- {
- /// <summary>
- /// Format code for PenState.
- /// </summary>
- /// <value>Returns "PEN ".</value>
- /// <seealso cref="InputStateBlock.format"/>
- public static FourCC Format => new FourCC('P', 'E', 'N');
- /// <summary>
- /// Current screen-space position of the pen.
- /// </summary>
- /// <value>Screen-space position.</value>
- /// <seealso cref="Pointer.position"/>
- [InputControl(usage = "Point")]
- [FieldOffset(0)]
- public Vector2 position;
- /// <summary>
- /// Screen-space motion delta.
- /// </summary>
- /// <value>Screen-space motion delta.</value>
- /// <seealso cref="Pointer.delta"/>
- [InputControl(usage = "Secondary2DMotion")]
- [FieldOffset(8)]
- public Vector2 delta;
- /// <summary>
- /// The way the pen is leaned over perpendicular to the tablet surface. X goes [-1..1] left to right
- /// (with -1 and 1 being completely flush to the surface) and Y goes [-1..1] bottom to top.
- /// </summary>
- /// <value>Amount pen is leaning over.</value>
- /// <seealso cref="Pen.tilt"/>
- [InputControl(layout = "Vector2", displayName = "Tilt", usage = "Tilt")]
- [FieldOffset(16)]
- public Vector2 tilt;
- /// <summary>
- /// Pressure with which the pen is pressed against the surface. 0 is none, 1 is full pressure.
- /// </summary>
- /// <value>Pressure with which the pen is pressed.</value>
- /// <remarks>
- /// May go beyond 1 depending on pressure calibration on the system. The maximum pressure point
- /// may be set to less than the physical maximum pressure point determined by the hardware.
- /// </remarks>
- /// <seealso cref="Pointer.pressure"/>
- [InputControl(layout = "Analog", usage = "Pressure", defaultState = 0.0f)]
- [FieldOffset(24)]
- public float pressure;
- /// <summary>
- /// Amount by which the pen is rotated around itself.
- /// </summary>
- /// <value>Rotation of the pen around itself.</value>
- /// <seealso cref="Pen.twist"/>
- [InputControl(layout = "Axis", displayName = "Twist", usage = "Twist")]
- [FieldOffset(28)]
- public float twist;
- /// <summary>
- /// Button mask for which buttons on the pen are active.
- /// </summary>
- /// <value>Bitmask for buttons on the pen.</value>
- [InputControl(name = "tip", displayName = "Tip", layout = "Button", bit = (int)PenButton.Tip, usage = "PrimaryAction")]
- [InputControl(name = "press", useStateFrom = "tip", synthetic = true, usages = new string[0])]
- [InputControl(name = "eraser", displayName = "Eraser", layout = "Button", bit = (int)PenButton.Eraser)]
- [InputControl(name = "inRange", displayName = "In Range?", layout = "Button", bit = (int)PenButton.InRange, synthetic = true)]
- [InputControl(name = "barrel1", displayName = "Barrel Button #1", layout = "Button", bit = (int)PenButton.BarrelFirst, alias = "barrelFirst", usage = "SecondaryAction")]
- [InputControl(name = "barrel2", displayName = "Barrel Button #2", layout = "Button", bit = (int)PenButton.BarrelSecond, alias = "barrelSecond")]
- [InputControl(name = "barrel3", displayName = "Barrel Button #3", layout = "Button", bit = (int)PenButton.BarrelThird, alias = "barrelThird")]
- [InputControl(name = "barrel4", displayName = "Barrel Button #4", layout = "Button", bit = (int)PenButton.BarrelFourth, alias = "barrelFourth")]
- // "Park" unused controls.
- [InputControl(name = "radius", layout = "Vector2", format = "VEC2", sizeInBits = 64, usage = "Radius", offset = InputStateBlock.AutomaticOffset)]
- [InputControl(name = "pointerId", layout = "Digital", format = "UINT", sizeInBits = 32, offset = InputStateBlock.AutomaticOffset)] ////TODO: this should be used
- [FieldOffset(32)]
- public ushort buttons;
- // Not currently used, but still needed in this struct for padding,
- // as il2cpp does not implement FieldOffset.
- [FieldOffset(34)]
- ushort displayIndex;
- /// <summary>
- /// Set or unset the bit in <see cref="buttons"/> for the given <paramref name="button"/>.
- /// </summary>
- /// <param name="button">Button whose state to set.</param>
- /// <param name="state">Whether the button is on or off.</param>
- /// <returns>Same PenState with an updated <see cref="buttons"/> mask.</returns>
- public PenState WithButton(PenButton button, bool state = true)
- {
- if (state)
- buttons |= (ushort)(1 << (int)button);
- else
- buttons &= (ushort)~(1 << (int)button);
- return this;
- }
- /// <inheritdoc />
- public FourCC format => Format;
- }
- }
- namespace UnityEngine.InputSystem
- {
- /// <summary>
- /// Enumeration of buttons on a <see cref="Pen"/>.
- /// </summary>
- public enum PenButton
- {
- /// <summary>
- /// Button at the tip of a pen.
- /// </summary>
- /// <seealso cref="Pen.tip"/>
- Tip,
- /// <summary>
- /// Button located end of pen opposite to <see cref="Tip"/>.
- /// </summary>
- /// <remarks>
- /// Pens do not necessarily have an eraser. If a pen doesn't, the respective button
- /// does nothing and will always be unpressed.
- /// </remarks>
- /// <seealso cref="Pen.eraser"/>
- Eraser,
- /// <summary>
- /// First button on the side of the pen.
- /// </summary>
- /// <see cref="Pen.firstBarrelButton"/>
- BarrelFirst,
- /// <summary>
- /// Second button on the side of the pen.
- /// </summary>
- /// <seealso cref="Pen.secondBarrelButton"/>
- BarrelSecond,
- /// <summary>
- /// Artificial button that indicates whether the pen is in detection range or not.
- /// </summary>
- /// <remarks>
- /// Range detection may not be supported by a pen/tablet.
- /// </remarks>
- /// <seealso cref="Pen.inRange"/>
- InRange,
- /// <summary>
- /// Third button on the side of the pen.
- /// </summary>
- /// <seealso cref="Pen.thirdBarrelButton"/>
- BarrelThird,
- /// <summary>
- /// Fourth button on the side of the pen.
- /// </summary>
- /// <see cref="Pen.fourthBarrelButton"/>
- BarrelFourth,
- /// <summary>
- /// Synonym for <see cref="BarrelFirst"/>.
- /// </summary>
- Barrel1 = BarrelFirst,
- /// <summary>
- /// Synonym for <see cref="BarrelSecond"/>.
- /// </summary>
- Barrel2 = BarrelSecond,
- /// <summary>
- /// Synonym for <see cref="BarrelThird"/>.
- /// </summary>
- Barrel3 = BarrelThird,
- /// <summary>
- /// Synonym for <see cref="BarrelFourth"/>.
- /// </summary>
- Barrel4 = BarrelFourth,
- }
- /// <summary>
- /// Represents a pen/stylus input device.
- /// </summary>
- /// <remarks>
- /// Unlike mice but like touch, pens are absolute pointing devices moving across a fixed
- /// surface area.
- ///
- /// The <see cref="tip"/> acts as a button that is considered pressed as long as the pen is in contact with the
- /// tablet surface.
- /// </remarks>
- [InputControlLayout(stateType = typeof(PenState), isGenericTypeOfDevice = true)]
- [Scripting.Preserve]
- public class Pen : Pointer
- {
- ////TODO: give the tip and eraser a very low press point
- /// <summary>
- /// The tip button of the pen.
- /// </summary>
- /// <value>Control representing the tip button.</value>
- /// <seealso cref="PenButton.Tip"/>
- public ButtonControl tip { get; private set; }
- /// <summary>
- /// The eraser button of the pen, i.e. the button on the end opposite to the tip.
- /// </summary>
- /// <value>Control representing the eraser button.</value>
- /// <remarks>
- /// If the pen does not have an eraser button, this control will still be present
- /// but will not trigger.
- /// </remarks>
- /// <seealso cref="PenButton.Eraser"/>
- public ButtonControl eraser { get; private set; }
- /// <summary>
- /// The button on the side of the pen barrel and located closer to the tip of the pen.
- /// </summary>
- /// <value>Control representing the first side button.</value>
- /// <remarks>
- /// If the pen does not have barrel buttons, this control will still be present
- /// but will not trigger.
- /// </remarks>
- /// <seealso cref="PenButton.BarrelFirst"/>
- public ButtonControl firstBarrelButton { get; private set; }
- /// <summary>
- /// The button on the side of the pen barrel and located closer to the eraser end of the pen.
- /// </summary>
- /// <value>Control representing the second side button.</value>
- /// <remarks>
- /// If the pen does not have barrel buttons, this control will still be present
- /// but will not trigger.
- /// </remarks>
- /// <seealso cref="PenButton.BarrelSecond"/>
- public ButtonControl secondBarrelButton { get; private set; }
- /// <summary>
- /// Third button the side of the pen barrel.
- /// </summary>
- /// <value>Control representing the third side button.</value>
- /// <remarks>
- /// If the pen does not have a third barrel buttons, this control will still be present
- /// but will not trigger.
- /// </remarks>
- /// <seealso cref="PenButton.BarrelThird"/>
- public ButtonControl thirdBarrelButton { get; private set; }
- /// <summary>
- /// Fourth button the side of the pen barrel.
- /// </summary>
- /// <value>Control representing the fourth side button.</value>
- /// <remarks>
- /// If the pen does not have a fourth barrel buttons, this control will still be present
- /// but will not trigger.
- /// </remarks>
- /// <seealso cref="PenButton.BarrelFourth"/>
- public ButtonControl fourthBarrelButton { get; private set; }
- /// <summary>
- /// Button control that indicates whether the pen is in range of the tablet surface or not.
- /// </summary>
- /// <remarks>
- /// This is a synthetic control (<see cref="InputControl.synthetic"/>).
- ///
- /// If range detection is not supported by the pen, this button will always be "pressed".
- /// </remarks>
- /// <seealso cref="PenButton.InRange"/>
- public ButtonControl inRange { get; private set; }
- /// <summary>
- /// Orientation of the pen relative to the tablet surface, i.e. the amount by which it is leaning
- /// over along the X and Y axis.
- /// </summary>
- /// <value>Control presenting the amount the pen is leaning over.</value>
- /// <remarks>
- /// X axis goes from [-1..1] left to right with -1 and 1 meaning the pen is flush with the tablet surface. Y axis
- /// goes from [-1..1] bottom to top.
- /// </remarks>
- public Vector2Control tilt { get; private set; }
- /// <summary>
- /// Rotation of the pointer around its own axis. 0 means the pointer is facing away from the user (12 'o clock position)
- /// and ~1 means the pointer has been rotated clockwise almost one full rotation.
- /// </summary>
- /// <value>Control representing the twist of the pen around itself.</value>
- /// <remarks>
- /// Twist is generally only supported by pens and even among pens, twist support is rare. An example product that
- /// supports twist is the Wacom Art Pen.
- ///
- /// The axis of rotation is the vector facing away from the pointer surface when the pointer is facing straight up
- /// (i.e. the surface normal of the pointer surface). When the pointer is tilted, the rotation axis is tilted along
- /// with it.
- /// </remarks>
- public AxisControl twist { get; private set; }
- /// <summary>
- /// The pen that was active or connected last or <c>null</c> if there is no pen.
- /// </summary>
- public new static Pen current { get; internal set; }
- /// <summary>
- /// Return the given pen button.
- /// </summary>
- /// <param name="button">Pen button to return.</param>
- /// <exception cref="InvalidEnumArgumentException"><paramref name="button"/> is not a valid pen button.</exception>
- public ButtonControl this[PenButton button]
- {
- get
- {
- switch (button)
- {
- case PenButton.Tip: return tip;
- case PenButton.Eraser: return eraser;
- case PenButton.BarrelFirst: return firstBarrelButton;
- case PenButton.BarrelSecond: return secondBarrelButton;
- case PenButton.BarrelThird: return thirdBarrelButton;
- case PenButton.BarrelFourth: return fourthBarrelButton;
- case PenButton.InRange: return inRange;
- default:
- throw new InvalidEnumArgumentException(nameof(button), (int)button, typeof(PenButton));
- }
- }
- }
- /// <summary>
- /// Make this the last used pen, i.e. <see cref="current"/>.
- /// </summary>
- /// <remarks>
- /// This is called automatically by the system when a pen is added or receives
- /// input.
- /// </remarks>
- public override void MakeCurrent()
- {
- base.MakeCurrent();
- current = this;
- }
- /// <summary>
- /// Called when the pen is removed from the system.
- /// </summary>
- protected override void OnRemoved()
- {
- base.OnRemoved();
- if (current == this)
- current = null;
- }
- /// <inheritdoc />
- protected override void FinishSetup()
- {
- tip = GetChildControl<ButtonControl>("tip");
- eraser = GetChildControl<ButtonControl>("eraser");
- firstBarrelButton = GetChildControl<ButtonControl>("barrel1");
- secondBarrelButton = GetChildControl<ButtonControl>("barrel2");
- thirdBarrelButton = GetChildControl<ButtonControl>("barrel3");
- fourthBarrelButton = GetChildControl<ButtonControl>("barrel4");
- inRange = GetChildControl<ButtonControl>("inRange");
- tilt = GetChildControl<Vector2Control>("tilt");
- twist = GetChildControl<AxisControl>("twist");
- base.FinishSetup();
- }
- }
- }
|