# HG changeset patch
# User sl
# Date 1417816239 -3600
# Node ID 8f7e35c3bfd162b9bd9f93a637c831ba6d535cec
# Parent  9a3e776550313356891c73f1493a26a246ee9dc9
Draft implementation of proper HID input report parsing based on HID descriptor.
diff -r 9a3e77655031 -r 8f7e35c3bfd1 RawInput.cs
--- a/RawInput.cs	Wed Dec 03 21:54:45 2014 +0100
+++ b/RawInput.cs	Fri Dec 05 22:50:39 2014 +0100
@@ -5,6 +5,9 @@
 
 namespace Win32
 {
+    /// 
+    /// Provide some utility functions for raw input handling.
+    /// 
     static class RawInput
     {
         /// 
@@ -90,5 +93,145 @@
             return success;
         }
 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        public static IntPtr GetPreParsedData(IntPtr hDevice)
+        {
+            uint ppDataSize = 256;
+            int result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, IntPtr.Zero, ref ppDataSize);
+            if (result != 0)
+            {
+                Debug.WriteLine("Failed to get raw input pre-parsed data size" + result + Marshal.GetLastWin32Error());
+                return IntPtr.Zero;
+            }
+
+            IntPtr ppData = Marshal.AllocHGlobal((int)ppDataSize);
+            result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, ppData, ref ppDataSize);
+            if (result <= 0)
+            {
+                Debug.WriteLine("Failed to get raw input pre-parsed data" + result + Marshal.GetLastWin32Error());
+                return IntPtr.Zero;
+            }
+            return ppData;
+        }
+
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /*
+        private bool GetUsageFromRawInput(TwinhanHidDriver driver, TwinhanHid device, NativeMethods.RAWINPUT input, IntPtr rawInput, out UsageType usageType, out int usage, out string usageName)
+        {
+            usageType = 0;
+            usage = 0;
+            usageName = string.Empty;
+            if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEKEYBOARD)
+            {
+                if (input.data.keyboard.Flags == NativeMethods.RawInputKeyboardFlag.RI_KEY_BREAK)
+                {
+                    _modifiers = 0;
+                    // Key up event. We don't handle repeats, so ignore this.
+                    return false;
+                }
+                NativeMethods.VirtualKey vk = input.data.keyboard.VKey;
+                if (vk == NativeMethods.VirtualKey.VK_CONTROL)
+                {
+                    _modifiers |= VirtualKeyModifier.Control;
+                    return false;
+                }
+                if (vk == NativeMethods.VirtualKey.VK_SHIFT)
+                {
+                    _modifiers |= VirtualKeyModifier.Shift;
+                    return false;
+                }
+                if (vk == NativeMethods.VirtualKey.VK_MENU)
+                {
+                    _modifiers |= VirtualKeyModifier.Alt;
+                    return false;
+                }
+                usageType = UsageType.Keyboard;
+                usage = (int)vk | (int)_modifiers;
+                usageName = vk.ToString();
+                if (_modifiers != 0)
+                {
+                    usageName += string.Format(", modifiers = {0}", _modifiers);
+                }
+            }
+            else if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEHID)
+            {
+                if ((!driver.IsTerraTec && device.Name.Contains("Col03")) || (driver.IsTerraTec && device.Name.Contains("Col02")))
+                {
+                    usageType = UsageType.Raw;
+                    usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
+                    usageName = string.Format("0x{0:x2}", usage);
+                }
+                else if (device.Name.Contains("Col05"))
+                {
+                    usageType = UsageType.Ascii;
+                    usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
+                    usageName = string.Format("0x{0:x2}", usage);
+                }
+                else
+                {
+                    byte[] report = new byte[input.data.hid.dwSizeHid];
+                    Marshal.Copy(IntPtr.Add(rawInput, HID_INPUT_DATA_OFFSET), report, 0, report.Length);
+                    uint usageCount = input.data.hid.dwCount;
+                    NativeMethods.USAGE_AND_PAGE[] usages = new NativeMethods.USAGE_AND_PAGE[usageCount];
+                    NativeMethods.HidStatus status = NativeMethods.HidP_GetUsagesEx(NativeMethods.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, device.PreParsedData, report, (uint)report.Length);
+                    if (status != NativeMethods.HidStatus.HIDP_STATUS_SUCCESS)
+                    {
+                        this.LogError("Twinhan HID remote: failed to get raw input HID usages, device = {0}, status = {1}", device.Name, status);
+                        Dump.DumpBinary(rawInput, (int)input.header.dwSize);
+                        return false;
+                    }
+                    if (usageCount > 1)
+                    {
+                        this.LogWarn("Twinhan HID remote: multiple simultaneous HID usages not supported");
+                    }
+                    NativeMethods.USAGE_AND_PAGE up = usages[0];
+                    HidUsagePage page = (HidUsagePage)up.UsagePage;
+                    usage = up.Usage;
+                    if (page != HidUsagePage.MceRemote && usage == 0)
+                    {
+                        // Key up event. We don't handle repeats, so ignore this.
+                        return false;
+                    }
+                    if (page == HidUsagePage.Consumer)
+                    {
+                        usageType = UsageType.Consumer;
+                        usageName = Enum.GetName(typeof(HidConsumerUsage), up.Usage);
+                    }
+                    else if (page == HidUsagePage.MceRemote)
+                    {
+                        usageType = UsageType.Mce;
+                        usageName = Enum.GetName(typeof(MceRemoteUsage), up.Usage);
+                    }
+                    else
+                    {
+                        this.LogError("Twinhan HID remote: unexpected usage page, device = {0}, page = {1}, usage = {2}", device.Name, page, up.Usage);
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                this.LogError("Twinhan HID remote: received input from unsupported input device type, device = {0}, type = {1}", device.Name, input.header.dwType);
+                return false;
+            }
+            return true;
+        }
+         * */
+
     }
 }
