| sl@27 |      1 | using System;
 | 
| sl@27 |      2 | using System.Windows.Forms;
 | 
| sl@27 |      3 | using System.Runtime.InteropServices;
 | 
| sl@27 |      4 | using System.Diagnostics;
 | 
| sl@27 |      5 | using System.Text;
 | 
| sl@27 |      6 | using Microsoft.Win32.SafeHandles;
 | 
| sl@27 |      7 | using Win32;
 | 
| sl@27 |      8 | using System.Collections.Generic;
 | 
| sl@41 |      9 | using System.Timers;
 | 
| sl@27 |     10 | 
 | 
| sl@27 |     11 | 
 | 
| sl@27 |     12 | namespace Hid
 | 
| sl@27 |     13 | {
 | 
| sl@27 |     14 |     /// <summary>
 | 
| sl@27 |     15 |     /// Represent a HID event.
 | 
| StephaneLenclud@54 |     16 |     /// TODO: Rename this into HidRawInput?
 | 
| sl@27 |     17 |     /// </summary>
 | 
| StephaneLenclud@54 |     18 |     public class HidEvent : IDisposable
 | 
| sl@27 |     19 |     {
 | 
| sl@27 |     20 |         public bool IsValid { get; private set; }
 | 
| StephaneLenclud@54 |     21 |         public bool IsForeground { get; private set; }
 | 
| StephaneLenclud@54 |     22 |         public bool IsBackground { get { return !IsForeground; } }
 | 
| sl@27 |     23 |         public bool IsMouse { get; private set; }
 | 
| sl@27 |     24 |         public bool IsKeyboard { get; private set; }
 | 
| sl@27 |     25 |         public bool IsGeneric { get; private set; }
 | 
| sl@29 |     26 |         public bool IsButtonDown { get { return Usages.Count == 1 && Usages[0] != 0; } }
 | 
| StephaneLenclud@49 |     27 |         public bool IsButtonUp { get { return Usages.Count == 0; } }
 | 
| sl@44 |     28 |         public bool IsRepeat { get { return RepeatCount != 0; } }
 | 
| sl@44 |     29 |         public uint RepeatCount { get; private set; }
 | 
| sl@27 |     30 | 
 | 
| sl@27 |     31 |         public HidDevice Device { get; private set; }
 | 
| sl@27 |     32 | 
 | 
| sl@27 |     33 |         public ushort UsagePage { get; private set; }
 | 
| sl@27 |     34 |         public ushort UsageCollection { get; private set; }
 | 
| sl@31 |     35 |         public uint UsageId { get { return ((uint)UsagePage << 16 | (uint)UsageCollection); } }
 | 
| StephaneLenclud@49 |     36 |         public List<ushort> Usages { get; private set; }
 | 
| StephaneLenclud@54 |     37 |         //TODO: We need a collection of input report
 | 
| StephaneLenclud@54 |     38 |         public byte[] InputReport { get; private set; }
 | 
| StephaneLenclud@54 |     39 |         //
 | 
| StephaneLenclud@54 |     40 |         public delegate void HidEventRepeatDelegate(HidEvent aHidEvent);
 | 
| sl@41 |     41 |         public event HidEventRepeatDelegate OnHidEventRepeat;
 | 
| sl@27 |     42 | 
 | 
| sl@41 |     43 |         private System.Timers.Timer Timer { get; set; }
 | 
| sl@44 |     44 |         public DateTime Time { get; private set; }
 | 
| sl@44 |     45 |         public DateTime OriginalTime { get; private set; }
 | 
| sl@41 |     46 | 
 | 
| sl@43 |     47 |         //Compute repeat delay and speed based on system settings
 | 
| sl@43 |     48 |         //Those computations were taken from the Petzold here: ftp://ftp.charlespetzold.com/ProgWinForms/4%20Custom%20Controls/NumericScan/NumericScan/ClickmaticButton.cs
 | 
| sl@43 |     49 |         private int iRepeatDelay = 250 * (1 + SystemInformation.KeyboardDelay);
 | 
| sl@43 |     50 |         private int iRepeatSpeed = 405 - 12 * SystemInformation.KeyboardSpeed;
 | 
| sl@43 |     51 | 
 | 
| sl@42 |     52 |         /// <summary>
 | 
| sl@42 |     53 |         /// Tells whether this event has already been disposed of.
 | 
| sl@42 |     54 |         /// </summary>
 | 
| sl@42 |     55 |         public bool IsStray { get { return Timer == null; } }
 | 
| sl@42 |     56 | 
 | 
| sl@43 |     57 |         /// <summary>
 | 
| sl@43 |     58 |         /// We typically dispose of events as soon as we get the corresponding key up signal.
 | 
| sl@43 |     59 |         /// </summary>
 | 
| sl@41 |     60 |         public void Dispose()
 | 
| sl@41 |     61 |         {
 | 
| sl@41 |     62 |             Timer.Enabled = false;
 | 
| sl@41 |     63 |             Timer.Dispose();
 | 
| sl@43 |     64 |             //Mark this event as a stray
 | 
| sl@41 |     65 |             Timer = null;
 | 
| sl@41 |     66 |         }
 | 
| sl@27 |     67 | 
 | 
| sl@27 |     68 |         /// <summary>
 | 
| sl@27 |     69 |         /// Initialize an HidEvent from a WM_INPUT message
 | 
| sl@27 |     70 |         /// </summary>
 | 
| sl@27 |     71 |         /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
 | 
| sl@41 |     72 |         public HidEvent(Message aMessage, HidEventRepeatDelegate aRepeatDelegate)
 | 
| sl@27 |     73 |         {
 | 
| sl@44 |     74 |             RepeatCount = 0;
 | 
| sl@27 |     75 |             IsValid = false;
 | 
| sl@27 |     76 |             IsKeyboard = false;
 | 
| sl@27 |     77 |             IsGeneric = false;
 | 
| StephaneLenclud@54 |     78 | 
 | 
| sl@27 |     79 | 
 | 
| sl@44 |     80 |             Time = DateTime.Now;
 | 
| sl@44 |     81 |             OriginalTime = DateTime.Now;
 | 
| sl@41 |     82 |             Timer = new System.Timers.Timer();
 | 
| sl@44 |     83 |             Timer.Elapsed += (sender, e) => OnRepeatTimerElapsed(sender, e, this);
 | 
| sl@27 |     84 |             Usages = new List<ushort>();
 | 
| sl@41 |     85 |             OnHidEventRepeat += aRepeatDelegate;
 | 
| sl@27 |     86 | 
 | 
| sl@27 |     87 |             if (aMessage.Msg != Const.WM_INPUT)
 | 
| sl@27 |     88 |             {
 | 
| sl@27 |     89 |                 //Has to be a WM_INPUT message
 | 
| sl@27 |     90 |                 return;
 | 
| sl@27 |     91 |             }
 | 
| sl@27 |     92 | 
 | 
| sl@27 |     93 |             if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUT)
 | 
| sl@27 |     94 |             {
 | 
| sl@27 |     95 |                 IsForeground = true;
 | 
| sl@27 |     96 |             }
 | 
| sl@27 |     97 |             else if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUTSINK)
 | 
| sl@27 |     98 |             {
 | 
| sl@27 |     99 |                 IsForeground = false;
 | 
| sl@27 |    100 |             }
 | 
| sl@27 |    101 | 
 | 
| sl@27 |    102 |             //Declare some pointers
 | 
| sl@27 |    103 |             IntPtr rawInputBuffer = IntPtr.Zero;
 | 
| sl@27 |    104 | 
 | 
| sl@27 |    105 |             try
 | 
| sl@27 |    106 |             {
 | 
| sl@27 |    107 |                 //Fetch raw input
 | 
| sl@27 |    108 |                 RAWINPUT rawInput = new RAWINPUT();
 | 
| StephaneLenclud@60 |    109 |                 if (!Win32.RawInput.GetRawInputData(aMessage.LParam, ref rawInput, ref rawInputBuffer))
 | 
| sl@27 |    110 |                 {
 | 
| sl@27 |    111 |                     return;
 | 
| sl@27 |    112 |                 }
 | 
| sl@27 |    113 | 
 | 
| sl@27 |    114 | 
 | 
| sl@27 |    115 |                 //Get various information about this HID device
 | 
| StephaneLenclud@54 |    116 |                 Device = new Hid.HidDevice(rawInput.header.hDevice);
 | 
| sl@27 |    117 | 
 | 
| StephaneLenclud@60 |    118 |                 if (rawInput.header.dwType == Win32.RawInputDeviceType.RIM_TYPEHID)  //Check that our raw input is HID                        
 | 
| sl@27 |    119 |                 {
 | 
| sl@27 |    120 |                     IsGeneric = true;
 | 
| sl@27 |    121 | 
 | 
| sl@27 |    122 |                     Debug.WriteLine("WM_INPUT source device is HID.");
 | 
| sl@27 |    123 |                     //Get Usage Page and Usage
 | 
| sl@27 |    124 |                     //Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
 | 
| StephaneLenclud@53 |    125 |                     UsagePage = Device.Info.hid.usUsagePage;
 | 
| StephaneLenclud@53 |    126 |                     UsageCollection = Device.Info.hid.usUsage;
 | 
| sl@27 |    127 | 
 | 
| sl@27 |    128 |                     if (!(rawInput.hid.dwSizeHid > 1     //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now
 | 
| sl@27 |    129 |                         && rawInput.hid.dwCount > 0))    //Check that we have at least one HID msg
 | 
| sl@27 |    130 |                     {
 | 
| sl@27 |    131 |                         return;
 | 
| sl@27 |    132 |                     }
 | 
| sl@27 |    133 | 
 | 
| sl@27 |    134 |                     //Allocate a buffer for one HID input
 | 
| StephaneLenclud@54 |    135 |                     InputReport = new byte[rawInput.hid.dwSizeHid];
 | 
| sl@27 |    136 | 
 | 
| sl@27 |    137 |                     Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)");
 | 
| sl@27 |    138 | 
 | 
| sl@27 |    139 |                     //For each HID input report in our raw input
 | 
| sl@27 |    140 |                     for (int i = 0; i < rawInput.hid.dwCount; i++)
 | 
| sl@27 |    141 |                     {
 | 
| sl@27 |    142 |                         //Compute the address from which to copy our HID input
 | 
| sl@27 |    143 |                         int hidInputOffset = 0;
 | 
| sl@27 |    144 |                         unsafe
 | 
| sl@27 |    145 |                         {
 | 
| sl@27 |    146 |                             byte* source = (byte*)rawInputBuffer;
 | 
| sl@27 |    147 |                             source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
 | 
| sl@27 |    148 |                             hidInputOffset = (int)source;
 | 
| sl@27 |    149 |                         }
 | 
| sl@27 |    150 | 
 | 
| sl@27 |    151 |                         //Copy HID input into our buffer
 | 
| StephaneLenclud@54 |    152 |                         Marshal.Copy(new IntPtr(hidInputOffset), InputReport, 0, (int)rawInput.hid.dwSizeHid);
 | 
| sl@27 |    153 | 
 | 
| sl@27 |    154 |                         //Print HID input report in our debug output
 | 
| StephaneLenclud@49 |    155 |                         //string hidDump = "HID input report: " + InputReportString();
 | 
| StephaneLenclud@49 |    156 |                         //Debug.WriteLine(hidDump);
 | 
| sl@27 |    157 | 
 | 
| StephaneLenclud@49 |    158 |                         //Do proper parsing of our HID report
 | 
| StephaneLenclud@54 |    159 |                         //First query our usage count
 | 
| StephaneLenclud@54 |    160 |                         uint usageCount = 0;
 | 
| StephaneLenclud@49 |    161 |                         Win32.USAGE_AND_PAGE[] usages = null;
 | 
| StephaneLenclud@54 |    162 |                         Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, InputReport, (uint)InputReport.Length);
 | 
| StephaneLenclud@54 |    163 |                         if (status == Win32.HidStatus.HIDP_STATUS_BUFFER_TOO_SMALL)
 | 
| StephaneLenclud@54 |    164 |                         {
 | 
| StephaneLenclud@54 |    165 |                             //Allocate a large enough buffer 
 | 
| StephaneLenclud@54 |    166 |                             usages = new Win32.USAGE_AND_PAGE[usageCount];
 | 
| StephaneLenclud@54 |    167 |                             //...and fetch our usages
 | 
| StephaneLenclud@52 |    168 |                             status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, InputReport, (uint)InputReport.Length);
 | 
| StephaneLenclud@54 |    169 |                             if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
 | 
| StephaneLenclud@54 |    170 |                             {
 | 
| StephaneLenclud@54 |    171 |                                 Debug.WriteLine("Second pass could not parse HID data: " + status.ToString());
 | 
| StephaneLenclud@54 |    172 |                             }
 | 
| StephaneLenclud@54 |    173 |                         }
 | 
| StephaneLenclud@54 |    174 |                         else if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
 | 
| StephaneLenclud@54 |    175 |                         {
 | 
| StephaneLenclud@54 |    176 |                             Debug.WriteLine("First pass could not parse HID data: " + status.ToString());
 | 
| StephaneLenclud@54 |    177 |                         }
 | 
| StephaneLenclud@49 |    178 | 
 | 
| StephaneLenclud@54 |    179 |                         Debug.WriteLine("Usage count: " + usageCount.ToString());
 | 
| StephaneLenclud@49 |    180 | 
 | 
| StephaneLenclud@52 |    181 |                         //Copy usages into this event
 | 
| StephaneLenclud@54 |    182 |                         if (usages != null)
 | 
| StephaneLenclud@54 |    183 |                         {
 | 
| StephaneLenclud@54 |    184 |                             foreach (USAGE_AND_PAGE up in usages)
 | 
| StephaneLenclud@54 |    185 |                             {
 | 
| StephaneLenclud@54 |    186 |                                 //Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
 | 
| StephaneLenclud@54 |    187 |                                 //Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
 | 
| StephaneLenclud@54 |    188 |                                 //Add this usage to our list
 | 
| StephaneLenclud@54 |    189 |                                 Usages.Add(up.Usage);
 | 
| StephaneLenclud@54 |    190 |                             }
 | 
| StephaneLenclud@54 |    191 |                         }
 | 
| sl@27 |    192 |                     }
 | 
