using System.Runtime.InteropServices; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; using UnityEngine.InputSystem.Utilities; using UnityEngine.Scripting; ////TODO: add capabilities indicating whether pressure is supported ////REVIEW: is there an opportunity to collapse "press" and "pressure" into one? after all, if there's any pressure, isn't the pointer pressed? ////REVIEW: should "displayIndex" be called "windowIndex"? or be part of a better thought-out multi-display API altogether? ////REVIEW: add click and clickCount controls directly to Pointer? //// (I gave this a look but in my initial try, found it somewhat difficult to add click detection at the Pointer level due //// to the extra state it involves) ////REVIEW: should we put lock state directly on Pointer? ////REVIEW: should pointer IDs be required to be globally unique across pointing devices? ////REVIEW: should we create new devices instead of using pointer IDs? ////FIXME: pointer deltas in EditorWindows need to be Y *down* ////REVIEW: kill EditorWindowSpace processor and add GetPositionInEditorWindowSpace() and GetDeltaInEditorWindowSpace()? //// (if we do this, every touch control has to get this, too) namespace UnityEngine.InputSystem.LowLevel { /// /// Default state structure for pointer devices. /// [StructLayout(LayoutKind.Sequential)] internal struct PointerState : IInputStateTypeInfo { public static FourCC kFormat => new FourCC('P', 'T', 'R'); uint pointerId; /// /// Position of the pointer in screen space. /// #if UNITY_EDITOR [InputControl(layout = "Vector2", displayName = "Position", usage = "Point", processors = "AutoWindowSpace", dontReset = true)] #else [InputControl(layout = "Vector2", displayName = "Position", usage = "Point", dontReset = true)] #endif public Vector2 position; ////REVIEW: if we have Secondary2DMotion on this, seems like this should be normalized [InputControl(layout = "Delta", displayName = "Delta", usage = "Secondary2DMotion")] public Vector2 delta; [InputControl(layout = "Analog", displayName = "Pressure", usage = "Pressure", defaultState = 1f)] public float pressure; [InputControl(layout = "Vector2", displayName = "Radius", usage = "Radius")] public Vector2 radius; [InputControl(name = "press", displayName = "Press", layout = "Button", format = "BIT", bit = 0)] public ushort buttons; [InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")] public ushort displayIndex; public FourCC format => kFormat; } } namespace UnityEngine.InputSystem { /// /// Base class for pointer-style devices moving on a 2D screen. /// /// /// This class abstracts over general "pointing" behavior where a pointer is moved across a 2D /// surface. Operating at the Pointer level allows treating Mouse, Pen, /// and Touchscreen all as pointers with a set of shared behaviors. /// /// Note that a pointer may have "multi-point" ability as is the case with multi-touch where /// multiple touches represent multiple concurrent "pointers". However, for any pointer device /// with multiple pointers, only one pointer is considered "primary" and drives the pointer /// controls present on the base class. /// /// /// /// [InputControlLayout(stateType = typeof(PointerState), isGenericTypeOfDevice = true)] public class Pointer : InputDevice, IInputStateCallbackReceiver { ////REVIEW: shouldn't this be done for every touch position, too? /// /// The current pointer coordinates in window space. /// /// Control representing the current position of the pointer on screen. /// /// Within player code, the coordinates are in the coordinate space of Unity's Display. /// /// Within editor code, the coordinates are in the coordinate space of the current EditorWindow /// This means that if you query the in EditorWindow.OnGUI, for example, /// the returned 2D vector will be in the coordinate space of your local GUI (same as /// Event.mousePosition). /// public Vector2Control position { get; protected set; } /// /// The current window-space motion delta of the pointer. /// /// Control representing the motion delta of the pointer. /// /// Every time a pointer is moved, it generates a motion delta. This control represents /// this motion. /// /// Note that some pointers have the ability to generate motion deltas without /// actually changing the position of the pointer. This is the case for /// which even when, for example, bumping up against the edges of the screen or when being /// locked in place, can generate motion. This means that activity on delta is not /// necessarily correlated with activity on . /// /// Deltas have two special behaviors attached to them that makes them quite unique /// among input controls. /// /// For one, deltas will automatically reset to (0,0) between frames. If, for example, /// the current delta value is (12,8), then after the next , /// the delta is automatically set to (0,0). More precisely, deltas will reset as part /// of . This happens every time regardless of whether /// there are pending motion events for the pointer or not. But because it happens in /// (that is, before events are processed), /// subsequent motion deltas are incorporated normally. /// /// Note that the resetting is visible to s. This means that when /// binding to a delta control from an action that is not using , /// you will see the action getting cancelled at the start of every frame. With a PassThrough /// actions, you will instead see it perform one extra time with a zero value. /// /// The other special behavior of deltas is accumulation. When receiving more than one /// motion update in a frame, deltas will not simply switch from one value to the other /// but instead accumulate them. For example, if two events are received for a pointer /// in a frame and one has a motion delta of (1,1) and the other has a motion delta /// of (2,2), then once has finished processing /// events, the value of the delta control will be (3,3) and not (2,2). /// /// Note that just like resetting, accumulation is also visible to s. /// This means that because the delta control changes value twice, the action will trigger /// twice but the value when it is triggered the second time will be (3,3) and /// not (2,2) even though that's the value received from the event. /// /// public DeltaControl delta { get; protected set; } ////REVIEW: move this down to only TouchScreen? /// /// Window-space radius of the pointer contact with the surface. /// /// Control representing the horizontal and vertical extents of the pointer contact. /// /// Usually, only touch input has radius detection. /// /// public Vector2Control radius { get; protected set; } /// /// Normalized pressure with which the pointer is currently pressed while in contact with the pointer surface. /// /// Control representing the pressure with which the pointer is pressed down. /// /// This is only meaningful for pointing devices that support pressure. Mice do not, pens usually do, and touch /// usually does on mobile platforms. /// /// Note that it is possible for the value to go above 1 even though it is considered normalized. The reason is /// that calibration on the system can put the maximum pressure point below the physically supported maximum value. /// public AxisControl pressure { get; protected set; } /// /// Whether the pointer is pressed down. /// /// /// What this means exactly depends on the nature of the pointer. For mice (), it means /// that the left button is pressed. For pens (), it means that the pen tip is touching /// the screen/tablet surface. For touchscreens (), it means that there is at least /// one finger touching the screen. /// public ButtonControl press { get; protected set; } /// /// The index of the display the Pointer is currently on. This is useful for multiple screen setups. /// This may not be supported on all platforms. When unsupported, this will always produce the index of the primary display i.e. zero. /// /// public IntegerControl displayIndex { get; protected set; } /// /// The pointer that was added or used last by the user or null if there is no pointer /// device connected to the system. /// /// Currently active Pointer or null. public static Pointer current { get; internal set; } /// public override void MakeCurrent() { base.MakeCurrent(); current = this; } /// protected override void OnRemoved() { base.OnRemoved(); if (current == this) current = null; } /// protected override void FinishSetup() { position = GetChildControl("position"); delta = GetChildControl("delta"); radius = GetChildControl("radius"); pressure = GetChildControl("pressure"); press = GetChildControl("press"); displayIndex = GetChildControl("displayIndex"); base.FinishSetup(); } /// /// Called whenever the input system advances by one frame. /// /// protected void OnNextUpdate() { InputState.Change(delta, Vector2.zero); } /// /// Called when the pointer receives a state event. /// /// The input event. protected unsafe void OnStateEvent(InputEventPtr eventPtr) { ////FIXME: This stuff makes pointer events too expensive; find a better way. delta.AccumulateValueInEvent(currentStatePtr, eventPtr); InputState.Change(this, eventPtr); } void IInputStateCallbackReceiver.OnNextUpdate() { OnNextUpdate(); } void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr) { OnStateEvent(eventPtr); } bool IInputStateCallbackReceiver.GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref uint offset) { return false; } } }