Author Topic: Hid Touch devices.  (Read 10403 times)

freddybbrain

  • Member
  • ***
  • Posts: 8
Hid Touch devices.
« 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?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #1 on: November 24, 2014, 09:03:22 pm »
See if this helps:

janaxelson.com/forum/index.php?topic=1324

If not, show how you are attempting to get the values.

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #2 on: November 24, 2014, 09:56:43 pm »
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.


Code: [Select]
                               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;
                                            }
                                        }
« Last Edit: November 24, 2014, 10:15:40 pm by freddybbrain »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #3 on: November 25, 2014, 11:21:04 am »
Not sure if this applies to what you're doing:

http://janaxelson.com/forum/index.php?topic=1269

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #4 on: November 26, 2014, 11:43:06 am »
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?


Code: [Select]
                               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;
                                            }
                                        }
« Last Edit: November 26, 2014, 11:46:44 am by freddybbrain »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #5 on: November 26, 2014, 12:36:11 pm »
When it breaks, what goes wrong?

Do you see the expected:

RIDInput.HIDP_DATA data in list

What is your declaration for HIDP_DATA?

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #6 on: November 26, 2014, 02:17:09 pm »
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;
            }

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #7 on: November 28, 2014, 10:18:50 am »
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.

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #8 on: November 28, 2014, 11:02:08 am »
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...

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #9 on: November 28, 2014, 11:13:29 am »
Glad you got it working; thanks for reporting what you found.

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #10 on: December 04, 2014, 10:07:37 am »
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?

Code: [Select]
    [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);

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #11 on: December 05, 2014, 10:20:06 am »
What is the return value of HidP_GetData when it fails?

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #12 on: December 05, 2014, 10:52:10 am »
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'.

freddybbrain

  • Member
  • ***
  • Posts: 8
Re: Hid Touch devices.
« Reply #13 on: December 07, 2014, 07:19:29 am »
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. :)

Code: [Select]
                        (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);

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: Hid Touch devices.
« Reply #14 on: December 07, 2014, 10:14:41 am »
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!