File size: 13,391 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
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;

        /// <summary>
        /// The index of the display that was moused.
        /// </summary>
        [InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")]
        [FieldOffset(26)]
        public ushort displayIndex;

        /// <summary>
        /// Number of clicks performed in succession.
        /// </summary>
        /// <value>Successive click count.</value>
        /// <seealso cref="Mouse.clickCount"/>
        [InputControl(name = "clickCount", 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");
            displayIndex = GetChildControl<IntegerControl>("displayIndex");
            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);
        }
    }
}