From 255b8b2865b87e9a3ef68748d7ae02e2573d2cf6 Mon Sep 17 00:00:00 2001 From: sctanf <36978460+sctanf@users.noreply.github.com> Date: Wed, 7 Jan 2026 04:13:33 -0500 Subject: [PATCH] Detect and use tracker PID during HID enumeration (#1556) --- gui/public/i18n/en/translation.ftl | 4 ++ .../settings/pages/GeneralSettings.tsx | 37 +++++++++++++++++++ .../trackers/hid/AndroidHIDManager.kt | 8 +++- .../main/java/dev/slimevr/config/HIDConfig.kt | 7 ++++ .../main/java/dev/slimevr/config/VRConfig.kt | 2 + .../rpc/settings/RPCSettingsBuilder.java | 5 +++ .../rpc/settings/RPCSettingsBuilderKotlin.kt | 11 ++++++ .../rpc/settings/RPCSettingsHandler.kt | 8 +++- .../tracking/trackers/hid/HIDCommon.kt | 1 + .../trackers/hid/DesktopHIDManager.kt | 26 +++++++++++-- solarxr-protocol | 2 +- 11 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 server/core/src/main/java/dev/slimevr/config/HIDConfig.kt diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index 914783abe..b39105e05 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -573,6 +573,10 @@ settings-general-tracker_mechanics-use_mag_on_all_trackers-description = Uses magnetometer on all trackers that have a compatible firmware for it, reducing drift in stable magnetic environments. Can be disabled per tracker in the tracker's settings. Please don't shutdown any of the trackers while toggling this! settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Use magnetometer on trackers +settings-general-tracker_mechanics-trackers_over_usb = Trackers over USB +settings-general-tracker_mechanics-trackers_over_usb-description = + Enables receiving HID tracker data over USB. Make sure connected trackers have connection over HID enabled! +settings-general-tracker_mechanics-trackers_over_usb-enabled-label = Allow HID trackers to connect directly over USB settings-stay_aligned = Stay Aligned settings-stay_aligned-description = Stay Aligned reduces drift by gradually adjusting your trackers to match your relaxed poses. diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx index d55478deb..174601a93 100644 --- a/gui/src/components/settings/pages/GeneralSettings.tsx +++ b/gui/src/components/settings/pages/GeneralSettings.tsx @@ -14,6 +14,7 @@ import { SettingsResponseT, SteamVRTrackersSettingT, TapDetectionSettingsT, + HIDSettingsT, } from 'solarxr-protocol'; import { useConfig } from '@/hooks/config'; import { useWebsocketAPI } from '@/hooks/websocket-api'; @@ -101,6 +102,9 @@ export type SettingsForm = { }; resetsSettings: ResetSettingsForm; stayAligned: StayAlignedSettingsForm; + hidSettings: { + trackersOverHID: boolean; + }; }; const defaultValues: SettingsForm = { @@ -156,6 +160,7 @@ const defaultValues: SettingsForm = { legTweaks: { correctionStrength: 0.3 }, resetsSettings: defaultResetSettings, stayAligned: defaultStayAlignedSettings, + hidSettings: { trackersOverHID: false }, }; export function GeneralSettings() { @@ -277,6 +282,10 @@ export function GeneralSettings() { settings.stayAligned = serializeStayAlignedSettings(values.stayAligned); + const hidSettings = new HIDSettingsT(); + hidSettings.trackersOverHid = values.hidSettings.trackersOverHID; + settings.hidSettings = hidSettings; + if (values.resetsSettings) { settings.resetsSettings = loadResetSettings(values.resetsSettings); } @@ -392,6 +401,12 @@ export function GeneralSettings() { ); } + if (settings.hidSettings) { + formData.hidSettings = { + trackersOverHID: settings.hidSettings.trackersOverHid, + }; + } + reset({ ...getValues(), ...formData }); }); @@ -689,6 +704,28 @@ export function GeneralSettings() { settingType="general" id="mechanics-magnetometer" /> +
+ + {l10n.getString( + 'settings-general-tracker_mechanics-trackers_over_usb' + )} + + }} + > + + +
+ } id="fksettings"> diff --git a/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt b/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt index 40fa6ad9b..22b537a90 100644 --- a/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt +++ b/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt @@ -8,10 +8,13 @@ import android.content.IntentFilter import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager import androidx.core.content.ContextCompat +import dev.slimevr.VRServer +import dev.slimevr.config.config import dev.slimevr.tracking.trackers.Device import dev.slimevr.tracking.trackers.Tracker import dev.slimevr.tracking.trackers.TrackerStatus import dev.slimevr.tracking.trackers.hid.HIDCommon +import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_PID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE @@ -91,7 +94,7 @@ class AndroidHIDManager( } fun checkConfigureDevice(usbDevice: UsbDevice, requestPermission: Boolean = false) { - if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && usbDevice.productId == HID_TRACKER_RECEIVER_PID) { + if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && (usbDevice.productId == HID_TRACKER_RECEIVER_PID || usbDevice.productId == HID_TRACKER_PID)) { if (usbManager.hasPermission(usbDevice)) { LogManager.info("[TrackerServer] Already have permission for ${usbDevice.deviceName}") proceedWithDeviceConfiguration(usbDevice) @@ -199,8 +202,9 @@ class AndroidHIDManager( } private fun deviceEnumerate(requestPermission: Boolean = false) { + val trackersOverHID: Boolean = VRServer.instance.configManager.vrConfig.hidConfig.trackersOverHID val hidDeviceList: MutableList = usbManager.deviceList.values.filter { - it.vendorId == HID_TRACKER_RECEIVER_VID && it.productId == HID_TRACKER_RECEIVER_PID + it.vendorId == HID_TRACKER_RECEIVER_VID && (it.productId == HID_TRACKER_RECEIVER_PID || (trackersOverHID && it.productId == HID_TRACKER_PID)) }.toMutableList() synchronized(devicesByHID) { // Work on devicesByHid and add/remove as necessary diff --git a/server/core/src/main/java/dev/slimevr/config/HIDConfig.kt b/server/core/src/main/java/dev/slimevr/config/HIDConfig.kt new file mode 100644 index 000000000..2945ee314 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/config/HIDConfig.kt @@ -0,0 +1,7 @@ +package dev.slimevr.config + +import com.fasterxml.jackson.annotation.JsonIgnore + +class HIDConfig { + var trackersOverHID = false +} diff --git a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt index 9ad2756f0..9ef33f5e0 100644 --- a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt @@ -42,6 +42,8 @@ class VRConfig { val stayAlignedConfig = StayAlignedConfig() + val hidConfig = HIDConfig() + @JsonDeserialize(using = TrackerConfigMapDeserializer::class) @JsonSerialize(keyUsing = StdKeySerializers.StringKeySerializer::class) private val trackers: MutableMap = HashMap() diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilder.java b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilder.java index 650f4b098..a06870e9f 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilder.java +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilder.java @@ -412,6 +412,11 @@ public class RPCSettingsBuilder { .createStayAlignedSettings( fbb, server.configManager.getVrConfig().getStayAlignedConfig() + ), + RPCSettingsBuilderKotlin.INSTANCE + .createHIDSettings( + fbb, + server.configManager.getVrConfig().getHidConfig() ) ); } diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilderKotlin.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilderKotlin.kt index 701da7438..3efe9d48e 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilderKotlin.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsBuilderKotlin.kt @@ -1,7 +1,9 @@ package dev.slimevr.protocol.rpc.settings import com.google.flatbuffers.FlatBufferBuilder +import dev.slimevr.config.HIDConfig import dev.slimevr.config.StayAlignedConfig +import solarxr_protocol.rpc.HIDSettings import solarxr_protocol.rpc.StayAlignedSettings object RPCSettingsBuilderKotlin { @@ -29,4 +31,13 @@ object RPCSettingsBuilderKotlin { config.flatRelaxedPose.footAngleInDeg, config.setupComplete, ) + + fun createHIDSettings( + fbb: FlatBufferBuilder, + config: HIDConfig, + ): Int = HIDSettings + .createHIDSettings( + fbb, + config.trackersOverHID, + ) } diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsHandler.kt index 2f4861aab..f2c882a04 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/settings/RPCSettingsHandler.kt @@ -369,6 +369,12 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) { config.flatRelaxedPose.footAngleInDeg = requestConfig.flatFootAngle() } + if (req.hidSettings() != null) { + val config = api.server.configManager.vrConfig.hidConfig + val requestConfig = req.hidSettings() + config.trackersOverHID = requestConfig.trackersOverHid() + } + api.server.configManager.saveConfig() } @@ -385,7 +391,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) { val settings = SettingsResponse .createSettingsResponse( fbb, - RPCSettingsBuilder.createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + RPCSettingsBuilder.createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ) val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SettingsResponse, settings) diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt index 558256840..8d9d4ce33 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt @@ -27,6 +27,7 @@ class HIDCommon { companion object { const val HID_TRACKER_RECEIVER_VID = 0x1209 const val HID_TRACKER_RECEIVER_PID = 0x7690 + const val HID_TRACKER_PID = 0x7692 const val PACKET_SIZE = 16 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 b571157d8..3f89940a1 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 @@ -1,9 +1,12 @@ package dev.slimevr.desktop.tracking.trackers.hid +import dev.slimevr.VRServer +import dev.slimevr.config.config import dev.slimevr.tracking.trackers.Device import dev.slimevr.tracking.trackers.Tracker import dev.slimevr.tracking.trackers.TrackerStatus import dev.slimevr.tracking.trackers.hid.HIDCommon +import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_PID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE @@ -55,7 +58,7 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer = mutableListOf() if (root != null) { var hidDeviceInfoStructure: HidDeviceInfoStructure? = root diff --git a/solarxr-protocol b/solarxr-protocol index 3400a6e6d..10a0ea778 160000 --- a/solarxr-protocol +++ b/solarxr-protocol @@ -1 +1 @@ -Subproject commit 3400a6e6dd4c66a0924b46b77445699d44a120e2 +Subproject commit 10a0ea778cff8a50f7985f41f97541bbe30d2469