Search code examples
c++windowswinapiraw-input

Raw Input - recive WM_INPUT while window/program is in background


I am trying to monitor and printout the RAWINPUT from a mouse sent to the foreground window, or just all RAWINPUT from the mouse in general.

the global hook LowLevelMouseProc does not work for me because it returns a MOUSEHOOKSTRUCT that does not give me the dx and dy.

The Raw Input API mentions that for WM_INPUT received when the current window is in the back ground, wParam will be set to RIM_INPUTSINK. But I have no idea how to receive WM_INPUT while the program is in the background.

here is some code explaining what I am trying to do.

int main()
{
    //regiter the monitoring device
    static bool raw_input_initialized = false;
    if (raw_input_initialized == false)
    {
        RAWINPUTDEVICE rid;

        rid.usUsagePage = 0x01; //Mouse
        rid.usUsage = 0x02;
        rid.dwFlags = 0;
        rid.hwndTarget = NULL;

        if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE)
        {
            exit(-1);
        }

        raw_input_initialized = true;
    }

    HWND targetWindow = { 0 };

    while (true)
    {
        targetWindow = GetForegroundWindow(); // get the window runing in the formost window;

        
        std::cout << targetWindow << '\n';
    }
    return 0;
}

// enterd every time there is a rawinput to ForegroundWindow, or alternatively just a rawinput in general
LRESULT CALLBACK targetWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    // print out the values that I need
    case WM_INPUT:
        UINT dataSize;
        GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER)); //Need to populate data size first
        std::cout << GET_RAWINPUT_CODE_WPARAM(wParam) << " code thing\n";
        if (dataSize > 0)
        {

            std::unique_ptr<BYTE[]> rawdata = std::make_unique<BYTE[]>(dataSize);

            if (GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, rawdata.get(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
            {
                RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(rawdata.get());
                if (raw->header.dwType == RIM_TYPEMOUSE)
                {
                    std::cout << raw->data.mouse.lLastX << std::endl;
                }
            }
        }
        break;
    }
}


Solution

  • But I have no idea how to receive WM_INPUT while the program is in the background.

    You need to specify the RIDEV_INPUTSINK flag when registering the device, per the RAWINPUTDEVICE documentation:

    dwFlags
    Type: DWORD

    Mode flag that specifies how to interpret the information provided by usUsagePage and usUsage. It can be zero (the default) or one of the following values. By default, the operating system sends raw input from devices with the specified top level collection (TLC) to the registered application as long as it has the window focus.

    ...

    RIDEV_INPUTSINK
    0x00000100

    If set, this enables the caller to receive the input even when the caller is not in the foreground. Note that hwndTarget must be specified.

    As such, you must specify an HWND to receive the WM_INPUT messages, and have a message loop to service that window.

    Try this:

    #include <iostream>
    #include <vector>
    #include <Windows.h>
    
    LRESULT CALLBACK targetWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int main()
    {
        HINSTANCE hInstance = GetModuleHandle(NULL);
    
        WNDCLASS wc = {};
        wc.lpfnWndProc = targetWindowProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = TEXT("MyRawInputWnd");
    
        if (!RegisterClass(&wc))
            return -1;
    
        HWND targetWindow = CreateWindowEx(0, wc.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
        if (!targetWindow)
            return -1;
    
        //register the monitoring device
        RAWINPUTDEVICE rid = {};
        rid.usUsagePage = 0x01; //Mouse
        rid.usUsage = 0x02;
        rid.dwFlags = RIDEV_INPUTSINK;
        rid.hwndTarget = targetWindow;
    
        if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
            return -1;
    
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        DestroyWindow(targetWindow);
    
        return 0;
    }
    
    LRESULT CALLBACK targetWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            // print out the values that I need
            case WM_INPUT: {
                UINT dataSize;
                GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER)); //Need to populate data size first
                std::cout << GET_RAWINPUT_CODE_WPARAM(wParam) << " code thing\n";
                if (dataSize > 0)
                {
                    std::vector<BYTE> rawdata(dataSize);
    
                    if (GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, rawdata.data(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
                    {
                        RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(rawdata.data());
                        if (raw->header.dwType == RIM_TYPEMOUSE)
                        {
                            std::cout << raw->data.mouse.lLastX << std::endl;
                        }
                    }
                }
                return 0;
            }
        }
    
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }