RawInput.cs
author sl
Fri, 05 Dec 2014 22:50:39 +0100
changeset 17 8f7e35c3bfd1
parent 10 17f8421146ba
child 19 d066e3999973
permissions -rw-r--r--
Draft implementation of proper HID input report parsing based on HID descriptor.
     1 using System;
     2 using System.Runtime.InteropServices;
     3 using System.Diagnostics;
     4 
     5 
     6 namespace Win32
     7 {
     8     /// <summary>
     9     /// Provide some utility functions for raw input handling.
    10     /// </summary>
    11     static class RawInput
    12     {
    13         /// <summary>
    14         /// 
    15         /// </summary>
    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)
    21         {
    22             bool success = true;
    23             rawInputBuffer = IntPtr.Zero;
    24 
    25             try
    26             {
    27                 uint dwSize = 0;
    28                 uint sizeOfHeader = (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
    29 
    30                 //Get the size of our raw input data.
    31                 Win32.Function.GetRawInputData(aRawInputHandle, Const.RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
    32 
    33                 //Allocate a large enough buffer
    34                  rawInputBuffer = Marshal.AllocHGlobal((int)dwSize);
    35 
    36                 //Now read our RAWINPUT data
    37                 if (Win32.Function.GetRawInputData(aRawInputHandle, Const.RID_INPUT, rawInputBuffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
    38                 {
    39                     return false;
    40                 }
    41 
    42                 //Cast our buffer
    43                 aRawInput = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
    44             }
    45             catch
    46             {
    47                 Debug.WriteLine("GetRawInputData failed!");
    48                 success = false;
    49             }
    50 
    51             return success;
    52         }
    53 
    54         /// <summary>
    55         /// 
    56         /// </summary>
    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)
    62         {
    63             bool success = true;
    64             IntPtr deviceInfoBuffer = IntPtr.Zero;
    65             try
    66             {
    67                 //Get Device Info
    68                 uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
    69                 deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
    70 
    71                 int res = Win32.Function.GetRawInputDeviceInfo(hDevice, Const.RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
    72                 if (res <= 0)
    73                 {
    74                     Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
    75                     return false;
    76                 }
    77 
    78                 //Cast our buffer
    79                 deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
    80             }
    81             catch
    82             {
    83                 Debug.WriteLine("GetRawInputData failed!");
    84                 success = false;
    85             }
    86             finally
    87             {
    88                 //Always executes, prevents memory leak
    89                 Marshal.FreeHGlobal(deviceInfoBuffer);
    90             }
    91 
    92             
    93             return success;
    94         }
    95 
    96         /// <summary>
    97         /// 
    98         /// </summary>
    99         /// <param name="device"></param>
   100         /// <returns></returns>
   101         public static IntPtr GetPreParsedData(IntPtr hDevice)
   102         {
   103             uint ppDataSize = 256;
   104             int result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, IntPtr.Zero, ref ppDataSize);
   105             if (result != 0)
   106             {
   107                 Debug.WriteLine("Failed to get raw input pre-parsed data size" + result + Marshal.GetLastWin32Error());
   108                 return IntPtr.Zero;
   109             }
   110 
   111             IntPtr ppData = Marshal.AllocHGlobal((int)ppDataSize);
   112             result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, ppData, ref ppDataSize);
   113             if (result <= 0)
   114             {
   115                 Debug.WriteLine("Failed to get raw input pre-parsed data" + result + Marshal.GetLastWin32Error());
   116                 return IntPtr.Zero;
   117             }
   118             return ppData;
   119         }
   120 
   121         /// <summary>
   122         /// 
   123         /// </summary>
   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>
   132         /// 
   133         /*
   134         private bool GetUsageFromRawInput(TwinhanHidDriver driver, TwinhanHid device, NativeMethods.RAWINPUT input, IntPtr rawInput, out UsageType usageType, out int usage, out string usageName)
   135         {
   136             usageType = 0;
   137             usage = 0;
   138             usageName = string.Empty;
   139             if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEKEYBOARD)
   140             {
   141                 if (input.data.keyboard.Flags == NativeMethods.RawInputKeyboardFlag.RI_KEY_BREAK)
   142                 {
   143                     _modifiers = 0;
   144                     // Key up event. We don't handle repeats, so ignore this.
   145                     return false;
   146                 }
   147                 NativeMethods.VirtualKey vk = input.data.keyboard.VKey;
   148                 if (vk == NativeMethods.VirtualKey.VK_CONTROL)
   149                 {
   150                     _modifiers |= VirtualKeyModifier.Control;
   151                     return false;
   152                 }
   153                 if (vk == NativeMethods.VirtualKey.VK_SHIFT)
   154                 {
   155                     _modifiers |= VirtualKeyModifier.Shift;
   156                     return false;
   157                 }
   158                 if (vk == NativeMethods.VirtualKey.VK_MENU)
   159                 {
   160                     _modifiers |= VirtualKeyModifier.Alt;
   161                     return false;
   162                 }
   163                 usageType = UsageType.Keyboard;
   164                 usage = (int)vk | (int)_modifiers;
   165                 usageName = vk.ToString();
   166                 if (_modifiers != 0)
   167                 {
   168                     usageName += string.Format(", modifiers = {0}", _modifiers);
   169                 }
   170             }
   171             else if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEHID)
   172             {
   173                 if ((!driver.IsTerraTec && device.Name.Contains("Col03")) || (driver.IsTerraTec && device.Name.Contains("Col02")))
   174                 {
   175                     usageType = UsageType.Raw;
   176                     usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
   177                     usageName = string.Format("0x{0:x2}", usage);
   178                 }
   179                 else if (device.Name.Contains("Col05"))
   180                 {
   181                     usageType = UsageType.Ascii;
   182                     usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1);
   183                     usageName = string.Format("0x{0:x2}", usage);
   184                 }
   185                 else
   186                 {
   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)
   193                     {
   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);
   196                         return false;
   197                     }
   198                     if (usageCount > 1)
   199                     {
   200                         this.LogWarn("Twinhan HID remote: multiple simultaneous HID usages not supported");
   201                     }
   202                     NativeMethods.USAGE_AND_PAGE up = usages[0];
   203                     HidUsagePage page = (HidUsagePage)up.UsagePage;
   204                     usage = up.Usage;
   205                     if (page != HidUsagePage.MceRemote && usage == 0)
   206                     {
   207                         // Key up event. We don't handle repeats, so ignore this.
   208                         return false;
   209                     }
   210                     if (page == HidUsagePage.Consumer)
   211                     {
   212                         usageType = UsageType.Consumer;
   213                         usageName = Enum.GetName(typeof(HidConsumerUsage), up.Usage);
   214                     }
   215                     else if (page == HidUsagePage.MceRemote)
   216                     {
   217                         usageType = UsageType.Mce;
   218                         usageName = Enum.GetName(typeof(MceRemoteUsage), up.Usage);
   219                     }
   220                     else
   221                     {
   222                         this.LogError("Twinhan HID remote: unexpected usage page, device = {0}, page = {1}, usage = {2}", device.Name, page, up.Usage);
   223                         return false;
   224                     }
   225                 }
   226             }
   227             else
   228             {
   229                 this.LogError("Twinhan HID remote: received input from unsupported input device type, device = {0}, type = {1}", device.Name, input.header.dwType);
   230                 return false;
   231             }
   232             return true;
   233         }
   234          * */
   235 
   236     }
   237 }