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;
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 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");
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;
}
}
}