/// <summary> /// Provide a description for the given capabilities. /// Notably describes axis on a gamepad/joystick. /// </summary> /// <param name="caps"></param> /// <returns></returns> public static string InputValueCapabilityDescription(HIDP_VALUE_CAPS caps) { if (!caps.IsRange && Enum.IsDefined(typeof(UsagePage), caps.UsagePage)) { Type usageType = Utils.UsageType((UsagePage)caps.UsagePage); if (usageType == null) { return("Input Value: " + Enum.GetName(typeof(UsagePage), caps.UsagePage) + " Usage 0x" + caps.NotRange.Usage.ToString("X2")); } string name = Enum.GetName(usageType, caps.NotRange.Usage); if (name == null) { // Could not find that usage in our enum. // Provide a relevant warning instead. name = "Usage 0x" + caps.NotRange.Usage.ToString("X2") + " not defined in " + usageType.Name; } else { // Prepend our usage type name name = usageType.Name + "." + name; } return("Input Value: " + name); } return(null); }
/// <summary> /// Provide the state of the dpad or hat switch if any. /// If no dpad is found we return 'at rest'. /// </summary> /// <returns></returns> public DirectionPadState GetDirectionPadState() { int index = GetValueCapabilitiesIndex((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)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); }
public Axis(HIDP_VALUE_CAPS aCaps) { if (aCaps.IsRange) { throw new ArgumentException("Range input values are not axis"); } if (!Enum.IsDefined(typeof(UsagePage), aCaps.UsagePage)) { throw new ArgumentException("Unknown axis usage page"); } Type usageType = Utils.UsageType((UsagePage)aCaps.UsagePage); if (usageType == null) { throw new ArgumentException("Unknown axis usage type"); } // Build our axis id which is combined of usage page and usage Id = IdFromValueCaps(aCaps); // Name = Enum.GetName(usageType, aCaps.NotRange.Usage); FullName = Enum.GetName(typeof(UsagePage), aCaps.UsagePage) + "." + Name; Capabilities = aCaps; }
public static string GetLinkName(this HIDP_VALUE_CAPS caps) { if (!caps.HasLink()) { return(""); } var pageType = Utils.UsageType((UsagePage)caps.LinkUsagePage); return(Enum.GetName(pageType, caps.LinkUsage) + $"({pageType.Name})[{caps.LinkCollection}]"); }
/// <summary> /// Utility method to check if the given input value is an axis /// </summary> /// <param name="aCaps"></param> /// <returns></returns> public static bool IsAxis(HIDP_VALUE_CAPS aCaps) { if (!aCaps.IsRange && Enum.IsDefined(typeof(UsagePage), aCaps.UsagePage)) { Type usageType = Utils.UsageType((UsagePage)aCaps.UsagePage); if (usageType == null) { return(false); } return(true); } return(false); }
public static int GetLinkIndex(this HIDP_VALUE_CAPS caps) => caps.LinkCollection;
public static bool HasLink(this HIDP_VALUE_CAPS caps) => caps.LinkUsage != 0;
public static float ConvertUnit(this HIDP_VALUE_CAPS caps, uint value_logical, bool friendly = true) { var t = getUnitTuple(caps.Units, caps.UnitsExp, friendly); return(t.Item1 * ((float)caps.PhysicalMin + (caps.PhysicalMax - caps.PhysicalMin) * (value_logical - caps.LogicalMin) / (caps.LogicalMax - caps.LogicalMin))); }
public static string GetUnit(this HIDP_VALUE_CAPS caps, bool friendly = true) { return(getUnitTuple(caps.Units, caps.UnitsExp, friendly).Item2); }
public static bool HasUnit(this HIDP_VALUE_CAPS caps) => caps.Units != 0;
public static string GetName(this HIDP_VALUE_CAPS caps) { var pageType = Utils.UsageType((UsagePage)caps.UsagePage); return(Enum.GetName(pageType, caps.NotRange.Usage) + $"({pageType.Name})"); }
public static TouchpadContact[] ParseInput(IntPtr lParam) { // Get RAWINPUT. uint rawInputSize = 0; uint rawInputHeaderSize = (uint)Marshal.SizeOf <RAWINPUTHEADER>(); if (GetRawInputData( lParam, RID_INPUT, IntPtr.Zero, ref rawInputSize, rawInputHeaderSize) != 0) { return(null); } RAWINPUT rawInput; byte[] rawHidRawData; IntPtr rawInputPointer = IntPtr.Zero; try { rawInputPointer = Marshal.AllocHGlobal((int)rawInputSize); if (GetRawInputData( lParam, RID_INPUT, rawInputPointer, ref rawInputSize, rawInputHeaderSize) != rawInputSize) { return(null); } rawInput = Marshal.PtrToStructure <RAWINPUT>(rawInputPointer); var rawInputData = new byte[rawInputSize]; Marshal.Copy(rawInputPointer, rawInputData, 0, rawInputData.Length); rawHidRawData = new byte[rawInput.Hid.dwSizeHid * rawInput.Hid.dwCount]; int rawInputOffset = (int)rawInputSize - rawHidRawData.Length; Buffer.BlockCopy(rawInputData, rawInputOffset, rawHidRawData, 0, rawHidRawData.Length); } finally { Marshal.FreeHGlobal(rawInputPointer); } // Parse RAWINPUT. IntPtr preparsedDataPointer = IntPtr.Zero; IntPtr rawHidRawDataPointer = IntPtr.Zero; try { uint preparsedDataSize = 0; if (GetRawInputDeviceInfo( rawInput.Header.hDevice, RIDI_PREPARSEDDATA, IntPtr.Zero, ref preparsedDataSize) != 0) { return(null); } preparsedDataPointer = Marshal.AllocHGlobal((int)preparsedDataSize); if (GetRawInputDeviceInfo( rawInput.Header.hDevice, RIDI_PREPARSEDDATA, preparsedDataPointer, ref preparsedDataSize) != preparsedDataSize) { return(null); } if (HidP_GetCaps( preparsedDataPointer, out HIDP_CAPS caps) != HIDP_STATUS_SUCCESS) { return(null); } ushort valueCapsLength = caps.NumberInputValueCaps; var valueCaps = new HIDP_VALUE_CAPS[valueCapsLength]; if (HidP_GetValueCaps( HIDP_REPORT_TYPE.HidP_Input, valueCaps, ref valueCapsLength, preparsedDataPointer) != HIDP_STATUS_SUCCESS) { return(null); } rawHidRawDataPointer = Marshal.AllocHGlobal(rawHidRawData.Length); Marshal.Copy(rawHidRawData, 0, rawHidRawDataPointer, rawHidRawData.Length); uint scanTime = 0; uint contactCount = 0; TouchpadContactCreator creator = new(); List <TouchpadContact> contacts = new(); foreach (var valueCap in valueCaps.OrderBy(x => x.LinkCollection)) { if (HidP_GetUsageValue( HIDP_REPORT_TYPE.HidP_Input, valueCap.UsagePage, valueCap.LinkCollection, valueCap.Usage, out uint value, preparsedDataPointer, rawHidRawDataPointer, (uint)rawHidRawData.Length) != HIDP_STATUS_SUCCESS) { continue; } // Usage Page and ID in Windows Precision Touchpad input reports // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-precision-touchpad-required-hid-top-level-collections#windows-precision-touchpad-input-reports switch (valueCap.LinkCollection) { case 0: switch (valueCap.UsagePage, valueCap.Usage) { case (0x0D, 0x56): // Scan Time scanTime = value; break; case (0x0D, 0x54): // Contact Count contactCount = value; break; } break;
public static int IdFromValueCaps(HIDP_VALUE_CAPS aCaps) { return(IdFromUsage(aCaps.UsagePage, aCaps.NotRange.Usage)); }