A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Runtime.InteropServices; 4using UnityEngine.InputSystem.LowLevel; 5using UnityEngine.InputSystem.Utilities; 6using Unity.Collections.LowLevel.Unsafe; 7using UnityEngine.InputSystem.Controls; 8using UnityEngine.InputSystem.Layouts; 9 10////TODO: runtime remapping of control usages on a per-device basis 11 12////TODO: finer-grained control over what devices deliver input while running in background 13//// (e.g. get gamepad input but do *not* get mouse and keyboard input) 14 15////REVIEW: should be possible to completely hijack the input stream of a device such that its original input is suppressed 16 17////REVIEW: can we construct the control tree of devices on demand so that the user never has to pay for 18//// the heap objects of devices that aren't used? 19 20// per device functions: 21// - update/poll 22// - IOCTL 23// - text input 24// - configuration change 25// - make current 26// - on remove (also resets current) 27// 28// Ideally, these would *not* be virtual methods on InputDevice but use a different process (which?) 29// for associating responses with devices 30 31namespace UnityEngine.InputSystem 32{ 33 /// <summary> 34 /// Represents an input device which is always the root of a hierarchy of <see cref="InputControl"/> instances. 35 /// </summary> 36 /// <remarks> 37 /// Input devices act as the container for control hierarchies. Every hierarchy has to have 38 /// a device at the root. Devices cannot occur as children of other controls. 39 /// 40 /// Devices are usually created automatically in response to hardware being discovered by the Unity 41 /// runtime. However, it is possible to manually add devices using methods such as <see 42 /// cref="InputSystem.AddDevice{TDevice}(string)"/>. 43 /// 44 /// <example> 45 /// <code> 46 /// // Add a "synthetic" gamepad that isn't actually backed by hardware. 47 /// var gamepad = InputSystem.AddDevice&lt;Gamepad&gt;(); 48 /// </code> 49 /// </example> 50 /// 51 /// There are subclasses representing the most common types of devices, like <see cref="Mouse"/>, 52 /// <see cref="Keyboard"/>, <see cref="Gamepad"/>, and <see cref="Touchscreen"/>. 53 /// 54 /// To create your own types of devices, you can derive from InputDevice and register your device 55 /// as a new "layout". 56 /// 57 /// <example> 58 /// <code> 59 /// // InputControlLayoutAttribute attribute is only necessary if you want 60 /// // to override default behavior that occurs when registering your device 61 /// // as a layout. 62 /// // The most common use of InputControlLayoutAttribute is to direct the system 63 /// // to a custom "state struct" through the `stateType` property. See below for details. 64 /// [InputControlLayout(displayName = "My Device", stateType = typeof(MyDeviceState))] 65 /// #if UNITY_EDITOR 66 /// [InitializeOnLoad] 67 /// #endif 68 /// public class MyDevice : InputDevice 69 /// { 70 /// public ButtonControl button { get; private set; } 71 /// public AxisControl axis { get; private set; } 72 /// 73 /// // Register the device. 74 /// static MyDevice() 75 /// { 76 /// // In case you want instance of your device to automatically be created 77 /// // when specific hardware is detected by the Unity runtime, you have to 78 /// // add one or more "device matchers" (InputDeviceMatcher) for the layout. 79 /// // These matchers are compared to an InputDeviceDescription received from 80 /// // the Unity runtime when a device is connected. You can add them either 81 /// // using InputSystem.RegisterLayoutMatcher() or by directly specifying a 82 /// // matcher when registering the layout. 83 /// InputSystem.RegisterLayout&lt;MyDevice&gt;( 84 /// // For the sake of demonstration, let's assume your device is a HID 85 /// // and you want to match by PID and VID. 86 /// matches: new InputDeviceMatcher() 87 /// .WithInterface("HID") 88 /// .WithCapability("PID", 1234) 89 /// .WithCapability("VID", 5678)); 90 /// } 91 /// 92 /// // This is only to trigger the static class constructor to automatically run 93 /// // in the player. 94 /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] 95 /// private static void InitializeInPlayer() {} 96 /// 97 /// protected override void FinishSetup() 98 /// { 99 /// base.FinishSetup(); 100 /// button = GetChildControl&lt;ButtonControl&gt;("button"); 101 /// axis = GetChildControl&lt;AxisControl&gt;("axis"); 102 /// } 103 /// } 104 /// 105 /// // A "state struct" describes the memory format used by a device. Each device can 106 /// // receive and store memory in its custom format. InputControls are then connected 107 /// // the individual pieces of memory and read out values from them. 108 /// [StructLayout(LayoutKind.Explicit, Size = 32)] 109 /// public struct MyDeviceState : IInputStateTypeInfo 110 /// { 111 /// // In the case of a HID (which we assume for the sake of this demonstration), 112 /// // the format will be "HID". In practice, the format will depend on how your 113 /// // particular device is connected and fed into the input system. 114 /// // The format is a simple FourCC code that "tags" state memory blocks for the 115 /// // device to give a base level of safety checks on memory operations. 116 /// public FourCC format => return new FourCC('H', 'I', 'D'); 117 /// 118 /// // InputControlAttributes on fields tell the input system to create controls 119 /// // for the public fields found in the struct. 120 /// 121 /// // Assume a 16bit field of buttons. Create one button that is tied to 122 /// // bit #3 (zero-based). Note that buttons do not need to be stored as bits. 123 /// // They can also be stored as floats or shorts, for example. 124 /// [InputControl(name = "button", layout = "Button", bit = 3)] 125 /// public ushort buttons; 126 /// 127 /// // Create a floating-point axis. The name, if not supplied, is taken from 128 /// // the field. 129 /// [InputControl(layout = "Axis")] 130 /// public short axis; 131 /// } 132 /// </code> 133 /// </example> 134 /// 135 /// Devices can have usages like any other control (<see cref="InputControl.usages"/>). Unlike other controls, 136 /// however, usages of InputDevices are allowed to be changed on the fly without requiring a change to the 137 /// device layout (see <see cref="InputSystem.SetDeviceUsage(InputDevice,string)"/>). 138 /// 139 /// For a more complete example of how to implement custom input devices, check out the "Custom Device" 140 /// sample which you can install from the Unity package manager. 141 /// 142 /// And, as always, you can also find more information in the <a href="../manual/Devices.html">manual</a>. 143 /// </remarks> 144 /// <seealso cref="InputControl"/> 145 /// <seealso cref="Mouse"/> 146 /// <seealso cref="Keyboard"/> 147 /// <seealso cref="Gamepad"/> 148 /// <seealso cref="Touchscreen"/> 149 public class InputDevice : InputControl 150 { 151 /// <summary> 152 /// Value of an invalid <see cref="deviceId"/>. 153 /// </summary> 154 /// <remarks> 155 /// The input system will not assigned this ID to any device. 156 /// </remarks> 157 public const int InvalidDeviceId = 0; 158 159 internal const int kLocalParticipantId = 0; 160 internal const int kInvalidDeviceIndex = -1; 161 162 /// <summary> 163 /// Metadata describing the device (product name etc.). 164 /// </summary> 165 /// <remarks> 166 /// The description of a device is unchanging over its lifetime and does not 167 /// comprise data about a device's configuration (which is considered mutable). 168 /// 169 /// In most cases, the description for a device is supplied by the Unity runtime. 170 /// This it the case for all <see cref="native"/> input devices. However, it is 171 /// also possible to inject new devices in the form of device descriptions into 172 /// the system using <see cref="InputSystem.AddDevice(InputDeviceDescription)"/>. 173 /// 174 /// The description of a device is what is matched by an <see cref="InputDeviceMatcher"/> 175 /// to find the <see cref="InputControl.layout"/> to use for a device. 176 /// </remarks> 177 public InputDeviceDescription description => m_Description; 178 179 ////REVIEW: When we can break the API, probably makes sense to replace this single bool with one for sending and one for receiving events 180 /// <summary> 181 /// Whether the device is currently enabled (that is, sends and receives events). 182 /// </summary> 183 /// <remarks> 184 /// A device that is disabled will not receive events. I.e. events that are being sent to the device 185 /// will be ignored. 186 /// 187 /// When disabling a <see cref="native"/> device, a <see cref="DisableDeviceCommand">disable command</see> will 188 /// also be sent to the <see cref="IInputRuntime">runtime</see>. It depends on the specific runtime whether the 189 /// device command is supported but if it is, the device will be disabled in the runtime and no longer send 190 /// events. This is especially important for devices such as <see cref="Sensor">sensors</see> that incur both 191 /// computation and battery consumption overhead while enabled. 192 /// 193 /// Specific types of devices can choose to start out in disabled state by default. This is generally the 194 /// case for <see cref="Sensor">sensors</see> to ensure that their overhead is only incurred when actually 195 /// being used by the application. 196 /// </remarks> 197 /// <seealso cref="InputSystem.EnableDevice"/> 198 /// <seealso cref="InputSystem.DisableDevice"/> 199 public bool enabled 200 { 201 get 202 { 203 #if UNITY_EDITOR 204 if (InputState.currentUpdateType == InputUpdateType.Editor && (m_DeviceFlags & DeviceFlags.DisabledWhileInBackground) != 0) 205 return true; 206 #endif 207 208 if ((m_DeviceFlags & (DeviceFlags.DisabledInFrontend | DeviceFlags.DisabledWhileInBackground)) != 0) 209 return false; 210 211 return QueryEnabledStateFromRuntime(); 212 } 213 } 214 215 ////TODO: rename this to canReceiveInputInBackground (once we can break API) 216 /// <summary> 217 /// If true, the device is capable of delivering input while the application is running in the background, i.e. 218 /// while <c>Application.isFocused</c> is false. 219 /// </summary> 220 /// <value>Whether the device can generate input while in the background.</value> 221 /// <remarks> 222 /// The value of this property is determined by three separator factors. 223 /// 224 /// For one, <see cref="native"/> devices have an inherent value for this property that can be retrieved through 225 /// <see cref="QueryCanRunInBackground"/>. This determines whether at the input collection level, the device is 226 /// capable of producing input independent of application. This is rare and only a select set of hardware, platform, 227 /// and SDK/API combinations support this. The prominent class of input devices that in general do support this 228 /// behavior are VR devices. 229 /// 230 /// Furthermore, the property may be force-set through a device's <see cref="InputControl.layout"/> by 231 /// means of <see cref="InputControlLayout.canRunInBackground"/>. 232 /// 233 /// Lastly, in the editor, the value of the property may be overridden depending on <see cref="InputSettings.editorInputBehaviorInPlayMode"/> 234 /// in case certain devices are automatically kept running in play mode even when no Game View has focus. 235 /// 236 /// Be aware that as far as players are concerned, only certain platforms support running Unity while not having focus. 237 /// On mobile platforms, for example, this is generally not supported. In this case, the value of this property 238 /// has no impact on input while the application does not have focus. See <see cref="InputSettings.backgroundBehavior"/> 239 /// for more details. 240 /// </remarks> 241 /// <seealso cref="InputSettings.backgroundBehavior"/> 242 /// <seealso cref="InputControlLayout.canRunInBackground"/> 243 public bool canRunInBackground 244 { 245 get 246 { 247 // In the editor, "background" refers to "game view not focused", not to the editor not being active. 248 // So, we modulate canRunInBackground depending on how input should behave WRT game view according 249 // to the input settings. 250 #if UNITY_EDITOR 251 var gameViewFocus = InputSystem.settings.editorInputBehaviorInPlayMode; 252 if (gameViewFocus == InputSettings.EditorInputBehaviorInPlayMode.AllDevicesRespectGameViewFocus) 253 return false; // No device considered being able to run without game view focus. 254 if (gameViewFocus == InputSettings.EditorInputBehaviorInPlayMode.PointersAndKeyboardsRespectGameViewFocus) 255 return !(this is Pointer || this is Keyboard); // Anything but pointers and keyboards considered as being able to run in background. 256 #endif 257 258 return canDeviceRunInBackground; 259 } 260 } 261 /// <summary> 262 /// In editor, it may differ from canRunInBackground depending on the gameViewFocus setting. 263 /// This property is used by Device Debug View 264 /// </summary> 265 /// <value>Whether the device should generate input while in the background.</value> 266 internal bool canDeviceRunInBackground 267 { 268 get 269 { 270 if ((m_DeviceFlags & DeviceFlags.CanRunInBackgroundHasBeenQueried) != 0) 271 return (m_DeviceFlags & DeviceFlags.CanRunInBackground) != 0; 272 273 var command = QueryCanRunInBackground.Create(); 274 m_DeviceFlags |= DeviceFlags.CanRunInBackgroundHasBeenQueried; 275 if (ExecuteCommand(ref command) >= 0 && command.canRunInBackground) 276 { 277 m_DeviceFlags |= DeviceFlags.CanRunInBackground; 278 return true; 279 } 280 281 m_DeviceFlags &= ~DeviceFlags.CanRunInBackground; 282 return false; 283 } 284 } 285 286 /// <summary> 287 /// Whether the device has been added to the system. 288 /// </summary> 289 /// <value>If true, the device is currently among the devices in <see cref="InputSystem.devices"/>.</value> 290 /// <remarks> 291 /// Devices may be removed at any time. Either when their hardware is unplugged or when they 292 /// are manually removed through <see cref="InputSystem.RemoveDevice"/> or by being excluded 293 /// through <see cref="InputSettings.supportedDevices"/>. When a device is removed, its instance, 294 /// however, will not disappear. This property can be used to check whether the device is part 295 /// of the current set of active devices. 296 /// </remarks> 297 /// <seealso cref="InputSystem.devices"/> 298 public bool added => m_DeviceIndex != kInvalidDeviceIndex; 299 300 /// <summary> 301 /// Whether the device is mirrored from a remote input system and not actually present 302 /// as a "real" device in the local system. 303 /// </summary> 304 /// <value>Whether the device mirrors a device from a remotely connected input system.</value> 305 /// <seealso cref="InputSystem.remoting"/> 306 /// <seealso cref="InputRemoting"/> 307 public bool remote => (m_DeviceFlags & DeviceFlags.Remote) == DeviceFlags.Remote; 308 309 /// <summary> 310 /// Whether the device comes from the <see cref="IInputRuntime">runtime</see> 311 /// </summary> 312 /// <value>Whether the device has been discovered by the Unity runtime.</value> 313 /// <remarks> 314 /// Devices can be discovered when <see cref="IInputRuntime.onDeviceDiscovered">reported</see> 315 /// by the runtime or they can be added manually through the various <see cref="InputSystem.AddDevice(InputDevice)"> 316 /// AddDevice</see> APIs. Devices reported by the runtime will return true for this 317 /// property whereas devices added manually will return false. 318 /// 319 /// Devices reported by the runtime will usually come from the Unity engine itself. 320 /// </remarks> 321 /// <seealso cref="IInputRuntime"/> 322 /// <seealso cref="IInputRuntime.onDeviceDiscovered"/> 323 public bool native => (m_DeviceFlags & DeviceFlags.Native) == DeviceFlags.Native; 324 325 /// <summary> 326 /// Whether the device requires an extra update before rendering. 327 /// </summary> 328 /// <remarks> 329 /// The value of this property is determined by <see cref="InputControlLayout.updateBeforeRender"/> in 330 /// the device's <see cref="InputControlLayout">control layout</see>. 331 /// 332 /// The extra update is necessary for tracking devices that are used in rendering code. For example, 333 /// the eye transforms of an HMD should be refreshed right before rendering as refreshing only in the 334 /// beginning of the frame will lead to a noticeable lag. 335 /// </remarks> 336 /// <seealso cref="InputUpdateType.BeforeRender"/> 337 public bool updateBeforeRender => (m_DeviceFlags & DeviceFlags.UpdateBeforeRender) == DeviceFlags.UpdateBeforeRender; 338 339 /// <summary> 340 /// Unique numeric ID for the device. 341 /// </summary> 342 /// <remarks> 343 /// This is only assigned once a device has been added to the system. No two devices will receive the same 344 /// ID and no device will receive an ID that another device used before even if the device was removed. The 345 /// only exception to this is if a device gets re-created as part of a layout change. For example, if a new 346 /// layout is registered that replaces the <see cref="Mouse"/> layout, all <see cref="Mouse"/> devices will 347 /// get recreated but will keep their existing device IDs. 348 /// 349 /// IDs are assigned by the input runtime. 350 /// </remarks> 351 /// <seealso cref="IInputRuntime.AllocateDeviceId"/> 352 public int deviceId => m_DeviceId; 353 354 /// <summary> 355 /// Timestamp of last state event used to update the device. 356 /// </summary> 357 /// <remarks> 358 /// Events other than <see cref="LowLevel.StateEvent"/> and <see cref="LowLevel.DeltaStateEvent"/> will 359 /// not cause lastUpdateTime to be changed. 360 /// The "timeline" is reset to 0 when entering play mode. If there are any events incoming or device 361 /// updates which occur prior to entering play mode, these will appear negative. 362 /// </remarks> 363 public double lastUpdateTime => m_LastUpdateTimeInternal - InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup; 364 365 public bool wasUpdatedThisFrame => m_CurrentUpdateStepCount == InputUpdate.s_UpdateStepCount; 366 367 /// <summary> 368 /// A flattened list of controls that make up the device. 369 /// </summary> 370 /// <remarks> 371 /// Does not allocate. 372 /// </remarks> 373 public ReadOnlyArray<InputControl> allControls => 374 // Since m_ChildrenForEachControl contains the device's children as well as the children 375 // of each control in the hierarchy, and since each control can only have a single parent, 376 // this list will actually deliver a flattened list of all controls in the hierarchy (and without 377 // the device itself being listed). 378 new ReadOnlyArray<InputControl>(m_ChildrenForEachControl); 379 380 ////REVIEW: This violates the constraint of controls being required to not have reference types as value types. 381 /// <inheritdoc/> 382 public override Type valueType => typeof(byte[]); 383 384 /// <inheritdoc/> 385 public override int valueSizeInBytes => (int)m_StateBlock.alignedSizeInBytes; 386 387 // This one just leads to confusion as you can access it from subclasses and then be surprised 388 // that it doesn't only include members of those classes. 389 [Obsolete("Use 'InputSystem.devices' instead. (UnityUpgradable) -> InputSystem.devices", error: false)] 390 public static ReadOnlyArray<InputDevice> all => InputSystem.devices; 391 392 /// <summary> 393 /// This constructor is public for the sake of <c>Activator.CreateInstance</c> only. To construct 394 /// devices, use methods such as <see cref="InputSystem.AddDevice{TDevice}(string)"/>. Manually 395 /// using <c>new</c> on InputDevice will not result in a usable device. 396 /// </summary> 397 public InputDevice() 398 { 399 m_DeviceId = InvalidDeviceId; 400 m_ParticipantId = kLocalParticipantId; 401 m_DeviceIndex = kInvalidDeviceIndex; 402 } 403 404 ////REVIEW: Is making devices be byte[] values really all that useful? Seems better than returning nulls but 405 //// at the same time, seems questionable. 406 407 /// <inheritdoc/> 408 public override unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize) 409 { 410 throw new NotImplementedException(); 411 } 412 413 /// <inheritdoc/> 414 public override unsafe object ReadValueFromStateAsObject(void* statePtr) 415 { 416 if (m_DeviceIndex == kInvalidDeviceIndex) 417 return null; 418 419 var numBytes = stateBlock.alignedSizeInBytes; 420 var array = new byte[numBytes]; 421 fixed(byte* arrayPtr = array) 422 { 423 var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset; 424 UnsafeUtility.MemCpy(arrayPtr, adjustedStatePtr, numBytes); 425 } 426 427 return array; 428 } 429 430 /// <inheritdoc/> 431 public override unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize) 432 { 433 if (statePtr == null) 434 throw new ArgumentNullException(nameof(statePtr)); 435 if (bufferPtr == null) 436 throw new ArgumentNullException(nameof(bufferPtr)); 437 if (bufferSize < valueSizeInBytes) 438 throw new ArgumentException($"Buffer too small (expected: {valueSizeInBytes}, actual: {bufferSize}"); 439 440 var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset; 441 UnsafeUtility.MemCpy(bufferPtr, adjustedStatePtr, m_StateBlock.alignedSizeInBytes); 442 } 443 444 /// <inheritdoc/> 445 public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr) 446 { 447 if (firstStatePtr == null) 448 throw new ArgumentNullException(nameof(firstStatePtr)); 449 if (secondStatePtr == null) 450 throw new ArgumentNullException(nameof(secondStatePtr)); 451 452 var adjustedFirstStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset; 453 var adjustedSecondStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset; 454 455 return UnsafeUtility.MemCmp(adjustedFirstStatePtr, adjustedSecondStatePtr, 456 m_StateBlock.alignedSizeInBytes) == 0; 457 } 458 459 /// <summary> 460 /// Called by the system when the configuration of the device has changed. 461 /// </summary> 462 /// <seealso cref="DeviceConfigurationEvent"/> 463 internal void NotifyConfigurationChanged() 464 { 465 // Mark all controls in the hierarchy as having their config out of date. 466 // We don't want to update configuration right away but rather wait until 467 // someone actually depends on it. 468 isConfigUpToDate = false; 469 for (var i = 0; i < m_ChildrenForEachControl.Length; ++i) 470 m_ChildrenForEachControl[i].isConfigUpToDate = false; 471 472 // Make sure we fetch the enabled/disabled state again. 473 m_DeviceFlags &= ~DeviceFlags.DisabledStateHasBeenQueriedFromRuntime; 474 475 OnConfigurationChanged(); 476 } 477 478 /// <summary> 479 /// Make this the current device of its type. 480 /// </summary> 481 /// <remarks> 482 /// This method is called automatically by the input system when a device is 483 /// added or when input is received on it. Many types of devices have <c>.current</c> 484 /// getters that allow querying the last used device of a specific type directly (for 485 /// example, see <see cref="Gamepad.current"/>). 486 /// 487 /// There is one special case, however, related to noise. A device that has noisy controls 488 /// (i.e. controls for which <see cref="InputControl.noisy"/> is true) may receive input events 489 /// that contain no meaningful user interaction but are simply just noise from the device. A 490 /// good example of this is the PS4 gamepad which has a built-in gyro and may thus constantly 491 /// feed events into the input system even if not being actually in use. If, for example, an 492 /// Xbox gamepad and PS4 gamepad are both connected to a PC and the user is playing with the 493 /// Xbox gamepad, the PS4 gamepad would still constantly make itself <see cref="Gamepad.current"/> 494 /// by simply flooding the system with events. Hence why by default, noise on <c>.current</c> getters 495 /// will be filtered out and a device will only see <c>MakeCurrent</c> getting called if their input 496 /// was detected on non-noisy controls. 497 /// </remarks> 498 /// <seealso cref="Pointer.current"/> 499 /// <seealso cref="Gamepad.current"/> 500 /// <seealso cref="Mouse.current"/> 501 /// <seealso cref="Pen.current"/> 502 public virtual void MakeCurrent() 503 { 504 } 505 506 /// <summary> 507 /// Called by the system when the device is added to <see cref="InputSystem.devices"/>. 508 /// </summary> 509 /// <remarks> 510 /// This is called <em>after</em> the device has already been added. 511 /// </remarks> 512 /// <seealso cref="InputSystem.devices"/> 513 /// <seealso cref="InputDeviceChange.Added"/> 514 /// <seealso cref="OnRemoved"/> 515 protected virtual void OnAdded() 516 { 517 } 518 519 /// <summary> 520 /// Called by the system when the device is removed from <see cref="InputSystem.devices"/>. 521 /// </summary> 522 /// <remarks> 523 /// This is called <em>after</em> the device has already been removed. 524 /// </remarks> 525 /// <seealso cref="InputSystem.devices"/> 526 /// <seealso cref="InputDeviceChange.Removed"/> 527 /// <seealso cref="OnRemoved"/> 528 protected virtual void OnRemoved() 529 { 530 } 531 532 /// <summary> 533 /// Called by the system when the device configuration is changed. This happens when the backend sends 534 /// a <see cref="DeviceConfigurationEvent"/> for the device. 535 /// </summary> 536 /// <remarks> 537 /// This method can be used to flush out cached information. An example of where this happens is <see cref="Controls.KeyControl"/> 538 /// caching information about the display name of a control. As this depends on the current keyboard layout, the information 539 /// has to be fetched dynamically (this happens using <see cref="QueryKeyNameCommand"/>). Whenever the keyboard layout changes, 540 /// the system sends a <see cref="DeviceConfigurationEvent"/> for the <see cref="Keyboard"/> at which point the device flushes 541 /// all cached key names. 542 /// </remarks> 543 /// <seealso cref="InputManager.OnUpdate"/> 544 /// <seealso cref="InputDeviceChange.ConfigurationChanged"/> 545 /// <seealso cref="OnConfigurationChanged"/>/// 546 protected virtual void OnConfigurationChanged() 547 { 548 } 549 550 ////TODO: add overridable OnDisable/OnEnable that fire the device commands 551 552 ////REVIEW: return just bool instead of long and require everything else to go in the command? 553 /// <summary> 554 /// Perform a device-specific command. 555 /// </summary> 556 /// <param name="command">Data for the command to be performed.</param> 557 /// <returns>A transfer-specific return code. Negative values are considered failure codes.</returns> 558 /// <remarks> 559 /// Commands allow devices to set up custom protocols without having to extend 560 /// the device API. This is most useful for devices implemented in the native Unity runtime 561 /// which, through the command interface, may provide custom, device-specific functions. 562 /// 563 /// This is a low-level API. It works in a similar way to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&amp;MSPPError=-2147217396" target="_blank"> 564 /// DeviceIoControl</a> on Windows and <a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/ioctl.2.html#//apple_ref/doc/man/2/ioctl" target="_blank">ioctl</a> 565 /// on UNIX-like systems. 566 /// </remarks> 567 public unsafe long ExecuteCommand<TCommand>(ref TCommand command) 568 where TCommand : struct, IInputDeviceCommandInfo 569 { 570 var commandPtr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command); 571 572 // Give callbacks first shot. 573 var manager = InputSystem.s_Manager; 574 manager.m_DeviceCommandCallbacks.LockForChanges(); 575 for (var i = 0; i < manager.m_DeviceCommandCallbacks.length; ++i) 576 { 577 try 578 { 579 var result = manager.m_DeviceCommandCallbacks[i](this, commandPtr); 580 if (result.HasValue) 581 return result.Value; 582 } 583 catch (Exception exception) 584 { 585 Debug.LogError($"{exception.GetType().Name} while executing 'InputSystem.onDeviceCommand' callbacks"); 586 Debug.LogException(exception); 587 } 588 } 589 manager.m_DeviceCommandCallbacks.UnlockForChanges(); 590 591 return ExecuteCommand((InputDeviceCommand*)UnsafeUtility.AddressOf(ref command)); 592 } 593 594 protected virtual unsafe long ExecuteCommand(InputDeviceCommand* commandPtr) 595 { 596 return InputRuntime.s_Instance.DeviceCommand(deviceId, commandPtr); 597 } 598 599 internal bool QueryEnabledStateFromRuntime() 600 { 601 // Fetch state from runtime, if necessary. 602 if ((m_DeviceFlags & DeviceFlags.DisabledStateHasBeenQueriedFromRuntime) == 0) 603 { 604 var command = QueryEnabledStateCommand.Create(); 605 if (ExecuteCommand(ref command) >= 0) 606 { 607 if (command.isEnabled) 608 m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime; 609 else 610 m_DeviceFlags |= DeviceFlags.DisabledInRuntime; 611 } 612 else 613 { 614 // We got no response on the enable/disable state. Assume device is enabled. 615 m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime; 616 } 617 618 // Only fetch enable/disable state again if we get a configuration change event. 619 m_DeviceFlags |= DeviceFlags.DisabledStateHasBeenQueriedFromRuntime; 620 } 621 622 return (m_DeviceFlags & DeviceFlags.DisabledInRuntime) == 0; 623 } 624 625 [Serializable] 626 [Flags] 627 internal enum DeviceFlags 628 { 629 UpdateBeforeRender = 1 << 0, 630 631 HasStateCallbacks = 1 << 1, 632 HasControlsWithDefaultState = 1 << 2, 633 HasDontResetControls = 1 << 10, 634 HasEventMerger = 1 << 13, 635 HasEventPreProcessor = 1 << 14, 636 637 Remote = 1 << 3, // It's a local mirror of a device from a remote player connection. 638 Native = 1 << 4, // It's a device created from data surfaced by NativeInputRuntime. 639 640 DisabledInFrontend = 1 << 5, // Explicitly disabled on the managed side. 641 DisabledInRuntime = 1 << 7, // Disabled in the native runtime. 642 DisabledWhileInBackground = 1 << 8, // Disabled while the player is running in the background. 643 DisabledStateHasBeenQueriedFromRuntime = 1 << 6, // Whether we have fetched the current enable/disable state from the runtime. 644 645 CanRunInBackground = 1 << 11, 646 CanRunInBackgroundHasBeenQueried = 1 << 12, 647 } 648 649 internal bool disabledInFrontend 650 { 651 get => (m_DeviceFlags & DeviceFlags.DisabledInFrontend) != 0; 652 set 653 { 654 if (value) 655 m_DeviceFlags |= DeviceFlags.DisabledInFrontend; 656 else 657 m_DeviceFlags &= ~DeviceFlags.DisabledInFrontend; 658 } 659 } 660 661 internal bool disabledInRuntime 662 { 663 get => (m_DeviceFlags & DeviceFlags.DisabledInRuntime) != 0; 664 set 665 { 666 if (value) 667 m_DeviceFlags |= DeviceFlags.DisabledInRuntime; 668 else 669 m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime; 670 } 671 } 672 673 internal bool disabledWhileInBackground 674 { 675 get => (m_DeviceFlags & DeviceFlags.DisabledWhileInBackground) != 0; 676 set 677 { 678 if (value) 679 m_DeviceFlags |= DeviceFlags.DisabledWhileInBackground; 680 else 681 m_DeviceFlags &= ~DeviceFlags.DisabledWhileInBackground; 682 } 683 } 684 685 internal DeviceFlags m_DeviceFlags; 686 internal int m_DeviceId; 687 internal int m_ParticipantId; 688 // Index in InputManager.m_Devices. 689 internal int m_DeviceIndex; 690 // Amount of bytes processed in the current update step. Used only for logging purposes. 691 internal uint m_CurrentProcessedEventBytesOnUpdate; 692 internal InputDeviceDescription m_Description; 693 694 /// <summary> 695 /// Timestamp of last event we received. 696 /// </summary> 697 /// <seealso cref="LowLevel.InputEvent.time"/> 698 internal double m_LastUpdateTimeInternal; 699 700 // Update count corresponding to the current front buffers that are active on the device. 701 // We use this to know when to flip buffers. 702 internal uint m_CurrentUpdateStepCount; 703 704 // List of aliases for all controls. Each control gets a slice of this array. 705 // See 'InputControl.aliases'. 706 // NOTE: The device's own aliases are part of this array as well. 707 internal InternedString[] m_AliasesForEachControl; 708 709 // List of usages for all controls. Each control gets a slice of this array. 710 // See 'InputControl.usages'. 711 // NOTE: The device's own usages are part of this array as well. They are always 712 // at the *end* of the array. 713 internal InternedString[] m_UsagesForEachControl; 714 // This one does NOT contain the device itself, i.e. it only contains controls on the device 715 // and may this be shorter than m_UsagesForEachControl. 716 internal InputControl[] m_UsageToControl; 717 718 // List of children for all controls. Each control gets a slice of this array. 719 // See 'InputControl.children'. 720 // NOTE: The device's own children are part of this array as well. 721 internal InputControl[] m_ChildrenForEachControl; 722 723 // Used with value caching to track updated button press states. 724 internal HashSet<int> m_UpdatedButtons; 725 726 // Used for updating button press states when we don't take the value caching path. 727 internal List<ButtonControl> m_ButtonControlsCheckingPressState; 728 729 // Once we hit about 45 ButtonControls being queried for wasPressedThisFrame/wasReleasedThisFrame, mark as such 730 // so that we can take the ReadValueCaching path for more efficient updating. 731 internal bool m_UseCachePathForButtonPresses = false; 732 733 // An ordered list of ints each containing a bit offset into the state of the device (*without* the added global 734 // offset), a bit count for the size of the state of the control, and an associated index into m_ChildrenForEachControl 735 // for the corresponding control. 736 // NOTE: This contains *leaf* controls only. 737 internal uint[] m_StateOffsetToControlMap; 738 739 // Holds the nodes that represent the tree of memory ranges that each control occupies. This is used when 740 // determining what controls have changed given a state event or partial state update. 741 internal ControlBitRangeNode[] m_ControlTreeNodes; 742 743 // An indirection table for control bit range nodes to point at zero or more controls. Indices are used to 744 // point into the m_ChildrenForEachControl array. 745 internal ushort[] m_ControlTreeIndices; 746 747 // When a device gets built from a layout, we create a binary tree from its controls where each node in the tree 748 // represents the range of bits that cover the left or right section of the parent range. For example, starting 749 // with the entire device state block as the parent, where the state block is 100 bits long, the left node will 750 // cover from bits 0-50, and the right from bits 51-99. For the left node, we'll get two more child nodes where 751 // the left will cover bits 0-25, and the right bits 26-49 and so on. Each node will point at any controls that 752 // either fit exactly into its range, or overlap the splitting point between both nodes. In reality, picking the 753 // mid-point to split each parent node is a little convoluted and will rarely be the absolute mid-point, but that's 754 // the basic idea. 755 // 756 // At runtime, when state events come in, we can then really quickly perform a bunch of memcmps on both sides of 757 // the tree and recurse down the branches that have changed. When nodes have controls, we can then check if those 758 // controls have changes, and mark them as stale so their cached values get updated the next time their values 759 // are read. 760 [StructLayout(LayoutKind.Sequential, Pack = 1)] 761 internal struct ControlBitRangeNode 762 { 763 // only store the end bit offset of each range because we always do a full tree traversal so 764 // the start offset is always calculated at each level. 765 public ushort endBitOffset; 766 767 // points to the location in the nodes array where the left child of this node lives, or -1 if there 768 // is no child. The right child is always at the next index. 769 public short leftChildIndex; 770 771 // each node can point at multiple controls (because multiple controls can use the same range in memory and 772 // also because of overlaps in bit ranges). The control indicies for each node are stored contiguously in the 773 // m_ControlTreeIndicies array on the device, which acts as an indirection table, and these two values tell 774 // us where to start for each node and how many controls this node points at. This is an unsigned short so that 775 // we could in theory support devices with up to 65535 controls. Each node however can only support 255 controls. 776 public ushort controlStartIndex; 777 public byte controlCount; 778 779 public ControlBitRangeNode(ushort endOffset) 780 { 781 controlStartIndex = 0; 782 controlCount = 0; 783 endBitOffset = endOffset; 784 leftChildIndex = -1; 785 } 786 } 787 788 // ATM we pack everything into 32 bits. Given we're operating on bit offsets and counts, this imposes some tight limits 789 // on controls and their associated state memory. Should this turn out to be a problem, bump m_StateOffsetToControlMap 790 // to a ulong[] and up the counts here to account for having 64 bits available instead of only 32. 791 internal const int kControlIndexBits = 10; // 1024 controls max. 792 internal const int kStateOffsetBits = 13; // 1024 bytes max state size for entire device. 793 internal const int kStateSizeBits = 9; // 64 bytes max for an individual leaf control. 794 795 internal static uint EncodeStateOffsetToControlMapEntry(uint controlIndex, uint stateOffsetInBits, uint stateSizeInBits) 796 { 797 Debug.Assert(kControlIndexBits < 32, $"Expected kControlIndexBits < 32, so we fit into the 32 bit wide bitmask"); 798 Debug.Assert(kStateOffsetBits < 32, $"Expected kStateOffsetBits < 32, so we fit into the 32 bit wide bitmask"); 799 Debug.Assert(kStateSizeBits < 32, $"Expected kStateSizeBits < 32, so we fit into the 32 bit wide bitmask"); 800 Debug.Assert(controlIndex < (1U << kControlIndexBits), "Control index beyond what is supported"); 801 Debug.Assert(stateOffsetInBits < (1U << kStateOffsetBits), "State offset beyond what is supported"); 802 Debug.Assert(stateSizeInBits < (1U << kStateSizeBits), "State size beyond what is supported"); 803 return stateOffsetInBits << (kControlIndexBits + kStateSizeBits) | stateSizeInBits << kControlIndexBits | controlIndex; 804 } 805 806 internal static void DecodeStateOffsetToControlMapEntry(uint entry, out uint controlIndex, 807 out uint stateOffset, out uint stateSize) 808 { 809 controlIndex = entry & (1U << kControlIndexBits) - 1; 810 stateOffset = entry >> (kControlIndexBits + kStateSizeBits); 811 stateSize = (entry >> kControlIndexBits) & (((1U << (kControlIndexBits + kStateSizeBits)) - 1) >> kControlIndexBits); 812 } 813 814 // NOTE: We don't store processors in a combined array the same way we do for 815 // usages and children as that would require lots of casting from 'object'. 816 817 /// <summary> 818 /// If true, the device has at least one control that has an explicit default state. 819 /// </summary> 820 internal bool hasControlsWithDefaultState 821 { 822 get => (m_DeviceFlags & DeviceFlags.HasControlsWithDefaultState) == DeviceFlags.HasControlsWithDefaultState; 823 set 824 { 825 if (value) 826 m_DeviceFlags |= DeviceFlags.HasControlsWithDefaultState; 827 else 828 m_DeviceFlags &= ~DeviceFlags.HasControlsWithDefaultState; 829 } 830 } 831 832 internal bool hasDontResetControls 833 { 834 get => (m_DeviceFlags & DeviceFlags.HasDontResetControls) == DeviceFlags.HasDontResetControls; 835 set 836 { 837 if (value) 838 m_DeviceFlags |= DeviceFlags.HasDontResetControls; 839 else 840 m_DeviceFlags &= ~DeviceFlags.HasDontResetControls; 841 } 842 } 843 844 internal bool hasStateCallbacks 845 { 846 get => (m_DeviceFlags & DeviceFlags.HasStateCallbacks) == DeviceFlags.HasStateCallbacks; 847 set 848 { 849 if (value) 850 m_DeviceFlags |= DeviceFlags.HasStateCallbacks; 851 else 852 m_DeviceFlags &= ~DeviceFlags.HasStateCallbacks; 853 } 854 } 855 856 internal bool hasEventMerger 857 { 858 get => (m_DeviceFlags & DeviceFlags.HasEventMerger) == DeviceFlags.HasEventMerger; 859 set 860 { 861 if (value) 862 m_DeviceFlags |= DeviceFlags.HasEventMerger; 863 else 864 m_DeviceFlags &= ~DeviceFlags.HasEventMerger; 865 } 866 } 867 868 internal bool hasEventPreProcessor 869 { 870 get => (m_DeviceFlags & DeviceFlags.HasEventPreProcessor) == DeviceFlags.HasEventPreProcessor; 871 set 872 { 873 if (value) 874 m_DeviceFlags |= DeviceFlags.HasEventPreProcessor; 875 else 876 m_DeviceFlags &= ~DeviceFlags.HasEventPreProcessor; 877 } 878 } 879 880 internal void AddDeviceUsage(InternedString usage) 881 { 882 var controlUsageCount = m_UsageToControl.LengthSafe(); 883 var totalUsageCount = controlUsageCount + m_UsageCount; 884 if (m_UsageCount == 0) 885 m_UsageStartIndex = totalUsageCount; 886 ArrayHelpers.AppendWithCapacity(ref m_UsagesForEachControl, ref totalUsageCount, usage); 887 ++m_UsageCount; 888 } 889 890 internal void RemoveDeviceUsage(InternedString usage) 891 { 892 var controlUsageCount = m_UsageToControl.LengthSafe(); 893 var totalUsageCount = controlUsageCount + m_UsageCount; 894 895 var index = ArrayHelpers.IndexOfValue(m_UsagesForEachControl, usage, m_UsageStartIndex, totalUsageCount); 896 if (index == -1) 897 return; 898 899 Debug.Assert(m_UsageCount > 0); 900 ArrayHelpers.EraseAtWithCapacity(m_UsagesForEachControl, ref totalUsageCount, index); 901 --m_UsageCount; 902 903 if (m_UsageCount == 0) 904 m_UsageStartIndex = default; 905 } 906 907 internal void ClearDeviceUsages() 908 { 909 for (var i = m_UsageStartIndex; i < m_UsageCount; ++i) 910 m_UsagesForEachControl[i] = default; 911 m_UsageCount = default; 912 } 913 914 internal bool RequestSync() 915 { 916 SetOptimizedControlDataTypeRecursively(); 917 918 var syncCommand = RequestSyncCommand.Create(); 919 return device.ExecuteCommand(ref syncCommand) >= 0; 920 } 921 922 internal bool RequestReset() 923 { 924 SetOptimizedControlDataTypeRecursively(); 925 926 var resetCommand = RequestResetCommand.Create(); 927 return device.ExecuteCommand(ref resetCommand) >= 0; 928 } 929 930 internal bool ExecuteEnableCommand() 931 { 932 SetOptimizedControlDataTypeRecursively(); 933 934 var command = EnableDeviceCommand.Create(); 935 return device.ExecuteCommand(ref command) >= 0; 936 } 937 938 internal bool ExecuteDisableCommand() 939 { 940 var command = DisableDeviceCommand.Create(); 941 return device.ExecuteCommand(ref command) >= 0; 942 } 943 944 internal void NotifyAdded() 945 { 946 OnAdded(); 947 } 948 949 internal void NotifyRemoved() 950 { 951 OnRemoved(); 952 } 953 954 internal static TDevice Build<TDevice>(string layoutName = default, string layoutVariants = default, InputDeviceDescription deviceDescription = default, bool noPrecompiledLayouts = false) 955 where TDevice : InputDevice 956 { 957 var internedLayoutName = new InternedString(layoutName); 958 959 if (internedLayoutName.IsEmpty()) 960 { 961 internedLayoutName = InputControlLayout.s_Layouts.TryFindLayoutForType(typeof(TDevice)); 962 if (internedLayoutName.IsEmpty()) 963 internedLayoutName = new InternedString(typeof(TDevice).Name); 964 } 965 966 // Fast path: see if we can use a precompiled version. 967 // NOTE: We currently do not support layout variants with precompiled layouts. 968 // NOTE: We remove precompiled layouts when they are invalidated by layout changes. So, we don't have to perform 969 // checks here. 970 if (!noPrecompiledLayouts && 971 string.IsNullOrEmpty(layoutVariants) && 972 InputControlLayout.s_Layouts.precompiledLayouts.TryGetValue(internedLayoutName, out var precompiledLayout)) 973 { 974 // Yes. This is pretty much a direct new() of the device. 975 return (TDevice)precompiledLayout.factoryMethod(); 976 } 977 978 // Slow path: use InputDeviceBuilder to construct the device from the InputControlLayout. 979 using (InputDeviceBuilder.Ref()) 980 { 981 InputDeviceBuilder.instance.Setup(internedLayoutName, new InternedString(layoutVariants), 982 deviceDescription: deviceDescription); 983 var device = InputDeviceBuilder.instance.Finish(); 984 if (!(device is TDevice deviceOfType)) 985 throw new ArgumentException( 986 $"Expected device of type '{typeof(TDevice).Name}' but got device of type '{device.GetType().Name}' instead", 987 "TDevice"); 988 989 return deviceOfType; 990 } 991 } 992 993 internal unsafe void WriteChangedControlStates(byte* deviceStateBuffer, void* statePtr, uint stateSizeInBytes, 994 uint stateOffsetInDevice) 995 { 996 Debug.Assert(m_ControlTreeNodes != null && m_ControlTreeIndices != null); 997 998 if (m_ControlTreeNodes.Length == 0) 999 return; 1000 1001 // Reset counter for how many controls have updated 1002 m_UpdatedButtons.Clear(); 1003 1004 // if we're dealing with a delta state event or just an individual control update through InputState.ChangeState 1005 // the size of the new data will not be the same size as the device state block, so use the 'partial' change state 1006 // method to update just those controls that overlap with the changed state. 1007 if (m_StateBlock.sizeInBits != stateSizeInBytes * 8) 1008 { 1009 if (m_ControlTreeNodes[0].leftChildIndex != -1) 1010 WritePartialChangedControlStatesInternal(stateSizeInBytes * 8, 1011 stateOffsetInDevice * 8, m_ControlTreeNodes[0], 0); 1012 } 1013 else 1014 { 1015 if (m_ControlTreeNodes[0].leftChildIndex != -1) 1016 WriteChangedControlStatesInternal(statePtr, deviceStateBuffer, m_ControlTreeNodes[0], 0); 1017 } 1018 } 1019 1020 private void WritePartialChangedControlStatesInternal(uint stateSizeInBits, 1021 uint stateOffsetInDeviceInBits, ControlBitRangeNode parentNode, uint startOffset) 1022 { 1023 var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex]; 1024 // TODO recheck 1025 if (Math.Max(stateOffsetInDeviceInBits, startOffset) <= 1026 Math.Min(stateOffsetInDeviceInBits + stateSizeInBits, leftNode.endBitOffset)) 1027 { 1028 var controlEndIndex = leftNode.controlStartIndex + leftNode.controlCount; 1029 for (int i = leftNode.controlStartIndex; i < controlEndIndex; i++) 1030 { 1031 var controlIndex = m_ControlTreeIndices[i]; 1032 var control = m_ChildrenForEachControl[controlIndex]; 1033 control.MarkAsStale(); 1034 if (control.isButton && ((ButtonControl)control).needsToCheckFramePress) 1035 m_UpdatedButtons.Add(controlIndex); 1036 } 1037 1038 if (leftNode.leftChildIndex != -1) 1039 WritePartialChangedControlStatesInternal(stateSizeInBits, stateOffsetInDeviceInBits, leftNode, startOffset); 1040 } 1041 1042 var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1]; 1043 // TODO recheck 1044 if (Math.Max(stateOffsetInDeviceInBits, leftNode.endBitOffset) <= 1045 Math.Min(stateOffsetInDeviceInBits + stateSizeInBits, rightNode.endBitOffset)) 1046 { 1047 var controlEndIndex = rightNode.controlStartIndex + rightNode.controlCount; 1048 for (int i = rightNode.controlStartIndex; i < controlEndIndex; i++) 1049 { 1050 var controlIndex = m_ControlTreeIndices[i]; 1051 var control = m_ChildrenForEachControl[controlIndex]; 1052 control.MarkAsStale(); 1053 if (control.isButton && ((ButtonControl)control).needsToCheckFramePress) 1054 m_UpdatedButtons.Add(controlIndex); 1055 } 1056 1057 if (rightNode.leftChildIndex != -1) 1058 WritePartialChangedControlStatesInternal(stateSizeInBits, stateOffsetInDeviceInBits, rightNode, leftNode.endBitOffset); 1059 } 1060 } 1061 1062 private void DumpControlBitRangeNode(int nodeIndex, ControlBitRangeNode node, uint startOffset, uint sizeInBits, List<string> output) 1063 { 1064 var names = new List<string>(); 1065 for (var i = 0; i < node.controlCount; i++) 1066 { 1067 var controlIndex = m_ControlTreeIndices[node.controlStartIndex + i]; 1068 var control = m_ChildrenForEachControl[controlIndex]; 1069 names.Add(control.path); 1070 } 1071 var namesStr = string.Join(", ", names); 1072 var children = node.leftChildIndex != -1 ? $" <{node.leftChildIndex}, {node.leftChildIndex + 1}>" : ""; 1073 output.Add($"{nodeIndex} [{startOffset}, {startOffset + sizeInBits}]{children}->{namesStr}"); 1074 } 1075 1076 private void DumpControlTree(ControlBitRangeNode parentNode, uint startOffset, List<string> output) 1077 { 1078 var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex]; 1079 var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1]; 1080 DumpControlBitRangeNode(parentNode.leftChildIndex, leftNode, startOffset, leftNode.endBitOffset - startOffset, output); 1081 DumpControlBitRangeNode(parentNode.leftChildIndex + 1, rightNode, leftNode.endBitOffset, (uint)(rightNode.endBitOffset - leftNode.endBitOffset), output); 1082 1083 if (leftNode.leftChildIndex != -1) 1084 DumpControlTree(leftNode, startOffset, output); 1085 1086 if (rightNode.leftChildIndex != -1) 1087 DumpControlTree(rightNode, leftNode.endBitOffset, output); 1088 } 1089 1090 internal string DumpControlTree() 1091 { 1092 var output = new List<string>(); 1093 DumpControlTree(m_ControlTreeNodes[0], 0, output); 1094 return string.Join("\n", output); 1095 } 1096 1097 private unsafe void WriteChangedControlStatesInternal(void* statePtr, 1098 byte* deviceStatePtr, ControlBitRangeNode parentNode, uint startOffset) 1099 { 1100 var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex]; 1101 1102 // have any bits in the region defined by the left node changed? 1103 // TODO recheck 1104 if (HasDataChangedInRange(deviceStatePtr, statePtr, startOffset, leftNode.endBitOffset - startOffset + 1)) 1105 { 1106 // update the state of any controls pointed to by the left node 1107 var controlEndIndex = leftNode.controlStartIndex + leftNode.controlCount; 1108 for (int i = leftNode.controlStartIndex; i < controlEndIndex; i++) 1109 { 1110 var controlIndex = m_ControlTreeIndices[i]; 1111 var control = m_ChildrenForEachControl[controlIndex]; 1112 1113 // nodes aren't always an exact fit for control memory ranges so check here if the control pointed 1114 // at by this node has actually changed state so we don't mark controls as stale needlessly. 1115 // We need to offset the device and new state pointers by the byte offset of the device state block 1116 // because all controls have this offset baked into them, but deviceStatePtr points at the already 1117 // offset block of device memory (remember, all devices share one big block of memory) and statePtr 1118 // points at a block of memory of the same size as the device state. 1119 if (!control.CompareState(deviceStatePtr - m_StateBlock.byteOffset, 1120 (byte*)statePtr - m_StateBlock.byteOffset, null)) 1121 { 1122 control.MarkAsStale(); 1123 if (control.isButton && ((ButtonControl)control).needsToCheckFramePress) 1124 m_UpdatedButtons.Add(controlIndex); 1125 } 1126 } 1127 1128 // process the left child node if it exists 1129 if (leftNode.leftChildIndex != -1) 1130 WriteChangedControlStatesInternal(statePtr, deviceStatePtr, 1131 leftNode, startOffset); 1132 } 1133 1134 // process the right child node if it exists 1135 var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1]; 1136 1137 Debug.Assert(leftNode.endBitOffset + (rightNode.endBitOffset - leftNode.endBitOffset) < m_StateBlock.sizeInBits, 1138 "Tried to check state memory outside the bounds of the current device."); 1139 1140 // if no bits in the range defined by the right node have changed, return 1141 // TODO recheck 1142 if (!HasDataChangedInRange(deviceStatePtr, statePtr, leftNode.endBitOffset, 1143 (uint)(rightNode.endBitOffset - leftNode.endBitOffset + 1))) 1144 return; 1145 1146 // update the state of any controls pointed to by the right node 1147 var rightNodeControlEndIndex = rightNode.controlStartIndex + rightNode.controlCount; 1148 for (int i = rightNode.controlStartIndex; i < rightNodeControlEndIndex; i++) 1149 { 1150 var controlIndex = m_ControlTreeIndices[i]; 1151 var control = m_ChildrenForEachControl[controlIndex]; 1152 1153 if (!control.CompareState(deviceStatePtr - m_StateBlock.byteOffset, 1154 (byte*)statePtr - m_StateBlock.byteOffset, null)) 1155 { 1156 control.MarkAsStale(); 1157 if (control.isButton && ((ButtonControl)control).needsToCheckFramePress) 1158 m_UpdatedButtons.Add(controlIndex); 1159 } 1160 } 1161 1162 if (rightNode.leftChildIndex != -1) 1163 WriteChangedControlStatesInternal(statePtr, deviceStatePtr, 1164 rightNode, leftNode.endBitOffset); 1165 } 1166 1167 private static unsafe bool HasDataChangedInRange(byte* deviceStatePtr, void* statePtr, uint startOffset, uint sizeInBits) 1168 { 1169 if (sizeInBits == 1) 1170 return MemoryHelpers.ReadSingleBit(deviceStatePtr, startOffset) != 1171 MemoryHelpers.ReadSingleBit(statePtr, startOffset); 1172 1173 return !MemoryHelpers.MemCmpBitRegion(deviceStatePtr, statePtr, 1174 startOffset, sizeInBits); 1175 } 1176 } 1177}