# HG changeset patch
# User StephaneLenclud
# Date 1426419898 -3600
# Node ID 5262f4b7c4ad762175cba7e5d1f01d7cda2fa449
# Parent 471b1d45c46a0d752967cadfc8d3901a92a09bfe
Adding support for usage value and thus joystick/gamepad.
Adding generic support for direction pad.
diff -r 471b1d45c46a -r 5262f4b7c4ad HidDevice.cs
--- a/HidDevice.cs Sun Mar 15 09:53:37 2015 +0100
+++ b/HidDevice.cs Sun Mar 15 12:44:58 2015 +0100
@@ -300,11 +300,11 @@
///
///
///
- public string InputValueCapabilityDescription(HIDP_VALUE_CAPS aCaps)
+ static public string InputValueCapabilityDescription(HIDP_VALUE_CAPS aCaps)
{
- if (!aCaps.IsRange && Enum.IsDefined(typeof(UsagePage), Capabilities.UsagePage))
+ if (!aCaps.IsRange && Enum.IsDefined(typeof(UsagePage), aCaps.UsagePage))
{
- Type usageType=Utils.UsageType((UsagePage)Capabilities.UsagePage);
+ Type usageType = Utils.UsageType((UsagePage)aCaps.UsagePage);
string name = Enum.GetName(usageType, aCaps.NotRange.Usage);
if (name == null)
{
diff -r 471b1d45c46a -r 5262f4b7c4ad HidEvent.cs
--- a/HidEvent.cs Sun Mar 15 09:53:37 2015 +0100
+++ b/HidEvent.cs Sun Mar 15 12:44:58 2015 +0100
@@ -12,6 +12,22 @@
namespace Hid
{
///
+ /// We provide utility functions to interpret gamepad dpad state.
+ ///
+ public enum DirectionPadState
+ {
+ Rest=-1,
+ Up=0,
+ UpRight=1,
+ Right=2,
+ DownRight=3,
+ Down=4,
+ DownLeft=5,
+ Left=6,
+ UpLeft=7
+ }
+
+ ///
/// Represent a HID event.
/// TODO: Rename this into HidRawInput?
///
@@ -39,6 +55,10 @@
public ushort UsageCollection { get; private set; }
public uint UsageId { get { return ((uint)UsagePage << 16 | (uint)UsageCollection); } }
public List Usages { get; private set; }
+ ///
+ /// Sorted in the same order as Device.InputValueCapabilities.
+ ///
+ public Dictionary UsageValues { get; private set; }
//TODO: We need a collection of input report
public byte[] InputReport { get; private set; }
//
@@ -86,6 +106,7 @@
Timer = new System.Timers.Timer();
Timer.Elapsed += (sender, e) => OnRepeatTimerElapsed(sender, e, this);
Usages = new List();
+ UsageValues = new Dictionary();
OnHidEventRepeat += aRepeatDelegate;
if (aMessage.Msg != Const.WM_INPUT)
@@ -159,45 +180,8 @@
//Copy HID input into our buffer
Marshal.Copy(new IntPtr(hidInputOffset), InputReport, 0, (int)RawInput.hid.dwSizeHid);
-
- //Print HID input report in our debug output
- //string hidDump = "HID input report: " + InputReportString();
- //Debug.WriteLine(hidDump);
-
- //Do proper parsing of our HID report
- //First query our usage count
- uint usageCount = 0;
- Win32.USAGE_AND_PAGE[] usages = null;
- Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, InputReport, (uint)InputReport.Length);
- if (status == Win32.HidStatus.HIDP_STATUS_BUFFER_TOO_SMALL)
- {
- //Allocate a large enough buffer
- usages = new Win32.USAGE_AND_PAGE[usageCount];
- //...and fetch our usages
- status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, InputReport, (uint)InputReport.Length);
- if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
- {
- Debug.WriteLine("Second pass could not parse HID data: " + status.ToString());
- }
- }
- else if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
- {
- Debug.WriteLine("First pass could not parse HID data: " + status.ToString());
- }
-
- Debug.WriteLine("Usage count: " + usageCount.ToString());
-
- //Copy usages into this event
- if (usages != null)
- {
- foreach (USAGE_AND_PAGE up in usages)
- {
- //Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
- //Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
- //Add this usage to our list
- Usages.Add(up.Usage);
- }
- }
+ //
+ ProcessInputReport(InputReport);
}
}
else if (RawInput.header.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
@@ -240,6 +224,153 @@
IsValid = true;
}
+ ///
+ ///
+ ///
+ private void ProcessInputReport(byte[] aInputReport)
+ {
+ //Print HID input report in our debug output
+ //string hidDump = "HID input report: " + InputReportString();
+ //Debug.WriteLine(hidDump);
+
+ //Get all our usages, those are typically the buttons currently pushed on a gamepad.
+ //For a remote control it's usually just the one button that was pushed.
+ GetUsages(aInputReport);
+
+ //Now process direction pad (d-pad, dpad) and axes
+ GetUsageValues(aInputReport);
+ }
+
+ ///
+ /// Typically fetches values of a joystick/gamepad axis and dpad directions.
+ ///
+ ///
+ private void GetUsageValues(byte[] aInputReport)
+ {
+ if (Device.InputValueCapabilities == null)
+ {
+ return;
+ }
+
+ foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
+ {
+ if (caps.IsRange)
+ {
+ //What should we do with those guys?
+ continue;
+ }
+
+ //Now fetch and add our usage value
+ uint usageValue = 0;
+ 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);
+ if (status == Win32.HidStatus.HIDP_STATUS_SUCCESS)
+ {
+ UsageValues[caps]=usageValue;
+ }
+ }
+ }
+
+ ///
+ /// Get all our usages, those are typically the buttons currently pushed on a gamepad.
+ /// For a remote control it's usually just the one button that was pushed.
+ ///
+ private void GetUsages(byte[] aInputReport)
+ {
+ //Do proper parsing of our HID report
+ //First query our usage count
+ uint usageCount = 0;
+ Win32.USAGE_AND_PAGE[] usages = null;
+ Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length);
+ if (status == Win32.HidStatus.HIDP_STATUS_BUFFER_TOO_SMALL)
+ {
+ //Allocate a large enough buffer
+ usages = new Win32.USAGE_AND_PAGE[usageCount];
+ //...and fetch our usages
+ status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length);
+ if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
+ {
+ Debug.WriteLine("Second pass could not parse HID data: " + status.ToString());
+ }
+ }
+ else if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
+ {
+ Debug.WriteLine("First pass could not parse HID data: " + status.ToString());
+ }
+
+ Debug.WriteLine("Usage count: " + usageCount.ToString());
+
+ //Copy usages into this event
+ if (usages != null)
+ {
+ foreach (USAGE_AND_PAGE up in usages)
+ {
+ //Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
+ //Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
+ //Add this usage to our list
+ Usages.Add(up.Usage);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public uint GetUsageValue(ushort aUsagePage, ushort aUsage)
+ {
+ foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
+ {
+ if (caps.IsRange)
+ {
+ //What should we do with those guys?
+ continue;
+ }
+
+ //Check if we have a match
+ if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage)
+ {
+ return UsageValues[caps];
+ }
+ }
+
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public int GetValueCapabilitiesIndex(ushort aUsagePage, ushort aUsage)
+ {
+ int i = -1;
+ foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
+ {
+ i++;
+ if (caps.IsRange)
+ {
+ //What should we do with those guys?
+ continue;
+ }
+
+ //Check if we have a match
+ if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage)
+ {
+ return i;
+ }
+ }
+
+ return i;
+ }
+
+
+ ///
+ /// TODO: Move this to another level?
+ ///
+ ///
public void StartRepeatTimer(double aInterval)
{
if (Timer == null)
@@ -275,6 +406,41 @@
}
///
+ /// Provide the state of the dpad or hat switch if any.
+ /// If no dpad is found we return 'at rest'.
+ ///
+ ///
+ public DirectionPadState GetDirectionPadState()
+ {
+ int index=GetValueCapabilitiesIndex((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)Hid.Usage.GenericDesktop.HatSwitch);
+ if (index < 0)
+ {
+ //No hat switch found
+ return DirectionPadState.Rest;
+ }
+
+ HIDP_VALUE_CAPS caps=Device.InputValueCapabilities[index];
+ if (caps.IsRange)
+ {
+ //Defensive
+ return DirectionPadState.Rest;
+ }
+
+ uint dpadUsageValue = UsageValues[caps];
+
+ if (dpadUsageValue < caps.LogicalMin || dpadUsageValue > caps.LogicalMax)
+ {
+ //Out of range means at rest
+ return DirectionPadState.Rest;
+ }
+
+ //Normalize value to start at zero
+ //TODO: more error check here?
+ DirectionPadState res = (DirectionPadState)((int)dpadUsageValue - caps.LogicalMin);
+ return res;
+ }
+
+ ///
/// Print information about this device to our debug output.
///
public void DebugWrite()
@@ -356,6 +522,52 @@
}
}
+ //If we are a gamepad display axis and dpad values
+ if (Device.IsGamePad)
+ {
+ //uint dpadUsageValue = GetUsageValue((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)Hid.Usage.GenericDesktop.HatSwitch);
+ //usageText = dpadUsageValue.ToString("X") + " (dpad), " + usageText;
+
+ if (usageText != "")
+ {
+ //Add a separator
+ usageText += " (Buttons)";
+ }
+
+ if (usageText != "")
+ {
+ //Add a separator
+ usageText += ", ";
+ }
+
+ usageText += GetDirectionPadState().ToString();
+
+ foreach (KeyValuePair entry in UsageValues)
+ {
+ if (entry.Key.IsRange)
+ {
+ continue;
+ }
+
+ Type usageType = Utils.UsageType((UsagePage)entry.Key.UsagePage);
+ if (usageType == null)
+ {
+ //TODO: check why this is happening on Logitech rumble gamepad 2.
+ //Probably some of our axis are hiding in there.
+ continue;
+ }
+ string name = Enum.GetName(usageType, entry.Key.NotRange.Usage);
+
+ if (usageText != "")
+ {
+ //Add a separator
+ usageText += ", ";
+ }
+ usageText += entry.Value.ToString("X") + " ("+ name +")";
+ }
+ }
+
+ //Now create our list item
ListViewItem item = new ListViewItem(new[] { usageText, InputReportString(), UsagePage.ToString("X2"), UsageCollection.ToString("X2"), RepeatCount.ToString(), Time.ToString("HH:mm:ss:fff") });
return item;
}
diff -r 471b1d45c46a -r 5262f4b7c4ad RawInput.cs
--- a/RawInput.cs Sun Mar 15 09:53:37 2015 +0100
+++ b/RawInput.cs Sun Mar 15 12:44:58 2015 +0100
@@ -215,7 +215,7 @@
{
foreach (HIDP_VALUE_CAPS caps in hidDevice.InputValueCapabilities)
{
- string des = hidDevice.InputValueCapabilityDescription(caps);
+ string des = Hid.HidDevice.InputValueCapabilityDescription(caps);
if (des != null)
{
node.Nodes.Add(des);
diff -r 471b1d45c46a -r 5262f4b7c4ad Win32Hid.cs
--- a/Win32Hid.cs Sun Mar 15 09:53:37 2015 +0100
+++ b/Win32Hid.cs Sun Mar 15 12:44:58 2015 +0100
@@ -52,7 +52,7 @@
///Report: PCHAR->CHAR*
///ReportLength: ULONG->unsigned int
[System.Runtime.InteropServices.DllImportAttribute("hid.dll", EntryPoint = "HidP_GetUsageValue", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
- public static extern HidStatus HidP_GetUsageValue(HIDP_REPORT_TYPE ReportType, ushort UsagePage, ushort LinkCollection, ushort Usage, ref uint UsageValue, System.IntPtr PreparsedData, System.IntPtr Report, uint ReportLength);
+ public static extern HidStatus HidP_GetUsageValue(HIDP_REPORT_TYPE ReportType, ushort UsagePage, ushort LinkCollection, ushort Usage, ref uint UsageValue, System.IntPtr PreparsedData, [MarshalAs(UnmanagedType.LPArray)] byte[] Report, uint ReportLength);