| sl@27 |    193 |                 }
 | 
| StephaneLenclud@60 |    194 |                 else if (rawInput.header.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
 | 
| sl@27 |    195 |                 {
 | 
| sl@27 |    196 |                     IsMouse = true;
 | 
| sl@27 |    197 | 
 | 
| StephaneLenclud@54 |    198 |                     Debug.WriteLine("WM_INPUT source device is Mouse.");
 | 
| sl@27 |    199 |                     // do mouse handling...
 | 
| sl@27 |    200 |                 }
 | 
| StephaneLenclud@60 |    201 |                 else if (rawInput.header.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
 | 
| sl@27 |    202 |                 {
 | 
| sl@27 |    203 |                     IsKeyboard = true;
 | 
| sl@27 |    204 | 
 | 
| sl@27 |    205 |                     Debug.WriteLine("WM_INPUT source device is Keyboard.");
 | 
| sl@27 |    206 |                     // do keyboard handling...
 | 
| StephaneLenclud@53 |    207 |                     Debug.WriteLine("Type: " + Device.Info.keyboard.dwType.ToString());
 | 
| StephaneLenclud@53 |    208 |                     Debug.WriteLine("SubType: " + Device.Info.keyboard.dwSubType.ToString());
 | 
| StephaneLenclud@53 |    209 |                     Debug.WriteLine("Mode: " + Device.Info.keyboard.dwKeyboardMode.ToString());
 | 
| StephaneLenclud@53 |    210 |                     Debug.WriteLine("Number of function keys: " + Device.Info.keyboard.dwNumberOfFunctionKeys.ToString());
 | 
| StephaneLenclud@53 |    211 |                     Debug.WriteLine("Number of indicators: " + Device.Info.keyboard.dwNumberOfIndicators.ToString());
 | 
| StephaneLenclud@53 |    212 |                     Debug.WriteLine("Number of keys total: " + Device.Info.keyboard.dwNumberOfKeysTotal.ToString());
 | 
| sl@27 |    213 |                 }
 | 
| sl@27 |    214 |             }
 | 
| sl@27 |    215 |             finally
 | 
| sl@27 |    216 |             {
 | 
| sl@27 |    217 |                 //Always executed when leaving our try block
 | 
| sl@27 |    218 |                 Marshal.FreeHGlobal(rawInputBuffer);
 | 
| sl@27 |    219 |             }
 | 
| sl@27 |    220 | 
 | 
| sl@47 |    221 |             //
 | 
| sl@47 |    222 |             if (IsButtonDown)
 | 
| sl@41 |    223 |             {
 | 
| StephaneLenclud@54 |    224 |                 //TODO: Make this optional
 | 
| StephaneLenclud@63 |    225 |                 //StartRepeatTimer(iRepeatDelay);
 | 
| sl@41 |    226 |             }
 | 
| StephaneLenclud@54 |    227 | 
 | 
| sl@27 |    228 |             IsValid = true;
 | 
| sl@27 |    229 |         }
 | 
