#pragma once auto CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM) -> LRESULT; struct RawInput { HANDLE mutex = nullptr; HWND hwnd = nullptr; bool ready = false; bool initialized = false; function updateKeyboard; function updateMouse; struct Device { HANDLE handle = nullptr; string path; enum class Type : uint { Keyboard, Mouse, Joypad } type; uint16_t vendorID = 0; uint16_t productID = 0; bool isXInputDevice = false; }; vector devices; auto find(uint16_t vendorID, uint16_t productID) -> maybe { for(auto& device : devices) { if(device.vendorID == vendorID && device.productID == productID) return device; } return nothing; } auto scanDevices() -> void { devices.reset(); uint deviceCount = 0; GetRawInputDeviceList(NULL, &deviceCount, sizeof(RAWINPUTDEVICELIST)); RAWINPUTDEVICELIST* list = new RAWINPUTDEVICELIST[deviceCount]; GetRawInputDeviceList(list, &deviceCount, sizeof(RAWINPUTDEVICELIST)); for(auto n : range(deviceCount)) { wchar_t path[4096]; uint size = sizeof(path) - 1; GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICENAME, &path, &size); RID_DEVICE_INFO info; info.cbSize = size = sizeof(RID_DEVICE_INFO); GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICEINFO, &info, &size); Device device; device.path = (const char*)utf8_t(path); device.handle = list[n].hDevice; if(info.dwType == RIM_TYPEKEYBOARD) { device.type = Device::Type::Keyboard; device.vendorID = 0; device.productID = 1; } if(info.dwType == RIM_TYPEMOUSE) { device.type = Device::Type::Mouse; device.vendorID = 0; device.productID = 2; } if(info.dwType == RIM_TYPEHID) { //verify that this is a joypad device if(info.hid.usUsagePage != 1 || (info.hid.usUsage != 4 && info.hid.usUsage != 5)) continue; device.type = Device::Type::Joypad; device.vendorID = info.hid.dwVendorId; device.productID = info.hid.dwProductId; if(device.path.find("IG_")) device.isXInputDevice = true; //"IG_" is only found inside XInput device paths } devices.append(device); } delete[] list; } auto windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT { if(msg != WM_INPUT) return DefWindowProc(hwnd, msg, wparam, lparam); uint size = 0; GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); RAWINPUT* input = new RAWINPUT[size]; GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER)); WaitForSingleObject(mutex, INFINITE); if(input->header.dwType == RIM_TYPEKEYBOARD) { if(updateKeyboard) updateKeyboard(input); } if(input->header.dwType == RIM_TYPEMOUSE) { if(updateMouse) updateMouse(input); } ReleaseMutex(mutex); LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER)); delete[] input; return result; } auto main() -> void { WNDCLASS wc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hInstance = GetModuleHandle(0); wc.lpfnWndProc = RawInputWindowProc; wc.lpszClassName = L"RawInputClass"; wc.lpszMenuName = 0; wc.style = CS_VREDRAW | CS_HREDRAW; RegisterClass(&wc); hwnd = CreateWindow(L"RawInputClass", L"RawInputClass", WS_POPUP, 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0); scanDevices(); RAWINPUTDEVICE device[2]; //capture all keyboard input device[0].usUsagePage = 1; device[0].usUsage = 6; device[0].dwFlags = RIDEV_INPUTSINK; device[0].hwndTarget = hwnd; //capture all mouse input device[1].usUsagePage = 1; device[1].usUsage = 2; device[1].dwFlags = RIDEV_INPUTSINK; device[1].hwndTarget = hwnd; RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE)); WaitForSingleObject(mutex, INFINITE); ready = true; ReleaseMutex(mutex); while(true) { MSG msg; GetMessage(&msg, hwnd, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } } }; static RawInput rawinput; auto WINAPI RawInputThreadProc(void*) -> DWORD { rawinput.main(); return 0; } auto CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT { return rawinput.windowProc(hwnd, msg, wparam, lparam); }