daily pastebin goal
18%
SHARE
TWEET

RawInput Multiple mice

PeterSvP Aug 23rd, 2016 (edited) 882 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* A RawInput implementation with HWND_MESSAGE window and Device State detection. Uses separate thread and accumulates deltas */
  2.  
  3. #include <atomic>
  4. #include <Windows.h>
  5. #include "stdafx.h"
  6. #include "MessageToString.h"
  7. #include <iostream>
  8. #include <vector>
  9. #include <mutex>
  10. #include <map>
  11. #include <sstream>
  12. #include <deque>
  13. using namespace std;
  14.  
  15. // State
  16. bool isInitialized = false;
  17. HANDLE runningThread = 0;
  18. HWND messageWindow = 0;
  19.  
  20. // Multi threading
  21. using Mutex = std::mutex;
  22. using MutexLocker = std::lock_guard<std::mutex>;
  23. #define EnsureMutexLocked(T) { if(T.try_lock()) Crash(); }
  24. #define Crash() { std::cout << "CRASH()"; ((void(*)())0)(); }
  25. #define StrongAssert(T) { if(!(T)) cout << "Assertion Failed" << ##T << "\n"; Crash(); }
  26.  
  27. // RawInput stuff
  28. const uint8_t RE_DEVICE_CONNECT = 0;
  29. const uint8_t RE_DEVICE_DISCONNECT = 1;
  30. const uint8_t RE_MOUSE = 2;
  31.  
  32. struct RawInputEvent
  33. {
  34.     int32_t devHandle;
  35.     int32_t x, y, wheel;
  36.     // pressed button
  37.     uint8_t press; 
  38.     //released button
  39.     uint8_t release;
  40.     // event type
  41.     uint8_t type;
  42. };
  43. vector<RawInputEvent> generatedEvents; // deltas must be accumulated
  44. Mutex dataMutex;
  45.  
  46. // Mouse state for now
  47. struct DeviceState
  48. {
  49.     int32_t x, y, z;    // x,y are delts movement, z is WheelDelta
  50.     uint8_t buttonStates;   // bad idea for polling
  51.     wstring name;
  52. };
  53.  
  54. // Global buffer for worker thread
  55. std::vector<char> m_RawInputMessageData; // Buffer
  56.  
  57. map<HANDLE, DeviceState> devices;
  58.  
  59. void printEvent(RawInputEvent e)
  60. {
  61.     stringstream ss;
  62.     switch (e.type)
  63.     {
  64.     case RE_DEVICE_CONNECT: ss << "RE_DEVICE_CONNECT"; break;
  65.     case RE_DEVICE_DISCONNECT: ss << "RE_DEVICE_DISCONNECT"; break;
  66.     case RE_MOUSE: ss << "RE_MOUSE"; break;
  67.     default: ss << "UNKNOWN(" << e.type << ")";
  68.     }  
  69.     ss << " " << e.devHandle << " (" << e.x << "; " << e.y << ")   DOWN=" << int(e.press) << "  UP=" << int(e.release) << "  w=" << e.wheel << "\n";
  70.     cout << ss.str();
  71. }
  72.  
  73. inline void AddEvent(uint8_t type, int32_t devHandle, uint8_t press, uint8_t release)
  74. {
  75.     MutexLocker locker(dataMutex);
  76.     RawInputEvent e;
  77.     e.x = 0;
  78.     e.y = 0;
  79.     e.wheel = 0;
  80.     e.type = type;
  81.     e.devHandle = devHandle;
  82.     e.press = press;
  83.     e.release = release;
  84.     generatedEvents.push_back(e);
  85.     printEvent(e);
  86. }
  87.  
  88. inline void AddEvent(RawInputEvent& ev)
  89. {
  90.     MutexLocker locker(dataMutex);
  91.     generatedEvents.push_back(ev);
  92.     printEvent(ev);
  93. }
  94.  
  95. void OnRawInput(HRAWINPUT handle)
  96. {
  97.     // Determine the size
  98.     UINT dataSize;
  99.     GetRawInputData(handle, RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER));    // get Size
  100.     if (dataSize == 0) return;
  101.     if (dataSize > m_RawInputMessageData.size()) m_RawInputMessageData.resize(dataSize);
  102.  
  103.     // Get the Data
  104.     void* dataBuf = &m_RawInputMessageData[0];
  105.     GetRawInputData(handle, RID_INPUT, dataBuf, &dataSize, sizeof(RAWINPUTHEADER)); // get Data
  106.     const RAWINPUT *raw = (const RAWINPUT*)dataBuf;
  107.  
  108.     // Mouse
  109.     //if (raw->header.dwType == RIM_TYPEMOUSE)
  110.    
  111.     HANDLE deviceHandle = raw->header.hDevice;
  112.     const RAWMOUSE& mouseData = raw->data.mouse;
  113.  
  114.     USHORT flags = mouseData.usButtonFlags;
  115.     short wheelDelta = (short)mouseData.usButtonData;
  116.     LONG x = mouseData.lLastX, y = mouseData.lLastY;
  117.  
  118.     // Some events are critical
  119.     if(flags & RI_MOUSE_LEFT_BUTTON_DOWN   ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 1, 0);
  120.     if(flags & RI_MOUSE_LEFT_BUTTON_UP     ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 1);
  121.     if(flags & RI_MOUSE_MIDDLE_BUTTON_DOWN ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 3, 0);
  122.     if(flags & RI_MOUSE_MIDDLE_BUTTON_UP   ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 3);
  123.     if(flags & RI_MOUSE_RIGHT_BUTTON_DOWN  ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 2, 0);
  124.     if(flags & RI_MOUSE_RIGHT_BUTTON_UP    ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 2);
  125.  
  126.    
  127.     // Some are to be accumulated
  128.     auto& dev = devices[raw->header.hDevice];
  129.     dev.x += x;
  130.     dev.y += y;
  131.     dev.z += wheelDelta;
  132.  
  133.     /*
  134.     wprintf(
  135.         L"Mouse: Device=0x%08X, Flags=%04x, WheelDelta=%d, X=%d, Y=%d\n",
  136.         deviceHandle, flags, wheelDelta, x, y);
  137.     /**/
  138. }
  139.  
  140.  
  141.  
  142. void OnDeviceChange(HRAWINPUT handle, bool connected)
  143. {
  144.     if (!connected)
  145.     {
  146.         RawInputEvent ev;
  147.         ev.devHandle = int32_t(handle);
  148.         ev.type = connected ? RE_DEVICE_CONNECT : RE_DEVICE_DISCONNECT;
  149.         ev.x = 0;
  150.         ev.y = 0;
  151.         AddEvent(ev);
  152.         MutexLocker locker(dataMutex);
  153.         devices.erase(handle);
  154.         return;
  155.     }
  156.  
  157.     // Determine the size, Get Device Name
  158.     std::vector<wchar_t> deviceNameData;
  159.     wstring deviceName;
  160.     UINT dataSize;
  161.     SetLastError(0);
  162.     GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, nullptr, &dataSize);
  163.     if (GetLastError()) return;
  164.     if (dataSize)
  165.     {
  166.         deviceNameData.resize(dataSize);
  167.         UINT result = GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, &deviceNameData[0], &dataSize);
  168.         if (result != UINT_MAX)
  169.         {
  170.             deviceName.assign(deviceNameData.begin(), deviceNameData.end());
  171.             wprintf(L"  Name=%s\n", deviceName.c_str());
  172.         }
  173.     }
  174.  
  175.     RID_DEVICE_INFO deviceInfo;
  176.     deviceInfo.cbSize = sizeof deviceInfo;
  177.     dataSize = sizeof deviceInfo;
  178.     UINT result = GetRawInputDeviceInfo(handle, RIDI_DEVICEINFO, &deviceInfo, &dataSize);
  179.     if (result != UINT_MAX)
  180.     {
  181.         wprintf(L"  Id=%u, Buttons=%u, SampleRate=%u, HorizontalWheel=%s\n",
  182.             deviceInfo.mouse.dwId,
  183.             deviceInfo.mouse.dwNumberOfButtons,
  184.             deviceInfo.mouse.dwSampleRate,
  185.             deviceInfo.mouse.fHasHorizontalWheel ? L"1" : L"0");
  186.  
  187.         // At this perfect moment, add OR remove the device
  188.         RawInputEvent ev;
  189.         ev.devHandle = int32_t(handle);
  190.         ev.type = RE_DEVICE_CONNECT;
  191.         ev.x = 0;
  192.         ev.y = 0;
  193.         AddEvent(ev);
  194.         MutexLocker locker(dataMutex);
  195.         devices[handle].name = deviceName;
  196.     }
  197. }
  198.  
  199. LRESULT CALLBACK RawInputWndProc(HWND wh, UINT msg, WPARAM wp, LPARAM lp)
  200. {
  201.     // Debugging Message pumps
  202.     //cout << WMMessageToStr(msg, true) << ":  W= " << wp << ";  L= " << lp << "\n";
  203.  
  204.     // 254: WM_INPUT_DEVICE_CHANGE
  205.     //      wp = 1 GIDC_ARRIVAL
  206.     //      wp = 2 GIDC_REMOVAL
  207.  
  208.     // 255: WM_INPUT
  209.  
  210.     if (msg == WM_INPUT_DEVICE_CHANGE)
  211.     {
  212.         if (wp==1)
  213.         {
  214.             OnDeviceChange((HRAWINPUT)lp, true);
  215.         }
  216.         else if (wp == 2)
  217.         {
  218.             OnDeviceChange((HRAWINPUT)lp, false);
  219.         }
  220.     }
  221.     else if (msg == WM_INPUT)
  222.     {
  223.         OnRawInput((HRAWINPUT)lp);
  224.     }
  225.  
  226.     return DefWindowProc(wh, msg, wp, lp);
  227. }
  228.  
  229. static const wchar_t* class_name = L"PI_DEV_RAWINPUT";
  230. void RawInputThread(LPVOID params)
  231. {
  232.     WNDCLASSEX wx = {};
  233.     wx.cbSize = sizeof(WNDCLASSEX);
  234.     wx.lpfnWndProc = RawInputWndProc;
  235.     wx.hInstance = NULL;
  236.     wx.lpszClassName = class_name;
  237.     HWND wh;
  238.     if (RegisterClassEx(&wx))
  239.     {
  240.         wh = CreateWindowEx(0, class_name, L"Pi-Dev RawInput [NS]", 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, NULL, NULL);
  241.         messageWindow = wh;
  242.         ShowWindow(wh, SW_SHOWMINNOACTIVE);
  243.  
  244.         RAWINPUTDEVICE device[4];
  245.        
  246.         // Mouse
  247.         device[0].usUsagePage = 0x01;
  248.         device[0].usUsage = 0x02;
  249.         device[0].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY;
  250.         device[0].hwndTarget = wh;
  251.        
  252.         // Gamepad
  253.         device[1].usUsagePage = 0x01;
  254.         device[1].usUsage = 0x05;
  255.         device[1].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY;
  256.         device[1].hwndTarget = wh;
  257.  
  258.         // Joystick
  259.         device[2].usUsagePage = 0x01;
  260.         device[2].usUsage = 0x04;
  261.         device[2].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY;
  262.         device[2].hwndTarget = wh;
  263.  
  264.         // Keyboard
  265.         device[3].usUsagePage = 0x01;
  266.         device[3].usUsage = 0x06;
  267.         device[3].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY;
  268.         device[3].hwndTarget = wh;
  269.        
  270.         // Register ONLY Mice
  271.         RegisterRawInputDevices(device, 1, sizeof RAWINPUTDEVICE);
  272.        
  273.  
  274.         MSG msg;
  275.         while (GetMessage(&msg, 0, 0, 0) > 0)
  276.         {
  277.             DispatchMessage(&msg);
  278.         }
  279.     }
  280. }
  281.  
  282. extern "C" __declspec(dllexport) int kill()
  283. {
  284.     SetLastError(0);
  285.     PostThreadMessage(GetThreadId(runningThread), WM_QUIT, 0, 0);
  286.     cout << "PostThreadMessage = " << GetLastError() << "\n";
  287.  
  288.     SetLastError(0);
  289.     UnregisterClass(class_name, NULL);
  290.     cout << "UnregisterClass = " << GetLastError() << "\n";
  291.     /**/
  292.  
  293.     messageWindow = 0;
  294.     runningThread = 0;
  295.     isInitialized = false;
  296.     return GetLastError();
  297. }
  298.  
  299. extern "C" __declspec(dllexport) bool init()
  300. {
  301.     kill();
  302.     // this is actually reinit()
  303.     cout << "init()";
  304.     MutexLocker locker(dataMutex);
  305.     devices.clear();
  306.     generatedEvents.clear();
  307.  
  308.     if (!isInitialized)
  309.     {
  310.         isInitialized = true;
  311.         runningThread = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(RawInputThread), NULL, 0, 0);
  312.         messageWindow = 0;
  313.         return true;
  314.     }
  315.  
  316.     return false;
  317. }
  318.  
  319. extern "C" __declspec(dllexport) void* poll()
  320. {
  321.     MutexLocker locker(dataMutex);
  322.     //cout << "==== Deltas =====\n";
  323.     int numItems = generatedEvents.size();
  324.     stringstream ss;
  325.     for (auto& d : devices)
  326.     {
  327.         RawInputEvent e;
  328.         e.devHandle = int32_t(d.first);
  329.         auto& data = d.second;
  330.         e.press = 0;
  331.         e.release = 0;
  332.         e.type = RE_MOUSE;
  333.         e.wheel = data.z;
  334.         e.x = data.x;
  335.         e.y = data.y;
  336.         if (e.x != 0 || e.y != 0)
  337.         {
  338.             ss.write((char*)&e, sizeof(RawInputEvent));
  339.             ++numItems;
  340.         }
  341.         cout << e.x << "; " << e.y << "\n";
  342.         // Zero accumulation fields
  343.         data.x = 0;
  344.         data.y = 0;
  345.         data.z = 0;
  346.     }
  347.     ss.write((char*)generatedEvents.data(), sizeof(RawInputEvent)*generatedEvents.size());
  348.     uint8_t* buf = (uint8_t*)CoTaskMemAlloc(4 + numItems*sizeof(RawInputEvent));
  349.     memcpy(buf, &numItems, 4);
  350.     memcpy(buf+4, ss.str().data(), numItems*sizeof(RawInputEvent));
  351.     generatedEvents.clear();
  352.     return buf;
  353. }
  354.  
  355. int main()
  356. {
  357.     cout << "sz = " << sizeof(RawInputEvent) << "\n";
  358.     init();
  359.     while (true)
  360.     {
  361.         if (GetAsyncKeyState(VK_HOME)) cout << "init() = " << init() << "\n";
  362.         if (GetAsyncKeyState(VK_END))  cout << "kill() = " << kill() << "\n";
  363.         Sleep(1000);
  364.         void* data = poll();
  365.         int d = 0;
  366.         memcpy(&d, data, 4);
  367.         //cout << d << "\n";
  368.     }
  369.     return 0;
  370. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top