| sl@27 |    230 | 
 | 
| sl@41 |    231 |         public void StartRepeatTimer(double aInterval)
 | 
| sl@41 |    232 |         {
 | 
| sl@41 |    233 |             if (Timer == null)
 | 
| sl@41 |    234 |             {
 | 
| sl@41 |    235 |                 return;
 | 
| sl@41 |    236 |             }
 | 
| sl@41 |    237 |             Timer.Enabled = false;
 | 
| sl@44 |    238 |             //Initial delay do not use auto reset
 | 
| sl@44 |    239 |             //After our initial delay however we do setup our timer one more time using auto reset
 | 
| StephaneLenclud@54 |    240 |             Timer.AutoReset = (RepeatCount != 0);
 | 
| StephaneLenclud@54 |    241 |             Timer.Interval = aInterval;
 | 
| StephaneLenclud@54 |    242 |             Timer.Enabled = true;
 | 
| sl@41 |    243 |         }
 | 
| sl@27 |    244 | 
 | 
| sl@44 |    245 |         static private void OnRepeatTimerElapsed(object sender, ElapsedEventArgs e, HidEvent aHidEvent)
 | 
| sl@41 |    246 |         {
 | 
| sl@42 |    247 |             if (aHidEvent.IsStray)
 | 
| sl@42 |    248 |             {
 | 
| sl@42 |    249 |                 //Skip events if canceled
 | 
| sl@42 |    250 |                 return;
 | 
| sl@42 |    251 |             }
 | 
| sl@44 |    252 | 
 | 
| sl@44 |    253 |             aHidEvent.RepeatCount++;
 | 
| sl@44 |    254 |             aHidEvent.Time = DateTime.Now;
 | 
| StephaneLenclud@54 |    255 |             if (aHidEvent.RepeatCount == 1)
 | 
| sl@44 |    256 |             {
 | 
| sl@44 |    257 |                 //Re-Start our timer only after the initial delay 
 | 
| sl@44 |    258 |                 aHidEvent.StartRepeatTimer(aHidEvent.iRepeatSpeed);
 | 
| sl@44 |    259 |             }
 | 
| sl@44 |    260 | 
 | 
| sl@44 |    261 |             //Broadcast our repeat event
 | 
| sl@44 |    262 |             aHidEvent.OnHidEventRepeat(aHidEvent);
 | 
| sl@41 |    263 |         }
 | 
