RAWINPUTHEADER hDevice null on WM_INPUT for laptop trackpad

1

I'm using raw input to handle input for generic devices, thus far all of my test cases have worked (keyboards, game pads and mice), but my laptops track pad is giving me a weird problem. When I get a WM_INPUT message from the track pad (movement or button presses) I receive almost all the correct information except for the hDevice in the RAWINPUT header

I'm getting all available HID devices through GetRawInputDeviceList (with RID_DEVICE_INFO) and WM_INPUT_DEVICE_CHANGE messages. I believe the track pad is found by the first method (mouse HID with 2 buttons, index 6).

HID: [0x00020043] active
HID: [0x00020047] active
HID: [0x00020049] active
HID: [0x0002004B] active
keyboard: [0x00010041] active
mouse: [0x0001003B] active
mouse: [0x00010039] active
mouse: [0x0001003B] added
mouse: [0x00010039] added
keyboard: [0x00010041] added
#ifndef UNICODE
#define UNICODE
#endif

#include <array>
#include <vector>
#include <Windows.h>

bool active = true;

const char* getTypeStr(DWORD type)
{
    if (type == RIM_TYPEMOUSE) return "mouse";
    else if (type == RIM_TYPEKEYBOARD) return "keyboard";
    else return "HID";
}

LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_INPUT)
    {
        if (GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT) // Only handle foreground events.
        {
            const HRAWINPUT hRawInput = reinterpret_cast<HRAWINPUT>(lParam);

            // Get the size of the data package.
            UINT32 size = 0;
            GetRawInputData(hRawInput, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER));

            // Ignore empty packets.
            if (size > 0)
            {
                PRAWINPUT input = reinterpret_cast<PRAWINPUT>(malloc(size));
                GetRawInputData(hRawInput, RID_HEADER, input, &size, sizeof(RAWINPUTHEADER));
                GetRawInputData(hRawInput, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));

                printf("Received WM_INPUT from 0x%p.\n", input->header.hDevice);

                free(input);
                return 0;
            }
        }
    }
    else if (msg == WM_INPUT_DEVICE_CHANGE)
    {
        const HANDLE hDevice = reinterpret_cast<HANDLE>(lParam);
        RID_DEVICE_INFO info;
        info.cbSize = sizeof(RID_DEVICE_INFO);
        UINT cbSize = info.cbSize;
        GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, &info, &cbSize);

        if (wParam == GIDC_ARRIVAL) printf("%s: [0x%p] added\n", getTypeStr(info.dwType), hDevice);
        else printf("%s: [0x%p] removed\n", getTypeStr(info.dwType), hDevice);
    }
    else if (msg == WM_CLOSE)
    {
        active = false;
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int main()
{
    // Create the window.
    const HINSTANCE hInstance = GetModuleHandle(nullptr);

    WNDCLASSEX wndEx =
    {
        sizeof(WNDCLASSEX),
        CS_DBLCLKS,
        wndProc,
        0,
        0,
        hInstance,
        nullptr,
        LoadCursor(nullptr, IDC_ARROW),
        (HBRUSH)(COLOR_WINDOW + 1),
        nullptr,
        L"TestWindow",
        nullptr
    };

    RegisterClassEx(&wndEx);
    const HWND hWnd = CreateWindow(L"TestWindow", L"TestWindow", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, 0, 0, 600, 600, nullptr, nullptr, hInstance, nullptr);
    ShowWindow(hWnd, SW_SHOW);

    // Log the connected devices.
    UINT32 deviceCnt;
    GetRawInputDeviceList(nullptr, &deviceCnt, sizeof(RAWINPUTDEVICELIST));

    std::vector<RAWINPUTDEVICELIST> devices{ deviceCnt };
    GetRawInputDeviceList(devices.data(), &deviceCnt, sizeof(RAWINPUTDEVICELIST));

    for (const RAWINPUTDEVICELIST cur : devices)
    {
        printf("%s: [0x%p] active\n", getTypeStr(cur.dwType), cur.hDevice);
    }

    // Register the raw input devices we want to get notifications from.
    std::array<RAWINPUTDEVICE, 3> rawInputDevices
    {
        RAWINPUTDEVICE {
            0x1,
            0x2,    // Mouse
            RIDEV_DEVNOTIFY,
            hWnd
        },
        RAWINPUTDEVICE {
            0x1,
            0x6,    //Keyboard
            RIDEV_DEVNOTIFY,
            hWnd
        },
        RAWINPUTDEVICE {
            0x1,
            0x5,    // Gamepad
            RIDEV_DEVNOTIFY,
            hWnd
        }
    };

    RegisterRawInputDevices(rawInputDevices.data(), rawInputDevices.size(), sizeof(RAWINPUTDEVICE));

    // Update loop.
    MSG msg;
    while (active)
    {
        while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    // Finalize.
    DestroyWindow(hWnd);
    UnregisterClass(L"TestWindow", hInstance);
}

I'm expecting the WM_INPUT message to give me a valid device handle but it doesn't.

Received WM_INPUT from 0x00000000.
The result for a move
input:
    header:
        dwType = 0
        dwSize = 48
        hDevice = 0x0000000000000000
        wParam = 0
    data (mouse):
        usFlags = 0
        usButtons = 0
        usButtonData = 0
        ulRawButtons = 0
        ILastX = 5
        ILastY = -6
        uIExtraInformation = 0
HID that's probably my track pad
    hDevice = 0x0000000000010039
    cbSize = 32
    dwType = 0
    mouse:
        dwId = 128
        dwNumberOfButtons = 2
        dwSampleRate = 0
        fHasHorizontalWheel = 0

Notifications from my integrated keyboard seem to be correct

Received WM_INPUT from 0x00010041.
c++
winapi
raw-input
asked on Stack Overflow Aug 19, 2019 by Arzana • edited Aug 20, 2019 by Arzana

1 Answer

1

The problem was that my touchpad is a precision touchpad, which means that there are some filters/transformations applied to it before the WM_INPUT notification. This was intended behaviour by the API but not documented (as far as I know).

Massive thanks to Eric Brown for answering this questions! Check the comment he left for the answer.

answered on Stack Overflow Aug 22, 2019 by Arzana

User contributions licensed under CC BY-SA 3.0