A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR
2using System;
3using System.Collections.Generic;
4using UnityEditor;
5using UnityEditor.IMGUI.Controls;
6using UnityEngine.InputSystem.Layouts;
7using UnityEngine.InputSystem.LowLevel;
8
9////TODO: use two columns for treeview and separate name and value
10
11namespace UnityEngine.InputSystem.HID.Editor
12{
13 /// <summary>
14 /// A window that dumps a raw HID descriptor in a tree view.
15 /// </summary>
16 /// <remarks>
17 /// Not specific to InputDevices of type <see cref="HID"/> so that it can work with
18 /// any <see cref="InputDevice"/> created for a device using the "HID" interface.
19 /// </remarks>
20 internal class HIDDescriptorWindow : EditorWindow, ISerializationCallbackReceiver
21 {
22 public static void CreateOrShowExisting(int deviceId, InputDeviceDescription deviceDescription)
23 {
24 // See if we have an existing window for the device and if so pop it
25 // in front.
26 if (s_OpenWindows != null)
27 {
28 for (var i = 0; i < s_OpenWindows.Count; ++i)
29 {
30 var existingWindow = s_OpenWindows[i];
31 if (existingWindow.m_DeviceId == deviceId)
32 {
33 existingWindow.Show();
34 existingWindow.Focus();
35 return;
36 }
37 }
38 }
39
40 // No, so create a new one.
41 var window = CreateInstance<HIDDescriptorWindow>();
42 window.InitializeWith(deviceId, deviceDescription);
43 window.minSize = new Vector2(270, 200);
44 window.Show();
45 window.titleContent = new GUIContent("HID Descriptor");
46 }
47
48 public void Awake()
49 {
50 AddToList();
51 }
52
53 public void OnDestroy()
54 {
55 RemoveFromList();
56 }
57
58 public void OnGUI()
59 {
60 if (!m_Initialized)
61 InitializeWith(m_DeviceId, m_DeviceDescription);
62
63 GUILayout.BeginHorizontal(EditorStyles.toolbar);
64 GUILayout.Label(m_Label, GUILayout.MinWidth(100), GUILayout.ExpandWidth(true));
65 GUILayout.EndHorizontal();
66
67 var rect = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true));
68 m_TreeView.OnGUI(rect);
69 }
70
71 private void InitializeWith(int deviceId, InputDeviceDescription deviceDescription)
72 {
73 m_DeviceId = deviceId;
74 m_DeviceDescription = deviceDescription;
75 m_Initialized = true;
76
77 // Set up tree view for HID descriptor.
78 var hidDescriptor = HID.ReadHIDDeviceDescriptor(ref m_DeviceDescription,
79 (ref InputDeviceCommand command) => InputRuntime.s_Instance.DeviceCommand(m_DeviceId, ref command));
80 if (m_TreeViewState == null)
81 m_TreeViewState = new TreeViewState();
82 m_TreeView = new HIDDescriptorTreeView(m_TreeViewState, hidDescriptor);
83 m_TreeView.SetExpanded(1, true);
84
85 m_Label = new GUIContent(
86 $"HID Descriptor for '{deviceDescription.manufacturer} {deviceDescription.product}'");
87 }
88
89 [NonSerialized] private bool m_Initialized;
90 [NonSerialized] private HIDDescriptorTreeView m_TreeView;
91 [NonSerialized] private GUIContent m_Label;
92
93 [SerializeField] private int m_DeviceId;
94 [SerializeField] private InputDeviceDescription m_DeviceDescription;
95 [SerializeField] private TreeViewState m_TreeViewState;
96
97 private void AddToList()
98 {
99 if (s_OpenWindows == null)
100 s_OpenWindows = new List<HIDDescriptorWindow>();
101 if (!s_OpenWindows.Contains(this))
102 s_OpenWindows.Add(this);
103 }
104
105 private void RemoveFromList()
106 {
107 if (s_OpenWindows != null)
108 s_OpenWindows.Remove(this);
109 }
110
111 private static List<HIDDescriptorWindow> s_OpenWindows;
112
113 private class HIDDescriptorTreeView : TreeView
114 {
115 private HID.HIDDeviceDescriptor m_Descriptor;
116
117 public HIDDescriptorTreeView(TreeViewState state, HID.HIDDeviceDescriptor descriptor)
118 : base(state)
119 {
120 m_Descriptor = descriptor;
121 Reload();
122 }
123
124 protected override TreeViewItem BuildRoot()
125 {
126 var id = 0;
127
128 var root = new TreeViewItem
129 {
130 id = id++,
131 depth = -1
132 };
133
134 var item = BuildDeviceItem(m_Descriptor, ref id);
135 root.AddChild(item);
136
137 return root;
138 }
139
140 private TreeViewItem BuildDeviceItem(HID.HIDDeviceDescriptor device, ref int id)
141 {
142 var item = new TreeViewItem
143 {
144 id = id++,
145 depth = 0,
146 displayName = "Device"
147 };
148
149 AddChild(item, string.Format("Vendor ID: 0x{0:X}", device.vendorId), ref id);
150 AddChild(item, string.Format("Product ID: 0x{0:X}", device.productId), ref id);
151 AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)device.usagePage, device.usagePage), ref id);
152 AddChild(item, string.Format("Usage: 0x{0:X}", device.usage), ref id);
153 AddChild(item, "Input Report Size: " + device.inputReportSize, ref id);
154 AddChild(item, "Output Report Size: " + device.outputReportSize, ref id);
155 AddChild(item, "Feature Report Size: " + device.featureReportSize, ref id);
156
157 // Elements.
158 if (device.elements != null)
159 {
160 var elementCount = device.elements.Length;
161 var elements = AddChild(item, elementCount + " Elements", ref id);
162 for (var i = 0; i < elementCount; ++i)
163 BuildElementItem(i, elements, device.elements[i], ref id);
164 }
165 else
166 AddChild(item, "0 Elements", ref id);
167
168 ////TODO: collections
169
170 return item;
171 }
172
173 private TreeViewItem BuildElementItem(int index, TreeViewItem parent, HID.HIDElementDescriptor element, ref int id)
174 {
175 var item = AddChild(parent, string.Format("Element {0} ({1})", index, element.reportType), ref id);
176
177 string usagePageString = HID.UsagePageToString(element.usagePage);
178 string usageString = HID.UsageToString(element.usagePage, element.usage);
179
180 AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)element.usagePage, usagePageString), ref id);
181 if (usageString != null)
182 AddChild(item, string.Format("Usage: 0x{0:X} ({1})", element.usage, usageString), ref id);
183 else
184 AddChild(item, string.Format("Usage: 0x{0:X}", element.usage), ref id);
185
186 AddChild(item, "Report Type: " + element.reportType, ref id);
187 AddChild(item, "Report ID: " + element.reportId, ref id);
188 AddChild(item, "Report Size in Bits: " + element.reportSizeInBits, ref id);
189 AddChild(item, "Report Bit Offset: " + element.reportOffsetInBits, ref id);
190 AddChild(item, "Collection Index: " + element.collectionIndex, ref id);
191 AddChild(item, string.Format("Unit: {0:X}", element.unit), ref id);
192 AddChild(item, string.Format("Unit Exponent: {0:X}", element.unitExponent), ref id);
193 AddChild(item, "Logical Min: " + element.logicalMin, ref id);
194 AddChild(item, "Logical Max: " + element.logicalMax, ref id);
195 AddChild(item, "Physical Min: " + element.physicalMin, ref id);
196 AddChild(item, "Physical Max: " + element.physicalMax, ref id);
197 AddChild(item, "Has Null State?: " + element.hasNullState, ref id);
198 AddChild(item, "Has Preferred State?: " + element.hasPreferredState, ref id);
199 AddChild(item, "Is Array?: " + element.isArray, ref id);
200 AddChild(item, "Is Non-Linear?: " + element.isNonLinear, ref id);
201 AddChild(item, "Is Relative?: " + element.isRelative, ref id);
202 AddChild(item, "Is Constant?: " + element.isConstant, ref id);
203 AddChild(item, "Is Wrapping?: " + element.isWrapping, ref id);
204
205 return item;
206 }
207
208 private TreeViewItem AddChild(TreeViewItem parent, string displayName, ref int id)
209 {
210 var item = new TreeViewItem
211 {
212 id = id++,
213 depth = parent.depth + 1,
214 displayName = displayName
215 };
216
217 parent.AddChild(item);
218
219 return item;
220 }
221 }
222
223 public void OnBeforeSerialize()
224 {
225 }
226
227 public void OnAfterDeserialize()
228 {
229 AddToList();
230 }
231 }
232}
233#endif // UNITY_EDITOR