| sl@27 |    264 | 
 | 
| StephaneLenclud@54 |    265 |         /// <summary>
 | 
| StephaneLenclud@54 |    266 |         /// Print information about this device to our debug output.
 | 
| StephaneLenclud@54 |    267 |         /// </summary>
 | 
| StephaneLenclud@54 |    268 |         public void DebugWrite()
 | 
| StephaneLenclud@54 |    269 |         {
 | 
| StephaneLenclud@54 |    270 |             if (!IsValid)
 | 
| StephaneLenclud@54 |    271 |             {
 | 
| StephaneLenclud@54 |    272 |                 Debug.WriteLine("==== Invalid HidEvent");
 | 
| StephaneLenclud@54 |    273 |                 return;
 | 
| StephaneLenclud@54 |    274 |             }
 | 
| StephaneLenclud@54 |    275 |             Device.DebugWrite();
 | 
| StephaneLenclud@54 |    276 |             if (IsGeneric) Debug.WriteLine("==== Generic");
 | 
| StephaneLenclud@54 |    277 |             if (IsKeyboard) Debug.WriteLine("==== Keyboard");
 | 
| StephaneLenclud@54 |    278 |             if (IsMouse) Debug.WriteLine("==== Mouse");
 | 
| StephaneLenclud@54 |    279 |             Debug.WriteLine("==== Foreground: " + IsForeground.ToString());
 | 
| StephaneLenclud@54 |    280 |             Debug.WriteLine("==== UsagePage: 0x" + UsagePage.ToString("X4"));
 | 
| StephaneLenclud@54 |    281 |             Debug.WriteLine("==== UsageCollection: 0x" + UsageCollection.ToString("X4"));
 | 
| StephaneLenclud@54 |    282 |             Debug.WriteLine("==== InputReport: 0x" + InputReportString());
 | 
| StephaneLenclud@54 |    283 |             foreach (ushort usage in Usages)
 | 
| StephaneLenclud@54 |    284 |             {
 | 
| StephaneLenclud@54 |    285 |                 Debug.WriteLine("==== Usage: 0x" + usage.ToString("X4"));
 | 
| StephaneLenclud@54 |    286 |             }
 | 
| StephaneLenclud@54 |    287 |         }
 | 
