device: a couple of fixes with Windows device input

This commit is contained in:
rdb 2019-01-01 16:23:16 +01:00
parent c61b480a41
commit 2bde2baed2
4 changed files with 70 additions and 46 deletions

View File

@ -277,7 +277,6 @@ on_input_device_arrival(HANDLE handle) {
if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) { if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) {
// This is a device we should handle via the XInput API. Check which of // This is a device we should handle via the XInput API. Check which of
// the four players was the lucky one. // the four players was the lucky one.
WinRawInputDevice idev(this, path);
if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) { if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) {
add_device(&_xinput_device0); add_device(&_xinput_device0);
} }

View File

@ -604,13 +604,16 @@ on_removal() {
_is_connected = false; _is_connected = false;
_handle = nullptr; _handle = nullptr;
if (_preparsed != nullptr) { if (_preparsed != nullptr) {
delete _preparsed; free(_preparsed);
_preparsed = nullptr; _preparsed = nullptr;
} }
_indices.clear(); _indices.clear();
_report_buttons.clear(); _report_buttons.clear();
} }
/**
* Called by InputDeviceManager when raw input is received for this device.
*/
void WinRawInputDevice:: void WinRawInputDevice::
on_input(PRAWINPUT input) { on_input(PRAWINPUT input) {
nassertv(input != nullptr); nassertv(input != nullptr);
@ -621,21 +624,33 @@ on_input(PRAWINPUT input) {
return; return;
} }
PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count);
nassertv(data != nullptr);
ULONG count;
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
for (DWORD i = 0; i < input->data.hid.dwCount; ++i) { for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
// The first byte is the report identifier. We need it to figure out process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
// which buttons are off, since each report only contains the buttons that ptr += input->data.hid.dwSizeHid;
// are "on". }
UCHAR report_id = ptr[0]; }
BitArray unset_buttons = _report_buttons[report_id];
count = _max_data_count; /**
NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, (PCHAR)ptr, input->data.hid.dwSizeHid); * 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) { if (status == HIDP_STATUS_SUCCESS) {
for (ULONG di = 0; di < count; ++di) { for (ULONG di = 0; di < count; ++di) {
if (data[di].DataIndex != _hat_data_index) { if (data[di].DataIndex != _hat_data_index) {
@ -672,9 +687,6 @@ on_input(PRAWINPUT input) {
<< "Failed to get data from raw device " << _path << "Failed to get data from raw device " << _path
<< " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n"; << " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n";
} }
ptr += input->data.hid.dwSizeHid;
}
} }
/** /**

View File

@ -34,6 +34,7 @@ public:
bool on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name); bool on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name);
void on_removal(); void on_removal();
void on_input(PRAWINPUT input); void on_input(PRAWINPUT input);
void process_report(PCHAR ptr, size_t size);
private: private:
virtual void do_poll(); virtual void do_poll();

View File

@ -161,6 +161,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst,
return false; return false;
} }
if (get_state(_index, &state) != ERROR_SUCCESS) {
return false;
}
// Extra check for VID/PID if we have it, just to be sure. // Extra check for VID/PID if we have it, just to be sure.
if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) || if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) ||
(caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) { (caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) {
@ -205,6 +209,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst,
*/ */
void XInputDevice:: void XInputDevice::
detect(InputDeviceManager *mgr) { detect(InputDeviceManager *mgr) {
if (!_initialized) {
nassertv_always(init_xinput());
}
bool connected = false; bool connected = false;
XINPUT_CAPABILITIES_EX caps = {0}; XINPUT_CAPABILITIES_EX caps = {0};
@ -225,6 +233,10 @@ detect(InputDeviceManager *mgr) {
_is_connected = connected; _is_connected = connected;
if (connected) { if (connected) {
_name = "XInput Device #";
_name += format_string(_index + 1);
_vendor_id = caps.VendorID;
_product_id = caps.ProductID;
init_device(caps, state); init_device(caps, state);
mgr->add_device(this); mgr->add_device(this);
} else { } else {