StephaneLenclud@76: // StephaneLenclud@76: // Copyright (C) 2014-2015 Stéphane Lenclud. StephaneLenclud@76: // StephaneLenclud@76: // This file is part of SharpLibHid. StephaneLenclud@76: // StephaneLenclud@76: // SharpDisplayManager is free software: you can redistribute it and/or modify StephaneLenclud@76: // it under the terms of the GNU General Public License as published by StephaneLenclud@76: // the Free Software Foundation, either version 3 of the License, or StephaneLenclud@76: // (at your option) any later version. StephaneLenclud@76: // StephaneLenclud@76: // SharpDisplayManager is distributed in the hope that it will be useful, StephaneLenclud@76: // but WITHOUT ANY WARRANTY; without even the implied warranty of StephaneLenclud@76: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the StephaneLenclud@76: // GNU General Public License for more details. StephaneLenclud@76: // StephaneLenclud@76: // You should have received a copy of the GNU General Public License StephaneLenclud@76: // along with SharpDisplayManager. If not, see . StephaneLenclud@76: // StephaneLenclud@76: StephaneLenclud@76: sl@27: using System; sl@27: using System.Windows.Forms; sl@27: using System.Runtime.InteropServices; sl@27: using System.Diagnostics; sl@27: using System.Text; sl@27: using Microsoft.Win32.SafeHandles; StephaneLenclud@77: using SharpLib.Win32; sl@27: using System.Collections.Generic; sl@41: using System.Timers; StephaneLenclud@77: using SharpLib.Hid.Usage; sl@27: sl@27: StephaneLenclud@77: namespace SharpLib.Hid sl@27: { sl@27: /// StephaneLenclud@73: /// We provide utility functions to interpret gamepad dpad state. StephaneLenclud@73: /// StephaneLenclud@73: public enum DirectionPadState StephaneLenclud@73: { StephaneLenclud@73: Rest=-1, StephaneLenclud@73: Up=0, StephaneLenclud@73: UpRight=1, StephaneLenclud@73: Right=2, StephaneLenclud@73: DownRight=3, StephaneLenclud@73: Down=4, StephaneLenclud@73: DownLeft=5, StephaneLenclud@73: Left=6, StephaneLenclud@73: UpLeft=7 StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// sl@27: /// Represent a HID event. StephaneLenclud@54: /// TODO: Rename this into HidRawInput? sl@27: /// StephaneLenclud@54: public class HidEvent : IDisposable sl@27: { sl@27: public bool IsValid { get; private set; } StephaneLenclud@54: public bool IsForeground { get; private set; } StephaneLenclud@54: public bool IsBackground { get { return !IsForeground; } } sl@27: public bool IsMouse { get; private set; } sl@27: public bool IsKeyboard { get; private set; } StephaneLenclud@72: /// StephaneLenclud@72: /// If this not a mouse or keyboard event then it's a generic HID event. StephaneLenclud@72: /// sl@27: public bool IsGeneric { get; private set; } sl@29: public bool IsButtonDown { get { return Usages.Count == 1 && Usages[0] != 0; } } StephaneLenclud@49: public bool IsButtonUp { get { return Usages.Count == 0; } } sl@44: public bool IsRepeat { get { return RepeatCount != 0; } } sl@44: public uint RepeatCount { get; private set; } sl@27: sl@27: public HidDevice Device { get; private set; } StephaneLenclud@72: public RAWINPUT RawInput { get {return iRawInput;} } StephaneLenclud@72: private RAWINPUT iRawInput; sl@27: sl@27: public ushort UsagePage { get; private set; } sl@27: public ushort UsageCollection { get; private set; } sl@31: public uint UsageId { get { return ((uint)UsagePage << 16 | (uint)UsageCollection); } } StephaneLenclud@49: public List Usages { get; private set; } StephaneLenclud@73: /// StephaneLenclud@73: /// Sorted in the same order as Device.InputValueCapabilities. StephaneLenclud@73: /// StephaneLenclud@73: public Dictionary UsageValues { get; private set; } StephaneLenclud@54: //TODO: We need a collection of input report StephaneLenclud@54: public byte[] InputReport { get; private set; } StephaneLenclud@54: // StephaneLenclud@54: public delegate void HidEventRepeatDelegate(HidEvent aHidEvent); sl@41: public event HidEventRepeatDelegate OnHidEventRepeat; sl@27: sl@41: private System.Timers.Timer Timer { get; set; } sl@44: public DateTime Time { get; private set; } sl@44: public DateTime OriginalTime { get; private set; } sl@41: sl@43: //Compute repeat delay and speed based on system settings sl@43: //Those computations were taken from the Petzold here: ftp://ftp.charlespetzold.com/ProgWinForms/4%20Custom%20Controls/NumericScan/NumericScan/ClickmaticButton.cs sl@43: private int iRepeatDelay = 250 * (1 + SystemInformation.KeyboardDelay); sl@43: private int iRepeatSpeed = 405 - 12 * SystemInformation.KeyboardSpeed; sl@43: sl@42: /// sl@42: /// Tells whether this event has already been disposed of. sl@42: /// sl@42: public bool IsStray { get { return Timer == null; } } sl@42: sl@43: /// sl@43: /// We typically dispose of events as soon as we get the corresponding key up signal. sl@43: /// sl@41: public void Dispose() sl@41: { sl@41: Timer.Enabled = false; sl@41: Timer.Dispose(); sl@43: //Mark this event as a stray sl@41: Timer = null; sl@41: } sl@27: sl@27: /// sl@27: /// Initialize an HidEvent from a WM_INPUT message sl@27: /// sl@27: /// Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice sl@41: public HidEvent(Message aMessage, HidEventRepeatDelegate aRepeatDelegate) sl@27: { sl@44: RepeatCount = 0; sl@27: IsValid = false; sl@27: IsKeyboard = false; sl@27: IsGeneric = false; StephaneLenclud@54: sl@44: Time = DateTime.Now; sl@44: OriginalTime = DateTime.Now; sl@41: Timer = new System.Timers.Timer(); sl@44: Timer.Elapsed += (sender, e) => OnRepeatTimerElapsed(sender, e, this); sl@27: Usages = new List(); StephaneLenclud@73: UsageValues = new Dictionary(); sl@41: OnHidEventRepeat += aRepeatDelegate; sl@27: sl@27: if (aMessage.Msg != Const.WM_INPUT) sl@27: { sl@27: //Has to be a WM_INPUT message sl@27: return; sl@27: } sl@27: sl@27: if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUT) sl@27: { sl@27: IsForeground = true; sl@27: } sl@27: else if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUTSINK) sl@27: { sl@27: IsForeground = false; sl@27: } sl@27: sl@27: //Declare some pointers sl@27: IntPtr rawInputBuffer = IntPtr.Zero; sl@27: sl@27: try sl@27: { sl@27: //Fetch raw input StephaneLenclud@72: iRawInput = new RAWINPUT(); StephaneLenclud@72: if (!Win32.RawInput.GetRawInputData(aMessage.LParam, ref iRawInput, ref rawInputBuffer)) sl@27: { StephaneLenclud@70: Debug.WriteLine("GetRawInputData failed!"); sl@27: return; sl@27: } sl@27: StephaneLenclud@70: //Our device can actually be null. StephaneLenclud@70: //This is notably happening for some keyboard events StephaneLenclud@72: if (RawInput.header.hDevice != IntPtr.Zero) StephaneLenclud@70: { StephaneLenclud@70: //Get various information about this HID device StephaneLenclud@76: Device = new HidDevice(RawInput.header.hDevice); StephaneLenclud@70: } sl@27: StephaneLenclud@72: if (RawInput.header.dwType == Win32.RawInputDeviceType.RIM_TYPEHID) //Check that our raw input is HID sl@27: { sl@27: IsGeneric = true; sl@27: sl@27: Debug.WriteLine("WM_INPUT source device is HID."); sl@27: //Get Usage Page and Usage sl@27: //Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4")); StephaneLenclud@53: UsagePage = Device.Info.hid.usUsagePage; StephaneLenclud@53: UsageCollection = Device.Info.hid.usUsage; sl@27: StephaneLenclud@72: 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 StephaneLenclud@72: && RawInput.hid.dwCount > 0)) //Check that we have at least one HID msg sl@27: { sl@27: return; sl@27: } sl@27: sl@27: //Allocate a buffer for one HID input StephaneLenclud@72: InputReport = new byte[RawInput.hid.dwSizeHid]; sl@27: StephaneLenclud@72: Debug.WriteLine("Raw input contains " + RawInput.hid.dwCount + " HID input report(s)"); sl@27: sl@27: //For each HID input report in our raw input StephaneLenclud@72: for (int i = 0; i < RawInput.hid.dwCount; i++) sl@27: { sl@27: //Compute the address from which to copy our HID input sl@27: int hidInputOffset = 0; sl@27: unsafe sl@27: { sl@27: byte* source = (byte*)rawInputBuffer; StephaneLenclud@72: source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (RawInput.hid.dwSizeHid * i); sl@27: hidInputOffset = (int)source; sl@27: } sl@27: sl@27: //Copy HID input into our buffer StephaneLenclud@72: Marshal.Copy(new IntPtr(hidInputOffset), InputReport, 0, (int)RawInput.hid.dwSizeHid); StephaneLenclud@73: // StephaneLenclud@73: ProcessInputReport(InputReport); sl@27: } sl@27: } StephaneLenclud@72: else if (RawInput.header.dwType == RawInputDeviceType.RIM_TYPEMOUSE) sl@27: { sl@27: IsMouse = true; sl@27: StephaneLenclud@54: Debug.WriteLine("WM_INPUT source device is Mouse."); sl@27: // do mouse handling... sl@27: } StephaneLenclud@72: else if (RawInput.header.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD) sl@27: { sl@27: IsKeyboard = true; sl@27: sl@27: Debug.WriteLine("WM_INPUT source device is Keyboard."); sl@27: // do keyboard handling... StephaneLenclud@70: if (Device != null) StephaneLenclud@70: { StephaneLenclud@70: Debug.WriteLine("Type: " + Device.Info.keyboard.dwType.ToString()); StephaneLenclud@70: Debug.WriteLine("SubType: " + Device.Info.keyboard.dwSubType.ToString()); StephaneLenclud@70: Debug.WriteLine("Mode: " + Device.Info.keyboard.dwKeyboardMode.ToString()); StephaneLenclud@70: Debug.WriteLine("Number of function keys: " + Device.Info.keyboard.dwNumberOfFunctionKeys.ToString()); StephaneLenclud@70: Debug.WriteLine("Number of indicators: " + Device.Info.keyboard.dwNumberOfIndicators.ToString()); StephaneLenclud@70: Debug.WriteLine("Number of keys total: " + Device.Info.keyboard.dwNumberOfKeysTotal.ToString()); StephaneLenclud@70: } sl@27: } sl@27: } sl@27: finally sl@27: { sl@27: //Always executed when leaving our try block sl@27: Marshal.FreeHGlobal(rawInputBuffer); sl@27: } sl@27: sl@47: // sl@47: if (IsButtonDown) sl@41: { StephaneLenclud@54: //TODO: Make this optional StephaneLenclud@63: //StartRepeatTimer(iRepeatDelay); sl@41: } StephaneLenclud@54: sl@27: IsValid = true; sl@27: } sl@27: StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: private void ProcessInputReport(byte[] aInputReport) StephaneLenclud@73: { StephaneLenclud@73: //Print HID input report in our debug output StephaneLenclud@73: //string hidDump = "HID input report: " + InputReportString(); StephaneLenclud@73: //Debug.WriteLine(hidDump); StephaneLenclud@73: StephaneLenclud@73: //Get all our usages, those are typically the buttons currently pushed on a gamepad. StephaneLenclud@73: //For a remote control it's usually just the one button that was pushed. StephaneLenclud@73: GetUsages(aInputReport); StephaneLenclud@73: StephaneLenclud@73: //Now process direction pad (d-pad, dpad) and axes StephaneLenclud@73: GetUsageValues(aInputReport); StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@73: /// Typically fetches values of a joystick/gamepad axis and dpad directions. StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: private void GetUsageValues(byte[] aInputReport) StephaneLenclud@73: { StephaneLenclud@73: if (Device.InputValueCapabilities == null) StephaneLenclud@73: { StephaneLenclud@73: return; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities) StephaneLenclud@73: { StephaneLenclud@73: if (caps.IsRange) StephaneLenclud@73: { StephaneLenclud@73: //What should we do with those guys? StephaneLenclud@73: continue; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: //Now fetch and add our usage value StephaneLenclud@73: uint usageValue = 0; StephaneLenclud@73: Win32.HidStatus status = Win32.Function.HidP_GetUsageValue(Win32.HIDP_REPORT_TYPE.HidP_Input, caps.UsagePage, caps.LinkCollection, caps.NotRange.Usage, ref usageValue, Device.PreParsedData, aInputReport, (uint)aInputReport.Length); StephaneLenclud@73: if (status == Win32.HidStatus.HIDP_STATUS_SUCCESS) StephaneLenclud@73: { StephaneLenclud@73: UsageValues[caps]=usageValue; StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@73: /// Get all our usages, those are typically the buttons currently pushed on a gamepad. StephaneLenclud@73: /// For a remote control it's usually just the one button that was pushed. StephaneLenclud@73: /// StephaneLenclud@73: private void GetUsages(byte[] aInputReport) StephaneLenclud@73: { StephaneLenclud@73: //Do proper parsing of our HID report StephaneLenclud@73: //First query our usage count StephaneLenclud@73: uint usageCount = 0; StephaneLenclud@73: Win32.USAGE_AND_PAGE[] usages = null; StephaneLenclud@73: Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length); StephaneLenclud@73: if (status == Win32.HidStatus.HIDP_STATUS_BUFFER_TOO_SMALL) StephaneLenclud@73: { StephaneLenclud@73: //Allocate a large enough buffer StephaneLenclud@73: usages = new Win32.USAGE_AND_PAGE[usageCount]; StephaneLenclud@73: //...and fetch our usages StephaneLenclud@73: status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length); StephaneLenclud@73: if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS) StephaneLenclud@73: { StephaneLenclud@73: Debug.WriteLine("Second pass could not parse HID data: " + status.ToString()); StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: else if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS) StephaneLenclud@73: { StephaneLenclud@73: Debug.WriteLine("First pass could not parse HID data: " + status.ToString()); StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: Debug.WriteLine("Usage count: " + usageCount.ToString()); StephaneLenclud@73: StephaneLenclud@73: //Copy usages into this event StephaneLenclud@73: if (usages != null) StephaneLenclud@73: { StephaneLenclud@73: foreach (USAGE_AND_PAGE up in usages) StephaneLenclud@73: { StephaneLenclud@73: //Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4")); StephaneLenclud@73: //Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4")); StephaneLenclud@73: //Add this usage to our list StephaneLenclud@73: Usages.Add(up.Usage); StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: public uint GetUsageValue(ushort aUsagePage, ushort aUsage) StephaneLenclud@73: { StephaneLenclud@73: foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities) StephaneLenclud@73: { StephaneLenclud@73: if (caps.IsRange) StephaneLenclud@73: { StephaneLenclud@73: //What should we do with those guys? StephaneLenclud@73: continue; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: //Check if we have a match StephaneLenclud@73: if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage) StephaneLenclud@73: { StephaneLenclud@73: return UsageValues[caps]; StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: return 0; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: public int GetValueCapabilitiesIndex(ushort aUsagePage, ushort aUsage) StephaneLenclud@73: { StephaneLenclud@73: int i = -1; StephaneLenclud@73: foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities) StephaneLenclud@73: { StephaneLenclud@73: i++; StephaneLenclud@73: if (caps.IsRange) StephaneLenclud@73: { StephaneLenclud@73: //What should we do with those guys? StephaneLenclud@73: continue; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: //Check if we have a match StephaneLenclud@73: if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage) StephaneLenclud@73: { StephaneLenclud@73: return i; StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: return i; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@73: /// TODO: Move this to another level? StephaneLenclud@73: /// StephaneLenclud@73: /// sl@41: public void StartRepeatTimer(double aInterval) sl@41: { sl@41: if (Timer == null) sl@41: { sl@41: return; sl@41: } sl@41: Timer.Enabled = false; sl@44: //Initial delay do not use auto reset sl@44: //After our initial delay however we do setup our timer one more time using auto reset StephaneLenclud@54: Timer.AutoReset = (RepeatCount != 0); StephaneLenclud@54: Timer.Interval = aInterval; StephaneLenclud@54: Timer.Enabled = true; sl@41: } sl@27: sl@44: static private void OnRepeatTimerElapsed(object sender, ElapsedEventArgs e, HidEvent aHidEvent) sl@41: { sl@42: if (aHidEvent.IsStray) sl@42: { sl@42: //Skip events if canceled sl@42: return; sl@42: } sl@44: sl@44: aHidEvent.RepeatCount++; sl@44: aHidEvent.Time = DateTime.Now; StephaneLenclud@54: if (aHidEvent.RepeatCount == 1) sl@44: { sl@44: //Re-Start our timer only after the initial delay sl@44: aHidEvent.StartRepeatTimer(aHidEvent.iRepeatSpeed); sl@44: } sl@44: sl@44: //Broadcast our repeat event sl@44: aHidEvent.OnHidEventRepeat(aHidEvent); sl@41: } sl@27: StephaneLenclud@54: /// StephaneLenclud@73: /// Provide the state of the dpad or hat switch if any. StephaneLenclud@73: /// If no dpad is found we return 'at rest'. StephaneLenclud@73: /// StephaneLenclud@73: /// StephaneLenclud@73: public DirectionPadState GetDirectionPadState() StephaneLenclud@73: { StephaneLenclud@77: int index=GetValueCapabilitiesIndex((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)GenericDesktop.HatSwitch); StephaneLenclud@73: if (index < 0) StephaneLenclud@73: { StephaneLenclud@73: //No hat switch found StephaneLenclud@73: return DirectionPadState.Rest; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: HIDP_VALUE_CAPS caps=Device.InputValueCapabilities[index]; StephaneLenclud@73: if (caps.IsRange) StephaneLenclud@73: { StephaneLenclud@73: //Defensive StephaneLenclud@73: return DirectionPadState.Rest; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: uint dpadUsageValue = UsageValues[caps]; StephaneLenclud@73: StephaneLenclud@73: if (dpadUsageValue < caps.LogicalMin || dpadUsageValue > caps.LogicalMax) StephaneLenclud@73: { StephaneLenclud@73: //Out of range means at rest StephaneLenclud@73: return DirectionPadState.Rest; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: //Normalize value to start at zero StephaneLenclud@73: //TODO: more error check here? StephaneLenclud@73: DirectionPadState res = (DirectionPadState)((int)dpadUsageValue - caps.LogicalMin); StephaneLenclud@73: return res; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: /// StephaneLenclud@54: /// Print information about this device to our debug output. StephaneLenclud@54: /// StephaneLenclud@54: public void DebugWrite() StephaneLenclud@54: { StephaneLenclud@54: if (!IsValid) StephaneLenclud@54: { StephaneLenclud@54: Debug.WriteLine("==== Invalid HidEvent"); StephaneLenclud@54: return; StephaneLenclud@54: } StephaneLenclud@70: StephaneLenclud@70: if (Device!=null) StephaneLenclud@70: { StephaneLenclud@70: Device.DebugWrite(); StephaneLenclud@70: } StephaneLenclud@70: StephaneLenclud@54: if (IsGeneric) Debug.WriteLine("==== Generic"); StephaneLenclud@54: if (IsKeyboard) Debug.WriteLine("==== Keyboard"); StephaneLenclud@54: if (IsMouse) Debug.WriteLine("==== Mouse"); StephaneLenclud@54: Debug.WriteLine("==== Foreground: " + IsForeground.ToString()); StephaneLenclud@54: Debug.WriteLine("==== UsagePage: 0x" + UsagePage.ToString("X4")); StephaneLenclud@54: Debug.WriteLine("==== UsageCollection: 0x" + UsageCollection.ToString("X4")); StephaneLenclud@54: Debug.WriteLine("==== InputReport: 0x" + InputReportString()); StephaneLenclud@54: foreach (ushort usage in Usages) StephaneLenclud@54: { StephaneLenclud@54: Debug.WriteLine("==== Usage: 0x" + usage.ToString("X4")); StephaneLenclud@54: } StephaneLenclud@54: } StephaneLenclud@49: StephaneLenclud@54: /// StephaneLenclud@54: /// StephaneLenclud@54: /// StephaneLenclud@54: /// StephaneLenclud@54: public string InputReportString() StephaneLenclud@54: { StephaneLenclud@70: if (InputReport == null) StephaneLenclud@70: { StephaneLenclud@70: return "null"; StephaneLenclud@70: } StephaneLenclud@70: StephaneLenclud@54: string hidDump = ""; StephaneLenclud@54: foreach (byte b in InputReport) StephaneLenclud@54: { StephaneLenclud@54: hidDump += b.ToString("X2"); StephaneLenclud@54: } StephaneLenclud@54: return hidDump; StephaneLenclud@54: } StephaneLenclud@49: StephaneLenclud@49: StephaneLenclud@54: /// StephaneLenclud@54: /// Create a list view item describing this HidEvent StephaneLenclud@54: /// StephaneLenclud@54: /// sl@42: public ListViewItem ToListViewItem() sl@42: { StephaneLenclud@49: string usageText = ""; sl@42: StephaneLenclud@54: foreach (ushort usage in Usages) StephaneLenclud@54: { StephaneLenclud@54: if (usageText != "") StephaneLenclud@54: { StephaneLenclud@54: //Add a separator StephaneLenclud@54: usageText += ", "; StephaneLenclud@54: } sl@42: StephaneLenclud@54: UsagePage usagePage = (UsagePage)UsagePage; StephaneLenclud@54: switch (usagePage) StephaneLenclud@54: { StephaneLenclud@77: case Hid.UsagePage.Consumer: StephaneLenclud@76: usageText += ((ConsumerControl)usage).ToString(); StephaneLenclud@54: break; sl@42: StephaneLenclud@77: case Hid.UsagePage.WindowsMediaCenterRemoteControl: StephaneLenclud@76: usageText += ((WindowsMediaCenterRemoteControl)usage).ToString(); StephaneLenclud@54: break; StephaneLenclud@49: StephaneLenclud@54: default: StephaneLenclud@54: usageText += usage.ToString("X2"); StephaneLenclud@54: break; StephaneLenclud@54: } StephaneLenclud@54: } StephaneLenclud@49: StephaneLenclud@73: //If we are a gamepad display axis and dpad values StephaneLenclud@73: if (Device.IsGamePad) StephaneLenclud@73: { StephaneLenclud@73: //uint dpadUsageValue = GetUsageValue((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)Hid.Usage.GenericDesktop.HatSwitch); StephaneLenclud@73: //usageText = dpadUsageValue.ToString("X") + " (dpad), " + usageText; StephaneLenclud@73: StephaneLenclud@73: if (usageText != "") StephaneLenclud@73: { StephaneLenclud@73: //Add a separator StephaneLenclud@73: usageText += " (Buttons)"; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: if (usageText != "") StephaneLenclud@73: { StephaneLenclud@73: //Add a separator StephaneLenclud@73: usageText += ", "; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: usageText += GetDirectionPadState().ToString(); StephaneLenclud@73: StephaneLenclud@73: foreach (KeyValuePair entry in UsageValues) StephaneLenclud@73: { StephaneLenclud@73: if (entry.Key.IsRange) StephaneLenclud@73: { StephaneLenclud@73: continue; StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: Type usageType = Utils.UsageType((UsagePage)entry.Key.UsagePage); StephaneLenclud@73: if (usageType == null) StephaneLenclud@73: { StephaneLenclud@73: //TODO: check why this is happening on Logitech rumble gamepad 2. StephaneLenclud@73: //Probably some of our axis are hiding in there. StephaneLenclud@73: continue; StephaneLenclud@73: } StephaneLenclud@73: string name = Enum.GetName(usageType, entry.Key.NotRange.Usage); StephaneLenclud@73: StephaneLenclud@73: if (usageText != "") StephaneLenclud@73: { StephaneLenclud@73: //Add a separator StephaneLenclud@73: usageText += ", "; StephaneLenclud@73: } StephaneLenclud@73: usageText += entry.Value.ToString("X") + " ("+ name +")"; StephaneLenclud@73: } StephaneLenclud@73: } StephaneLenclud@73: StephaneLenclud@73: //Now create our list item StephaneLenclud@54: ListViewItem item = new ListViewItem(new[] { usageText, InputReportString(), UsagePage.ToString("X2"), UsageCollection.ToString("X2"), RepeatCount.ToString(), Time.ToString("HH:mm:ss:fff") }); sl@42: return item; sl@42: } sl@42: sl@27: } sl@27: sl@27: }