| StephaneLenclud@49 |    288 | 
 | 
| StephaneLenclud@54 |    289 |         /// <summary>
 | 
| StephaneLenclud@54 |    290 |         /// 
 | 
| StephaneLenclud@54 |    291 |         /// </summary>
 | 
| StephaneLenclud@54 |    292 |         /// <returns></returns>
 | 
| StephaneLenclud@54 |    293 |         public string InputReportString()
 | 
| StephaneLenclud@54 |    294 |         {
 | 
| StephaneLenclud@54 |    295 |             string hidDump = "";
 | 
| StephaneLenclud@54 |    296 |             foreach (byte b in InputReport)
 | 
| StephaneLenclud@54 |    297 |             {
 | 
| StephaneLenclud@54 |    298 |                 hidDump += b.ToString("X2");
 | 
| StephaneLenclud@54 |    299 |             }
 | 
| StephaneLenclud@54 |    300 |             return hidDump;
 | 
| StephaneLenclud@54 |    301 |         }
 | 
| StephaneLenclud@49 |    302 | 
 | 
| StephaneLenclud@49 |    303 | 
 | 
| StephaneLenclud@54 |    304 |         /// <summary>
 | 
| StephaneLenclud@54 |    305 |         /// Create a list view item describing this HidEvent
 | 