\ No newline at end of file
diff -r 9a3e77655031 -r 8f7e35c3bfd1 RemoteControlDevice.cs
--- a/RemoteControlDevice.cs	Wed Dec 03 21:54:45 2014 +0100
+++ b/RemoteControlDevice.cs	Fri Dec 05 22:50:39 2014 +0100
@@ -395,8 +395,10 @@
                 Debug.WriteLine("================BACKGROUND");
             }
 
-            //Declare a pointer
+            //Declare some pointers
             IntPtr rawInputBuffer = IntPtr.Zero;
+            //My understanding is that this is basically our HID descriptor
+            IntPtr preParsedData = IntPtr.Zero;
 
             try
             {
@@ -421,6 +423,9 @@
                     //Get Usage Page and Usage
                     Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
 
+
+                    preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice);
+
                     //
                     HidUsageHandler usagePageHandler=null;
 
@@ -449,7 +454,7 @@
 
 
                     //Allocate a buffer for one HID input
-                    byte[] hidInput = new byte[rawInput.hid.dwSizeHid];
+                    byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid];
 
                     //For each HID input in our raw input
                     for (int i = 0; i < rawInput.hid.dwCount; i++)
@@ -464,11 +469,11 @@
                         }
 
                         //Copy HID input into our buffer
-                        Marshal.Copy(new IntPtr(hidInputOffset), hidInput, 0, rawInput.hid.dwSizeHid);
+                        Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid);
 
                         //Print HID raw input in our debug output
                         string hidDump = "HID " + rawInput.hid.dwCount + "/" + rawInput.hid.dwSizeHid + ":";
-                        foreach (byte b in hidInput)
+                        foreach (byte b in hidInputReport)
                         {
                             hidDump += b.ToString("X2");
                         }
@@ -476,19 +481,40 @@
                         
                         ushort usage = 0;
                         //hidInput[0] //Not sure what's the meaning of the code at offset 0
-                        if (hidInput.Length == 2)
+                        if (hidInputReport.Length == 2)
                         {
                             //Single byte code
-                            usage = hidInput[1]; //Get button code
+                            usage = hidInputReport[1]; //Get button code
                         }
-                        else if (hidInput.Length > 2) //Defensive
+                        else if (hidInputReport.Length > 2) //Defensive
                         {
                             //Assuming double bytes code
-                            usage = (ushort)((hidInput[2] << 8) + hidInput[1]);
+                            usage = (ushort)((hidInputReport[2] << 8) + hidInputReport[1]);
                         }
 
                         Debug.WriteLine("Usage: 0x" + usage.ToString("X4"));
 
+                        //Proper parsing now
+                        //byte[] report = new byte[input.data.hid.dwSizeHid];
+                        //Marshal.Copy(IntPtr.Add(rawInput, HID_INPUT_DATA_OFFSET), report, 0, report.Length);
+                        uint usageCount = rawInput.hid.dwCount;
+                        Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount];
+                        Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length);
+                        if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
+                        {
+                            Debug.WriteLine("Could not parse HID data!");
+                            //this.LogError("Twinhan HID remote: failed to get raw input HID usages, device = {0}, status = {1}", device.Name, status);
+                            //Dump.DumpBinary(rawInput, (int)input.header.dwSize);
+                            //return false;
+                        }
+                        else
+                        {
+                            Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
+                            Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
+                        }
+
+
+
                         //Call on our Usage Page handler
                         usagePageHandler(usage);
                     }
