From 2bde2baed23f1c0d350b0b0232c0a796326d3d4e Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Jan 2019 16:23:16 +0100 Subject: [PATCH] device: a couple of fixes with Windows device input --- panda/src/device/winInputDeviceManager.cxx | 1 - panda/src/device/winRawInputDevice.cxx | 102 ++++++++++++--------- panda/src/device/winRawInputDevice.h | 1 + panda/src/device/xInputDevice.cxx | 12 +++ 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/panda/src/device/winInputDeviceManager.cxx b/panda/src/device/winInputDeviceManager.cxx index b7fefc7c3e..765fb81816 100644 --- a/panda/src/device/winInputDeviceManager.cxx +++ b/panda/src/device/winInputDeviceManager.cxx @@ -277,7 +277,6 @@ on_input_device_arrival(HANDLE handle) { if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) { // This is a device we should handle via the XInput API. Check which of // the four players was the lucky one. - WinRawInputDevice idev(this, path); if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) { add_device(&_xinput_device0); } diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx index f49a7e0d7f..eeb98f112d 100644 --- a/panda/src/device/winRawInputDevice.cxx +++ b/panda/src/device/winRawInputDevice.cxx @@ -604,13 +604,16 @@ on_removal() { _is_connected = false; _handle = nullptr; if (_preparsed != nullptr) { - delete _preparsed; + free(_preparsed); _preparsed = nullptr; } _indices.clear(); _report_buttons.clear(); } +/** + * Called by InputDeviceManager when raw input is received for this device. + */ void WinRawInputDevice:: on_input(PRAWINPUT input) { nassertv(input != nullptr); @@ -621,59 +624,68 @@ on_input(PRAWINPUT input) { return; } - PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count); - nassertv(data != nullptr); - ULONG count; - LightMutexHolder holder(_lock); for (DWORD i = 0; i < input->data.hid.dwCount; ++i) { - // The first byte is the report identifier. We need it to figure out - // which buttons are off, since each report only contains the buttons that - // are "on". - UCHAR report_id = ptr[0]; - BitArray unset_buttons = _report_buttons[report_id]; + process_report((PCHAR)ptr, input->data.hid.dwSizeHid); + ptr += input->data.hid.dwSizeHid; + } +} - count = _max_data_count; - NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, (PCHAR)ptr, input->data.hid.dwSizeHid); - if (status == HIDP_STATUS_SUCCESS) { - for (ULONG di = 0; di < count; ++di) { - if (data[di].DataIndex != _hat_data_index) { - const Index &idx = _indices[data[di].DataIndex]; - if (idx._axis >= 0) { - if (idx._signed) { - axis_changed(idx._axis, (SHORT)data[di].RawValue); - } else { - axis_changed(idx._axis, data[di].RawValue); - } +/** + * Processes a single HID report. Assumes the lock is held. + */ +void WinRawInputDevice:: +process_report(PCHAR ptr, size_t size) { + // The first byte is the report identifier. We need it to figure out which + // buttons are off, since each report only contains the "on" buttons. + UCHAR report_id = ptr[0]; + BitArray unset_buttons; + + if (report_id < _report_buttons.size()) { + unset_buttons = _report_buttons[report_id]; + } + + PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count); + nassertv(data != nullptr); + + ULONG count = _max_data_count; + NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size); + if (status == HIDP_STATUS_SUCCESS) { + for (ULONG di = 0; di < count; ++di) { + if (data[di].DataIndex != _hat_data_index) { + const Index &idx = _indices[data[di].DataIndex]; + if (idx._axis >= 0) { + if (idx._signed) { + axis_changed(idx._axis, (SHORT)data[di].RawValue); + } else { + axis_changed(idx._axis, data[di].RawValue); } - if (idx._button >= 0) { - unset_buttons.clear_bit(idx._button); - button_changed(idx._button, (data[di].On != FALSE)); - } - } else { - int value = (int)data[di].RawValue - _hat_data_minimum; - button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left - button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right - button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down - button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up } + if (idx._button >= 0) { + unset_buttons.clear_bit(idx._button); + button_changed(idx._button, (data[di].On != FALSE)); + } + } else { + int value = (int)data[di].RawValue - _hat_data_minimum; + button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left + button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right + button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down + button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up } - - // Now unset the buttons in this report that aren't pressed. - int button_index = unset_buttons.get_lowest_on_bit(); - while (button_index >= 0) { - button_changed(button_index, false); - unset_buttons.clear_bit(button_index); - button_index = unset_buttons.get_lowest_on_bit(); - } - } else if (device_cat.is_spam()) { - device_cat.spam() - << "Failed to get data from raw device " << _path - << " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n"; } - ptr += input->data.hid.dwSizeHid; + // Now unset the buttons in this report that aren't pressed. + int button_index = unset_buttons.get_lowest_on_bit(); + while (button_index >= 0) { + button_changed(button_index, false); + unset_buttons.clear_bit(button_index); + button_index = unset_buttons.get_lowest_on_bit(); + } + } else if (device_cat.is_spam()) { + device_cat.spam() + << "Failed to get data from raw device " << _path + << " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n"; } } diff --git a/panda/src/device/winRawInputDevice.h b/panda/src/device/winRawInputDevice.h index c5cba8c615..8c880604be 100644 --- a/panda/src/device/winRawInputDevice.h +++ b/panda/src/device/winRawInputDevice.h @@ -34,6 +34,7 @@ public: bool on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name); void on_removal(); void on_input(PRAWINPUT input); + void process_report(PCHAR ptr, size_t size); private: virtual void do_poll(); diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index 42f9c4fbef..4621d467b9 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -161,6 +161,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, return false; } + if (get_state(_index, &state) != ERROR_SUCCESS) { + return false; + } + // Extra check for VID/PID if we have it, just to be sure. if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) || (caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) { @@ -205,6 +209,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst, */ void XInputDevice:: detect(InputDeviceManager *mgr) { + if (!_initialized) { + nassertv_always(init_xinput()); + } + bool connected = false; XINPUT_CAPABILITIES_EX caps = {0}; @@ -225,6 +233,10 @@ detect(InputDeviceManager *mgr) { _is_connected = connected; if (connected) { + _name = "XInput Device #"; + _name += format_string(_index + 1); + _vendor_id = caps.VendorID; + _product_id = caps.ProductID; init_device(caps, state); mgr->add_device(this); } else {