323 lines
13 KiB
C#
323 lines
13 KiB
C#
|
using System.Runtime.InteropServices;
|
||
|
using UnityEngine.InputSystem.Controls;
|
||
|
using UnityEngine.InputSystem.Layouts;
|
||
|
using UnityEngine.InputSystem.LowLevel;
|
||
|
using UnityEngine.InputSystem.Utilities;
|
||
|
|
||
|
////TODO: option to allow to constrain mouse input to the screen area (i.e. no input once mouse leaves player window)
|
||
|
|
||
|
namespace UnityEngine.InputSystem.LowLevel
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Combine a single pointer with buttons and a scroll wheel.
|
||
|
/// </summary>
|
||
|
// IMPORTANT: State layout must match with MouseInputState in native.
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 30)]
|
||
|
public struct MouseState : IInputStateTypeInfo
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Memory format identifier for MouseState.
|
||
|
/// </summary>
|
||
|
/// <value>Returns "MOUS".</value>
|
||
|
/// <seealso cref="InputStateBlock.format"/>
|
||
|
public static FourCC Format => new FourCC('M', 'O', 'U', 'S');
|
||
|
|
||
|
/// <summary>
|
||
|
/// Screen-space position of the mouse in pixels.
|
||
|
/// </summary>
|
||
|
/// <value>Position of mouse on screen.</value>
|
||
|
/// <seealso cref="Pointer.position"/>
|
||
|
[InputControl(usage = "Point", dontReset = true)] // Mouse should stay put when we reset devices.
|
||
|
[FieldOffset(0)]
|
||
|
public Vector2 position;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Screen-space motion delta of the mouse in pixels.
|
||
|
/// </summary>
|
||
|
/// <value>Mouse movement.</value>
|
||
|
/// <seealso cref="Pointer.delta"/>
|
||
|
[InputControl(usage = "Secondary2DMotion", layout = "Delta")]
|
||
|
[FieldOffset(8)]
|
||
|
public Vector2 delta;
|
||
|
|
||
|
////REVIEW: have half-axis buttons on the scroll axes? (up, down, left, right)
|
||
|
/// <summary>
|
||
|
/// Scroll-wheel delta of the mouse.
|
||
|
/// </summary>
|
||
|
/// <value>Scroll wheel delta.</value>
|
||
|
/// <seealso cref="Mouse.scroll"/>
|
||
|
[InputControl(displayName = "Scroll", layout = "Delta")]
|
||
|
[InputControl(name = "scroll/x", aliases = new[] { "horizontal" }, usage = "ScrollHorizontal", displayName = "Left/Right")]
|
||
|
[InputControl(name = "scroll/y", aliases = new[] { "vertical" }, usage = "ScrollVertical", displayName = "Up/Down", shortDisplayName = "Wheel")]
|
||
|
[FieldOffset(16)]
|
||
|
public Vector2 scroll;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Button mask for which buttons on the mouse are currently pressed.
|
||
|
/// </summary>
|
||
|
/// <value>Button state mask.</value>
|
||
|
/// <seealso cref="MouseButton"/>
|
||
|
/// <seealso cref="Mouse.leftButton"/>
|
||
|
/// <seealso cref="Mouse.middleButton"/>
|
||
|
/// <seealso cref="Mouse.rightButton"/>
|
||
|
/// <seealso cref="Mouse.forwardButton"/>
|
||
|
/// <seealso cref="Mouse.backButton"/>
|
||
|
[InputControl(name = "press", useStateFrom = "leftButton", synthetic = true, usages = new string[0])]
|
||
|
[InputControl(name = "leftButton", layout = "Button", bit = (int)MouseButton.Left, usage = "PrimaryAction", displayName = "Left Button", shortDisplayName = "LMB")]
|
||
|
[InputControl(name = "rightButton", layout = "Button", bit = (int)MouseButton.Right, usage = "SecondaryAction", displayName = "Right Button", shortDisplayName = "RMB")]
|
||
|
[InputControl(name = "middleButton", layout = "Button", bit = (int)MouseButton.Middle, displayName = "Middle Button", shortDisplayName = "MMB")]
|
||
|
[InputControl(name = "forwardButton", layout = "Button", bit = (int)MouseButton.Forward, usage = "Forward", displayName = "Forward")]
|
||
|
[InputControl(name = "backButton", layout = "Button", bit = (int)MouseButton.Back, usage = "Back", displayName = "Back")]
|
||
|
[FieldOffset(24)]
|
||
|
// "Park" all the controls that are common to pointers but aren't use for mice such that they get
|
||
|
// appended to the end of device state where they will always have default values.
|
||
|
////FIXME: InputDeviceBuilder will get fooled and set up an incorrect state layout if we don't force this to VEC2; InputControlLayout will
|
||
|
//// "infer" USHT as the format which will then end up with a layout where two 4 byte float controls are "packed" into a 16bit sized parent;
|
||
|
//// in other words, setting VEC2 here manually should *not* be necessary
|
||
|
[InputControl(name = "pressure", layout = "Axis", usage = "Pressure", offset = InputStateBlock.AutomaticOffset, format = "FLT", sizeInBits = 32)]
|
||
|
[InputControl(name = "radius", layout = "Vector2", usage = "Radius", offset = InputStateBlock.AutomaticOffset, format = "VEC2", sizeInBits = 64)]
|
||
|
[InputControl(name = "pointerId", layout = "Digital", format = "BIT", sizeInBits = 1, offset = InputStateBlock.AutomaticOffset)] // Will stay at 0.
|
||
|
public ushort buttons;
|
||
|
|
||
|
// Not currently used, but still needed in this struct for padding,
|
||
|
// as il2cpp does not implement FieldOffset.
|
||
|
[FieldOffset(26)]
|
||
|
ushort displayIndex;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Number of clicks performed in succession.
|
||
|
/// </summary>
|
||
|
/// <value>Successive click count.</value>
|
||
|
/// <seealso cref="Mouse.clickCount"/>
|
||
|
[InputControl(layout = "Integer", displayName = "Click Count", synthetic = true)]
|
||
|
[FieldOffset(28)]
|
||
|
public ushort clickCount;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set the button mask for the given button.
|
||
|
/// </summary>
|
||
|
/// <param name="button">Button whose state to set.</param>
|
||
|
/// <param name="state">Whether to set the bit on or off.</param>
|
||
|
/// <returns>The same MouseState with the change applied.</returns>
|
||
|
/// <seealso cref="buttons"/>
|
||
|
public MouseState WithButton(MouseButton button, bool state = true)
|
||
|
{
|
||
|
Debug.Assert((int)button < 16, $"Expected button < 16, so we fit into the 16 bit wide bitmask");
|
||
|
var bit = 1U << (int)button;
|
||
|
if (state)
|
||
|
buttons |= (ushort)bit;
|
||
|
else
|
||
|
buttons &= (ushort)~bit;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns <see cref="Format"/>.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="InputStateBlock.format"/>
|
||
|
public FourCC format => Format;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Button indices for <see cref="MouseState.buttons"/>.
|
||
|
/// </summary>
|
||
|
public enum MouseButton
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Left mouse button.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="Mouse.leftButton"/>
|
||
|
Left,
|
||
|
|
||
|
/// <summary>
|
||
|
/// Right mouse button.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="Mouse.rightButton"/>
|
||
|
Right,
|
||
|
|
||
|
/// <summary>
|
||
|
/// Middle mouse button.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="Mouse.middleButton"/>
|
||
|
Middle,
|
||
|
|
||
|
/// <summary>
|
||
|
/// Second side button.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="Mouse.forwardButton"/>
|
||
|
Forward,
|
||
|
|
||
|
/// <summary>
|
||
|
/// First side button.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="Mouse.backButton"/>
|
||
|
Back
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace UnityEngine.InputSystem
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// An input device representing a mouse.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// Adds a scroll wheel and a typical 3-button setup with a left, middle, and right
|
||
|
/// button.
|
||
|
///
|
||
|
/// To control cursor display and behavior, use <see cref="UnityEngine.Cursor"/>.
|
||
|
/// </remarks>
|
||
|
[InputControlLayout(stateType = typeof(MouseState), isGenericTypeOfDevice = true)]
|
||
|
public class Mouse : Pointer, IInputStateCallbackReceiver
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// The horizontal and vertical scroll wheels.
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the mouse scroll wheels.</value>
|
||
|
/// <remarks>
|
||
|
/// The <c>x</c> component corresponds to the horizontal scroll wheel, the
|
||
|
/// <c>y</c> component to the vertical scroll wheel. Most mice do not have
|
||
|
/// horizontal scroll wheels and will thus only see activity on <c>y</c>.
|
||
|
/// </remarks>
|
||
|
public DeltaControl scroll { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The left mouse button.
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the left mouse button.</value>
|
||
|
public ButtonControl leftButton { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The middle mouse button.
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the middle mouse button.</value>
|
||
|
public ButtonControl middleButton { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The right mouse button.
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the right mouse button.</value>
|
||
|
public ButtonControl rightButton { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The first side button, often labeled/used as "back".
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the back button on the mouse.</value>
|
||
|
/// <remarks>
|
||
|
/// On Windows, this corresponds to <c>RI_MOUSE_BUTTON_4</c>.
|
||
|
/// </remarks>
|
||
|
public ButtonControl backButton { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The second side button, often labeled/used as "forward".
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the forward button on the mouse.</value>
|
||
|
/// <remarks>
|
||
|
/// On Windows, this corresponds to <c>RI_MOUSE_BUTTON_5</c>.
|
||
|
/// </remarks>
|
||
|
public ButtonControl forwardButton { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Number of times any of the mouse buttons has been clicked in succession within
|
||
|
/// the system-defined click time threshold.
|
||
|
/// </summary>
|
||
|
/// <value>Control representing the mouse click count.</value>
|
||
|
public IntegerControl clickCount { get; protected set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// The mouse that was added or updated last or null if there is no mouse
|
||
|
/// connected to the system.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="InputDevice.MakeCurrent"/>
|
||
|
public new static Mouse current { get; private set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when the mouse becomes the current mouse.
|
||
|
/// </summary>
|
||
|
public override void MakeCurrent()
|
||
|
{
|
||
|
base.MakeCurrent();
|
||
|
current = this;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when the mouse is added to the system.
|
||
|
/// </summary>
|
||
|
protected override void OnAdded()
|
||
|
{
|
||
|
base.OnAdded();
|
||
|
|
||
|
if (native && s_PlatformMouseDevice == null)
|
||
|
s_PlatformMouseDevice = this;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when the device is removed from the system.
|
||
|
/// </summary>
|
||
|
protected override void OnRemoved()
|
||
|
{
|
||
|
base.OnRemoved();
|
||
|
if (current == this)
|
||
|
current = null;
|
||
|
}
|
||
|
|
||
|
internal static Mouse s_PlatformMouseDevice;
|
||
|
|
||
|
////REVIEW: how should we handle this being called from EditorWindow's? (where the editor window space processor will turn coordinates automatically into editor window space)
|
||
|
/// <summary>
|
||
|
/// Move the operating system's mouse cursor.
|
||
|
/// </summary>
|
||
|
/// <param name="position">New position in player window space.</param>
|
||
|
/// <remarks>
|
||
|
/// The <see cref="Pointer.position"/> property will not update immediately but rather will update in the
|
||
|
/// next input update.
|
||
|
/// </remarks>
|
||
|
public void WarpCursorPosition(Vector2 position)
|
||
|
{
|
||
|
var command = WarpMousePositionCommand.Create(position);
|
||
|
ExecuteCommand(ref command);
|
||
|
}
|
||
|
|
||
|
/// <inheritdoc />
|
||
|
protected override void FinishSetup()
|
||
|
{
|
||
|
scroll = GetChildControl<DeltaControl>("scroll");
|
||
|
leftButton = GetChildControl<ButtonControl>("leftButton");
|
||
|
middleButton = GetChildControl<ButtonControl>("middleButton");
|
||
|
rightButton = GetChildControl<ButtonControl>("rightButton");
|
||
|
forwardButton = GetChildControl<ButtonControl>("forwardButton");
|
||
|
backButton = GetChildControl<ButtonControl>("backButton");
|
||
|
clickCount = GetChildControl<IntegerControl>("clickCount");
|
||
|
base.FinishSetup();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Implements <see cref="IInputStateCallbackReceiver.OnNextUpdate"/> for the mouse.
|
||
|
/// </summary>
|
||
|
protected new void OnNextUpdate()
|
||
|
{
|
||
|
base.OnNextUpdate();
|
||
|
InputState.Change(scroll, Vector2.zero);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Implements <see cref="IInputStateCallbackReceiver.OnStateEvent"/> for the mouse.
|
||
|
/// </summary>
|
||
|
/// <param name="eventPtr"></param>
|
||
|
protected new unsafe void OnStateEvent(InputEventPtr eventPtr)
|
||
|
{
|
||
|
scroll.AccumulateValueInEvent(currentStatePtr, eventPtr);
|
||
|
base.OnStateEvent(eventPtr);
|
||
|
}
|
||
|
|
||
|
void IInputStateCallbackReceiver.OnNextUpdate()
|
||
|
{
|
||
|
OnNextUpdate();
|
||
|
}
|
||
|
|
||
|
void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr)
|
||
|
{
|
||
|
OnStateEvent(eventPtr);
|
||
|
}
|
||
|
}
|
||
|
}
|