| StephaneLenclud@54 |    306 |         /// </summary>
 | 
| StephaneLenclud@54 |    307 |         /// <returns></returns>
 | 
| sl@42 |    308 |         public ListViewItem ToListViewItem()
 | 
| sl@42 |    309 |         {
 | 
| StephaneLenclud@49 |    310 |             string usageText = "";
 | 
| sl@42 |    311 | 
 | 
| StephaneLenclud@54 |    312 |             foreach (ushort usage in Usages)
 | 
| StephaneLenclud@54 |    313 |             {
 | 
| StephaneLenclud@54 |    314 |                 if (usageText != "")
 | 
| StephaneLenclud@54 |    315 |                 {
 | 
| StephaneLenclud@54 |    316 |                     //Add a separator
 | 
| StephaneLenclud@54 |    317 |                     usageText += ", ";
 | 
| StephaneLenclud@54 |    318 |                 }
 | 
| sl@42 |    319 | 
 | 
| StephaneLenclud@54 |    320 |                 UsagePage usagePage = (UsagePage)UsagePage;
 | 
| StephaneLenclud@54 |    321 |                 switch (usagePage)
 | 
| StephaneLenclud@54 |    322 |                 {
 | 
| StephaneLenclud@54 |    323 |                     case Hid.UsagePage.Consumer:
 | 
| StephaneLenclud@54 |    324 |                         usageText += ((Hid.UsageTables.ConsumerControl)usage).ToString();
 | 
| StephaneLenclud@54 |    325 |                         break;
 | 
| sl@42 |    326 | 
 | 
| StephaneLenclud@54 |    327 |                     case Hid.UsagePage.WindowsMediaCenterRemoteControl:
 | 
| StephaneLenclud@54 |    328 |                         usageText += ((Hid.UsageTables.WindowsMediaCenterRemoteControl)usage).ToString();
 | 
| StephaneLenclud@54 |    329 |                         break;
 | 
| StephaneLenclud@49 |    330 | 
 | 
| StephaneLenclud@54 |    331 |                     default:
 | 
| StephaneLenclud@54 |    332 |                         usageText += usage.ToString("X2");
 | 
| StephaneLenclud@54 |    333 |                         break;
 | 
| StephaneLenclud@54 |    334 |                 }
 | 
| StephaneLenclud@54 |    335 |             }
 | 
| StephaneLenclud@49 |    336 | 
 | 
| StephaneLenclud@54 |    337 |             ListViewItem item = new ListViewItem(new[] { usageText, InputReportString(), UsagePage.ToString("X2"), UsageCollection.ToString("X2"), RepeatCount.ToString(), Time.ToString("HH:mm:ss:fff") });
 | 
| sl@42 |    338 |             return item;
 | 
| sl@42 |    339 |         }
 | 
| sl@42 |    340 | 
 | 
| sl@27 |    341 |     }
 | 
| sl@27 |    342 | 
 | 
| sl@27 |    343 | } |