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 { /// /// Combine a single pointer with buttons and a scroll wheel. /// // IMPORTANT: State layout must match with MouseInputState in native. [StructLayout(LayoutKind.Explicit, Size = 30)] public struct MouseState : IInputStateTypeInfo { /// /// Memory format identifier for MouseState. /// /// Returns "MOUS". /// public static FourCC Format => new FourCC('M', 'O', 'U', 'S'); /// /// Screen-space position of the mouse in pixels. /// /// Position of mouse on screen. /// [InputControl(usage = "Point", dontReset = true)] // Mouse should stay put when we reset devices. [FieldOffset(0)] public Vector2 position; /// /// Screen-space motion delta of the mouse in pixels. /// /// Mouse movement. /// [InputControl(usage = "Secondary2DMotion", layout = "Delta")] [FieldOffset(8)] public Vector2 delta; ////REVIEW: have half-axis buttons on the scroll axes? (up, down, left, right) /// /// Scroll-wheel delta of the mouse. /// /// Scroll wheel delta. /// [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; /// /// Button mask for which buttons on the mouse are currently pressed. /// /// Button state mask. /// /// /// /// /// /// [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; /// /// The index of the display that was moused. /// [InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")] [FieldOffset(26)] public ushort displayIndex; /// /// Number of clicks performed in succession. /// /// Successive click count. /// [InputControl(name = "clickCount", layout = "Integer", displayName = "Click Count", synthetic = true)] [FieldOffset(28)] public ushort clickCount; /// /// Set the button mask for the given button. /// /// Button whose state to set. /// Whether to set the bit on or off. /// The same MouseState with the change applied. /// 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; } /// /// Returns . /// /// public FourCC format => Format; } /// /// Button indices for . /// public enum MouseButton { /// /// Left mouse button. /// /// Left, /// /// Right mouse button. /// /// Right, /// /// Middle mouse button. /// /// Middle, /// /// Second side button. /// /// Forward, /// /// First side button. /// /// Back } } namespace UnityEngine.InputSystem { /// /// An input device representing a mouse. /// /// /// Adds a scroll wheel and a typical 3-button setup with a left, middle, and right /// button. /// /// To control cursor display and behavior, use . /// [InputControlLayout(stateType = typeof(MouseState), isGenericTypeOfDevice = true)] public class Mouse : Pointer, IInputStateCallbackReceiver { /// /// The horizontal and vertical scroll wheels. /// /// Control representing the mouse scroll wheels. /// /// The x component corresponds to the horizontal scroll wheel, the /// y component to the vertical scroll wheel. Most mice do not have /// horizontal scroll wheels and will thus only see activity on y. /// public DeltaControl scroll { get; protected set; } /// /// The left mouse button. /// /// Control representing the left mouse button. public ButtonControl leftButton { get; protected set; } /// /// The middle mouse button. /// /// Control representing the middle mouse button. public ButtonControl middleButton { get; protected set; } /// /// The right mouse button. /// /// Control representing the right mouse button. public ButtonControl rightButton { get; protected set; } /// /// The first side button, often labeled/used as "back". /// /// Control representing the back button on the mouse. /// /// On Windows, this corresponds to RI_MOUSE_BUTTON_4. /// public ButtonControl backButton { get; protected set; } /// /// The second side button, often labeled/used as "forward". /// /// Control representing the forward button on the mouse. /// /// On Windows, this corresponds to RI_MOUSE_BUTTON_5. /// public ButtonControl forwardButton { get; protected set; } /// /// Number of times any of the mouse buttons has been clicked in succession within /// the system-defined click time threshold. /// /// Control representing the mouse click count. public IntegerControl clickCount { get; protected set; } /// /// The mouse that was added or updated last or null if there is no mouse /// connected to the system. /// /// public new static Mouse current { get; private set; } /// /// Called when the mouse becomes the current mouse. /// public override void MakeCurrent() { base.MakeCurrent(); current = this; } /// /// Called when the mouse is added to the system. /// protected override void OnAdded() { base.OnAdded(); if (native && s_PlatformMouseDevice == null) s_PlatformMouseDevice = this; } /// /// Called when the device is removed from the system. /// 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) /// /// Move the operating system's mouse cursor. /// /// New position in player window space. /// /// The property will not update immediately but rather will update in the /// next input update. /// public void WarpCursorPosition(Vector2 position) { var command = WarpMousePositionCommand.Create(position); ExecuteCommand(ref command); } /// protected override void FinishSetup() { scroll = GetChildControl("scroll"); leftButton = GetChildControl("leftButton"); middleButton = GetChildControl("middleButton"); rightButton = GetChildControl("rightButton"); forwardButton = GetChildControl("forwardButton"); backButton = GetChildControl("backButton"); displayIndex = GetChildControl("displayIndex"); clickCount = GetChildControl("clickCount"); base.FinishSetup(); } /// /// Implements for the mouse. /// protected new void OnNextUpdate() { base.OnNextUpdate(); InputState.Change(scroll, Vector2.zero); } /// /// Implements for the mouse. /// /// 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); } } }