diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt b/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt index 3f89940a10..bdca51f8d1 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt @@ -71,6 +71,7 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer): List { + // If we see a serial number, we can assume that the device is the same + // This is to accommodate an additional HID control port (I believe the native port should not be used for the receiver’s control loop). + //TODO: Maybe has a receiver that uses two HID ports simultaneously? + val seenSerials = HashSet() + val result = mutableListOf() + for (d in devices) { + val serial = d.serialNumber + if (serial.isNullOrBlank()) { + result.add(d) + } else if (seenSerials.add(serial)) { + result.add(d) + } + } + return result + } + private fun removeDevice(hidDevice: HidDevice) { this.devicesByHID[hidDevice]?.let { synchronized(this.devices) { @@ -107,6 +125,7 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer = mutableListOf() + val hidDeviceListFull: MutableList = mutableListOf() if (root != null) { var hidDeviceInfoStructure: HidDeviceInfoStructure? = root do { - hidDeviceList.add(HidDevice(hidDeviceInfoStructure, null, hidServicesSpecification)) + hidDeviceListFull.add(HidDevice(hidDeviceInfoStructure, null, hidServicesSpecification)) hidDeviceInfoStructure = hidDeviceInfoStructure?.next() } while (hidDeviceInfoStructure != null) HidApi.freeEnumeration(root) } + // Remove duplicates + val hidDeviceList = keepFirstHidPerPhysicalDevice(hidDeviceListFull) + val enumeratedSet = hidDeviceListFull.toSet() + val keptSet = hidDeviceList.toSet() + val droppedDuplicate = hidDeviceListFull.filter { it !in keptSet } + val droppedSet = droppedDuplicate.toSet() synchronized(devicesByHID) { // Work on devicesByHid and add/remove as necessary - val removeList: MutableList = devicesByHID.keys.toMutableList() - removeList.removeAll(hidDeviceList) - for (device in removeList) { - removeDevice(device) + for (device in devicesByHID.keys.toList()) { + if (device !in enumeratedSet) { + removeDevice(device) + } else if (device in droppedSet) { + removeDevice(device) + } } // Quickly reattaching a device may not be detected, so always try to open existing devices for (device in devicesByHID.keys) { // a receiver sends keep-alive data at 10 packets/s - if (lastDataByHID[device]!! > 100) { // try to reopen device if no data was received recently (about >100ms) + if (lastDataByHID.getOrDefault(device, 0) > 100) { // try to reopen device if no data was received recently (about >100ms) LogManager.info("[TrackerServer] Reopening device ${device.serialNumber} after no data received") device.open() } } - hidDeviceList.removeAll(devicesByHID.keys) // addList - for (device in hidDeviceList) { + val addList = hidDeviceList.toMutableList() + addList.removeAll(devicesByHID.keys) // addList + for (device in addList) { checkConfigureDevice(device) } }