A game about forced loneliness, made by TACStudios
1#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
2using System;
3using UnityEngine.InputSystem.LowLevel;
4using UnityEngine.UI;
5
6#if UNITY_EDITOR
7using UnityEngine.InputSystem.Editor;
8#endif
9
10////TODO: respect cursor lock mode
11
12////TODO: investigate how driving the HW cursor behaves when FPS drops low
13//// (also, maybe we can add support where we turn the gamepad mouse on and off automatically based on whether the system mouse is used)
14
15////TODO: add support for acceleration
16
17////TODO: automatically scale mouse speed to resolution such that it stays constant regardless of resolution
18
19////TODO: make it work with PlayerInput such that it will automatically look up actions in the actual PlayerInput instance it is used with (based on the action IDs it has)
20
21////REVIEW: should we default the SW cursor position to the center of the screen?
22
23////REVIEW: consider this for inclusion directly in the input system
24
25namespace UnityEngine.InputSystem.UI
26{
27 /// <summary>
28 /// A component that creates a virtual <see cref="Mouse"/> device and drives its input from gamepad-style inputs. This effectively
29 /// adds a software mouse cursor.
30 /// </summary>
31 /// <remarks>
32 /// This component can be used with UIs that are designed for mouse input, i.e. need to be operated with a cursor.
33 /// By hooking up the <see cref="InputAction"/>s of this component to gamepad input and directing <see cref="cursorTransform"/>
34 /// to the UI transform of the cursor, you can use this component to drive an on-screen cursor.
35 ///
36 /// Note that this component does not actually trigger UI input itself. Instead, it creates a virtual <see cref="Mouse"/>
37 /// device which can then be picked up elsewhere (such as by <see cref="InputSystemUIInputModule"/>) where mouse/pointer input
38 /// is expected.
39 ///
40 /// Also note that if there is a <see cref="Mouse"/> added by the platform, it is not impacted by this component. More specifically,
41 /// the system mouse cursor will not be moved or otherwise used by this component.
42 ///
43 /// Input from the component is visible in the same frame as the source input on its actions by virtue of using <see cref="InputState.Change"/>.
44 /// </remarks>
45 /// <seealso cref="Gamepad"/>
46 /// <seealso cref="Mouse"/>
47 [AddComponentMenu("Input/Virtual Mouse")]
48 [HelpURL(InputSystem.kDocUrl + "/manual/UISupport.html#virtual-mouse-cursor-control")]
49 public class VirtualMouseInput : MonoBehaviour
50 {
51 /// <summary>
52 /// Optional transform that will be updated to correspond to the current mouse position.
53 /// </summary>
54 /// <value>Transform to update with mouse position.</value>
55 /// <remarks>
56 /// This is useful for having a UI object that directly represents the mouse cursor. Simply add both the
57 /// <c>VirtualMouseInput</c> component and an <a href="https://docs.unity3d.com/Manual/script-Image.html">Image</a>
58 /// component and hook the <a href="https://docs.unity3d.com/ScriptReference/RectTransform.html">RectTransform</a>
59 /// component for the UI object into here. The object as a whole will then follow the generated mouse cursor
60 /// motion.
61 /// </remarks>
62 public RectTransform cursorTransform
63 {
64 get => m_CursorTransform;
65 set => m_CursorTransform = value;
66 }
67
68 /// <summary>
69 /// How many pixels per second the cursor travels in one axis when the respective axis from
70 /// <see cref="stickAction"/> is 1.
71 /// </summary>
72 /// <value>Mouse speed in pixels per second.</value>
73 public float cursorSpeed
74 {
75 get => m_CursorSpeed;
76 set => m_CursorSpeed = value;
77 }
78
79 /// <summary>
80 /// Determines which cursor representation to use. If this is set to <see cref="CursorMode.SoftwareCursor"/>
81 /// (the default), then <see cref="cursorGraphic"/> and <see cref="cursorTransform"/> define a software cursor
82 /// that is made to correspond to the position of <see cref="virtualMouse"/>. If this is set to <see
83 /// cref="CursorMode.HardwareCursorIfAvailable"/> and there is a native <see cref="Mouse"/> device present,
84 /// the component will take over that mouse device and disable it (so as for it to not also generate position
85 /// updates). It will then use <see cref="Mouse.WarpCursorPosition"/> to move the system mouse cursor to
86 /// correspond to the position of the <see cref="virtualMouse"/>. In this case, <see cref="cursorGraphic"/>
87 /// will be disabled and <see cref="cursorTransform"/> will not be updated.
88 /// </summary>
89 /// <value>Whether the system mouse cursor (if present) should be made to correspond with the virtual mouse position.</value>
90 /// <remarks>
91 /// Note that regardless of which mode is used for the cursor, mouse input is expected to be picked up from <see cref="virtualMouse"/>.
92 ///
93 /// Note that if <see cref="CursorMode.HardwareCursorIfAvailable"/> is used, the software cursor is still used
94 /// if no native <see cref="Mouse"/> device is present.
95 /// </remarks>
96 public CursorMode cursorMode
97 {
98 get => m_CursorMode;
99 set
100 {
101 if (m_CursorMode == value)
102 return;
103
104 // If we're turning it off, make sure we re-enable the system mouse.
105 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable && m_SystemMouse != null)
106 {
107 InputSystem.EnableDevice(m_SystemMouse);
108 m_SystemMouse = null;
109 }
110
111 m_CursorMode = value;
112
113 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable)
114 TryEnableHardwareCursor();
115 else if (m_CursorGraphic != null)
116 m_CursorGraphic.enabled = true;
117 }
118 }
119
120 /// <summary>
121 /// The UI graphic element that represents the mouse cursor.
122 /// </summary>
123 /// <value>Graphic element for the software mouse cursor.</value>
124 /// <remarks>
125 /// If <see cref="cursorMode"/> is set to <see cref="CursorMode.HardwareCursorIfAvailable"/>, this graphic will
126 /// be disabled.
127 ///
128 /// Also, this UI component implicitly determines the <c>Canvas</c> that defines the screen area for the cursor.
129 /// The canvas that this graphic is on will be looked up using <c>GetComponentInParent</c> and then the <c>Canvas.pixelRect</c>
130 /// of the canvas is used as the bounds for the cursor motion range.
131 /// </remarks>
132 /// <seealso cref="CursorMode.SoftwareCursor"/>
133 public Graphic cursorGraphic
134 {
135 get => m_CursorGraphic;
136 set
137 {
138 m_CursorGraphic = value;
139 TryFindCanvas();
140 }
141 }
142
143 /// <summary>
144 /// Multiplier for values received from <see cref="scrollWheelAction"/>.
145 /// </summary>
146 /// <value>Multiplier for scroll values.</value>
147 public float scrollSpeed
148 {
149 get => m_ScrollSpeed;
150 set => m_ScrollSpeed = value;
151 }
152
153 /// <summary>
154 /// The virtual mouse device that the component feeds with input.
155 /// </summary>
156 /// <value>Instance of virtual mouse or <c>null</c>.</value>
157 /// <remarks>
158 /// This is only initialized after the component has been enabled for the first time. Note that
159 /// when subsequently disabling the component, the property will continue to return the mouse device
160 /// but the device will not be added to the system while the component is not enabled.
161 /// </remarks>
162 public Mouse virtualMouse => m_VirtualMouse;
163
164 /// <summary>
165 /// The Vector2 stick input that drives the mouse cursor, i.e. <see cref="Pointer.position"/> on
166 /// <see cref="virtualMouse"/> and the <a
167 /// href="https://docs.unity3d.com/ScriptReference/RectTransform-anchoredPosition.html">anchoredPosition</a>
168 /// on <see cref="cursorTransform"/> (if set).
169 /// </summary>
170 /// <value>Stick input that drives cursor position.</value>
171 /// <remarks>
172 /// This should normally be bound to controls such as <see cref="Gamepad.leftStick"/> and/or
173 /// <see cref="Gamepad.rightStick"/>.
174 /// </remarks>
175 public InputActionProperty stickAction
176 {
177 get => m_StickAction;
178 set => SetAction(ref m_StickAction, value);
179 }
180
181 /// <summary>
182 /// Optional button input that determines when <see cref="Mouse.leftButton"/> is pressed on
183 /// <see cref="virtualMouse"/>.
184 /// </summary>
185 /// <value>Input for <see cref="Mouse.leftButton"/>.</value>
186 public InputActionProperty leftButtonAction
187 {
188 get => m_LeftButtonAction;
189 set
190 {
191 if (m_ButtonActionTriggeredDelegate != null)
192 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, false);
193 SetAction(ref m_LeftButtonAction, value);
194 if (m_ButtonActionTriggeredDelegate != null)
195 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, true);
196 }
197 }
198
199 /// <summary>
200 /// Optional button input that determines when <see cref="Mouse.rightButton"/> is pressed on
201 /// <see cref="virtualMouse"/>.
202 /// </summary>
203 /// <value>Input for <see cref="Mouse.rightButton"/>.</value>
204 public InputActionProperty rightButtonAction
205 {
206 get => m_RightButtonAction;
207 set
208 {
209 if (m_ButtonActionTriggeredDelegate != null)
210 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, false);
211 SetAction(ref m_RightButtonAction, value);
212 if (m_ButtonActionTriggeredDelegate != null)
213 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, true);
214 }
215 }
216
217 /// <summary>
218 /// Optional button input that determines when <see cref="Mouse.middleButton"/> is pressed on
219 /// <see cref="virtualMouse"/>.
220 /// </summary>
221 /// <value>Input for <see cref="Mouse.middleButton"/>.</value>
222 public InputActionProperty middleButtonAction
223 {
224 get => m_MiddleButtonAction;
225 set
226 {
227 if (m_ButtonActionTriggeredDelegate != null)
228 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, false);
229 SetAction(ref m_MiddleButtonAction, value);
230 if (m_ButtonActionTriggeredDelegate != null)
231 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, true);
232 }
233 }
234
235 /// <summary>
236 /// Optional button input that determines when <see cref="Mouse.forwardButton"/> is pressed on
237 /// <see cref="virtualMouse"/>.
238 /// </summary>
239 /// <value>Input for <see cref="Mouse.forwardButton"/>.</value>
240 public InputActionProperty forwardButtonAction
241 {
242 get => m_ForwardButtonAction;
243 set
244 {
245 if (m_ButtonActionTriggeredDelegate != null)
246 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, false);
247 SetAction(ref m_ForwardButtonAction, value);
248 if (m_ButtonActionTriggeredDelegate != null)
249 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, true);
250 }
251 }
252
253 /// <summary>
254 /// Optional button input that determines when <see cref="Mouse.forwardButton"/> is pressed on
255 /// <see cref="virtualMouse"/>.
256 /// </summary>
257 /// <value>Input for <see cref="Mouse.forwardButton"/>.</value>
258 public InputActionProperty backButtonAction
259 {
260 get => m_BackButtonAction;
261 set
262 {
263 if (m_ButtonActionTriggeredDelegate != null)
264 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, false);
265 SetAction(ref m_BackButtonAction, value);
266 if (m_ButtonActionTriggeredDelegate != null)
267 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, true);
268 }
269 }
270
271 /// <summary>
272 /// Optional Vector2 value input that determines the value of <see cref="Mouse.scroll"/> on
273 /// <see cref="virtualMouse"/>.
274 /// </summary>
275 /// <value>Input for <see cref="Mouse.scroll"/>.</value>
276 /// <remarks>
277 /// In case you want to only bind vertical scrolling, simply have a <see cref="Composites.Vector2Composite"/>
278 /// with only <c>Up</c> and <c>Down</c> bound and <c>Left</c> and <c>Right</c> deleted or bound to nothing.
279 /// </remarks>
280 public InputActionProperty scrollWheelAction
281 {
282 get => m_ScrollWheelAction;
283 set => SetAction(ref m_ScrollWheelAction, value);
284 }
285
286 protected void OnEnable()
287 {
288 // Hijack system mouse, if enabled.
289 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable)
290 TryEnableHardwareCursor();
291
292 // Add mouse device.
293 if (m_VirtualMouse == null)
294 m_VirtualMouse = (Mouse)InputSystem.AddDevice("VirtualMouse");
295 else if (!m_VirtualMouse.added)
296 InputSystem.AddDevice(m_VirtualMouse);
297
298 // Set initial cursor position.
299 if (m_CursorTransform != null)
300 {
301 var position = m_CursorTransform.anchoredPosition;
302 InputState.Change(m_VirtualMouse.position, position);
303 m_SystemMouse?.WarpCursorPosition(position);
304 }
305
306 // Hook into input update.
307 if (m_AfterInputUpdateDelegate == null)
308 m_AfterInputUpdateDelegate = OnAfterInputUpdate;
309 InputSystem.onAfterUpdate += m_AfterInputUpdateDelegate;
310
311 // Hook into actions.
312 if (m_ButtonActionTriggeredDelegate == null)
313 m_ButtonActionTriggeredDelegate = OnButtonActionTriggered;
314 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, true);
315 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, true);
316 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, true);
317 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, true);
318 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, true);
319
320 // Enable actions.
321 m_StickAction.action?.Enable();
322 m_LeftButtonAction.action?.Enable();
323 m_RightButtonAction.action?.Enable();
324 m_MiddleButtonAction.action?.Enable();
325 m_ForwardButtonAction.action?.Enable();
326 m_BackButtonAction.action?.Enable();
327 m_ScrollWheelAction.action?.Enable();
328 }
329
330 protected void OnDisable()
331 {
332 // Remove mouse device.
333 if (m_VirtualMouse != null && m_VirtualMouse.added)
334 InputSystem.RemoveDevice(m_VirtualMouse);
335
336 // Let go of system mouse.
337 if (m_SystemMouse != null)
338 {
339 InputSystem.EnableDevice(m_SystemMouse);
340 m_SystemMouse = null;
341 }
342
343 // Remove ourselves from input update.
344 if (m_AfterInputUpdateDelegate != null)
345 InputSystem.onAfterUpdate -= m_AfterInputUpdateDelegate;
346
347 // Disable actions.
348 m_StickAction.action?.Disable();
349 m_LeftButtonAction.action?.Disable();
350 m_RightButtonAction.action?.Disable();
351 m_MiddleButtonAction.action?.Disable();
352 m_ForwardButtonAction.action?.Disable();
353 m_BackButtonAction.action?.Disable();
354 m_ScrollWheelAction.action?.Disable();
355
356 // Unhock from actions.
357 if (m_ButtonActionTriggeredDelegate != null)
358 {
359 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, false);
360 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, false);
361 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, false);
362 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, false);
363 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, false);
364 }
365
366 m_LastTime = default;
367 m_LastStickValue = default;
368 }
369
370 private void TryFindCanvas()
371 {
372 m_Canvas = m_CursorGraphic?.GetComponentInParent<Canvas>();
373 }
374
375 private void TryEnableHardwareCursor()
376 {
377 var devices = InputSystem.devices;
378 for (var i = 0; i < devices.Count; ++i)
379 {
380 var device = devices[i];
381 if (device.native && device is Mouse mouse)
382 {
383 m_SystemMouse = mouse;
384 break;
385 }
386 }
387
388 if (m_SystemMouse == null)
389 {
390 if (m_CursorGraphic != null)
391 m_CursorGraphic.enabled = true;
392 return;
393 }
394
395 InputSystem.DisableDevice(m_SystemMouse);
396
397 // Sync position.
398 if (m_VirtualMouse != null)
399 m_SystemMouse.WarpCursorPosition(m_VirtualMouse.position.value);
400
401 // Turn off mouse cursor image.
402 if (m_CursorGraphic != null)
403 m_CursorGraphic.enabled = false;
404 }
405
406 private void UpdateMotion()
407 {
408 if (m_VirtualMouse == null)
409 return;
410
411 // Read current stick value.
412 var stickAction = m_StickAction.action;
413 if (stickAction == null)
414 return;
415 var stickValue = stickAction.ReadValue<Vector2>();
416 if (Mathf.Approximately(0, stickValue.x) && Mathf.Approximately(0, stickValue.y))
417 {
418 // Motion has stopped.
419 m_LastTime = default;
420 m_LastStickValue = default;
421 }
422 else
423 {
424 var currentTime = InputState.currentTime;
425 if (Mathf.Approximately(0, m_LastStickValue.x) && Mathf.Approximately(0, m_LastStickValue.y))
426 {
427 // Motion has started.
428 m_LastTime = currentTime;
429 }
430
431 // Compute delta.
432 var deltaTime = (float)(currentTime - m_LastTime);
433 var delta = new Vector2(m_CursorSpeed * stickValue.x * deltaTime, m_CursorSpeed * stickValue.y * deltaTime);
434
435 // Update position.
436 var currentPosition = m_VirtualMouse.position.value;
437 var newPosition = currentPosition + delta;
438
439 ////REVIEW: for the hardware cursor, clamp to something else?
440 // Clamp to canvas.
441 if (m_Canvas != null)
442 {
443 // Clamp to canvas.
444 var pixelRect = m_Canvas.pixelRect;
445 newPosition.x = Mathf.Clamp(newPosition.x, pixelRect.xMin, pixelRect.xMax);
446 newPosition.y = Mathf.Clamp(newPosition.y, pixelRect.yMin, pixelRect.yMax);
447 }
448
449 ////REVIEW: the fact we have no events on these means that actions won't have an event ID to go by; problem?
450 InputState.Change(m_VirtualMouse.position, newPosition);
451 InputState.Change(m_VirtualMouse.delta, delta);
452
453 // Update software cursor transform, if any.
454 if (m_CursorTransform != null &&
455 (m_CursorMode == CursorMode.SoftwareCursor ||
456 (m_CursorMode == CursorMode.HardwareCursorIfAvailable && m_SystemMouse == null)))
457 m_CursorTransform.anchoredPosition = newPosition;
458
459 m_LastStickValue = stickValue;
460 m_LastTime = currentTime;
461
462 // Update hardware cursor.
463 m_SystemMouse?.WarpCursorPosition(newPosition);
464 }
465
466 // Update scroll wheel.
467 var scrollAction = m_ScrollWheelAction.action;
468 if (scrollAction != null)
469 {
470 var scrollValue = scrollAction.ReadValue<Vector2>();
471 scrollValue.x *= m_ScrollSpeed;
472 scrollValue.y *= m_ScrollSpeed;
473
474 InputState.Change(m_VirtualMouse.scroll, scrollValue);
475 }
476 }
477
478 [Header("Cursor")]
479 [Tooltip("Whether the component should set the cursor position of the hardware mouse cursor, if one is available. If so, "
480 + "the software cursor pointed (to by 'Cursor Graphic') will be hidden.")]
481 [SerializeField] private CursorMode m_CursorMode;
482 [Tooltip("The graphic that represents the software cursor. This is hidden if a hardware cursor (see 'Cursor Mode') is used.")]
483 [SerializeField] private Graphic m_CursorGraphic;
484 [Tooltip("The transform for the software cursor. Will only be set if a software cursor is used (see 'Cursor Mode'). Moving the cursor "
485 + "updates the anchored position of the transform.")]
486 [SerializeField] private RectTransform m_CursorTransform;
487
488 [Header("Motion")]
489 [Tooltip("Speed in pixels per second with which to move the cursor. Scaled by the input from 'Stick Action'.")]
490 [SerializeField] private float m_CursorSpeed = 400;
491 [Tooltip("Scale factor to apply to 'Scroll Wheel Action' when setting the mouse 'scrollWheel' control.")]
492 [SerializeField] private float m_ScrollSpeed = 45;
493
494 [Space(10)]
495 [Tooltip("Vector2 action that moves the cursor left/right (X) and up/down (Y) on screen.")]
496 [SerializeField] private InputActionProperty m_StickAction;
497 [Tooltip("Button action that triggers a left-click on the mouse.")]
498 [SerializeField] private InputActionProperty m_LeftButtonAction;
499 [Tooltip("Button action that triggers a middle-click on the mouse.")]
500 [SerializeField] private InputActionProperty m_MiddleButtonAction;
501 [Tooltip("Button action that triggers a right-click on the mouse.")]
502 [SerializeField] private InputActionProperty m_RightButtonAction;
503 [Tooltip("Button action that triggers a forward button (button #4) click on the mouse.")]
504 [SerializeField] private InputActionProperty m_ForwardButtonAction;
505 [Tooltip("Button action that triggers a back button (button #5) click on the mouse.")]
506 [SerializeField] private InputActionProperty m_BackButtonAction;
507 [Tooltip("Vector2 action that feeds into the mouse 'scrollWheel' action (scaled by 'Scroll Speed').")]
508 [SerializeField] private InputActionProperty m_ScrollWheelAction;
509
510 private Canvas m_Canvas; // Canvas that gives the motion range for the software cursor.
511 private Mouse m_VirtualMouse;
512 private Mouse m_SystemMouse;
513 private Action m_AfterInputUpdateDelegate;
514 private Action<InputAction.CallbackContext> m_ButtonActionTriggeredDelegate;
515 private double m_LastTime;
516 private Vector2 m_LastStickValue;
517
518 private void OnButtonActionTriggered(InputAction.CallbackContext context)
519 {
520 if (m_VirtualMouse == null)
521 return;
522
523 // The button controls are bit controls. We can't (yet?) use InputState.Change to state
524 // the change of those controls as the state update machinery of InputManager only supports
525 // byte region updates. So we just grab the full state of our virtual mouse, then update
526 // the button in there and then simply overwrite the entire state.
527
528 var action = context.action;
529 MouseButton? button = null;
530 if (action == m_LeftButtonAction.action)
531 button = MouseButton.Left;
532 else if (action == m_RightButtonAction.action)
533 button = MouseButton.Right;
534 else if (action == m_MiddleButtonAction.action)
535 button = MouseButton.Middle;
536 else if (action == m_ForwardButtonAction.action)
537 button = MouseButton.Forward;
538 else if (action == m_BackButtonAction.action)
539 button = MouseButton.Back;
540
541 if (button != null)
542 {
543 var isPressed = context.control.IsPressed();
544 m_VirtualMouse.CopyState<MouseState>(out var mouseState);
545 mouseState.WithButton(button.Value, isPressed);
546
547 InputState.Change(m_VirtualMouse, mouseState);
548 }
549 }
550
551 private static void SetActionCallback(InputActionProperty field, Action<InputAction.CallbackContext> callback, bool install = true)
552 {
553 var action = field.action;
554 if (action == null)
555 return;
556
557 // We don't need the performed callback as our mouse buttons are binary and thus
558 // we only care about started (1) and canceled (0).
559
560 if (install)
561 {
562 action.started += callback;
563 action.canceled += callback;
564 }
565 else
566 {
567 action.started -= callback;
568 action.canceled -= callback;
569 }
570 }
571
572 private static void SetAction(ref InputActionProperty field, InputActionProperty value)
573 {
574 var oldValue = field;
575 field = value;
576
577 if (oldValue.reference == null)
578 {
579 var oldAction = oldValue.action;
580 if (oldAction != null && oldAction.enabled)
581 {
582 oldAction.Disable();
583 if (value.reference == null)
584 value.action?.Enable();
585 }
586 }
587 }
588
589 private void OnAfterInputUpdate()
590 {
591 UpdateMotion();
592 }
593
594 /// <summary>
595 /// Determines how the cursor for the virtual mouse is represented.
596 /// </summary>
597 /// <seealso cref="cursorMode"/>
598 public enum CursorMode
599 {
600 /// <summary>
601 /// The cursor is represented as a UI element. See <see cref="cursorGraphic"/>.
602 /// </summary>
603 SoftwareCursor,
604
605 /// <summary>
606 /// If a native <see cref="Mouse"/> device is present, its cursor will be used and driven
607 /// by the virtual mouse using <see cref="Mouse.WarpCursorPosition"/>. The software cursor
608 /// referenced by <see cref="cursorGraphic"/> will be disabled.
609 ///
610 /// Note that if no native <see cref="Mouse"/> is present, behavior will fall back to
611 /// <see cref="SoftwareCursor"/>.
612 /// </summary>
613 HardwareCursorIfAvailable,
614 }
615
616 #if UNITY_EDITOR
617 [UnityEditor.CustomEditor(typeof(VirtualMouseInput))]
618 private class VirtualMouseInputEditor : UnityEditor.Editor
619 {
620 public void OnDisable()
621 {
622 new InputComponentEditorAnalytic(InputSystemComponent.VirtualMouseInput).Send();
623 new VirtualMouseInputEditorAnalytic(this).Send();
624 }
625 }
626 #endif
627 }
628}
629#endif // PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI