PORTS Forum
Ports and Interfaces => USB => Topic started by: freddybbrain on November 24, 2014, 12:35:21 pm
-
I'm trying to read hid touch device data (from any touch device) directly through the HidPxxx functions into an application and have managed to get x/y/logicalmax/width/height/touchId, and touch count values but the usage values for Tip and InRange are always 0. I know the values are present in the data because I can see them flagging when I cast the rawinputdata to a byte array and watch them. This is happening with a surface pro 3, surface pro 2, and acer T230H monitor.
Does anyone have any experience with this and can tell me if it's standard behavior?
-
See if this helps:
janaxelson.com/forum/index.php?topic=1324
If not, show how you are attempting to get the values.
-
I'm processing raw input.
lpb is the raw input data cast to a byte array.
I have all the data I need except for in range or tip. Without a lift off event I have no way to implement a touch up event.
int bufferSize = 0;
RIDInput.GetRawInputDeviceInfo(ri.Header.hDevice, RIDI_PREPARSEDDATA, IntPtr.Zero, ref bufferSize);
IntPtr pPreparsedData = Marshal.AllocHGlobal(bufferSize);
RIDInput.GetRawInputDeviceInfo(ri.Header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, ref bufferSize);
HIDP_CAPS Caps = new HIDP_CAPS();
HidP_GetCaps(pPreparsedData, ref Caps);
HidP_Value_Caps[] pValueCaps = new HidP_Value_Caps[Caps.NumberInputValueCaps];
short capsLength = Caps.NumberInputValueCaps;
HidP_GetValueCaps(0, pValueCaps, ref capsLength, pPreparsedData);
for (int j = 0; j < pValueCaps.Length; j++)
{
int ret = 0;
ushort usage = 0;
if (pValueCaps[j].IsRange)
{
ret = HidP_GetUsageValue(0, (short)pValueCaps[j].UsagePage, 0, pValueCaps[j].Range.UsageMin, ref value, pPreparsedData,
lpb, lpb.Length);
usage = pValueCaps[j].Range.UsageMin;
}
else
{
ret = HidP_GetUsageValue(0, (short)pValueCaps[j].UsagePage, 0, pValueCaps[j].NotRange.Usage, ref value, pPreparsedData,
lpb, lpb.Length);
usage = pValueCaps[j].NotRange.Usage;
}
if (ret != HIDP_STATUS_SUCCESS)
continue;
switch (pValueCaps[j].UsagePage)
{
case 0x01:
switch (usage)
{
case 0x30:
xMax = (int)pValueCaps[j].LogicalMax;
x = value;
break;
case 0x31:
yMax = (int)pValueCaps[j].LogicalMax;
y = value;
break;
}
break;
case 0x0D:
switch (usage)
{
case 0x30:
Console.WriteLine("Pressure " + value);
break;
case 0x32: // In-Range
inRange = value;
break;
case 0x42: // Tip
tip = value;
break;
case 0x47: // Confidence
Console.WriteLine("Confidence " + value);
break;
case 0x48:
width = value;
break;
case 0x49:
height = value;
break;
case 0x51:
touchId = (byte)value;
break;
case 0x54:
touchCount = value;
break;
case 0x55:
Console.WriteLine("contacts");
maxContacts = value;
break;
// Scan Time
case 0x56:
Console.WriteLine(value);
break;
}
break;
}
}
-
Not sure if this applies to what you're doing:
http://janaxelson.com/forum/index.php?topic=1269
-
I managed to get the data I needed by looping through the button caps array looking for the usage I needed and then using that button's data index as the index into a HIDP_DATA array and checking if that is on or off. Worked great for the T230H and Surface Pro 3, but fails on Surface Pro 2....
My application is a toolbar/context menu aimed at tablet pc's. (radialmenu.weebly.com) I'm attempting to read touch input globally so users can use a toolbar at the same time they are using a pen as you would a keyboard. I managed to get it working using Raw Input and creating a custom structure for every device I want to support using data captured by some of the users of the application, but this is not a good solution. I saw another application (TouchMe gesture studio) that processes touch globally, without having an active window and does so by reading directly from the touch drivers, so I started down this path trying to use HIDP functions so I could support ALL touch screens.
I only need 5 values, X, xMax(LogicalMax), Y, yMax(LogicalMax), and Tip (or inrange, but should be tip) only the Tip/Inrange is giving me a problem, as this isn't in the value caps array... it's in the button caps array. The method below seems like overkill for a single value (Tip or InRange) and it breaks on the Surface Pro 2.
What is the proper way to handle button caps?
RIDInput.HIDP_DATA[] list = new RIDInput.HIDP_DATA[dataLength];
ret = RIDInput.HidP_GetData(0, list, ref dataLength, pPreparsedData, lpb, Caps.InputReportByteLength);
if (ret != RIDInput.HIDP_STATUS_SUCCESS) RIDInput.GetHidPError(ret);
for (int k = 0; k < pButtonValCaps.Length; k++)
{
switch (pButtonValCaps[k].NotRange.Usage)
{
case 0x32: // InRange
case 0x42: // Tip
foreach (RIDInput.HIDP_DATA data in list)
{
if (data.DataIndex == pButtonValCaps[k].NotRange.DataIndex)
{
inRange = data.On;
}
}
break;
}
}
-
When it breaks, what goes wrong?
Do you see the expected:
RIDInput.HIDP_DATA data in list
What is your declaration for HIDP_DATA?
-
It seems that the device that isn't working has 6 link collection nodes, the other devices don't. I'm guessing it has something to do with that and I have to figure out how to process those.
[StructLayout(LayoutKind.Explicit)]
public struct HIDP_DATA
{
[FieldOffset(0)]
public short DataIndex;
[FieldOffset(2)]
public short Reserved;
[FieldOffset(4)]
public int RawValue;
[FieldOffset(4), MarshalAs(UnmanagedType.U1)]
public bool On;
}
-
It sounds like you're doing a good job of narrowing it down. If you can post the report descriptor or CAPS structure for the non-working device, someone might have an idea.
-
I got it working by changing the order that I loop through the HIDP_DATA list and HIDP_ButtonCaps list. Was looping through the button caps and inside that loop looping through the hidp_data list, switching them around fixed it...
-
Glad you got it working; thanks for reporting what you found.
-
So after getting the previous problem sorted everything seemed to work great on windows 8.1 x64 systems, until I tested on a x86 windows 7 machine, HidP_GetData returns an empty list of HIDP_DATA structures. The - byte[] Report - that I'm passing is the same on both x86 and x64 machines so I'm thinking that's not the problem. Any ideas what's causing this?
[StructLayout(LayoutKind.Explicit)]
public struct HIDP_DATA
{
[FieldOffset(0)]
public short DataIndex;
[FieldOffset(2)]
public short Reserved;
[FieldOffset(4)]
public int RawValue;
[FieldOffset(4), MarshalAs(UnmanagedType.U1)]
public bool On;
}
[DllImport("hid.dll", SetLastError = true)]
public static extern int HidP_GetData(Int32 ReportType, [In, Out] HIDP_DATA[] DataList,
ref int DataLength, IntPtr PreparsedData, [MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 5)] byte[] Report,
int ReportLength);
-
What is the return value of HidP_GetData when it fails?
-
It returns that it was successful, but the DataLength member after the call is 0, as if none of the HIDP_Data values are set to 'On'.
-
Strange, fixed it on win 7 x86 by offsetting the pointer to the data by 4 bytes when getting the raw input data. Odd that the very same data worked on both x86/x64 when I was processing the byte array manually and the needed data indices were the same on either x86 or x64. But using the HidP_ functions I have to add a 4 byte offset for x86 to get the proper data....
Atleast it's fixed. :)
(riBuffer is a pointer to the raw data returned from GetRawInputData)
IntPtr source;
if (IntPtr.Size == 4)
source = new IntPtr(riBuffer.ToInt32() + IntPtr.Size + 4 + RIDInput.HeaderSize);
else
source = new IntPtr(riBuffer.ToInt64() + IntPtr.Size + RIDInput.HeaderSize);
ri = (RawInput)Marshal.PtrToStructure(riBuffer, typeof(RawInput));
lpb = new byte[ri.Data.HID.dwSizeHID * ri.Data.HID.dwCount];
Marshal.Copy(source, lpb, 0, lpb.Length);
// Previous code that didn't work with HIDP_GetData, but did when accessing the array myself (lpb) on both x86/x64.
// Marshal.Copy(new IntPtr((riBuffer.ToInt64() + IntPtr.Size + 4 + RIDInput.HeaderSize)), lpb, 0, lpb.Length);
-
Excellent. It makes sense that it had something to do with data size, but I have no explanation for why it appeared different when processing manually!