@@ -510,6 +536,7 @@
             {
                 //Always executed when leaving our try block
                 Marshal.FreeHGlobal(rawInputBuffer);
+                Marshal.FreeHGlobal(preParsedData);
             }
 		}
 
diff -r 9a3e77655031 -r 8f7e35c3bfd1 RemoteControlSample.csproj
--- a/RemoteControlSample.csproj	Wed Dec 03 21:54:45 2014 +0100
+++ b/RemoteControlSample.csproj	Fri Dec 05 22:50:39 2014 +0100
@@ -132,6 +132,7 @@
     
       Code
     
+    
     
     
     
diff -r 9a3e77655031 -r 8f7e35c3bfd1 Win32Hid.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Win32Hid.cs	Fri Dec 05 22:50:39 2014 +0100
@@ -0,0 +1,69 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Win32
+{
+
+    static partial class Function
+    {
+        [DllImport("hid.dll", CharSet = CharSet.Unicode)]
+        public static extern HidStatus HidP_GetUsagesEx(HIDP_REPORT_TYPE ReportType, ushort LinkCollection, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] USAGE_AND_PAGE[] ButtonList, ref uint UsageLength, IntPtr PreparsedData, [MarshalAs(UnmanagedType.LPArray)] byte[] Report, uint ReportLength);
+    }
+
+
+    static partial class Macro
+    {
+
+
+    }
+
+
+    static partial class Const
+    {
+
+
+    }
+
+
+    public enum HIDP_REPORT_TYPE : ushort
+    {
+        HidP_Input = 0,
+        HidP_Output,
+        HidP_Feature
+    }
+
+
+    public enum HidStatus : uint
+    {
+        HIDP_STATUS_SUCCESS = 0x110000,
+        HIDP_STATUS_NULL = 0x80110001,
+        HIDP_STATUS_INVALID_PREPARSED_DATA = 0xc0110001,
+        HIDP_STATUS_INVALID_REPORT_TYPE = 0xc0110002,
+        HIDP_STATUS_INVALID_REPORT_LENGTH = 0xc0110003,
+        HIDP_STATUS_USAGE_NOT_FOUND = 0xc0110004,
+        HIDP_STATUS_VALUE_OUT_OF_RANGE = 0xc0110005,
+        HIDP_STATUS_BAD_LOG_PHY_VALUES = 0xc0110006,
+        HIDP_STATUS_BUFFER_TOO_SMALL = 0xc0110007,
+        HIDP_STATUS_INTERNAL_ERROR = 0xc0110008,
+        HIDP_STATUS_I8042_TRANS_UNKNOWN = 0xc0110009,
+        HIDP_STATUS_INCOMPATIBLE_REPORT_ID = 0xc011000a,
+        HIDP_STATUS_NOT_VALUE_ARRAY = 0xc011000b,
+        HIDP_STATUS_IS_VALUE_ARRAY = 0xc011000c,
+        HIDP_STATUS_DATA_INDEX_NOT_FOUND = 0xc011000d,
+        HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE = 0xc011000e,
+        HIDP_STATUS_BUTTON_NOT_PRESSED = 0xc011000f,
+        HIDP_STATUS_REPORT_DOES_NOT_EXIST = 0xc0110010,
+        HIDP_STATUS_NOT_IMPLEMENTED = 0xc0110020,
+        HIDP_STATUS_I8242_TRANS_UNKNOWN = 0xc0110009
+    }
+
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct USAGE_AND_PAGE
+    {
+        public ushort Usage;
+        public ushort UsagePage;
+    };
+
+
+}
\ No newline at end of file
diff -r 9a3e77655031 -r 8f7e35c3bfd1 Win32RawInput.cs
--- a/Win32RawInput.cs	Wed Dec 03 21:54:45 2014 +0100
+++ b/Win32RawInput.cs	Fri Dec 05 22:50:39 2014 +0100
@@ -219,9 +219,9 @@
     internal struct RAWHID
     {
         [MarshalAs(UnmanagedType.U4)]
-        public int dwSizeHid;
+        public uint dwSizeHid;
         [MarshalAs(UnmanagedType.U4)]
-        public int dwCount;
+        public uint dwCount;
         //
         //BYTE  bRawData[1];
     }