Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice

Bug XRUIInputModule does not consider multiple monitors for calculating the mouse position

Discussion in 'XR Interaction Toolkit and Input' started by jpfranssen_, Jan 11, 2024.

  1. jpfranssen_

    jpfranssen_

    Joined:
    Jul 20, 2018
    Posts:
    11
    When using the XRUIInputModule with "Enable Mouse Input = true" the mouse processing does not take multiple displays into account.

    I'm having this issue when I want to render the VR mirror display on the main screen and have a second screen with UI.

    I have created a quick fix by copying the package in the packages folder locally and modified the following:

    MouseModel.cs now contains a property called displayIndex which is set from XRUIInputModule.cs in the
    ProcessMouse() method.

    m_MouseState.displayIndex = Mouse.current.displayIndex.ReadValue();


    XRUIInputModule.cs
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.Controls;
    4. using UnityEngine.XR.Interaction.Toolkit.Utilities.Pooling;
    5.  
    6. namespace UnityEngine.XR.Interaction.Toolkit.UI
    7. {
    8.     /// <summary>
    9.     /// Matches the UI Model to the state of the Interactor.
    10.     /// </summary>
    11.     public interface IUIInteractor
    12.     {
    13.         /// <summary>
    14.         /// Updates the current UI Model to match the state of the Interactor.
    15.         /// </summary>
    16.         /// <param name="model">The returned model that will match this Interactor.</param>
    17.         void UpdateUIModel(ref TrackedDeviceModel model);
    18.  
    19.         /// <summary>
    20.         /// Attempts to retrieve the current UI Model.
    21.         /// </summary>
    22.         /// <param name="model">The returned model that reflects the UI state of this Interactor.</param>
    23.         /// <returns>Returns <see langword="true"/> if the model was able to retrieved. Otherwise, returns <see langword="false"/>.</returns>
    24.         bool TryGetUIModel(out TrackedDeviceModel model);
    25.     }
    26.  
    27.     /// <summary>
    28.     /// Matches the UI Model to the state of the Interactor with support for hover events.
    29.     /// </summary>
    30.     public interface IUIHoverInteractor : IUIInteractor
    31.     {
    32.         /// <summary>
    33.         /// The event that is called when the Interactor begins hovering over a UI element.
    34.         /// </summary>
    35.         /// <remarks>
    36.         /// The <see cref="UIHoverEventArgs"/> passed to each listener is only valid while the event is invoked,
    37.         /// do not hold a reference to it.
    38.         /// </remarks>
    39.         UIHoverEnterEvent uiHoverEntered { get; }
    40.  
    41.         /// <summary>
    42.         /// The event that is called when this Interactor ends hovering over a UI element.
    43.         /// </summary>
    44.         /// <remarks>
    45.         /// The <see cref="UIHoverEventArgs"/> passed to each listener is only valid while the event is invoked,
    46.         /// do not hold a reference to it.
    47.         /// </remarks>
    48.         UIHoverExitEvent uiHoverExited { get; }
    49.  
    50.         /// <summary>
    51.         /// The <see cref="XRUIInputModule"/> calls this method when the Interactor begins hovering over a UI element.
    52.         /// </summary>
    53.         /// <param name="args">Event data containing the UI element that is being hovered over.</param>
    54.         /// <remarks>
    55.         /// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
    56.         /// </remarks>
    57.         /// <seealso cref="OnUIHoverExited(UIHoverEventArgs)"/>
    58.         void OnUIHoverEntered(UIHoverEventArgs args);
    59.  
    60.         /// <summary>
    61.         /// The <see cref="XRUIInputModule"/> calls this method when the Interactor ends hovering over a UI element.
    62.         /// </summary>
    63.         /// <param name="args">Event data containing the UI element that is no longer hovered over.</param>
    64.         /// <remarks>
    65.         /// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
    66.         /// </remarks>
    67.         /// <seealso cref="OnUIHoverEntered(UIHoverEventArgs)"/>
    68.         void OnUIHoverExited(UIHoverEventArgs args);
    69.     }
    70.  
    71.     /// <summary>
    72.     /// Custom class for input modules that send UI input in XR.
    73.     /// </summary>
    74.     [AddComponentMenu("Event/XR UI Input Module", 11)]
    75.     [HelpURL(XRHelpURLConstants.k_XRUIInputModule)]
    76.     public partial class XRUIInputModule : UIInputModule
    77.     {
    78.         struct RegisteredInteractor
    79.         {
    80.             public IUIInteractor interactor;
    81.             public TrackedDeviceModel model;
    82.  
    83.             public RegisteredInteractor(IUIInteractor interactor, int deviceIndex)
    84.             {
    85.                 this.interactor = interactor;
    86.                 model = new TrackedDeviceModel(deviceIndex);
    87.             }
    88.         }
    89.  
    90.         struct RegisteredTouch
    91.         {
    92.             public bool isValid;
    93.             public int touchId;
    94.             public TouchModel model;
    95.  
    96.             public RegisteredTouch(Touch touch, int deviceIndex)
    97.             {
    98.                 touchId = touch.fingerId;
    99.                 model = new TouchModel(deviceIndex);
    100.                 isValid = true;
    101.             }
    102.         }
    103.  
    104.         /// <summary>
    105.         /// Represents which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.
    106.         /// </summary>
    107.         /// /// <seealso cref="activeInputMode"/>
    108.         public enum ActiveInputMode
    109.         {
    110.             /// <summary>
    111.             /// Only use input polled through the built-in Unity Input Manager (Old).
    112.             /// </summary>
    113.             InputManagerBindings,
    114.  
    115.             /// <summary>
    116.             /// Only use input polled from <see cref="InputActionReference"/> through the newer Input System package.
    117.             /// </summary>
    118.             InputSystemActions,
    119.  
    120.             /// <summary>
    121.             /// Scan through input from both Unity Input Manager and Input System action references.
    122.             /// Note: This may cause undesired effects or may impact performance if input configuration is duplicated.
    123.             /// </summary>
    124.             Both,
    125.         }
    126.  
    127. #if !ENABLE_INPUT_SYSTEM || !ENABLE_LEGACY_INPUT_MANAGER
    128.         [HideInInspector]
    129. #endif
    130.         [SerializeField]
    131.         [Tooltip("Represents which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.")]
    132.         ActiveInputMode m_ActiveInputMode;
    133.  
    134.         /// <summary>
    135.         /// Configures which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.
    136.         /// </summary>
    137.         /// <seealso cref="ActiveInputMode"/>
    138.         public ActiveInputMode activeInputMode
    139.         {
    140.             get => m_ActiveInputMode;
    141.             set => m_ActiveInputMode = value;
    142.         }
    143.  
    144.         [SerializeField, HideInInspector]
    145.         [Tooltip("The maximum distance to ray cast with tracked devices to find hit objects.")]
    146.         float m_MaxTrackedDeviceRaycastDistance = 1000f;
    147.  
    148.         [Header("Input Devices")]
    149.         [SerializeField]
    150.         [Tooltip("If true, will forward 3D tracked device data to UI elements.")]
    151.         bool m_EnableXRInput = true;
    152.  
    153.         /// <summary>
    154.         /// If <see langword="true"/>, will forward 3D tracked device data to UI elements.
    155.         /// </summary>
    156.         public bool enableXRInput
    157.         {
    158.             get => m_EnableXRInput;
    159.             set => m_EnableXRInput = value;
    160.         }
    161.  
    162.         [SerializeField]
    163.         [Tooltip("If true, will forward 2D mouse data to UI elements.")]
    164.         bool m_EnableMouseInput = true;
    165.  
    166.         /// <summary>
    167.         /// If <see langword="true"/>, will forward 2D mouse data to UI elements.
    168.         /// </summary>
    169.         public bool enableMouseInput
    170.         {
    171.             get => m_EnableMouseInput;
    172.             set => m_EnableMouseInput = value;
    173.         }
    174.  
    175.         [SerializeField]
    176.         [Tooltip("If true, will forward 2D touch data to UI elements.")]
    177.         bool m_EnableTouchInput = true;
    178.  
    179.         /// <summary>
    180.         /// If <see langword="true"/>, will forward 2D touch data to UI elements.
    181.         /// </summary>
    182.         public bool enableTouchInput
    183.         {
    184.             get => m_EnableTouchInput;
    185.             set => m_EnableTouchInput = value;
    186.         }
    187.  
    188. #if ENABLE_INPUT_SYSTEM
    189.         [Header("Input System UI Actions")]
    190. #else
    191.         [HideInInspector]
    192. #endif
    193.         [SerializeField]
    194.         [Tooltip("Pointer input action reference, such as a mouse or single-finger touch device.")]
    195.         InputActionReference m_PointAction;
    196.         /// <summary>
    197.         /// The Input System action to use to move the pointer on the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    198.         /// </summary>
    199.         public InputActionReference pointAction
    200.         {
    201.             get => m_PointAction;
    202.             set => SetInputAction(ref m_PointAction, value);
    203.         }
    204.  
    205. #if !ENABLE_INPUT_SYSTEM
    206.         [HideInInspector]
    207. #endif
    208.         [SerializeField]
    209.         [Tooltip("Left-click input action reference, typically the left button on a mouse.")]
    210.         InputActionReference m_LeftClickAction;
    211.         /// <summary>
    212.         /// The Input System action to use to determine whether the left button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    213.         /// </summary>
    214.         public InputActionReference leftClickAction
    215.         {
    216.             get => m_LeftClickAction;
    217.             set => SetInputAction(ref m_LeftClickAction, value);
    218.         }
    219.  
    220. #if !ENABLE_INPUT_SYSTEM
    221.         [HideInInspector]
    222. #endif
    223.         [SerializeField]
    224.         [Tooltip("Middle-click input action reference, typically the middle button on a mouse.")]
    225.         InputActionReference m_MiddleClickAction;
    226.         /// <summary>
    227.         /// The Input System action to use to determine whether the middle button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    228.         /// </summary>
    229.         public InputActionReference middleClickAction
    230.         {
    231.             get => m_MiddleClickAction;
    232.             set => SetInputAction(ref m_MiddleClickAction, value);
    233.         }
    234.  
    235. #if !ENABLE_INPUT_SYSTEM
    236.         [HideInInspector]
    237. #endif
    238.         [SerializeField]
    239.         [Tooltip("Right-click input action reference, typically the right button on a mouse.")]
    240.         InputActionReference m_RightClickAction;
    241.         /// <summary>
    242.         /// The Input System action to use to determine whether the right button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    243.         /// </summary>
    244.         public InputActionReference rightClickAction
    245.         {
    246.             get => m_RightClickAction;
    247.             set => SetInputAction(ref m_RightClickAction, value);
    248.         }
    249.  
    250. #if !ENABLE_INPUT_SYSTEM
    251.         [HideInInspector]
    252. #endif
    253.         [SerializeField]
    254.         [Tooltip("Scroll wheel input action reference, typically the scroll wheel on a mouse.")]
    255.         InputActionReference m_ScrollWheelAction;
    256.         /// <summary>
    257.         /// The Input System action to use to move the pointer on the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    258.         /// </summary>
    259.         public InputActionReference scrollWheelAction
    260.         {
    261.             get => m_ScrollWheelAction;
    262.             set => SetInputAction(ref m_ScrollWheelAction, value);
    263.         }
    264.  
    265. #if !ENABLE_INPUT_SYSTEM
    266.         [HideInInspector]
    267. #endif
    268.         [SerializeField]
    269.         [Tooltip("Navigation input action reference will change which UI element is currently selected to the one up, down, left of or right of the currently selected one.")]
    270.         InputActionReference m_NavigateAction;
    271.         /// <summary>
    272.         /// The Input System action to use to navigate the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    273.         /// </summary>
    274.         public InputActionReference navigateAction
    275.         {
    276.             get => m_NavigateAction;
    277.             set => SetInputAction(ref m_NavigateAction, value);
    278.         }
    279.  
    280. #if !ENABLE_INPUT_SYSTEM
    281.         [HideInInspector]
    282. #endif
    283.         [SerializeField]
    284.         [Tooltip("Submit input action reference will trigger a submission of the currently selected UI in the Event System.")]
    285.         InputActionReference m_SubmitAction;
    286.         /// <summary>
    287.         /// The Input System action to use for submitting or activating a UI element. Must be a <see cref="ButtonControl"/> Control.
    288.         /// </summary>
    289.         public InputActionReference submitAction
    290.         {
    291.             get => m_SubmitAction;
    292.             set => SetInputAction(ref m_SubmitAction, value);
    293.         }
    294.  
    295. #if !ENABLE_INPUT_SYSTEM
    296.         [HideInInspector]
    297. #endif
    298.         [SerializeField]
    299.         [Tooltip("Cancel input action reference will trigger canceling out of the currently selected UI in the Event System.")]
    300.         InputActionReference m_CancelAction;
    301.         /// <summary>
    302.         /// The Input System action to use for cancelling or backing out of a UI element. Must be a <see cref="ButtonControl"/> Control.
    303.         /// </summary>
    304.         public InputActionReference cancelAction
    305.         {
    306.             get => m_CancelAction;
    307.             set => SetInputAction(ref m_CancelAction, value);
    308.         }
    309.  
    310. #if !ENABLE_INPUT_SYSTEM
    311.         [HideInInspector]
    312. #endif
    313.         [SerializeField]
    314.         [Tooltip("When enabled, built-in Input System actions will be used if no Input System UI Actions are assigned.")]
    315.         bool m_EnableBuiltinActionsAsFallback = true;
    316.         /// <summary>
    317.         /// When enabled, built-in Input System actions will be used if no Input System UI Actions are assigned. This uses the
    318.         /// currently enabled Input System devices: <see cref="Mouse.current"/>, <see cref="Gamepad.current"/>, and <see cref="Joystick.current"/>.
    319.         /// </summary>
    320.         public bool enableBuiltinActionsAsFallback
    321.         {
    322.             get => m_EnableBuiltinActionsAsFallback;
    323.             set
    324.             {
    325.                 m_EnableBuiltinActionsAsFallback = value;
    326.                 m_UseBuiltInInputSystemActions = m_EnableBuiltinActionsAsFallback && !InputActionReferencesAreSet();
    327.             }
    328.         }
    329.  
    330. #if ENABLE_LEGACY_INPUT_MANAGER
    331.         [Header("Input Manager (Old) Gamepad/Joystick Bindings")]
    332. #else
    333.         [HideInInspector]
    334. #endif
    335.         [SerializeField]
    336.         [Tooltip("If true, will use the Input Manager (Old) configuration to forward gamepad data to UI elements.")]
    337.         bool m_EnableGamepadInput = true;
    338.  
    339.         /// <summary>
    340.         /// If <see langword="true"/>, will forward gamepad data to UI elements.
    341.         /// </summary>
    342.         public bool enableGamepadInput
    343.         {
    344.             get => m_EnableGamepadInput;
    345.             set => m_EnableGamepadInput = value;
    346.         }
    347.  
    348. #if !ENABLE_LEGACY_INPUT_MANAGER
    349.         [HideInInspector]
    350. #endif
    351.         [SerializeField]
    352.         [Tooltip("If true, will use the Input Manager (Old) configuration to forward joystick data to UI elements.")]
    353.         bool m_EnableJoystickInput = true;
    354.  
    355.         /// <summary>
    356.         /// If <see langword="true"/>, will forward joystick data to UI elements.
    357.         /// </summary>
    358.         public bool enableJoystickInput
    359.         {
    360.             get => m_EnableJoystickInput;
    361.             set => m_EnableJoystickInput = value;
    362.         }
    363.  
    364. #if !ENABLE_LEGACY_INPUT_MANAGER
    365.         [HideInInspector]
    366. #endif
    367.         [SerializeField]
    368.         [Tooltip("Name of the horizontal axis for gamepad/joystick UI navigation when using the old Input Manager.")]
    369.         string m_HorizontalAxis = "Horizontal";
    370.  
    371.         /// <summary>
    372.         /// Name of the horizontal axis for UI navigation when using the old Input Manager.
    373.         /// </summary>
    374.         public string horizontalAxis
    375.         {
    376.             get => m_HorizontalAxis;
    377.             set => m_HorizontalAxis = value;
    378.         }
    379.  
    380. #if !ENABLE_LEGACY_INPUT_MANAGER
    381.         [HideInInspector]
    382. #endif
    383.         [SerializeField]
    384.         [Tooltip("Name of the vertical axis for gamepad/joystick UI navigation when using the old Input Manager.")]
    385.         string m_VerticalAxis = "Vertical";
    386.  
    387.         /// <summary>
    388.         /// Name of the vertical axis for UI navigation when using the old Input Manager.
    389.         /// </summary>
    390.         public string verticalAxis
    391.         {
    392.             get => m_VerticalAxis;
    393.             set => m_VerticalAxis = value;
    394.         }
    395.  
    396. #if !ENABLE_LEGACY_INPUT_MANAGER
    397.         [HideInInspector]
    398. #endif
    399.         [SerializeField]
    400.         [Tooltip("Name of the gamepad/joystick button to use for UI selection or submission when using the old Input Manager.")]
    401.         string m_SubmitButton = "Submit";
    402.  
    403.         /// <summary>
    404.         /// Name of the gamepad/joystick button to use for UI selection or submission when using the old Input Manager.
    405.         /// </summary>
    406.         public string submitButton
    407.         {
    408.             get => m_SubmitButton;
    409.             set => m_SubmitButton = value;
    410.         }
    411.  
    412. #if !ENABLE_LEGACY_INPUT_MANAGER
    413.         [HideInInspector]
    414. #endif
    415.         [SerializeField]
    416.         [Tooltip("Name of the gamepad/joystick button to use for UI cancel or back commands when using the old Input Manager.")]
    417.         string m_CancelButton = "Cancel";
    418.  
    419.         /// <summary>
    420.         /// Name of the gamepad/joystick button to use for UI cancel or back commands when using the old Input Manager.
    421.         /// </summary>
    422.         public string cancelButton
    423.         {
    424.             get => m_CancelButton;
    425.             set => m_CancelButton = value;
    426.         }
    427.  
    428.         int m_RollingPointerId;
    429.         bool m_UseBuiltInInputSystemActions;
    430.  
    431.         MouseModel m_MouseState;
    432.         NavigationModel m_NavigationState;
    433.  
    434.         internal const float kPixelPerLine = 20f;
    435.  
    436.         readonly List<RegisteredTouch> m_RegisteredTouches = new List<RegisteredTouch>();
    437.         readonly List<RegisteredInteractor> m_RegisteredInteractors = new List<RegisteredInteractor>();
    438.  
    439.         // Reusable event args
    440.         readonly LinkedPool<UIHoverEventArgs> m_UIHoverEventArgs = new LinkedPool<UIHoverEventArgs>(() => new UIHoverEventArgs(), collectionCheck: false);
    441.  
    442.         /// <summary>
    443.         /// See <a href="https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnEnable.html">MonoBehavior.OnEnable</a>.
    444.         /// </summary>
    445.         protected override void OnEnable()
    446.         {
    447.             base.OnEnable();
    448.  
    449.             // Check active input mode is correct
    450. #if !ENABLE_INPUT_SYSTEM && ENABLE_LEGACY_INPUT_MANAGER
    451.             m_ActiveInputMode = ActiveInputMode.InputManagerBindings;
    452. #elif ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
    453.             m_ActiveInputMode = ActiveInputMode.InputSystemActions;
    454. #endif
    455.             m_MouseState = new MouseModel(m_RollingPointerId++);
    456.             m_NavigationState = new NavigationModel();
    457.  
    458.             m_UseBuiltInInputSystemActions = m_EnableBuiltinActionsAsFallback && !InputActionReferencesAreSet();
    459.  
    460.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    461.                 EnableAllActions();
    462.         }
    463.  
    464.         /// <summary>
    465.         /// See <a href="https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnDisable.html">MonoBehavior.OnDisable</a>.
    466.         /// </summary>
    467.         protected override void OnDisable()
    468.         {
    469.             RemovePointerEventData(m_MouseState.pointerId);
    470.  
    471.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    472.                 DisableAllActions();
    473.  
    474.             base.OnDisable();
    475.         }
    476.  
    477.         /// <summary>
    478.         /// Register an <see cref="IUIInteractor"/> with the UI system.
    479.         /// Calling this will enable it to start interacting with UI.
    480.         /// </summary>
    481.         /// <param name="interactor">The <see cref="IUIInteractor"/> to use.</param>
    482.         public void RegisterInteractor(IUIInteractor interactor)
    483.         {
    484.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    485.             {
    486.                 if (m_RegisteredInteractors[i].interactor == interactor)
    487.                     return;
    488.             }
    489.  
    490.             m_RegisteredInteractors.Add(new RegisteredInteractor(interactor, m_RollingPointerId++));
    491.         }
    492.  
    493.         /// <summary>
    494.         /// Unregisters an <see cref="IUIInteractor"/> with the UI system.
    495.         /// This cancels all UI Interaction and makes the <see cref="IUIInteractor"/> no longer able to affect UI.
    496.         /// </summary>
    497.         /// <param name="interactor">The <see cref="IUIInteractor"/> to stop using.</param>
    498.         public void UnregisterInteractor(IUIInteractor interactor)
    499.         {
    500.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    501.             {
    502.                 if (m_RegisteredInteractors[i].interactor == interactor)
    503.                 {
    504.                     var registeredInteractor = m_RegisteredInteractors[i];
    505.                     registeredInteractor.interactor = null;
    506.                     m_RegisteredInteractors[i] = registeredInteractor;
    507.                     return;
    508.                 }
    509.             }
    510.         }
    511.  
    512.         /// <summary>
    513.         /// Gets an <see cref="IUIInteractor"/> from its corresponding Unity UI Pointer Id.
    514.         /// This can be used to identify individual Interactors from the underlying UI Events.
    515.         /// </summary>
    516.         /// <param name="pointerId">A unique integer representing an object that can point at UI.</param>
    517.         /// <returns>Returns the interactor associated with <paramref name="pointerId"/>.
    518.         /// Returns <see langword="null"/> if no Interactor is associated (e.g. if it's a mouse event).</returns>
    519.         public IUIInteractor GetInteractor(int pointerId)
    520.         {
    521.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    522.             {
    523.                 if (m_RegisteredInteractors[i].model.pointerId == pointerId)
    524.                 {
    525.                     return m_RegisteredInteractors[i].interactor;
    526.                 }
    527.             }
    528.  
    529.             return null;
    530.         }
    531.  
    532.         /// <summary>Retrieves the UI Model for a selected <see cref="IUIInteractor"/>.</summary>
    533.         /// <param name="interactor">The <see cref="IUIInteractor"/> you want the model for.</param>
    534.         /// <param name="model">The returned model that reflects the UI state of the <paramref name="interactor"/>.</param>
    535.         /// <returns>Returns <see langword="true"/> if the model was able to retrieved. Otherwise, returns <see langword="false"/>.</returns>
    536.         public bool GetTrackedDeviceModel(IUIInteractor interactor, out TrackedDeviceModel model)
    537.         {
    538.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    539.             {
    540.                 if (m_RegisteredInteractors[i].interactor == interactor)
    541.                 {
    542.                     model = m_RegisteredInteractors[i].model;
    543.                     return true;
    544.                 }
    545.             }
    546.  
    547.             model = new TrackedDeviceModel(-1);
    548.             return false;
    549.         }
    550.  
    551.         /// <inheritdoc />
    552.         protected override void DoProcess()
    553.         {
    554.             if (m_EnableXRInput)
    555.             {
    556.                 for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    557.                 {
    558.                     var registeredInteractor = m_RegisteredInteractors[i];
    559.  
    560.                     var oldTarget = registeredInteractor.model.implementationData.pointerTarget;
    561.  
    562.                     // If device is removed, we send a default state to unclick any UI
    563.                     if (registeredInteractor.interactor == null)
    564.                     {
    565.                         registeredInteractor.model.Reset(false);
    566.                         ProcessTrackedDevice(ref registeredInteractor.model, true);
    567.                         RemovePointerEventData(registeredInteractor.model.pointerId);
    568.                         m_RegisteredInteractors.RemoveAt(i--);
    569.                     }
    570.                     else
    571.                     {
    572.                         registeredInteractor.interactor.UpdateUIModel(ref registeredInteractor.model);
    573.                         ProcessTrackedDevice(ref registeredInteractor.model);
    574.                         m_RegisteredInteractors[i] = registeredInteractor;
    575.                     }
    576.                     // If hover target changed, send event
    577.                     var newTarget = registeredInteractor.model.implementationData.pointerTarget;
    578.                     if (oldTarget != newTarget)
    579.                     {
    580.                         using (m_UIHoverEventArgs.Get(out var args))
    581.                         {
    582.                             args.interactorObject = registeredInteractor.interactor;
    583.                             args.deviceModel = registeredInteractor.model;
    584.                             if (args.interactorObject != null && args.interactorObject is IUIHoverInteractor hoverInteractor)
    585.                             {
    586.                                 if (oldTarget != null)
    587.                                 {
    588.                                     args.uiObject = oldTarget;
    589.                                     hoverInteractor.OnUIHoverExited(args);
    590.                                 }
    591.  
    592.                                 if (newTarget != null)
    593.                                 {
    594.                                     args.uiObject = newTarget;
    595.                                     hoverInteractor.OnUIHoverEntered(args);
    596.                                 }
    597.                             }
    598.                         }
    599.                     }
    600.                 }
    601.             }
    602.  
    603.             // Touch needs to take precedence because of the mouse emulation layer
    604.             var hasTouches = false;
    605.             if (m_EnableTouchInput)
    606.                 hasTouches = ProcessTouches();
    607.  
    608.             if (m_EnableMouseInput && !hasTouches)
    609.                 ProcessMouse();
    610.  
    611.             ProcessNavigation();
    612.         }
    613.  
    614.         void ProcessMouse()
    615.         {
    616.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    617.             {
    618.                 if (m_UseBuiltInInputSystemActions)
    619.                 {
    620.                     if (Mouse.current != null)
    621.                     {
    622.                         m_MouseState.position = Mouse.current.position.ReadValue();
    623.                         m_MouseState.displayIndex = Mouse.current.displayIndex.ReadValue();
    624.                         m_MouseState.scrollDelta = Mouse.current.scroll.ReadValue() * (1 / kPixelPerLine);
    625.                         m_MouseState.leftButtonPressed = Mouse.current.leftButton.isPressed;
    626.                         m_MouseState.rightButtonPressed = Mouse.current.rightButton.isPressed;
    627.                         m_MouseState.middleButtonPressed = Mouse.current.middleButton.isPressed;
    628.                     }
    629.                 }
    630.                 else
    631.                 {
    632.                     if (IsActionEnabled(m_PointAction))
    633.                         m_MouseState.position = m_PointAction.action.ReadValue<Vector2>();
    634.                     if (IsActionEnabled(m_ScrollWheelAction))
    635.                         m_MouseState.scrollDelta = m_ScrollWheelAction.action.ReadValue<Vector2>() * (1 / kPixelPerLine);
    636.                     if (IsActionEnabled(m_LeftClickAction))
    637.                         m_MouseState.leftButtonPressed = m_LeftClickAction.action.IsPressed();
    638.                     if (IsActionEnabled(m_RightClickAction))
    639.                         m_MouseState.rightButtonPressed = m_RightClickAction.action.IsPressed();
    640.                     if (IsActionEnabled(m_MiddleClickAction))
    641.                         m_MouseState.middleButtonPressed = m_MiddleClickAction.action.IsPressed();
    642.                 }
    643.             }
    644.  
    645.             if (m_ActiveInputMode != ActiveInputMode.InputSystemActions && Input.mousePresent)
    646.             {
    647.                 m_MouseState.position = Input.mousePosition;
    648.                 m_MouseState.scrollDelta = Input.mouseScrollDelta;
    649.                 m_MouseState.leftButtonPressed = Input.GetMouseButton(0);
    650.                 m_MouseState.rightButtonPressed = Input.GetMouseButton(1);
    651.                 m_MouseState.middleButtonPressed = Input.GetMouseButton(2);
    652.             }
    653.  
    654.             ProcessMouseState(ref m_MouseState);
    655.         }
    656.  
    657.         bool ProcessTouches()
    658.         {
    659.             var hasTouches = Input.touchCount > 0;
    660.             if (!hasTouches)
    661.                 return false;
    662.  
    663.             var touchCount = Input.touchCount;
    664.             for (var touchIndex = 0; touchIndex < touchCount; ++touchIndex)
    665.             {
    666.                 var touch = Input.GetTouch(touchIndex);
    667.                 var registeredTouchIndex = -1;
    668.  
    669.                 // Find if touch already exists
    670.                 for (var j = 0; j < m_RegisteredTouches.Count; j++)
    671.                 {
    672.                     if (touch.fingerId == m_RegisteredTouches[j].touchId)
    673.                     {
    674.                         registeredTouchIndex = j;
    675.                         break;
    676.                     }
    677.                 }
    678.  
    679.                 if (registeredTouchIndex < 0)
    680.                 {
    681.                     // Not found, search empty pool
    682.                     for (var j = 0; j < m_RegisteredTouches.Count; j++)
    683.                     {
    684.                         if (!m_RegisteredTouches[j].isValid)
    685.                         {
    686.                             // Re-use the Id
    687.                             var pointerId = m_RegisteredTouches[j].model.pointerId;
    688.                             m_RegisteredTouches[j] = new RegisteredTouch(touch, pointerId);
    689.                             registeredTouchIndex = j;
    690.                             break;
    691.                         }
    692.                     }
    693.  
    694.                     if (registeredTouchIndex < 0)
    695.                     {
    696.                         // No Empty slots, add one
    697.                         registeredTouchIndex = m_RegisteredTouches.Count;
    698.                         m_RegisteredTouches.Add(new RegisteredTouch(touch, m_RollingPointerId++));
    699.                     }
    700.                 }
    701.  
    702.                 var registeredTouch = m_RegisteredTouches[registeredTouchIndex];
    703.                 registeredTouch.model.selectPhase = touch.phase;
    704.                 registeredTouch.model.position = touch.position;
    705.                 m_RegisteredTouches[registeredTouchIndex] = registeredTouch;
    706.             }
    707.  
    708.             for (var i = 0; i < m_RegisteredTouches.Count; i++)
    709.             {
    710.                 var registeredTouch = m_RegisteredTouches[i];
    711.                 ProcessTouch(ref registeredTouch.model);
    712.                 if (registeredTouch.model.selectPhase == TouchPhase.Ended || registeredTouch.model.selectPhase == TouchPhase.Canceled)
    713.                     registeredTouch.isValid = false;
    714.                 m_RegisteredTouches[i] = registeredTouch;
    715.             }
    716.  
    717.             return true;
    718.         }
    719.  
    720.         void ProcessNavigation()
    721.         {
    722.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    723.             {
    724.                 if (m_UseBuiltInInputSystemActions)
    725.                 {
    726.                     if (Gamepad.current != null)
    727.                     {
    728.                         // Combine left stick and dpad for navigation movement
    729.                         m_NavigationState.move = Gamepad.current.leftStick.ReadValue() + Gamepad.current.dpad.ReadValue();
    730.                         m_NavigationState.submitButtonDown = Gamepad.current.buttonSouth.isPressed;
    731.                         m_NavigationState.cancelButtonDown = Gamepad.current.buttonEast.isPressed;
    732.                     }
    733.                     if (Joystick.current != null)
    734.                     {
    735.                         // Combine main joystick and hatswitch for navigation movement
    736.                         m_NavigationState.move = Joystick.current.stick.ReadValue() +
    737.                             (Joystick.current.hatswitch != null ? Joystick.current.hatswitch.ReadValue() : Vector2.zero);
    738.                         m_NavigationState.submitButtonDown = Joystick.current.trigger.isPressed;
    739.                         // This will always be false until we can rely on a secondary button from the joystick
    740.                         m_NavigationState.cancelButtonDown = false;
    741.                     }
    742.                 }
    743.                 else
    744.                 {
    745.                     if (IsActionEnabled(m_NavigateAction))
    746.                         m_NavigationState.move = m_NavigateAction.action.ReadValue<Vector2>();
    747.                     if (IsActionEnabled(m_SubmitAction))
    748.                         m_NavigationState.submitButtonDown = m_SubmitAction.action.WasPressedThisFrame();
    749.                     if (IsActionEnabled(m_CancelAction))
    750.                         m_NavigationState.cancelButtonDown = m_CancelAction.action.WasPressedThisFrame();
    751.                 }
    752.             }
    753.  
    754.             if (m_ActiveInputMode != ActiveInputMode.InputSystemActions && (m_EnableGamepadInput || m_EnableJoystickInput) && Input.GetJoystickNames().Length > 0)
    755.             {
    756.                 m_NavigationState.move = new Vector2(Input.GetAxis(m_HorizontalAxis), Input.GetAxis(m_VerticalAxis));
    757.                 m_NavigationState.submitButtonDown = Input.GetButton(m_SubmitButton);
    758.                 m_NavigationState.cancelButtonDown = Input.GetButton(m_CancelButton);
    759.             }
    760.  
    761.             base.ProcessNavigationState(ref m_NavigationState);
    762.         }
    763.  
    764.         bool InputActionReferencesAreSet()
    765.         {
    766.             return (m_PointAction != null ||
    767.                 m_LeftClickAction != null ||
    768.                 m_RightClickAction != null ||
    769.                 m_MiddleClickAction != null ||
    770.                 m_NavigateAction != null ||
    771.                 m_SubmitAction != null ||
    772.                 m_CancelAction != null ||
    773.                 m_ScrollWheelAction != null);
    774.         }
    775.  
    776.         void EnableAllActions()
    777.         {
    778.             EnableInputAction(m_PointAction);
    779.             EnableInputAction(m_LeftClickAction);
    780.             EnableInputAction(m_RightClickAction);
    781.             EnableInputAction(m_MiddleClickAction);
    782.             EnableInputAction(m_NavigateAction);
    783.             EnableInputAction(m_SubmitAction);
    784.             EnableInputAction(m_CancelAction);
    785.             EnableInputAction(m_ScrollWheelAction);
    786.         }
    787.  
    788.         void DisableAllActions()
    789.         {
    790.             DisableInputAction(m_PointAction);
    791.             DisableInputAction(m_LeftClickAction);
    792.             DisableInputAction(m_RightClickAction);
    793.             DisableInputAction(m_MiddleClickAction);
    794.             DisableInputAction(m_NavigateAction);
    795.             DisableInputAction(m_SubmitAction);
    796.             DisableInputAction(m_CancelAction);
    797.             DisableInputAction(m_ScrollWheelAction);
    798.         }
    799.  
    800.         static bool IsActionEnabled(InputActionReference inputAction)
    801.         {
    802.             return inputAction != null && inputAction.action != null && inputAction.action.enabled;
    803.         }
    804.  
    805.         static void EnableInputAction(InputActionReference inputAction)
    806.         {
    807.             if (inputAction == null || inputAction.action == null)
    808.                 return;
    809.             inputAction.action.Enable();
    810.         }
    811.  
    812.         static void DisableInputAction(InputActionReference inputAction)
    813.         {
    814.             if (inputAction == null || inputAction.action == null)
    815.                 return;
    816.             inputAction.action.Disable();
    817.         }
    818.  
    819.         void SetInputAction(ref InputActionReference inputAction, InputActionReference value)
    820.         {
    821.             if (Application.isPlaying && inputAction != null)
    822.                 inputAction.action?.Disable();
    823.  
    824.             inputAction = value;
    825.  
    826.             if (Application.isPlaying && isActiveAndEnabled && inputAction != null)
    827.                 inputAction.action?.Enable();
    828.         }
    829.     }
    830. }
    831.  
    MouseModel.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine.EventSystems;
    4. using UnityEngine.InputSystem.Controls;
    5.  
    6. namespace UnityEngine.XR.Interaction.Toolkit.UI
    7. {
    8.     /// <summary>
    9.     /// A series of flags to determine if a button has been pressed or released since the last time checked.
    10.     /// Useful for identifying press/release events that occur in a single frame or sample.
    11.     /// </summary>
    12.     [Flags]
    13.     public enum ButtonDeltaState
    14.     {
    15.         /// <summary>
    16.         /// No change since last time checked.
    17.         /// </summary>
    18.         NoChange = 0,
    19.  
    20.         /// <summary>
    21.         /// Button pressed since last time checked.
    22.         /// </summary>
    23.         Pressed = 1 << 0,
    24.  
    25.         /// <summary>
    26.         /// Button released since last time checked.
    27.         /// </summary>
    28.         Released = 1 << 1,
    29.     }
    30.  
    31.     /// <summary>
    32.     /// Represents the state of a single mouse button within the Unity UI (UGUI) system. Keeps track of various book-keeping regarding clicks, drags, and presses.
    33.     /// Can be converted to and from PointerEventData for sending into Unity UI (UGUI).
    34.     /// </summary>
    35.     public struct MouseButtonModel
    36.     {
    37.         internal struct ImplementationData
    38.         {
    39.             /// <summary>
    40.             /// Used to cache whether or not the current mouse button is being dragged.
    41.             /// </summary>
    42.             /// <seealso cref="PointerEventData.dragging"/>
    43.             public bool isDragging { get; set; }
    44.  
    45.             /// <summary>
    46.             /// Used to cache the last time this button was pressed.
    47.             /// </summary>
    48.             /// <seealso cref="PointerEventData.clickTime"/>
    49.             public float pressedTime { get; set; }
    50.  
    51.             /// <summary>
    52.             /// The position on the screen that this button was last pressed.
    53.             /// In the same scale as <see cref="MouseModel.position"/>, and caches the same value as <see cref="PointerEventData.pressPosition"/>.
    54.             /// </summary>
    55.             /// <seealso cref="PointerEventData.pressPosition"/>
    56.             public Vector2 pressedPosition { get; set; }
    57.  
    58.             /// <summary>
    59.             /// The Raycast data from the time it was pressed.
    60.             /// </summary>
    61.             /// <seealso cref="PointerEventData.pointerPressRaycast"/>
    62.             public RaycastResult pressedRaycast { get; set; }
    63.  
    64.             /// <summary>
    65.             /// The last GameObject pressed on that can handle press or click events.
    66.             /// </summary>
    67.             /// <seealso cref="PointerEventData.pointerPress"/>
    68.             public GameObject pressedGameObject { get; set; }
    69.  
    70.             /// <summary>
    71.             /// The last GameObject pressed on regardless of whether it can handle events or not.
    72.             /// </summary>
    73.             /// <seealso cref="PointerEventData.rawPointerPress"/>
    74.             public GameObject pressedGameObjectRaw { get; set; }
    75.  
    76.             /// <summary>
    77.             /// The GameObject currently being dragged if any.
    78.             /// </summary>
    79.             /// <seealso cref="PointerEventData.pointerDrag"/>
    80.             public GameObject draggedGameObject { get; set; }
    81.  
    82.             /// <summary>
    83.             /// Resets this object to its default, unused state.
    84.             /// </summary>
    85.             public void Reset()
    86.             {
    87.                 isDragging = false;
    88.                 pressedTime = 0f;
    89.                 pressedPosition = Vector2.zero;
    90.                 pressedRaycast = new RaycastResult();
    91.                 pressedGameObject = pressedGameObjectRaw = draggedGameObject = null;
    92.             }
    93.         }
    94.  
    95.         /// <summary>
    96.         /// Used to store the current binary state of the button. When set, will also track the changes between calls of <see cref="OnFrameFinished"/> in <see cref="lastFrameDelta"/>.
    97.         /// </summary>
    98.         public bool isDown
    99.         {
    100.             get => m_IsDown;
    101.             set
    102.             {
    103.                 if (m_IsDown != value)
    104.                 {
    105.                     m_IsDown = value;
    106.                     lastFrameDelta |= value ? ButtonDeltaState.Pressed : ButtonDeltaState.Released;
    107.                 }
    108.             }
    109.         }
    110.  
    111.         /// <summary>
    112.         /// A set of flags to identify the changes that have occurred between calls of <see cref="OnFrameFinished"/>.
    113.         /// </summary>
    114.         internal ButtonDeltaState lastFrameDelta { get; private set; }
    115.  
    116.         /// <summary>
    117.         /// Resets this object to it's default, unused state.
    118.         /// </summary>
    119.         public void Reset()
    120.         {
    121.             lastFrameDelta = ButtonDeltaState.NoChange;
    122.             m_IsDown = false;
    123.  
    124.             m_ImplementationData.Reset();
    125.         }
    126.  
    127.         /// <summary>
    128.         /// Call this on each frame in order to reset properties that detect whether or not a certain condition was met this frame.
    129.         /// </summary>
    130.         public void OnFrameFinished() => lastFrameDelta = ButtonDeltaState.NoChange;
    131.  
    132.         /// <summary>
    133.         /// Fills a <see cref="PointerEventData"/> with this mouse button's internally cached values.
    134.         /// </summary>
    135.         /// <param name="eventData">These objects are used to send data through the Unity UI (UGUI) system.</param>
    136.         public void CopyTo(PointerEventData eventData)
    137.         {
    138.             eventData.dragging = m_ImplementationData.isDragging;
    139.             eventData.clickTime = m_ImplementationData.pressedTime;
    140.             eventData.pressPosition = m_ImplementationData.pressedPosition;
    141.             eventData.pointerPressRaycast = m_ImplementationData.pressedRaycast;
    142.             eventData.pointerPress = m_ImplementationData.pressedGameObject;
    143.             eventData.rawPointerPress = m_ImplementationData.pressedGameObjectRaw;
    144.             eventData.pointerDrag = m_ImplementationData.draggedGameObject;
    145.         }
    146.  
    147.         /// <summary>
    148.         /// Fills this object with the values from a <see cref="PointerEventData"/>.
    149.         /// </summary>
    150.         /// <param name="eventData">These objects are used to send data through the Unity UI (UGUI) system.</param>
    151.         public void CopyFrom(PointerEventData eventData)
    152.         {
    153.             m_ImplementationData.isDragging = eventData.dragging;
    154.             m_ImplementationData.pressedTime = eventData.clickTime;
    155.             m_ImplementationData.pressedPosition = eventData.pressPosition;
    156.             m_ImplementationData.pressedRaycast = eventData.pointerPressRaycast;
    157.             m_ImplementationData.pressedGameObject = eventData.pointerPress;
    158.             m_ImplementationData.pressedGameObjectRaw = eventData.rawPointerPress;
    159.             m_ImplementationData.draggedGameObject = eventData.pointerDrag;
    160.         }
    161.  
    162.         bool m_IsDown;
    163.         ImplementationData m_ImplementationData;
    164.     }
    165.  
    166.     struct MouseModel
    167.     {
    168.         internal struct InternalData
    169.         {
    170.             /// <summary>
    171.             /// This tracks the current GUI targets being hovered over.
    172.             /// </summary>
    173.             /// <seealso cref="PointerEventData.hovered"/>
    174.             public List<GameObject> hoverTargets { get; set; }
    175.  
    176.             /// <summary>
    177.             /// Tracks the current enter/exit target being hovered over at any given moment.
    178.             /// </summary>
    179.             /// <seealso cref="PointerEventData.pointerEnter"/>
    180.             public GameObject pointerTarget { get; set; }
    181.  
    182.             public void Reset()
    183.             {
    184.                 pointerTarget = null;
    185.  
    186.                 if (hoverTargets == null)
    187.                     hoverTargets = new List<GameObject>();
    188.                 else
    189.                     hoverTargets.Clear();
    190.             }
    191.         }
    192.  
    193.         /// <summary>
    194.         /// An Id representing a unique pointer.
    195.         /// </summary>
    196.         public int pointerId { get; }
    197.  
    198.         /// <summary>
    199.         /// A boolean value representing whether any mouse data has changed this frame, meaning that events should be processed.
    200.         /// </summary>
    201.         /// <remarks>
    202.         /// This only checks for changes in mouse state (<see cref="position"/>, <see cref="leftButton"/>, <see cref="rightButton"/>, <see cref="middleButton"/>, or <see cref="scrollDelta"/>).
    203.         /// </remarks>
    204.         public bool changedThisFrame { get; private set; }
    205.  
    206.         Vector2 m_Position;
    207.  
    208.         public Vector2 position
    209.         {
    210.             get => m_Position;
    211.             set
    212.             {
    213.                 if (m_Position != value)
    214.                 {
    215.                     deltaPosition = value - m_Position;
    216.                     m_Position = value;
    217.                     changedThisFrame = true;
    218.                 }
    219.             }
    220.         }
    221.  
    222.         /// <summary>
    223.         /// The pixel-space change in <see cref="position"/> since the last call to <see cref="OnFrameFinished"/>.
    224.         /// </summary>
    225.         public Vector2 deltaPosition { get; private set; }
    226.  
    227.         Vector2 m_ScrollDelta;
    228.  
    229.         /// <summary>
    230.         /// The amount of scroll since the last call to <see cref="OnFrameFinished"/>.
    231.         /// </summary>
    232.         public Vector2 scrollDelta
    233.         {
    234.             get => m_ScrollDelta;
    235.             set
    236.             {
    237.                 if (m_ScrollDelta != value)
    238.                 {
    239.                     m_ScrollDelta = value;
    240.                     changedThisFrame = true;
    241.                 }
    242.             }
    243.         }
    244.  
    245.         MouseButtonModel m_LeftButton;
    246.  
    247.         /// <summary>
    248.         /// Cached data and button state representing a left mouse button on a mouse.
    249.         /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states.
    250.         /// </summary>
    251.         public MouseButtonModel leftButton
    252.         {
    253.             get => m_LeftButton;
    254.             set
    255.             {
    256.                 changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
    257.                 m_LeftButton = value;
    258.             }
    259.         }
    260.  
    261.         /// <summary>
    262.         /// Sets the pressed state of the left mouse button.
    263.         /// </summary>
    264.         public bool leftButtonPressed
    265.         {
    266.             set
    267.             {
    268.                 changedThisFrame |= m_LeftButton.isDown != value;
    269.                 m_LeftButton.isDown = value;
    270.             }
    271.         }
    272.  
    273.         MouseButtonModel m_RightButton;
    274.  
    275.         /// <summary>
    276.         /// Cached data and button state representing a right mouse button on a mouse.
    277.         /// Unity UI (UGUI) uses this to keep track of persistent click, press, and drag states.
    278.         /// </summary>
    279.         public MouseButtonModel rightButton
    280.         {
    281.             get => m_RightButton;
    282.             set
    283.             {
    284.                 changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
    285.                 m_RightButton = value;
    286.             }
    287.         }
    288.  
    289.         /// <summary>
    290.         /// Sets the pressed state of the right mouse button.
    291.         /// </summary>
    292.         public bool rightButtonPressed
    293.         {
    294.             set
    295.             {
    296.                 changedThisFrame |= m_RightButton.isDown != value;
    297.                 m_RightButton.isDown = value;
    298.             }
    299.         }
    300.  
    301.         MouseButtonModel m_MiddleButton;
    302.  
    303.         /// <summary>
    304.         /// Cached data and button state representing a middle mouse button on a mouse.
    305.         /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states.
    306.         /// </summary>
    307.         public MouseButtonModel middleButton
    308.         {
    309.             get => m_MiddleButton;
    310.             set
    311.             {
    312.                 changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange);
    313.                 m_MiddleButton = value;
    314.             }
    315.         }
    316.  
    317.         /// <summary>
    318.         /// Sets the pressed state of the middle mouse button.
    319.         /// </summary>
    320.         public bool middleButtonPressed
    321.         {
    322.             set
    323.             {
    324.                 changedThisFrame |= m_MiddleButton.isDown != value;
    325.                 m_MiddleButton.isDown = value;
    326.             }
    327.         }
    328.      
    329.         public int displayIndex
    330.         {
    331.             get => m_DisplayIndex;
    332.             set
    333.             {
    334.                 if (m_DisplayIndex != value)
    335.                 {
    336.                     m_DisplayIndex = value;
    337.                     changedThisFrame = true;
    338.                 }
    339.             }
    340.         }
    341.      
    342.         private int m_DisplayIndex;
    343.  
    344.         InternalData m_InternalData;
    345.      
    346.  
    347.         public MouseModel(int pointerId)
    348.         {
    349.             this.pointerId = pointerId;
    350.             changedThisFrame = false;
    351.             m_Position = Vector2.zero;
    352.             deltaPosition = Vector2.zero;
    353.             m_ScrollDelta = Vector2.zero;
    354.  
    355.             m_LeftButton = new MouseButtonModel();
    356.             m_RightButton = new MouseButtonModel();
    357.             m_MiddleButton = new MouseButtonModel();
    358.             m_LeftButton.Reset();
    359.             m_RightButton.Reset();
    360.             m_MiddleButton.Reset();
    361.             m_DisplayIndex = 0;
    362.             m_InternalData = new InternalData();
    363.             m_InternalData.Reset();
    364.         }
    365.  
    366.         /// <summary>
    367.         /// Call this at the end of polling for per-frame changes.  This resets delta values, such as <see cref="deltaPosition"/>, <see cref="scrollDelta"/>, and <see cref="MouseButtonModel.lastFrameDelta"/>.
    368.         /// </summary>
    369.         public void OnFrameFinished()
    370.         {
    371.             changedThisFrame = false;
    372.             deltaPosition = Vector2.zero;
    373.             m_DisplayIndex = 0;
    374.             m_ScrollDelta = Vector2.zero;
    375.             m_LeftButton.OnFrameFinished();
    376.             m_RightButton.OnFrameFinished();
    377.             m_MiddleButton.OnFrameFinished();
    378.         }
    379.  
    380.         public void CopyTo(PointerEventData eventData)
    381.         {
    382.             eventData.pointerId = pointerId;
    383.             eventData.position = position;
    384.             eventData.delta = deltaPosition;
    385.             eventData.scrollDelta = scrollDelta;
    386.             eventData.pointerEnter = m_InternalData.pointerTarget;
    387.             eventData.hovered.Clear();
    388.             eventData.hovered.AddRange(m_InternalData.hoverTargets);
    389. #if UNITY_2022_3_OR_NEWER
    390.             eventData.displayIndex = displayIndex;
    391. #endif
    392.         }
    393.  
    394.         public void CopyFrom(PointerEventData eventData)
    395.         {
    396.             var hoverTargets = m_InternalData.hoverTargets;
    397.             m_InternalData.hoverTargets.Clear();
    398.             m_InternalData.hoverTargets.AddRange(eventData.hovered);
    399.             m_InternalData.hoverTargets = hoverTargets;
    400.             m_InternalData.pointerTarget = eventData.pointerEnter;
    401. #if UNITY_2022_3_OR_NEWER
    402.             m_DisplayIndex = eventData.displayIndex;
    403. #endif
    404.         }
    405.     }
    406. }
    407.  
     
    Last edited: Jan 11, 2024
    ntt_ebehar likes this.
  2. ntt_ebehar

    ntt_ebehar

    Joined:
    Sep 12, 2023
    Posts:
    9
    I just ran across this issue myself. I will test to see if this works for me and confirm later.
     
  3. ericprovencher

    ericprovencher

    Unity Technologies

    Joined:
    Dec 17, 2020
    Posts:
    252
    This bug has been replicated and is being investigated internally. Please stand by as we work out a fix.
     
    ntt_ebehar likes this.
  4. ntt_ebehar

    ntt_ebehar

    Joined:
    Sep 12, 2023
    Posts:
    9
    This worked for me but I had to make an additional edit to XRUIInputModule.cs. The new version is pasted below.

    My project uses the built in input system actions, so in ProcessMouse() the if condition
    Code (CSharp):
    1.  if (m_UseBuiltInInputSystemActions)
    equates to false. So with @jeepee-ef 's solution I still wasn't receiving mouse inputs on secondary monitors. But all I had to do was simply copy and paste the same line jeepee-ef added into the else statement and it worked. Thanks for figuring this out 2 weeks before I needed it @jeepee-ef :D <3

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.Controls;
    4. using UnityEngine.XR.Interaction.Toolkit.Utilities.Pooling;
    5.  
    6. namespace UnityEngine.XR.Interaction.Toolkit.UI
    7. {
    8.     /// <summary>
    9.     /// Matches the UI Model to the state of the Interactor.
    10.     /// </summary>
    11.     public interface IUIInteractor
    12.     {
    13.         /// <summary>
    14.         /// Updates the current UI Model to match the state of the Interactor.
    15.         /// </summary>
    16.         /// <param name="model">The returned model that will match this Interactor.</param>
    17.         void UpdateUIModel(ref TrackedDeviceModel model);
    18.  
    19.         /// <summary>
    20.         /// Attempts to retrieve the current UI Model.
    21.         /// </summary>
    22.         /// <param name="model">The returned model that reflects the UI state of this Interactor.</param>
    23.         /// <returns>Returns <see langword="true"/> if the model was able to retrieved. Otherwise, returns <see langword="false"/>.</returns>
    24.         bool TryGetUIModel(out TrackedDeviceModel model);
    25.     }
    26.  
    27.     /// <summary>
    28.     /// Matches the UI Model to the state of the Interactor with support for hover events.
    29.     /// </summary>
    30.     public interface IUIHoverInteractor : IUIInteractor
    31.     {
    32.         /// <summary>
    33.         /// The event that is called when the Interactor begins hovering over a UI element.
    34.         /// </summary>
    35.         /// <remarks>
    36.         /// The <see cref="UIHoverEventArgs"/> passed to each listener is only valid while the event is invoked,
    37.         /// do not hold a reference to it.
    38.         /// </remarks>
    39.         UIHoverEnterEvent uiHoverEntered { get; }
    40.  
    41.         /// <summary>
    42.         /// The event that is called when this Interactor ends hovering over a UI element.
    43.         /// </summary>
    44.         /// <remarks>
    45.         /// The <see cref="UIHoverEventArgs"/> passed to each listener is only valid while the event is invoked,
    46.         /// do not hold a reference to it.
    47.         /// </remarks>
    48.         UIHoverExitEvent uiHoverExited { get; }
    49.  
    50.         /// <summary>
    51.         /// The <see cref="XRUIInputModule"/> calls this method when the Interactor begins hovering over a UI element.
    52.         /// </summary>
    53.         /// <param name="args">Event data containing the UI element that is being hovered over.</param>
    54.         /// <remarks>
    55.         /// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
    56.         /// </remarks>
    57.         /// <seealso cref="OnUIHoverExited(UIHoverEventArgs)"/>
    58.         void OnUIHoverEntered(UIHoverEventArgs args);
    59.  
    60.         /// <summary>
    61.         /// The <see cref="XRUIInputModule"/> calls this method when the Interactor ends hovering over a UI element.
    62.         /// </summary>
    63.         /// <param name="args">Event data containing the UI element that is no longer hovered over.</param>
    64.         /// <remarks>
    65.         /// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
    66.         /// </remarks>
    67.         /// <seealso cref="OnUIHoverEntered(UIHoverEventArgs)"/>
    68.         void OnUIHoverExited(UIHoverEventArgs args);
    69.     }
    70.  
    71.     /// <summary>
    72.     /// Custom class for input modules that send UI input in XR.
    73.     /// </summary>
    74.     [AddComponentMenu("Event/XR UI Input Module", 11)]
    75.     [HelpURL(XRHelpURLConstants.k_XRUIInputModule)]
    76.     public partial class XRUIInputModule : UIInputModule
    77.     {
    78.         struct RegisteredInteractor
    79.         {
    80.             public IUIInteractor interactor;
    81.             public TrackedDeviceModel model;
    82.  
    83.             public RegisteredInteractor(IUIInteractor interactor, int deviceIndex)
    84.             {
    85.                 this.interactor = interactor;
    86.                 model = new TrackedDeviceModel(deviceIndex);
    87.             }
    88.         }
    89.  
    90.         struct RegisteredTouch
    91.         {
    92.             public bool isValid;
    93.             public int touchId;
    94.             public TouchModel model;
    95.  
    96.             public RegisteredTouch(Touch touch, int deviceIndex)
    97.             {
    98.                 touchId = touch.fingerId;
    99.                 model = new TouchModel(deviceIndex);
    100.                 isValid = true;
    101.             }
    102.         }
    103.  
    104.         /// <summary>
    105.         /// Represents which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.
    106.         /// </summary>
    107.         /// /// <seealso cref="activeInputMode"/>
    108.         public enum ActiveInputMode
    109.         {
    110.             /// <summary>
    111.             /// Only use input polled through the built-in Unity Input Manager (Old).
    112.             /// </summary>
    113.             InputManagerBindings,
    114.  
    115.             /// <summary>
    116.             /// Only use input polled from <see cref="InputActionReference"/> through the newer Input System package.
    117.             /// </summary>
    118.             InputSystemActions,
    119.  
    120.             /// <summary>
    121.             /// Scan through input from both Unity Input Manager and Input System action references.
    122.             /// Note: This may cause undesired effects or may impact performance if input configuration is duplicated.
    123.             /// </summary>
    124.             Both,
    125.         }
    126.  
    127. #if !ENABLE_INPUT_SYSTEM || !ENABLE_LEGACY_INPUT_MANAGER
    128.         [HideInInspector]
    129. #endif
    130.         [SerializeField]
    131.         [Tooltip("Represents which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.")]
    132.         ActiveInputMode m_ActiveInputMode;
    133.  
    134.         /// <summary>
    135.         /// Configures which Active Input Mode will be used in the situation where the Active Input Handling project setting is set to Both.
    136.         /// </summary>
    137.         /// <seealso cref="ActiveInputMode"/>
    138.         public ActiveInputMode activeInputMode
    139.         {
    140.             get => m_ActiveInputMode;
    141.             set => m_ActiveInputMode = value;
    142.         }
    143.  
    144.         [SerializeField, HideInInspector]
    145.         [Tooltip("The maximum distance to ray cast with tracked devices to find hit objects.")]
    146.         float m_MaxTrackedDeviceRaycastDistance = 1000f;
    147.  
    148.         [Header("Input Devices")]
    149.         [SerializeField]
    150.         [Tooltip("If true, will forward 3D tracked device data to UI elements.")]
    151.         bool m_EnableXRInput = true;
    152.  
    153.         /// <summary>
    154.         /// If <see langword="true"/>, will forward 3D tracked device data to UI elements.
    155.         /// </summary>
    156.         public bool enableXRInput
    157.         {
    158.             get => m_EnableXRInput;
    159.             set => m_EnableXRInput = value;
    160.         }
    161.  
    162.         [SerializeField]
    163.         [Tooltip("If true, will forward 2D mouse data to UI elements.")]
    164.         bool m_EnableMouseInput = true;
    165.  
    166.         /// <summary>
    167.         /// If <see langword="true"/>, will forward 2D mouse data to UI elements.
    168.         /// </summary>
    169.         public bool enableMouseInput
    170.         {
    171.             get => m_EnableMouseInput;
    172.             set => m_EnableMouseInput = value;
    173.         }
    174.  
    175.         [SerializeField]
    176.         [Tooltip("If true, will forward 2D touch data to UI elements.")]
    177.         bool m_EnableTouchInput = true;
    178.  
    179.         /// <summary>
    180.         /// If <see langword="true"/>, will forward 2D touch data to UI elements.
    181.         /// </summary>
    182.         public bool enableTouchInput
    183.         {
    184.             get => m_EnableTouchInput;
    185.             set => m_EnableTouchInput = value;
    186.         }
    187.  
    188. #if ENABLE_INPUT_SYSTEM
    189.         [Header("Input System UI Actions")]
    190. #else
    191.         [HideInInspector]
    192. #endif
    193.         [SerializeField]
    194.         [Tooltip("Pointer input action reference, such as a mouse or single-finger touch device.")]
    195.         InputActionReference m_PointAction;
    196.         /// <summary>
    197.         /// The Input System action to use to move the pointer on the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    198.         /// </summary>
    199.         public InputActionReference pointAction
    200.         {
    201.             get => m_PointAction;
    202.             set => SetInputAction(ref m_PointAction, value);
    203.         }
    204.  
    205. #if !ENABLE_INPUT_SYSTEM
    206.         [HideInInspector]
    207. #endif
    208.         [SerializeField]
    209.         [Tooltip("Left-click input action reference, typically the left button on a mouse.")]
    210.         InputActionReference m_LeftClickAction;
    211.         /// <summary>
    212.         /// The Input System action to use to determine whether the left button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    213.         /// </summary>
    214.         public InputActionReference leftClickAction
    215.         {
    216.             get => m_LeftClickAction;
    217.             set => SetInputAction(ref m_LeftClickAction, value);
    218.         }
    219.  
    220. #if !ENABLE_INPUT_SYSTEM
    221.         [HideInInspector]
    222. #endif
    223.         [SerializeField]
    224.         [Tooltip("Middle-click input action reference, typically the middle button on a mouse.")]
    225.         InputActionReference m_MiddleClickAction;
    226.         /// <summary>
    227.         /// The Input System action to use to determine whether the middle button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    228.         /// </summary>
    229.         public InputActionReference middleClickAction
    230.         {
    231.             get => m_MiddleClickAction;
    232.             set => SetInputAction(ref m_MiddleClickAction, value);
    233.         }
    234.  
    235. #if !ENABLE_INPUT_SYSTEM
    236.         [HideInInspector]
    237. #endif
    238.         [SerializeField]
    239.         [Tooltip("Right-click input action reference, typically the right button on a mouse.")]
    240.         InputActionReference m_RightClickAction;
    241.         /// <summary>
    242.         /// The Input System action to use to determine whether the right button of a pointer is pressed. Must be a <see cref="ButtonControl"/> Control.
    243.         /// </summary>
    244.         public InputActionReference rightClickAction
    245.         {
    246.             get => m_RightClickAction;
    247.             set => SetInputAction(ref m_RightClickAction, value);
    248.         }
    249.  
    250. #if !ENABLE_INPUT_SYSTEM
    251.         [HideInInspector]
    252. #endif
    253.         [SerializeField]
    254.         [Tooltip("Scroll wheel input action reference, typically the scroll wheel on a mouse.")]
    255.         InputActionReference m_ScrollWheelAction;
    256.         /// <summary>
    257.         /// The Input System action to use to move the pointer on the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    258.         /// </summary>
    259.         public InputActionReference scrollWheelAction
    260.         {
    261.             get => m_ScrollWheelAction;
    262.             set => SetInputAction(ref m_ScrollWheelAction, value);
    263.         }
    264.  
    265. #if !ENABLE_INPUT_SYSTEM
    266.         [HideInInspector]
    267. #endif
    268.         [SerializeField]
    269.         [Tooltip("Navigation input action reference will change which UI element is currently selected to the one up, down, left of or right of the currently selected one.")]
    270.         InputActionReference m_NavigateAction;
    271.         /// <summary>
    272.         /// The Input System action to use to navigate the currently active UI. Must be a <see cref="Vector2Control"/> Control.
    273.         /// </summary>
    274.         public InputActionReference navigateAction
    275.         {
    276.             get => m_NavigateAction;
    277.             set => SetInputAction(ref m_NavigateAction, value);
    278.         }
    279.  
    280. #if !ENABLE_INPUT_SYSTEM
    281.         [HideInInspector]
    282. #endif
    283.         [SerializeField]
    284.         [Tooltip("Submit input action reference will trigger a submission of the currently selected UI in the Event System.")]
    285.         InputActionReference m_SubmitAction;
    286.         /// <summary>
    287.         /// The Input System action to use for submitting or activating a UI element. Must be a <see cref="ButtonControl"/> Control.
    288.         /// </summary>
    289.         public InputActionReference submitAction
    290.         {
    291.             get => m_SubmitAction;
    292.             set => SetInputAction(ref m_SubmitAction, value);
    293.         }
    294.  
    295. #if !ENABLE_INPUT_SYSTEM
    296.         [HideInInspector]
    297. #endif
    298.         [SerializeField]
    299.         [Tooltip("Cancel input action reference will trigger canceling out of the currently selected UI in the Event System.")]
    300.         InputActionReference m_CancelAction;
    301.         /// <summary>
    302.         /// The Input System action to use for cancelling or backing out of a UI element. Must be a <see cref="ButtonControl"/> Control.
    303.         /// </summary>
    304.         public InputActionReference cancelAction
    305.         {
    306.             get => m_CancelAction;
    307.             set => SetInputAction(ref m_CancelAction, value);
    308.         }
    309.  
    310. #if !ENABLE_INPUT_SYSTEM
    311.         [HideInInspector]
    312. #endif
    313.         [SerializeField]
    314.         [Tooltip("When enabled, built-in Input System actions will be used if no Input System UI Actions are assigned.")]
    315.         bool m_EnableBuiltinActionsAsFallback = true;
    316.         /// <summary>
    317.         /// When enabled, built-in Input System actions will be used if no Input System UI Actions are assigned. This uses the
    318.         /// currently enabled Input System devices: <see cref="Mouse.current"/>, <see cref="Gamepad.current"/>, and <see cref="Joystick.current"/>.
    319.         /// </summary>
    320.         public bool enableBuiltinActionsAsFallback
    321.         {
    322.             get => m_EnableBuiltinActionsAsFallback;
    323.             set
    324.             {
    325.                 m_EnableBuiltinActionsAsFallback = value;
    326.                 m_UseBuiltInInputSystemActions = m_EnableBuiltinActionsAsFallback && !InputActionReferencesAreSet();
    327.             }
    328.         }
    329.  
    330. #if ENABLE_LEGACY_INPUT_MANAGER
    331.         [Header("Input Manager (Old) Gamepad/Joystick Bindings")]
    332. #else
    333.         [HideInInspector]
    334. #endif
    335.         [SerializeField]
    336.         [Tooltip("If true, will use the Input Manager (Old) configuration to forward gamepad data to UI elements.")]
    337.         bool m_EnableGamepadInput = true;
    338.  
    339.         /// <summary>
    340.         /// If <see langword="true"/>, will forward gamepad data to UI elements.
    341.         /// </summary>
    342.         public bool enableGamepadInput
    343.         {
    344.             get => m_EnableGamepadInput;
    345.             set => m_EnableGamepadInput = value;
    346.         }
    347.  
    348. #if !ENABLE_LEGACY_INPUT_MANAGER
    349.         [HideInInspector]
    350. #endif
    351.         [SerializeField]
    352.         [Tooltip("If true, will use the Input Manager (Old) configuration to forward joystick data to UI elements.")]
    353.         bool m_EnableJoystickInput = true;
    354.  
    355.         /// <summary>
    356.         /// If <see langword="true"/>, will forward joystick data to UI elements.
    357.         /// </summary>
    358.         public bool enableJoystickInput
    359.         {
    360.             get => m_EnableJoystickInput;
    361.             set => m_EnableJoystickInput = value;
    362.         }
    363.  
    364. #if !ENABLE_LEGACY_INPUT_MANAGER
    365.         [HideInInspector]
    366. #endif
    367.         [SerializeField]
    368.         [Tooltip("Name of the horizontal axis for gamepad/joystick UI navigation when using the old Input Manager.")]
    369.         string m_HorizontalAxis = "Horizontal";
    370.  
    371.         /// <summary>
    372.         /// Name of the horizontal axis for UI navigation when using the old Input Manager.
    373.         /// </summary>
    374.         public string horizontalAxis
    375.         {
    376.             get => m_HorizontalAxis;
    377.             set => m_HorizontalAxis = value;
    378.         }
    379.  
    380. #if !ENABLE_LEGACY_INPUT_MANAGER
    381.         [HideInInspector]
    382. #endif
    383.         [SerializeField]
    384.         [Tooltip("Name of the vertical axis for gamepad/joystick UI navigation when using the old Input Manager.")]
    385.         string m_VerticalAxis = "Vertical";
    386.  
    387.         /// <summary>
    388.         /// Name of the vertical axis for UI navigation when using the old Input Manager.
    389.         /// </summary>
    390.         public string verticalAxis
    391.         {
    392.             get => m_VerticalAxis;
    393.             set => m_VerticalAxis = value;
    394.         }
    395.  
    396. #if !ENABLE_LEGACY_INPUT_MANAGER
    397.         [HideInInspector]
    398. #endif
    399.         [SerializeField]
    400.         [Tooltip("Name of the gamepad/joystick button to use for UI selection or submission when using the old Input Manager.")]
    401.         string m_SubmitButton = "Submit";
    402.  
    403.         /// <summary>
    404.         /// Name of the gamepad/joystick button to use for UI selection or submission when using the old Input Manager.
    405.         /// </summary>
    406.         public string submitButton
    407.         {
    408.             get => m_SubmitButton;
    409.             set => m_SubmitButton = value;
    410.         }
    411.  
    412. #if !ENABLE_LEGACY_INPUT_MANAGER
    413.         [HideInInspector]
    414. #endif
    415.         [SerializeField]
    416.         [Tooltip("Name of the gamepad/joystick button to use for UI cancel or back commands when using the old Input Manager.")]
    417.         string m_CancelButton = "Cancel";
    418.  
    419.         /// <summary>
    420.         /// Name of the gamepad/joystick button to use for UI cancel or back commands when using the old Input Manager.
    421.         /// </summary>
    422.         public string cancelButton
    423.         {
    424.             get => m_CancelButton;
    425.             set => m_CancelButton = value;
    426.         }
    427.  
    428.         int m_RollingPointerId;
    429.         bool m_UseBuiltInInputSystemActions;
    430.  
    431.         MouseModel m_MouseState;
    432.         NavigationModel m_NavigationState;
    433.  
    434.         internal const float kPixelPerLine = 20f;
    435.  
    436.         readonly List<RegisteredTouch> m_RegisteredTouches = new List<RegisteredTouch>();
    437.         readonly List<RegisteredInteractor> m_RegisteredInteractors = new List<RegisteredInteractor>();
    438.  
    439.         // Reusable event args
    440.         readonly LinkedPool<UIHoverEventArgs> m_UIHoverEventArgs = new LinkedPool<UIHoverEventArgs>(() => new UIHoverEventArgs(), collectionCheck: false);
    441.  
    442.         /// <summary>
    443.         /// See <a href="https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnEnable.html">MonoBehavior.OnEnable</a>.
    444.         /// </summary>
    445.         protected override void OnEnable()
    446.         {
    447.             base.OnEnable();
    448.  
    449.             // Check active input mode is correct
    450. #if !ENABLE_INPUT_SYSTEM && ENABLE_LEGACY_INPUT_MANAGER
    451.             m_ActiveInputMode = ActiveInputMode.InputManagerBindings;
    452. #elif ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
    453.             m_ActiveInputMode = ActiveInputMode.InputSystemActions;
    454. #endif
    455.             m_MouseState = new MouseModel(m_RollingPointerId++);
    456.             m_NavigationState = new NavigationModel();
    457.  
    458.             m_UseBuiltInInputSystemActions = m_EnableBuiltinActionsAsFallback && !InputActionReferencesAreSet();
    459.  
    460.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    461.                 EnableAllActions();
    462.         }
    463.  
    464.         /// <summary>
    465.         /// See <a href="https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnDisable.html">MonoBehavior.OnDisable</a>.
    466.         /// </summary>
    467.         protected override void OnDisable()
    468.         {
    469.             RemovePointerEventData(m_MouseState.pointerId);
    470.  
    471.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    472.                 DisableAllActions();
    473.  
    474.             base.OnDisable();
    475.         }
    476.  
    477.         /// <summary>
    478.         /// Register an <see cref="IUIInteractor"/> with the UI system.
    479.         /// Calling this will enable it to start interacting with UI.
    480.         /// </summary>
    481.         /// <param name="interactor">The <see cref="IUIInteractor"/> to use.</param>
    482.         public void RegisterInteractor(IUIInteractor interactor)
    483.         {
    484.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    485.             {
    486.                 if (m_RegisteredInteractors[i].interactor == interactor)
    487.                     return;
    488.             }
    489.  
    490.             m_RegisteredInteractors.Add(new RegisteredInteractor(interactor, m_RollingPointerId++));
    491.         }
    492.  
    493.         /// <summary>
    494.         /// Unregisters an <see cref="IUIInteractor"/> with the UI system.
    495.         /// This cancels all UI Interaction and makes the <see cref="IUIInteractor"/> no longer able to affect UI.
    496.         /// </summary>
    497.         /// <param name="interactor">The <see cref="IUIInteractor"/> to stop using.</param>
    498.         public void UnregisterInteractor(IUIInteractor interactor)
    499.         {
    500.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    501.             {
    502.                 if (m_RegisteredInteractors[i].interactor == interactor)
    503.                 {
    504.                     var registeredInteractor = m_RegisteredInteractors[i];
    505.                     registeredInteractor.interactor = null;
    506.                     m_RegisteredInteractors[i] = registeredInteractor;
    507.                     return;
    508.                 }
    509.             }
    510.         }
    511.  
    512.         /// <summary>
    513.         /// Gets an <see cref="IUIInteractor"/> from its corresponding Unity UI Pointer Id.
    514.         /// This can be used to identify individual Interactors from the underlying UI Events.
    515.         /// </summary>
    516.         /// <param name="pointerId">A unique integer representing an object that can point at UI.</param>
    517.         /// <returns>Returns the interactor associated with <paramref name="pointerId"/>.
    518.         /// Returns <see langword="null"/> if no Interactor is associated (e.g. if it's a mouse event).</returns>
    519.         public IUIInteractor GetInteractor(int pointerId)
    520.         {
    521.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    522.             {
    523.                 if (m_RegisteredInteractors[i].model.pointerId == pointerId)
    524.                 {
    525.                     return m_RegisteredInteractors[i].interactor;
    526.                 }
    527.             }
    528.  
    529.             return null;
    530.         }
    531.  
    532.         /// <summary>Retrieves the UI Model for a selected <see cref="IUIInteractor"/>.</summary>
    533.         /// <param name="interactor">The <see cref="IUIInteractor"/> you want the model for.</param>
    534.         /// <param name="model">The returned model that reflects the UI state of the <paramref name="interactor"/>.</param>
    535.         /// <returns>Returns <see langword="true"/> if the model was able to retrieved. Otherwise, returns <see langword="false"/>.</returns>
    536.         public bool GetTrackedDeviceModel(IUIInteractor interactor, out TrackedDeviceModel model)
    537.         {
    538.             for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    539.             {
    540.                 if (m_RegisteredInteractors[i].interactor == interactor)
    541.                 {
    542.                     model = m_RegisteredInteractors[i].model;
    543.                     return true;
    544.                 }
    545.             }
    546.  
    547.             model = new TrackedDeviceModel(-1);
    548.             return false;
    549.         }
    550.  
    551.         /// <inheritdoc />
    552.         protected override void DoProcess()
    553.         {
    554.             if (m_EnableXRInput)
    555.             {
    556.                 for (var i = 0; i < m_RegisteredInteractors.Count; i++)
    557.                 {
    558.                     var registeredInteractor = m_RegisteredInteractors[i];
    559.  
    560.                     var oldTarget = registeredInteractor.model.implementationData.pointerTarget;
    561.  
    562.                     // If device is removed, we send a default state to unclick any UI
    563.                     if (registeredInteractor.interactor == null)
    564.                     {
    565.                         registeredInteractor.model.Reset(false);
    566.                         ProcessTrackedDevice(ref registeredInteractor.model, true);
    567.                         RemovePointerEventData(registeredInteractor.model.pointerId);
    568.                         m_RegisteredInteractors.RemoveAt(i--);
    569.                     }
    570.                     else
    571.                     {
    572.                         registeredInteractor.interactor.UpdateUIModel(ref registeredInteractor.model);
    573.                         ProcessTrackedDevice(ref registeredInteractor.model);
    574.                         m_RegisteredInteractors[i] = registeredInteractor;
    575.                     }
    576.                     // If hover target changed, send event
    577.                     var newTarget = registeredInteractor.model.implementationData.pointerTarget;
    578.                     if (oldTarget != newTarget)
    579.                     {
    580.                         using (m_UIHoverEventArgs.Get(out var args))
    581.                         {
    582.                             args.interactorObject = registeredInteractor.interactor;
    583.                             args.deviceModel = registeredInteractor.model;
    584.                             if (args.interactorObject != null && args.interactorObject is IUIHoverInteractor hoverInteractor)
    585.                             {
    586.                                 if (oldTarget != null)
    587.                                 {
    588.                                     args.uiObject = oldTarget;
    589.                                     hoverInteractor.OnUIHoverExited(args);
    590.                                 }
    591.  
    592.                                 if (newTarget != null)
    593.                                 {
    594.                                     args.uiObject = newTarget;
    595.                                     hoverInteractor.OnUIHoverEntered(args);
    596.                                 }
    597.                             }
    598.                         }
    599.                     }
    600.                 }
    601.             }
    602.  
    603.             // Touch needs to take precedence because of the mouse emulation layer
    604.             var hasTouches = false;
    605.             if (m_EnableTouchInput)
    606.                 hasTouches = ProcessTouches();
    607.  
    608.             if (m_EnableMouseInput && !hasTouches)
    609.                 ProcessMouse();
    610.  
    611.             ProcessNavigation();
    612.         }
    613.  
    614.         void ProcessMouse()
    615.         {
    616.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    617.             {
    618.                 if (m_UseBuiltInInputSystemActions)            
    619.                 {
    620.                     if (Mouse.current != null)
    621.                     {
    622.                         m_MouseState.position = Mouse.current.position.ReadValue();
    623.                         // NOTE: Fixes mouse not interacting with on secondary monitors.
    624.                         m_MouseState.displayIndex = Mouse.current.displayIndex.ReadValue();
    625.                         // END NOTE
    626.                         m_MouseState.scrollDelta = Mouse.current.scroll.ReadValue() * (1 / kPixelPerLine);
    627.                         m_MouseState.leftButtonPressed = Mouse.current.leftButton.isPressed;
    628.                         m_MouseState.rightButtonPressed = Mouse.current.rightButton.isPressed;
    629.                         m_MouseState.middleButtonPressed = Mouse.current.middleButton.isPressed;
    630.                     }
    631.                 }
    632.                 else
    633.                 {
    634.                     if (IsActionEnabled(m_PointAction))
    635.                         m_MouseState.position = m_PointAction.action.ReadValue<Vector2>();
    636.                     if (IsActionEnabled(m_ScrollWheelAction))
    637.                         m_MouseState.scrollDelta = m_ScrollWheelAction.action.ReadValue<Vector2>() * (1 / kPixelPerLine);
    638.                     if (IsActionEnabled(m_LeftClickAction))
    639.                         m_MouseState.leftButtonPressed = m_LeftClickAction.action.IsPressed();
    640.                     if (IsActionEnabled(m_RightClickAction))
    641.                         m_MouseState.rightButtonPressed = m_RightClickAction.action.IsPressed();
    642.                     if (IsActionEnabled(m_MiddleClickAction))
    643.                         m_MouseState.middleButtonPressed = m_MiddleClickAction.action.IsPressed();
    644.                  
    645.                     // NOTE: Fixes mouse not interacting with on secondary monitors.
    646.                     m_MouseState.displayIndex = Mouse.current.displayIndex.ReadValue();
    647.                     // END NOTE
    648.                 }
    649.             }
    650.  
    651.             if (m_ActiveInputMode != ActiveInputMode.InputSystemActions && Input.mousePresent)
    652.             {
    653.                 m_MouseState.position = Input.mousePosition;
    654.                 m_MouseState.scrollDelta = Input.mouseScrollDelta;
    655.                 m_MouseState.leftButtonPressed = Input.GetMouseButton(0);
    656.                 m_MouseState.rightButtonPressed = Input.GetMouseButton(1);
    657.                 m_MouseState.middleButtonPressed = Input.GetMouseButton(2);
    658.             }
    659.  
    660.             ProcessMouseState(ref m_MouseState);
    661.         }
    662.  
    663.         bool ProcessTouches()
    664.         {
    665.             var hasTouches = Input.touchCount > 0;
    666.             if (!hasTouches)
    667.                 return false;
    668.  
    669.             var touchCount = Input.touchCount;
    670.             for (var touchIndex = 0; touchIndex < touchCount; ++touchIndex)
    671.             {
    672.                 var touch = Input.GetTouch(touchIndex);
    673.                 var registeredTouchIndex = -1;
    674.  
    675.                 // Find if touch already exists
    676.                 for (var j = 0; j < m_RegisteredTouches.Count; j++)
    677.                 {
    678.                     if (touch.fingerId == m_RegisteredTouches[j].touchId)
    679.                     {
    680.                         registeredTouchIndex = j;
    681.                         break;
    682.                     }
    683.                 }
    684.  
    685.                 if (registeredTouchIndex < 0)
    686.                 {
    687.                     // Not found, search empty pool
    688.                     for (var j = 0; j < m_RegisteredTouches.Count; j++)
    689.                     {
    690.                         if (!m_RegisteredTouches[j].isValid)
    691.                         {
    692.                             // Re-use the Id
    693.                             var pointerId = m_RegisteredTouches[j].model.pointerId;
    694.                             m_RegisteredTouches[j] = new RegisteredTouch(touch, pointerId);
    695.                             registeredTouchIndex = j;
    696.                             break;
    697.                         }
    698.                     }
    699.  
    700.                     if (registeredTouchIndex < 0)
    701.                     {
    702.                         // No Empty slots, add one
    703.                         registeredTouchIndex = m_RegisteredTouches.Count;
    704.                         m_RegisteredTouches.Add(new RegisteredTouch(touch, m_RollingPointerId++));
    705.                     }
    706.                 }
    707.  
    708.                 var registeredTouch = m_RegisteredTouches[registeredTouchIndex];
    709.                 registeredTouch.model.selectPhase = touch.phase;
    710.                 registeredTouch.model.position = touch.position;
    711.                 m_RegisteredTouches[registeredTouchIndex] = registeredTouch;
    712.             }
    713.  
    714.             for (var i = 0; i < m_RegisteredTouches.Count; i++)
    715.             {
    716.                 var registeredTouch = m_RegisteredTouches[i];
    717.                 ProcessTouch(ref registeredTouch.model);
    718.                 if (registeredTouch.model.selectPhase == TouchPhase.Ended || registeredTouch.model.selectPhase == TouchPhase.Canceled)
    719.                     registeredTouch.isValid = false;
    720.                 m_RegisteredTouches[i] = registeredTouch;
    721.             }
    722.  
    723.             return true;
    724.         }
    725.  
    726.         void ProcessNavigation()
    727.         {
    728.             if (m_ActiveInputMode != ActiveInputMode.InputManagerBindings)
    729.             {
    730.                 if (m_UseBuiltInInputSystemActions)
    731.                 {
    732.                     if (Gamepad.current != null)
    733.                     {
    734.                         // Combine left stick and dpad for navigation movement
    735.                         m_NavigationState.move = Gamepad.current.leftStick.ReadValue() + Gamepad.current.dpad.ReadValue();
    736.                         m_NavigationState.submitButtonDown = Gamepad.current.buttonSouth.isPressed;
    737.                         m_NavigationState.cancelButtonDown = Gamepad.current.buttonEast.isPressed;
    738.                     }
    739.                     if (Joystick.current != null)
    740.                     {
    741.                         // Combine main joystick and hatswitch for navigation movement
    742.                         m_NavigationState.move = Joystick.current.stick.ReadValue() +
    743.                             (Joystick.current.hatswitch != null ? Joystick.current.hatswitch.ReadValue() : Vector2.zero);
    744.                         m_NavigationState.submitButtonDown = Joystick.current.trigger.isPressed;
    745.                         // This will always be false until we can rely on a secondary button from the joystick
    746.                         m_NavigationState.cancelButtonDown = false;
    747.                     }
    748.                 }
    749.                 else
    750.                 {
    751.                     if (IsActionEnabled(m_NavigateAction))
    752.                         m_NavigationState.move = m_NavigateAction.action.ReadValue<Vector2>();
    753.                     if (IsActionEnabled(m_SubmitAction))
    754.                         m_NavigationState.submitButtonDown = m_SubmitAction.action.WasPressedThisFrame();
    755.                     if (IsActionEnabled(m_CancelAction))
    756.                         m_NavigationState.cancelButtonDown = m_CancelAction.action.WasPressedThisFrame();
    757.                 }
    758.             }
    759.  
    760.             if (m_ActiveInputMode != ActiveInputMode.InputSystemActions && (m_EnableGamepadInput || m_EnableJoystickInput) && Input.GetJoystickNames().Length > 0)
    761.             {
    762.                 m_NavigationState.move = new Vector2(Input.GetAxis(m_HorizontalAxis), Input.GetAxis(m_VerticalAxis));
    763.                 m_NavigationState.submitButtonDown = Input.GetButton(m_SubmitButton);
    764.                 m_NavigationState.cancelButtonDown = Input.GetButton(m_CancelButton);
    765.             }
    766.  
    767.             base.ProcessNavigationState(ref m_NavigationState);
    768.         }
    769.  
    770.         bool InputActionReferencesAreSet()
    771.         {
    772.             return (m_PointAction != null ||
    773.                 m_LeftClickAction != null ||
    774.                 m_RightClickAction != null ||
    775.                 m_MiddleClickAction != null ||
    776.                 m_NavigateAction != null ||
    777.                 m_SubmitAction != null ||
    778.                 m_CancelAction != null ||
    779.                 m_ScrollWheelAction != null);
    780.         }
    781.  
    782.         void EnableAllActions()
    783.         {
    784.             EnableInputAction(m_PointAction);
    785.             EnableInputAction(m_LeftClickAction);
    786.             EnableInputAction(m_RightClickAction);
    787.             EnableInputAction(m_MiddleClickAction);
    788.             EnableInputAction(m_NavigateAction);
    789.             EnableInputAction(m_SubmitAction);
    790.             EnableInputAction(m_CancelAction);
    791.             EnableInputAction(m_ScrollWheelAction);
    792.         }
    793.  
    794.         void DisableAllActions()
    795.         {
    796.             DisableInputAction(m_PointAction);
    797.             DisableInputAction(m_LeftClickAction);
    798.             DisableInputAction(m_RightClickAction);
    799.             DisableInputAction(m_MiddleClickAction);
    800.             DisableInputAction(m_NavigateAction);
    801.             DisableInputAction(m_SubmitAction);
    802.             DisableInputAction(m_CancelAction);
    803.             DisableInputAction(m_ScrollWheelAction);
    804.         }
    805.  
    806.         static bool IsActionEnabled(InputActionReference inputAction)
    807.         {
    808.             return inputAction != null && inputAction.action != null && inputAction.action.enabled;
    809.         }
    810.  
    811.         static void EnableInputAction(InputActionReference inputAction)
    812.         {
    813.             if (inputAction == null || inputAction.action == null)
    814.                 return;
    815.             inputAction.action.Enable();
    816.         }
    817.  
    818.         static void DisableInputAction(InputActionReference inputAction)
    819.         {
    820.             if (inputAction == null || inputAction.action == null)
    821.                 return;
    822.             inputAction.action.Disable();
    823.         }
    824.  
    825.         void SetInputAction(ref InputActionReference inputAction, InputActionReference value)
    826.         {
    827.             if (Application.isPlaying && inputAction != null)
    828.                 inputAction.action?.Disable();
    829.  
    830.             inputAction = value;
    831.  
    832.             if (Application.isPlaying && isActiveAndEnabled && inputAction != null)
    833.                 inputAction.action?.Enable();
    834.         }
    835.     }
    836. }
     
    jpfranssen_ likes this.
  5. ntt_ebehar

    ntt_ebehar

    Joined:
    Sep 12, 2023
    Posts:
    9
  6. jpfranssen_

    jpfranssen_

    Joined:
    Jul 20, 2018
    Posts:
    11