18 #if defined(_WIN32) && !defined(CPPPARSER) 24 class InputThread :
public Thread {
26 InputThread(WinInputDeviceManager *manager) :
27 Thread(
"input",
"input"), _manager(manager) {}
30 virtual void thread_main();
32 WinInputDeviceManager *_manager;
41 WinInputDeviceManager::
42 WinInputDeviceManager() :
47 _message_hwnd(nullptr) {
51 _xinput_device0.local_object();
52 _xinput_device1.local_object();
53 _xinput_device2.local_object();
54 _xinput_device3.local_object();
57 HMODULE module = LoadLibraryA(
"cfgmgr32.dll");
59 _CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module,
"CM_Get_DevNode_PropertyW");
61 _CM_Get_DevNode_PropertyW =
nullptr;
67 if (Thread::is_threading_supported()) {
68 PT(
Thread) thread =
new InputThread(
this);
69 thread->start(TP_normal,
false);
80 WinInputDeviceManager::
81 ~WinInputDeviceManager() {
82 if (_message_hwnd !=
nullptr) {
84 if (Thread::is_threading_supported()) {
85 HWND hwnd = _message_hwnd;
87 SendMessage(hwnd, WM_QUIT, 0, 0);
92 destroy_message_loop();
100 void WinInputDeviceManager::
101 device_destroyed(WinRawInputDevice *device) {
104 if (device->_handle !=
nullptr) {
105 _raw_devices.erase(device->_handle);
108 _raw_devices_by_path.erase(device->_path);
114 void WinInputDeviceManager::
115 on_input(HRAWINPUT handle) {
117 if (GetRawInputData(handle, RID_INPUT,
nullptr, &size,
sizeof(RAWINPUTHEADER)) < 0) {
121 PRAWINPUT data = (PRAWINPUT)alloca(size);
122 if (GetRawInputData(handle, RID_INPUT, data, &size,
sizeof(RAWINPUTHEADER)) <= 0) {
127 PT(WinRawInputDevice) device;
130 auto it = _raw_devices.find(data->header.hDevice);
131 if (it != _raw_devices.end()) {
135 if (device !=
nullptr) {
136 device->on_input(data);
143 void WinInputDeviceManager::
144 on_input_device_arrival(HANDLE handle) {
147 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME,
nullptr, &size) != 0) {
151 char *path = (
char *)alloca(size);
152 if (path ==
nullptr ||
153 GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (
void *)path, &size) < 0) {
157 if (device_cat.is_debug()) {
159 <<
"GIDC_ARRIVAL: " << path <<
"\n";
163 RID_DEVICE_INFO info;
164 info.cbSize =
sizeof(RID_DEVICE_INFO);
165 size =
sizeof(RID_DEVICE_INFO);
166 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICEINFO, &info, &size) <= 0) {
171 while (path[0] ==
'\\' || path[0] ==
'?' || path[0] ==
'.') {
194 std::string name, manufacturer;
196 CONFIGRET ret = CM_Locate_DevNodeA(&inst, (DEVINSTID_A)path, CM_LOCATE_DEVNODE_PHANTOM);
197 if (ret == CR_SUCCESS) {
200 if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) {
204 if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) {
205 if (strcmp(buffer,
"(Standard system devices)") != 0) {
206 manufacturer.assign(buffer);
215 while (CM_Get_Parent(&parent, cur, 0) == CR_SUCCESS) {
217 std::string dev_class;
218 if (CM_Get_DevNode_Registry_Property(parent, CM_DRP_CLASS, 0, buffer, &buflen, 0) == CR_SUCCESS) {
219 if (strcmp(buffer,
"USB") == 0) {
223 dev_class.assign(buffer);
229 if (manufacturer.empty() &&
230 CM_Get_DevNode_Registry_Property(cur, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) {
231 if (strcmp(buffer,
"(Standard system devices)") != 0) {
232 manufacturer.assign(buffer);
238 static const DEVPROPKEY bus_reported_device_desc = {
239 {0x540b947e, 0x8b40, 0x45bc, {0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2}},
244 if (dev_class ==
"HIDClass" && _CM_Get_DevNode_PropertyW !=
nullptr &&
245 _CM_Get_DevNode_PropertyW(cur, &bus_reported_device_desc, &type, (PBYTE)buffer, &buflen, 0) == CR_SUCCESS &&
246 type == DEVPROP_TYPE_STRING) {
249 wchar_t *wbuffer = (
wchar_t *)buffer;
250 size_t wlen = wcsnlen_s(wbuffer,
sizeof(buffer) /
sizeof(
wchar_t));
251 while (wlen > 0 && iswspace(wbuffer[wlen - 1])) {
255 name.assign(encoder.
encode_wtext(std::wstring(wbuffer, wlen)));
259 if (CM_Get_DevNode_Registry_Property(cur, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) {
262 if (strcmp(buffer,
"USB Input Device") != 0) {
268 }
else if (device_cat.is_debug()) {
271 <<
"Could not locate device node " << path <<
" (" << ret <<
")\n";
276 if (info.dwType == RIM_TYPEHID && strstr(path,
"&IG_") !=
nullptr &&
277 XInputDevice::init_xinput()) {
280 if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) {
281 add_device(&_xinput_device0);
283 if (_xinput_device1.check_arrival(info, inst, name, manufacturer)) {
284 add_device(&_xinput_device1);
286 if (_xinput_device2.check_arrival(info, inst, name, manufacturer)) {
287 add_device(&_xinput_device2);
289 if (_xinput_device3.check_arrival(info, inst, name, manufacturer)) {
290 add_device(&_xinput_device3);
300 PT(WinRawInputDevice) device;
301 auto it = _raw_devices_by_path.find(path);
302 if (it != _raw_devices_by_path.end()) {
305 device =
new WinRawInputDevice(
this, path);
306 _raw_devices_by_path[path] = device;
309 if (device->on_arrival(handle, info, move(name))) {
310 _raw_devices[handle] = device;
311 _connected_devices.add_device(device);
313 if (device_cat.is_debug()) {
315 <<
"Discovered input device " << *device <<
"\n";
317 throw_event(
"connect-device", device.p());
324 void WinInputDeviceManager::
325 on_input_device_removal(HANDLE handle) {
332 PT(WinRawInputDevice) device;
335 auto it = _raw_devices.find(handle);
336 if (it != _raw_devices.end()) {
337 device = std::move(it->second);
338 _raw_devices.erase(it);
339 device->on_removal();
341 if (_connected_devices.remove_device(device)) {
342 throw_event(
"disconnect-device", device.p());
344 if (device_cat.is_debug()) {
346 <<
"Removed input device " << *device <<
"\n";
356 void WinInputDeviceManager::
364 HWND WinInputDeviceManager::
365 setup_message_loop() {
370 wc.cbSize =
sizeof(WNDCLASSEX);
371 wc.lpfnWndProc = window_proc;
372 wc.hInstance = GetModuleHandle(
nullptr);
373 wc.lpszClassName =
"InputDeviceManager";
374 if (!RegisterClassEx(&wc)) {
376 <<
"Failed to register message-only window class for input device detection.\n";
378 _message_hwnd = CreateWindowEx(0, wc.lpszClassName,
"InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE,
nullptr,
nullptr,
nullptr);
379 if (!_message_hwnd) {
381 <<
"Failed to create message-only window for input device detection.\n";
386 RAWINPUTDEVICE rid[3];
387 rid[0].usUsagePage = 1;
389 rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
390 rid[0].hwndTarget = _message_hwnd;
391 rid[1].usUsagePage = 1;
393 rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
394 rid[1].hwndTarget = _message_hwnd;
395 rid[2].usUsagePage = 1;
397 rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
398 rid[2].hwndTarget = _message_hwnd;
399 if (!RegisterRawInputDevices(rid, 3,
sizeof(RAWINPUTDEVICE))) {
401 <<
"Failed to register raw input devices.\n";
406 HANDLE xinput_handle;
407 RAWINPUTDEVICELIST devices[64];
408 UINT num_devices = 64;
409 num_devices = GetRawInputDeviceList(devices, &num_devices,
sizeof(RAWINPUTDEVICELIST));
410 if (num_devices == (UINT)-1) {
413 for (UINT i = 0; i < num_devices; ++i) {
414 if (devices[i].dwType != RIM_TYPEHID) {
417 HANDLE handle = devices[i].hDevice;
419 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME,
nullptr, &size) != 0) {
423 char *path = (
char *)alloca(size);
424 if (path ==
nullptr ||
425 GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (
void *)path, &size) < 0) {
429 if (strstr(path,
"&IG_") !=
nullptr) {
430 xinput_handle = handle;
434 if (num_xinput == 1) {
436 on_input_device_arrival(xinput_handle);
437 }
else if (num_xinput > 0) {
439 _xinput_device0.detect(
this);
440 _xinput_device1.detect(
this);
441 _xinput_device2.detect(
this);
442 _xinput_device3.detect(
this);
445 return _message_hwnd;
452 void WinInputDeviceManager::
453 destroy_message_loop() {
457 std::swap(_message_hwnd, hwnd);
468 LRESULT WINAPI WinInputDeviceManager::
469 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
470 WinInputDeviceManager *mgr;
474 if (mgr !=
nullptr) {
475 mgr->on_input((HRAWINPUT)lparam);
479 case WM_INPUT_DEVICE_CHANGE:
480 switch (LOWORD(wparam)) {
483 if (mgr !=
nullptr) {
484 mgr->on_input_device_arrival((HANDLE)lparam);
490 if (mgr !=
nullptr) {
491 mgr->on_input_device_removal((HANDLE)lparam);
500 return DefWindowProcW(hwnd, msg, wparam, lparam);
509 WinInputDeviceManager *manager = _manager;
510 HWND hwnd = manager->setup_message_loop();
515 if (device_cat.is_debug()) {
517 <<
"Started input device listener thread.\n";
521 while (GetMessage(&msg,
nullptr, 0, 0) > 0) {
522 TranslateMessage(&msg);
523 DispatchMessage(&msg);
526 if (device_cat.is_debug()) {
528 <<
"Stopping input device listener thread.\n";
531 manager->destroy_message_loop();
533 #endif // HAVE_THREADS This class can be used to convert text between multiple representations, e.g.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.
std::string encode_wtext(const std::wstring &wtext) const
Encodes a wide-text string into a single-char string, according to the current encoding.
A thread; that is, a lightweight process.