Handling Multiple Mice with Raw Input

Warning! Some information on this page is older than 6 years now. I keep it for reference, but it probably doesn't reflect my current knowledge and beliefs.

Thu
21
Mar 2013

When you connect second USB mouse to your computer, you don't have second cursor to move on the screen and click on things. Instead you can move and click the single cursor with both mice. What if you want to distinguish messages from multiple mice in your program? I found the solution here. I downloaded and looked at the source code of MouseParty.zip linked from this page.

Handling multiple mice is possible using part of WinAPI called Raw Input. If you have <Windows.h> included, have your WinAPI window and message loop, you only need to initialize Raw Input like this:

RAWINPUTDEVICE device;
device.usUsagePage = 0x01;
device.usUsage = 0x02;
device.dwFlags = 0;
device.hwndTarget = 0;
RegisterRawInputDevices(&device, 1, sizeof device);

You then start to receive WM_INPUT message, which should be handled this way:

case WM_INPUT:
  OnRawInput(
    GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT,
    (HRAWINPUT)lParam);
  // Must call DefWindowProc!
  break;

Where OnRawInput is my function:

std::vector<char> m_RawInputMessageData; // Buffer

void OnRawInput(bool inForeground, HRAWINPUT hRawInput)
{
    UINT dataSize;
    GetRawInputData(
      hRawInput, RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER));

    if(dataSize == 0)
      return;
    if(dataSize > m_RawInputMessageData.size())
        m_RawInputMessageData.resize(dataSize);

    void* dataBuf = &m_RawInputMessageData[0];
    GetRawInputData(
      hRawInput, RID_INPUT, dataBuf, &dataSize, sizeof(RAWINPUTHEADER));

    const RAWINPUT *raw = (const RAWINPUT*)dataBuf;
    if (raw->header.dwType == RIM_TYPEMOUSE)
    {
        HANDLE deviceHandle = raw->header.hDevice;

        const RAWMOUSE& mouseData = raw->data.mouse;

        USHORT flags = mouseData.usButtonFlags;
        short wheelDelta = (short)mouseData.usButtonData;
        LONG x = mouseData.lLastX, y = mouseData.lLastY;

        wprintf(
            L"Mouse: Device=0x%08X, Flags=%04x, WheelDelta=%d, X=%d, Y=%d\n",
            deviceHandle, flags, wheelDelta, x, y);
    }
}
Mouse: Device=0x0001003D, Flags=0001, WheelDelta=0, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0002, WheelDelta=0, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0000, WheelDelta=0, X=-1, Y=0
Mouse: Device=0x0001003D, Flags=0004, WheelDelta=0, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0008, WheelDelta=0, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0400, WheelDelta=120, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0400, WheelDelta=-120, X=0, Y=0
Mouse: Device=0x0001003D, Flags=0000, WheelDelta=0, X=1, Y=0
Mouse: Device=0x0001003D, Flags=0000, WheelDelta=0, X=1, Y=0
Mouse: Device=0x0001003D, Flags=0000, WheelDelta=0, X=2, Y=-1
Mouse: Device=0x0001003D, Flags=0000, WheelDelta=0, X=1, Y=-1

If you want to know in advance what mice are available in the system, you can enumerate them with following code:

UINT numDevices;
GetRawInputDeviceList(
  NULL, &numDevices, sizeof(RAWINPUTDEVICELIST));
if(numDevices == 0) return;

std::vector<RAWINPUTDEVICELIST> deviceList(numDevices);
GetRawInputDeviceList(
  &deviceList[0], &numDevices, sizeof(RAWINPUTDEVICELIST));

std::vector<wchar_t> deviceNameData;
wstring deviceName;
for(UINT i = 0; i < numDevices; ++i)
{
  const RAWINPUTDEVICELIST& device = deviceList[i];
  if(device.dwType == RIM_TYPEMOUSE)
  {
    wprintf(L"Mouse: Handle=0x%08X\n", device.hDevice);

    UINT dataSize;
    GetRawInputDeviceInfo(
      device.hDevice, RIDI_DEVICENAME, nullptr, &dataSize);
    if(dataSize)
    {
      deviceNameData.resize(dataSize);
      UINT result = GetRawInputDeviceInfo(
        device.hDevice, RIDI_DEVICENAME, &deviceNameData[0], &dataSize);
      if(result != UINT_MAX)
      {
        deviceName.assign(deviceNameData.begin(), deviceNameData.end());
        wprintf(L"  Name=%s\n", deviceName.c_str());
      }
    }

    RID_DEVICE_INFO deviceInfo;
    deviceInfo.cbSize = sizeof deviceInfo;
    dataSize = sizeof deviceInfo;
    UINT result = GetRawInputDeviceInfo(
      device.hDevice, RIDI_DEVICEINFO, &deviceInfo, &dataSize);
    if(result != UINT_MAX)
    {
      assert(deviceInfo.dwType == RIM_TYPEMOUSE);
      wprintf(
        L"  Id=%u, Buttons=%u, SampleRate=%u, HorizontalWheel=%s\n",
        deviceInfo.mouse.dwId,
        deviceInfo.mouse.dwNumberOfButtons,
        deviceInfo.mouse.dwSampleRate,
        deviceInfo.mouse.fHasHorizontalWheel ? L"1" : L"0");
    }
  }
}

But the system on my PC reports 3 mouse devices even when I have only one USB mouse connected. Names are also quite cryptic. So it's not very useful I think.

Mouse: Handle=0x0001003F
  Name=\\?\Root#RDP_MOU#0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
  Id=2, Buttons=2, SampleRate=60, HorizontalWheel=0
Mouse: Handle=0x0001003D
  Name=\\?\HID#VID_09DA&PID_000E#6&2ff89cff&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
  Id=256, Buttons=5, SampleRate=100, HorizontalWheel=0
Mouse: Handle=0x0001003B
  Name=\\?\HID#VID_062A&PID_0201&MI_01&Col01#7&136f4655&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
  Id=256, Buttons=0, SampleRate=0, HorizontalWheel=0

Comments | #winapi Share

Comments

[Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2024