19 #if defined(_WIN32) && !defined(CPPPARSER)
25 class InputThread :
public Thread {
27 InputThread(WinInputDeviceManager *manager) :
28 Thread(
"input",
"input"), _manager(manager) {}
31 virtual void thread_main();
33 WinInputDeviceManager *_manager;
42 WinInputDeviceManager::
43 WinInputDeviceManager() :
48 _message_hwnd(nullptr) {
52 _xinput_device0.local_object();
53 _xinput_device1.local_object();
54 _xinput_device2.local_object();
55 _xinput_device3.local_object();
58 HMODULE module = LoadLibraryA(
"cfgmgr32.dll");
60 _CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module,
"CM_Get_DevNode_PropertyW");
62 _CM_Get_DevNode_PropertyW =
nullptr;
71 PT(
Thread) thread =
new InputThread(
this);
72 thread->start(TP_normal,
false);
83 WinInputDeviceManager::
84 ~WinInputDeviceManager() {
85 if (_message_hwnd !=
nullptr) {
88 HWND hwnd = _message_hwnd;
90 SendMessage(hwnd, WM_QUIT, 0, 0);
95 destroy_message_loop();
103 void WinInputDeviceManager::
104 device_destroyed(WinRawInputDevice *device) {
107 if (device->_handle !=
nullptr) {
108 _raw_devices.erase(device->_handle);
111 _raw_devices_by_path.erase(device->_path);
117 void WinInputDeviceManager::
118 on_input(HRAWINPUT handle) {
120 if (GetRawInputData(handle, RID_INPUT,
nullptr, &size,
sizeof(RAWINPUTHEADER)) < 0) {
124 PRAWINPUT data = (PRAWINPUT)alloca(size);
125 if (GetRawInputData(handle, RID_INPUT, data, &size,
sizeof(RAWINPUTHEADER)) <= 0) {
130 PT(WinRawInputDevice) device;
133 auto it = _raw_devices.find(data->header.hDevice);
134 if (it != _raw_devices.end()) {
138 if (device !=
nullptr) {
139 device->on_input(data);
146 void WinInputDeviceManager::
147 on_input_device_arrival(HANDLE handle) {
150 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME,
nullptr, &size) != 0) {
154 char *path = (
char *)alloca(size);
155 if (path ==
nullptr ||
156 GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (
void *)path, &size) < 0) {
160 if (device_cat.is_debug()) {
162 <<
"GIDC_ARRIVAL: " << path <<
"\n";
166 RID_DEVICE_INFO info;
167 info.cbSize =
sizeof(RID_DEVICE_INFO);
168 size =
sizeof(RID_DEVICE_INFO);
169 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICEINFO, &info, &size) <= 0) {
174 while (path[0] ==
'\\' || path[0] ==
'?' || path[0] ==
'.') {
197 std::string name, manufacturer;
199 CONFIGRET ret = CM_Locate_DevNodeA(&inst, (DEVINSTID_A)path, CM_LOCATE_DEVNODE_PHANTOM);
200 if (ret == CR_SUCCESS) {
203 if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) {
207 if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) {
208 if (strcmp(buffer,
"(Standard system devices)") != 0) {
209 manufacturer.assign(buffer);
218 while (CM_Get_Parent(&parent, cur, 0) == CR_SUCCESS) {
220 std::string dev_class;
221 if (CM_Get_DevNode_Registry_Property(parent, CM_DRP_CLASS, 0, buffer, &buflen, 0) == CR_SUCCESS) {
222 if (strcmp(buffer,
"USB") == 0) {
226 dev_class.assign(buffer);
232 if (manufacturer.empty() &&
233 CM_Get_DevNode_Registry_Property(cur, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) {
234 if (strcmp(buffer,
"(Standard system devices)") != 0) {
235 manufacturer.assign(buffer);
241 static const DEVPROPKEY bus_reported_device_desc = {
242 {0x540b947e, 0x8b40, 0x45bc, {0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2}},
247 if (dev_class ==
"HIDClass" && _CM_Get_DevNode_PropertyW !=
nullptr &&
248 _CM_Get_DevNode_PropertyW(cur, &bus_reported_device_desc, &type, (PBYTE)buffer, &buflen, 0) == CR_SUCCESS &&
249 type == DEVPROP_TYPE_STRING) {
252 wchar_t *wbuffer = (
wchar_t *)buffer;
253 size_t wlen = wcsnlen_s(wbuffer,
sizeof(buffer) /
sizeof(
wchar_t));
254 while (wlen > 0 && iswspace(wbuffer[wlen - 1])) {
258 name.assign(encoder.
encode_wtext(std::wstring(wbuffer, wlen)));
262 if (CM_Get_DevNode_Registry_Property(cur, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) {
265 if (strcmp(buffer,
"USB Input Device") != 0) {
271 }
else if (device_cat.is_debug()) {
274 <<
"Could not locate device node " << path <<
" (" << ret <<
")\n";
279 if (info.dwType == RIM_TYPEHID && strstr(path,
"&IG_") !=
nullptr &&
280 XInputDevice::init_xinput()) {
283 if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) {
284 add_device(&_xinput_device0);
286 if (_xinput_device1.check_arrival(info, inst, name, manufacturer)) {
287 add_device(&_xinput_device1);
289 if (_xinput_device2.check_arrival(info, inst, name, manufacturer)) {
290 add_device(&_xinput_device2);
292 if (_xinput_device3.check_arrival(info, inst, name, manufacturer)) {
293 add_device(&_xinput_device3);
303 PT(WinRawInputDevice) device;
304 auto it = _raw_devices_by_path.find(path);
305 if (it != _raw_devices_by_path.end()) {
308 device =
new WinRawInputDevice(
this, path);
309 _raw_devices_by_path[path] = device;
312 if (device->on_arrival(handle, info, move(name))) {
313 _raw_devices[handle] = device;
314 _connected_devices.add_device(device);
316 if (device_cat.is_debug()) {
318 <<
"Discovered input device " << *device <<
"\n";
320 throw_event(
"connect-device", device.p());
327 void WinInputDeviceManager::
328 on_input_device_removal(HANDLE handle) {
335 PT(WinRawInputDevice) device;
338 auto it = _raw_devices.find(handle);
339 if (it != _raw_devices.end()) {
340 device = std::move(it->second);
341 _raw_devices.erase(it);
342 device->on_removal();
344 if (_connected_devices.remove_device(device)) {
345 throw_event(
"disconnect-device", device.p());
347 if (device_cat.is_debug()) {
349 <<
"Removed input device " << *device <<
"\n";
359 void WinInputDeviceManager::
367 HWND WinInputDeviceManager::
368 setup_message_loop() {
373 wc.cbSize =
sizeof(WNDCLASSEX);
374 wc.lpfnWndProc = window_proc;
375 wc.hInstance = GetModuleHandle(
nullptr);
376 wc.lpszClassName =
"InputDeviceManager";
377 if (!RegisterClassEx(&wc)) {
379 <<
"Failed to register message-only window class for input device detection.\n";
381 _message_hwnd = CreateWindowEx(0, wc.lpszClassName,
"InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE,
nullptr,
nullptr,
nullptr);
382 if (!_message_hwnd) {
384 <<
"Failed to create message-only window for input device detection.\n";
389 RAWINPUTDEVICE rid[4];
390 rid[0].usUsagePage = 1;
392 rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
393 rid[0].hwndTarget = _message_hwnd;
394 rid[1].usUsagePage = 1;
396 rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
397 rid[1].hwndTarget = _message_hwnd;
398 rid[2].usUsagePage = 1;
400 rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
401 rid[2].hwndTarget = _message_hwnd;
402 rid[3].usUsagePage = HID_USAGE_PAGE_DIGITIZER;
404 rid[3].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
405 rid[3].hwndTarget = _message_hwnd;
406 if (!RegisterRawInputDevices(rid, 4,
sizeof(RAWINPUTDEVICE))) {
408 <<
"Failed to register raw input devices.\n";
413 HANDLE xinput_handle;
414 RAWINPUTDEVICELIST devices[64];
415 UINT num_devices = 64;
416 num_devices = GetRawInputDeviceList(devices, &num_devices,
sizeof(RAWINPUTDEVICELIST));
417 if (num_devices == (UINT)-1) {
420 for (UINT i = 0; i < num_devices; ++i) {
421 if (devices[i].dwType != RIM_TYPEHID) {
424 HANDLE handle = devices[i].hDevice;
426 if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME,
nullptr, &size) != 0) {
430 char *path = (
char *)alloca(size);
431 if (path ==
nullptr ||
432 GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (
void *)path, &size) < 0) {
436 if (strstr(path,
"&IG_") !=
nullptr) {
437 xinput_handle = handle;
441 if (num_xinput == 1) {
443 on_input_device_arrival(xinput_handle);
444 }
else if (num_xinput > 0) {
446 _xinput_device0.detect(
this);
447 _xinput_device1.detect(
this);
448 _xinput_device2.detect(
this);
449 _xinput_device3.detect(
this);
452 return _message_hwnd;
459 void WinInputDeviceManager::
460 destroy_message_loop() {
464 std::swap(_message_hwnd, hwnd);
475 void WinInputDeviceManager::
478 WinInputDeviceManager *mgr = (WinInputDeviceManager *)_global_ptr;
479 if (mgr !=
nullptr) {
481 HWND hwnd = mgr->_message_hwnd;
483 PostMessage(hwnd, WM_QUIT, 0, 0);
492 LRESULT WINAPI WinInputDeviceManager::
493 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
494 WinInputDeviceManager *mgr;
498 if (mgr !=
nullptr) {
499 mgr->on_input((HRAWINPUT)lparam);
503 case WM_INPUT_DEVICE_CHANGE:
504 switch (LOWORD(wparam)) {
507 if (mgr !=
nullptr) {
508 mgr->on_input_device_arrival((HANDLE)lparam);
514 if (mgr !=
nullptr) {
515 mgr->on_input_device_removal((HANDLE)lparam);
524 return DefWindowProcW(hwnd, msg, wparam, lparam);
533 WinInputDeviceManager *manager = _manager;
534 HWND hwnd = manager->setup_message_loop();
539 if (device_cat.is_debug()) {
541 <<
"Started input device listener thread.\n";
545 #ifdef SIMPLE_THREADS
549 if (PeekMessage(&msg,
nullptr, 0, 0, PM_REMOVE)) {
550 if (msg.message == WM_QUIT) {
553 TranslateMessage(&msg);
554 DispatchMessage(&msg);
560 while (GetMessage(&msg,
nullptr, 0, 0) > 0) {
561 TranslateMessage(&msg);
562 DispatchMessage(&msg);
566 if (device_cat.is_debug()) {
568 <<
"Stopping input device listener thread.\n";
571 manager->destroy_message_loop();
Similar to MutexHolder, but for a light mutex.
This class can be used to convert text between multiple representations, e.g.
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.
is_threading_supported
Returns true if threading support has been compiled in and enabled, or false if no threading is avail...
static void force_yield()
Suspends the current thread for the rest of the current epoch.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.