Draft implementation of proper HID input report parsing based on HID descriptor.
2 using System.Runtime.InteropServices;
3 using System.Diagnostics;
9 /// Provide some utility functions for raw input handling.
16 /// <param name="aRawInputHandle"></param>
17 /// <param name="aRawInput"></param>
18 /// <param name="rawInputBuffer">Caller must free up memory on the pointer using Marshal.FreeHGlobal</param>
19 /// <returns></returns>
20 public static bool GetRawInputData(IntPtr aRawInputHandle, ref RAWINPUT aRawInput, ref IntPtr rawInputBuffer)
23 rawInputBuffer = IntPtr.Zero;
28 uint sizeOfHeader = (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
30 //Get the size of our raw input data.
31 Win32.Function.GetRawInputData(aRawInputHandle, Const.RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
33 //Allocate a large enough buffer
34 rawInputBuffer = Marshal.AllocHGlobal((int)dwSize);
36 //Now read our RAWINPUT data
37 if (Win32.Function.GetRawInputData(aRawInputHandle, Const.RID_INPUT, rawInputBuffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
43 aRawInput = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
47 Debug.WriteLine("GetRawInputData failed!");
57 /// <param name="aRawInputHandle"></param>
58 /// <param name="aUsagePage"></param>
59 /// <param name="aUsage"></param>
60 /// <returns></returns>
61 public static bool GetDeviceInfo(IntPtr hDevice, ref RID_DEVICE_INFO deviceInfo)
64 IntPtr deviceInfoBuffer = IntPtr.Zero;
68 uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
69 deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
71 int res = Win32.Function.GetRawInputDeviceInfo(hDevice, Const.RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
74 Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
79 deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
83 Debug.WriteLine("GetRawInputData failed!");
88 //Always executes, prevents memory leak
89 Marshal.FreeHGlobal(deviceInfoBuffer);
99 /// <param name="device"></param>
100 /// <returns></returns>
101 public static IntPtr GetPreParsedData(IntPtr hDevice)
103 uint ppDataSize = 256;
104 int result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, IntPtr.Zero, ref ppDataSize);
107 Debug.WriteLine("Failed to get raw input pre-parsed data size" + result + Marshal.GetLastWin32Error());
111 IntPtr ppData = Marshal.AllocHGlobal((int)ppDataSize);
112 result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, ppData, ref ppDataSize);
115 Debug.WriteLine("Failed to get raw input pre-parsed data" + result + Marshal.GetLastWin32Error());
124 /// <param name="driver"></param>
125 /// <param name="device"></param>
126 /// <param name="input"></param>
127 /// <param name="rawInput"></param>
128 /// <param name="usageType"></param>
129 /// <param name="usage"></param>
130 /// <param name="usageName"></param>
131 /// <returns></returns>
134 private bool GetUsageFromRawInput(TwinhanHidDriver driver, TwinhanHid device, NativeMethods.RAWINPUT input, IntPtr rawInput, out UsageType usageType, out int usage, out string usageName)
138 usageName = string.Empty;
139 if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEKEYBOARD)
141 if (input.data.keyboard.Flags == NativeMethods.RawInputKeyboardFlag.RI_KEY_BREAK)
144 // Key up event. We don't handle repeats, so ignore this.
147 NativeMethods.VirtualKey vk = input.data.keyboard.VKey;
148 if (vk == NativeMethods.VirtualKey.VK_CONTROL)
150 _modifiers |= VirtualKeyModifier.Control;
153 if (vk == NativeMethods.VirtualKey.VK_SHIFT)
155 _modifiers |= VirtualKeyModifier.Shift;
158 if (vk == NativeMethods.VirtualKey.VK_MENU)
160 _modifiers |= VirtualKeyModifier.Alt;
163 usageType = UsageType.Keyboard;
164 usage = (int)vk | (int)_modifiers;
165 usageName = vk.ToString();
168 usageName += string.Format(", modifiers = {0}", _modifiers);
171 else if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEHID)
173 if ((!driver.IsTerraTec && device.Name.Contains("Col03")) || (driver.IsTerraTec && device.Name.Contains("Col02")))
175 usageType = UsageType.Raw;
176 usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
177 usageName = string.Format("0x{0:x2}", usage);
179 else if (device.Name.Contains("Col05"))
181 usageType = UsageType.Ascii;
182 usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
183 usageName = string.Format("0x{0:x2}", usage);
187 byte[] report = new byte[input.data.hid.dwSizeHid];
188 Marshal.Copy(IntPtr.Add(rawInput, HID_INPUT_DATA_OFFSET), report, 0, report.Length);
189 uint usageCount = input.data.hid.dwCount;
190 NativeMethods.USAGE_AND_PAGE[] usages = new NativeMethods.USAGE_AND_PAGE[usageCount];
191 NativeMethods.HidStatus status = NativeMethods.HidP_GetUsagesEx(NativeMethods.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, device.PreParsedData, report, (uint)report.Length);
192 if (status != NativeMethods.HidStatus.HIDP_STATUS_SUCCESS)
194 this.LogError("Twinhan HID remote: failed to get raw input HID usages, device = {0}, status = {1}", device.Name, status);
195 Dump.DumpBinary(rawInput, (int)input.header.dwSize);
200 this.LogWarn("Twinhan HID remote: multiple simultaneous HID usages not supported");
202 NativeMethods.USAGE_AND_PAGE up = usages[0];
203 HidUsagePage page = (HidUsagePage)up.UsagePage;
205 if (page != HidUsagePage.MceRemote && usage == 0)
207 // Key up event. We don't handle repeats, so ignore this.
210 if (page == HidUsagePage.Consumer)
212 usageType = UsageType.Consumer;
213 usageName = Enum.GetName(typeof(HidConsumerUsage), up.Usage);
215 else if (page == HidUsagePage.MceRemote)
217 usageType = UsageType.Mce;
218 usageName = Enum.GetName(typeof(MceRemoteUsage), up.Usage);
222 this.LogError("Twinhan HID remote: unexpected usage page, device = {0}, page = {1}, usage = {2}", device.Name, page, up.Usage);
229 this.LogError("Twinhan HID remote: received input from unsupported input device type, device = {0}, type = {1}", device.Name, input.header.dwType);