// https://pastebin.com/0Szi8ga6 /* A RawInput implementation with HWND_MESSAGE window and Device State detection. Uses separate thread and accumulates deltas */ #include #include #include "stdafx.h" #include "MessageToString.h" #include #include #include #include #include #include using namespace std; // State bool isInitialized = false; HANDLE runningThread = 0; HWND messageWindow = 0; // Multi threading using Mutex = std::mutex; using MutexLocker = std::lock_guard; #define EnsureMutexLocked(T) { if(T.try_lock()) Crash(); } #define Crash() { std::cout << "CRASH()"; ((void(*)())0)(); } #define StrongAssert(T) { if(!(T)) cout << "Assertion Failed" << ##T << "\n"; Crash(); } // RawInput stuff const uint8_t RE_DEVICE_CONNECT = 0; const uint8_t RE_DEVICE_DISCONNECT = 1; const uint8_t RE_MOUSE = 2; struct RawInputEvent { int32_t devHandle; int32_t x, y, wheel; // pressed button uint8_t press; //released button uint8_t release; // event type uint8_t type; }; vector generatedEvents; // deltas must be accumulated Mutex dataMutex; // Mouse state for now struct DeviceState { int32_t x, y, z; // x,y are delts movement, z is WheelDelta uint8_t buttonStates; // bad idea for polling wstring name; }; // Global buffer for worker thread std::vector m_RawInputMessageData; // Buffer map devices; void printEvent(RawInputEvent e) { stringstream ss; switch (e.type) { case RE_DEVICE_CONNECT: ss << "RE_DEVICE_CONNECT"; break; case RE_DEVICE_DISCONNECT: ss << "RE_DEVICE_DISCONNECT"; break; case RE_MOUSE: ss << "RE_MOUSE"; break; default: ss << "UNKNOWN(" << e.type << ")"; } ss << " " << e.devHandle << " (" << e.x << "; " << e.y << ") DOWN=" << int(e.press) << " UP=" << int(e.release) << " w=" << e.wheel << "\n"; cout << ss.str(); } inline void AddEvent(uint8_t type, int32_t devHandle, uint8_t press, uint8_t release) { MutexLocker locker(dataMutex); RawInputEvent e; e.x = 0; e.y = 0; e.wheel = 0; e.type = type; e.devHandle = devHandle; e.press = press; e.release = release; generatedEvents.push_back(e); printEvent(e); } inline void AddEvent(RawInputEvent& ev) { MutexLocker locker(dataMutex); generatedEvents.push_back(ev); printEvent(ev); } void OnRawInput(HRAWINPUT handle) { // Determine the size UINT dataSize; GetRawInputData(handle, RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER)); // get Size if (dataSize == 0) return; if (dataSize > m_RawInputMessageData.size()) m_RawInputMessageData.resize(dataSize); // Get the Data void* dataBuf = &m_RawInputMessageData[0]; GetRawInputData(handle, RID_INPUT, dataBuf, &dataSize, sizeof(RAWINPUTHEADER)); // get Data const RAWINPUT *raw = (const RAWINPUT*)dataBuf; // Mouse //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; // Some events are critical if(flags & RI_MOUSE_LEFT_BUTTON_DOWN ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 1, 0); if(flags & RI_MOUSE_LEFT_BUTTON_UP ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 1); if(flags & RI_MOUSE_MIDDLE_BUTTON_DOWN ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 3, 0); if(flags & RI_MOUSE_MIDDLE_BUTTON_UP ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 3); if(flags & RI_MOUSE_RIGHT_BUTTON_DOWN ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 2, 0); if(flags & RI_MOUSE_RIGHT_BUTTON_UP ) AddEvent(RE_MOUSE, int32_t(raw->header.hDevice), 0, 2); // Some are to be accumulated auto& dev = devices[raw->header.hDevice]; dev.x += x; dev.y += y; dev.z += wheelDelta; /* wprintf( L"Mouse: Device=0x%08X, Flags=%04x, WheelDelta=%d, X=%d, Y=%d\n", deviceHandle, flags, wheelDelta, x, y); /**/ } void OnDeviceChange(HRAWINPUT handle, bool connected) { if (!connected) { RawInputEvent ev; ev.devHandle = int32_t(handle); ev.type = connected ? RE_DEVICE_CONNECT : RE_DEVICE_DISCONNECT; ev.x = 0; ev.y = 0; AddEvent(ev); MutexLocker locker(dataMutex); devices.erase(handle); return; } // Determine the size, Get Device Name std::vector deviceNameData; wstring deviceName; UINT dataSize; SetLastError(0); GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, nullptr, &dataSize); if (GetLastError()) return; if (dataSize) { deviceNameData.resize(dataSize); UINT result = GetRawInputDeviceInfo(handle, 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(handle, RIDI_DEVICEINFO, &deviceInfo, &dataSize); if (result != UINT_MAX) { 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"); // At this perfect moment, add OR remove the device RawInputEvent ev; ev.devHandle = int32_t(handle); ev.type = RE_DEVICE_CONNECT; ev.x = 0; ev.y = 0; AddEvent(ev); MutexLocker locker(dataMutex); devices[handle].name = deviceName; } } LRESULT CALLBACK RawInputWndProc(HWND wh, UINT msg, WPARAM wp, LPARAM lp) { // Debugging Message pumps //cout << WMMessageToStr(msg, true) << ": W= " << wp << "; L= " << lp << "\n"; // 254: WM_INPUT_DEVICE_CHANGE // wp = 1 GIDC_ARRIVAL // wp = 2 GIDC_REMOVAL // 255: WM_INPUT if (msg == WM_INPUT_DEVICE_CHANGE) { if (wp==1) { OnDeviceChange((HRAWINPUT)lp, true); } else if (wp == 2) { OnDeviceChange((HRAWINPUT)lp, false); } } else if (msg == WM_INPUT) { OnRawInput((HRAWINPUT)lp); } return DefWindowProc(wh, msg, wp, lp); } static const wchar_t* class_name = L"PI_DEV_RAWINPUT"; void RawInputThread(LPVOID params) { WNDCLASSEX wx = {}; wx.cbSize = sizeof(WNDCLASSEX); wx.lpfnWndProc = RawInputWndProc; wx.hInstance = NULL; wx.lpszClassName = class_name; HWND wh; if (RegisterClassEx(&wx)) { wh = CreateWindowEx(0, class_name, L"Pi-Dev RawInput [NS]", 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, NULL, NULL); messageWindow = wh; ShowWindow(wh, SW_SHOWMINNOACTIVE); RAWINPUTDEVICE device[4]; // Mouse device[0].usUsagePage = 0x01; device[0].usUsage = 0x02; device[0].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; device[0].hwndTarget = wh; // Gamepad device[1].usUsagePage = 0x01; device[1].usUsage = 0x05; device[1].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; device[1].hwndTarget = wh; // Joystick device[2].usUsagePage = 0x01; device[2].usUsage = 0x04; device[2].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; device[2].hwndTarget = wh; // Keyboard device[3].usUsagePage = 0x01; device[3].usUsage = 0x06; device[3].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY; device[3].hwndTarget = wh; // Register ONLY Mice RegisterRawInputDevices(device, 1, sizeof RAWINPUTDEVICE); MSG msg; while (GetMessage(&msg, 0, 0, 0) > 0) { DispatchMessage(&msg); } } } extern "C" __declspec(dllexport) int kill() { SetLastError(0); PostThreadMessage(GetThreadId(runningThread), WM_QUIT, 0, 0); cout << "PostThreadMessage = " << GetLastError() << "\n"; SetLastError(0); UnregisterClass(class_name, NULL); cout << "UnregisterClass = " << GetLastError() << "\n"; /**/ messageWindow = 0; runningThread = 0; isInitialized = false; return GetLastError(); } extern "C" __declspec(dllexport) bool init() { kill(); // this is actually reinit() cout << "init()"; MutexLocker locker(dataMutex); devices.clear(); generatedEvents.clear(); if (!isInitialized) { isInitialized = true; runningThread = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(RawInputThread), NULL, 0, 0); messageWindow = 0; return true; } return false; } extern "C" __declspec(dllexport) void* poll() { MutexLocker locker(dataMutex); //cout << "==== Deltas =====\n"; int numItems = generatedEvents.size(); stringstream ss; for (auto& d : devices) { RawInputEvent e; e.devHandle = int32_t(d.first); auto& data = d.second; e.press = 0; e.release = 0; e.type = RE_MOUSE; e.wheel = data.z; e.x = data.x; e.y = data.y; if (e.x != 0 || e.y != 0) { ss.write((char*)&e, sizeof(RawInputEvent)); ++numItems; } cout << e.x << "; " << e.y << "\n"; // Zero accumulation fields data.x = 0; data.y = 0; data.z = 0; } ss.write((char*)generatedEvents.data(), sizeof(RawInputEvent)*generatedEvents.size()); uint8_t* buf = (uint8_t*)CoTaskMemAlloc(4 + numItems*sizeof(RawInputEvent)); memcpy(buf, &numItems, 4); memcpy(buf+4, ss.str().data(), numItems*sizeof(RawInputEvent)); generatedEvents.clear(); return buf; } int main() { cout << "sz = " << sizeof(RawInputEvent) << "\n"; init(); while (true) { if (GetAsyncKeyState(VK_HOME)) cout << "init() = " << init() << "\n"; if (GetAsyncKeyState(VK_END)) cout << "kill() = " << kill() << "\n"; Sleep(1000); void* data = poll(); int d = 0; memcpy(&d, data, 4); //cout << d << "\n"; } return 0; }