diff --git a/CustomHeadsetOpenVR/src/Driver/DeviceProvider.cpp b/CustomHeadsetOpenVR/src/Driver/DeviceProvider.cpp index 86a8861..4941b4f 100644 --- a/CustomHeadsetOpenVR/src/Driver/DeviceProvider.cpp +++ b/CustomHeadsetOpenVR/src/Driver/DeviceProvider.cpp @@ -384,6 +384,25 @@ static bool InputPathInteresting(const std::string &lower){ || lower.find("pinch") != std::string::npos; } +static bool NativeHandSerial(const char* serial){ + if(!serial){ return false; } + return strcmp(serial, "VRLINKQ_Hand_Left") == 0 + || strcmp(serial, "VRLINKQ_Hand_Right") == 0; +} + +static bool PhysicalGalaxyControllerSerial(const char* serial){ + if(!serial){ return false; } + std::string value = serial; + return value.rfind("SamsungVST-Controller", 0) == 0 + || (value.rfind("VRLINK", 0) == 0 + && value.find("Controller") != std::string::npos); +} + +static bool NativeHandDiagnosticPath(const std::string &lower){ + return lower.find("index_pinch") != std::string::npos + || lower.find("/input/grip") != std::string::npos; +} + // classify a component path into a distortion tuner control role. exact // suffix matches against the confirmed vrlink surface (session log): joystick // x/y scalars + joystick/a/b/x/y click booleans + grip value scalars. @@ -407,17 +426,24 @@ static int TunerRoleForPath(const std::string &lower, bool isScalar){ return 0; } -void CustomHeadsetDeviceProvider::OnInputComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle){ - if(!name || handle == vr::k_ulInvalidInputComponentHandle){ +void CustomHeadsetDeviceProvider::OnInputComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle, vr::EVRInputError error){ + if(!name || error != vr::VRInputError_None || handle == vr::k_ulInvalidInputComponentHandle){ + if(name && error != vr::VRInputError_None){ + DriverLog("HandInputDiag: boolean create FAILED container=%llu path=%s error=%d", + (unsigned long long)container, name, (int)error); + } return; } InputComponentInfo info; info.container = container; + info.openVRID = ResolveContainerId(container); info.name = name; std::string lower = info.name; for(auto &c : lower){ c = (char)tolower(c); } info.interesting = InputPathInteresting(lower); info.tunerRole = TunerRoleForPath(lower, false); + info.nativeHand = info.openVRID != vr::k_unTrackedDeviceIndexInvalid && IsNativeHand(info.openVRID); + info.diagnostic = info.nativeHand && NativeHandDiagnosticPath(lower); // hand classification from the quest layout: x/y buttons exist only on // the left controller, a/b only on the right. once known, resolve the // openVR id too so pose updates can be routed per hand. @@ -427,7 +453,7 @@ void CustomHeadsetDeviceProvider::OnInputComponentCreated(vr::PropertyContainerH // lock itself, and std::mutex is non-recursive — nesting it here // deadlocked vrserver at the first x/click creation and tripped a // SteamVR safe-mode block (session 24 regression) - uint32_t id = ResolveContainerId(container); + uint32_t id = info.openVRID; std::lock_guard handGuard(poseLogLock); containerHand[container] = hand; if(id != vr::k_unTrackedDeviceIndexInvalid){ @@ -436,32 +462,42 @@ void CustomHeadsetDeviceProvider::OnInputComponentCreated(vr::PropertyContainerH } // always log creates: component names are the map of vrlink's input // surface, and not having them cost a session - DriverLog("InputTap: boolean component container=%llu path=%s handle=%llu%s", + DriverLog("InputTap: boolean component container=%llu path=%s handle=%llu id=%u%s%s", (unsigned long long)container, name, (unsigned long long)handle, - info.interesting ? " [watched]" : ""); + info.openVRID, info.interesting ? " [watched]" : "", + info.nativeHand ? " [native-hand passthrough]" : ""); std::lock_guard guard(poseLogLock); inputComponents[handle] = info; } -void CustomHeadsetDeviceProvider::OnScalarComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle){ - if(!name || handle == vr::k_ulInvalidInputComponentHandle){ +void CustomHeadsetDeviceProvider::OnScalarComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle, vr::EVRInputError error){ + if(!name || error != vr::VRInputError_None || handle == vr::k_ulInvalidInputComponentHandle){ + if(name && error != vr::VRInputError_None){ + DriverLog("HandInputDiag: scalar create FAILED container=%llu path=%s error=%d", + (unsigned long long)container, name, (int)error); + } return; } InputComponentInfo info; info.container = container; + info.openVRID = ResolveContainerId(container); info.name = name; info.isScalar = true; std::string lower = info.name; for(auto &c : lower){ c = (char)tolower(c); } info.interesting = InputPathInteresting(lower); info.tunerRole = TunerRoleForPath(lower, true); - DriverLog("InputTap: scalar component container=%llu path=%s handle=%llu%s", + info.nativeHand = info.openVRID != vr::k_unTrackedDeviceIndexInvalid && IsNativeHand(info.openVRID); + info.diagnostic = info.nativeHand && NativeHandDiagnosticPath(lower); + DriverLog("InputTap: scalar component container=%llu path=%s handle=%llu id=%u%s%s", (unsigned long long)container, name, (unsigned long long)handle, - info.interesting ? " [watched]" : ""); + info.openVRID, info.interesting ? " [watched]" : "", + info.nativeHand ? " [native-hand passthrough]" : ""); // grip capacitive touch: vrlink never creates /input/grip/touch for these // controllers; synthesize it next to grip/value (before taking the lock: // the create call re-enters our own hook) - bool wantGripTouch = driverConfig.galaxyXr.nativeInputProfile && driverConfig.galaxyXr.synthesizeGripTouch + bool wantGripTouch = !info.nativeHand + && driverConfig.galaxyXr.nativeInputProfile && driverConfig.galaxyXr.synthesizeGripTouch && lower.size() >= 17 && lower.compare(lower.size() - 17, 17, "/input/grip/value") == 0; if(wantGripTouch && vr::VRDriverInput()){ vr::VRInputComponentHandle_t touchHandle = vr::k_ulInvalidInputComponentHandle; @@ -477,7 +513,33 @@ void CustomHeadsetDeviceProvider::OnScalarComponentCreated(vr::PropertyContainer inputComponents[handle] = info; } -void CustomHeadsetDeviceProvider::OnScalarComponentUpdated(vr::VRInputComponentHandle_t handle, float value){ +void CustomHeadsetDeviceProvider::OnScalarComponentUpdated(vr::VRInputComponentHandle_t handle, float value, double timeOffset, vr::EVRInputError error){ + { + double now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count() / 1000000.0; + bool doLog = false; + std::string name; + uint32_t id = vr::k_unTrackedDeviceIndexInvalid; + { + std::lock_guard guard(poseLogLock); + auto found = inputComponents.find(handle); + if(found != inputComponents.end() && found->second.diagnostic){ + InputComponentInfo &info = found->second; + doLog = error != vr::VRInputError_None || !info.diagHaveScalar + || std::fabs(value - info.diagLastScalar) >= 0.10f + || now - info.diagLastLogTime >= 0.5; + info.diagHaveScalar = true; + info.diagLastScalar = value; + if(doLog){ info.diagLastLogTime = now; } + name = info.name; + id = info.openVRID; + } + } + if(doLog){ + DriverLog("HandInputDiag: scalar id=%u path=%s value=%.3f timeOffset=%.4f result=%d", + id, name.c_str(), value, timeOffset, (int)error); + } + } // grip touch from grip value (hysteresis 0.03 / 0.015); updated outside the lock { vr::VRInputComponentHandle_t touchHandle = vr::k_ulInvalidInputComponentHandle; @@ -554,7 +616,28 @@ void CustomHeadsetDeviceProvider::OnScalarComponentUpdated(vr::VRInputComponentH } } -void CustomHeadsetDeviceProvider::OnBooleanComponentUpdated(vr::VRInputComponentHandle_t handle, bool value){ +void CustomHeadsetDeviceProvider::OnBooleanComponentUpdated(vr::VRInputComponentHandle_t handle, bool value, double timeOffset, vr::EVRInputError error){ + { + bool doLog = false; + std::string name; + uint32_t id = vr::k_unTrackedDeviceIndexInvalid; + { + std::lock_guard guard(poseLogLock); + auto found = inputComponents.find(handle); + if(found != inputComponents.end() && found->second.diagnostic){ + InputComponentInfo &info = found->second; + doLog = error != vr::VRInputError_None || !info.diagHaveBool || info.diagLastBool != value; + info.diagHaveBool = true; + info.diagLastBool = value; + name = info.name; + id = info.openVRID; + } + } + if(doLog){ + DriverLog("HandInputDiag: boolean id=%u path=%s value=%d timeOffset=%.4f result=%d", + id, name.c_str(), (int)value, timeOffset, (int)error); + } + } if(tunerInputActive.load(std::memory_order_relaxed)){ std::lock_guard tunerGuard(poseLogLock); auto found = inputComponents.find(handle); @@ -915,11 +998,21 @@ static std::map skeletonTapHands; void CustomHeadsetDeviceProvider::OnSkeletonComponentCreated(vr::PropertyContainerHandle_t container, const char *name, const char *skeletonPath, vr::VRInputComponentHandle_t handle){ std::string path = skeletonPath ? skeletonPath : ""; int hand = path.find("right") != std::string::npos ? 1 : 0; + uint32_t id = ResolveContainerId(container); + bool physicalController = id != vr::k_unTrackedDeviceIndexInvalid && IsStreamedController(id); + DriverLog("SkeletonTap: component %s (%s) hand=%s handle=%llu id=%u mode=%s", + name ? name : "?", path.c_str(), hand ? "right" : "left", + (unsigned long long)handle, id, + physicalController ? "physical-controller-adjustable" : "passthrough"); + // Native hands (and anything not positively identified as a physical + // Galaxy XR controller) keep their original skeleton data unchanged. + if(!physicalController){ + return; + } { std::lock_guard lock(skeletonTapMutex); skeletonTapHands[handle] = hand; } - DriverLog("SkeletonTap: component %s (%s) hand=%s handle=%llu", name ? name : "?", path.c_str(), hand ? "right" : "left", (unsigned long long)handle); } bool CustomHeadsetDeviceProvider::HandleSkeletonUpdate(vr::VRInputComponentHandle_t handle, const vr::VRBoneTransform_t *bones, uint32_t count, vr::VRBoneTransform_t *outBones){ @@ -1080,6 +1173,12 @@ static void CaInit(double P[6], double p0Var, double v0Var, double a0Var){ } bool CustomHeadsetDeviceProvider::HandleDevicePoseUpdated(uint32_t openVRID, vr::DriverPose_t &pose){ + // Native hand devices are published by vrlink with Controller class but + // are not physical Galaxy XR controllers. Their pose, tracking state, + // velocity and timing must reach SteamVR byte-for-byte unchanged. + if(openVRID != vr::k_unTrackedDeviceIndex_Hmd && IsNativeHand(openVRID)){ + return true; + } // raw tracking status, captured BEFORE forceTracking can launder it. // the estimators gate on these: forceTracking's job is keeping // devices alive for SteamVR, not feeding fake-OK into filters. @@ -4438,12 +4537,12 @@ bool CustomHeadsetDeviceProvider::HandleDevicePoseUpdated(uint32_t openVRID, vr: return true; } -bool CustomHeadsetDeviceProvider::IsStreamedController(uint32_t openVRID){ +CustomHeadsetDeviceProvider::StreamedDeviceKind CustomHeadsetDeviceProvider::GetStreamedDeviceKind(uint32_t openVRID){ { std::lock_guard guard(streamedIdentityLock); - auto found = streamedControllerCache.find(openVRID); - if(found != streamedControllerCache.end()){ - return found->second != 0; + auto found = streamedDeviceKindCache.find(openVRID); + if(found != streamedDeviceKindCache.end()){ + return found->second; } } // property query with NO lock held (concurrency law: never call out @@ -4452,20 +4551,33 @@ bool CustomHeadsetDeviceProvider::IsStreamedController(uint32_t openVRID){ vr::ETrackedPropertyError propError = vr::TrackedProp_Success; char serial[128] = {}; vr::VRProperties()->GetStringProperty(container, vr::Prop_SerialNumber_String, serial, sizeof(serial), &propError); - bool streamed = false; + StreamedDeviceKind kind = StreamedDeviceKind::Other; if(propError == vr::TrackedProp_Success){ - streamed = strncmp(serial, "VRLINK", 6) == 0 || strncmp(serial, "SamsungVST", 10) == 0; + if(NativeHandSerial(serial)){ + kind = StreamedDeviceKind::NativeHand; + }else if(PhysicalGalaxyControllerSerial(serial)){ + kind = StreamedDeviceKind::PhysicalController; + } }else{ // property not readable yet: do not cache, do not touch - return false; + return StreamedDeviceKind::Other; } { std::lock_guard guard(streamedIdentityLock); - streamedControllerCache[openVRID] = streamed ? 1 : 0; + streamedDeviceKindCache[openVRID] = kind; } - DriverLog("VelocityFix: id=%u serial=%s streamed=%d%s", openVRID, serial, streamed ? 1 : 0, - streamed ? "" : " (native velocity, never touched)"); - return streamed; + const char* kindName = kind == StreamedDeviceKind::PhysicalController ? "physical-controller" + : (kind == StreamedDeviceKind::NativeHand ? "native-hand-passthrough" : "other-passthrough"); + DriverLog("DeviceClassifier: id=%u serial=%s kind=%s", openVRID, serial, kindName); + return kind; +} + +bool CustomHeadsetDeviceProvider::IsNativeHand(uint32_t openVRID){ + return GetStreamedDeviceKind(openVRID) == StreamedDeviceKind::NativeHand; +} + +bool CustomHeadsetDeviceProvider::IsStreamedController(uint32_t openVRID){ + return GetStreamedDeviceKind(openVRID) == StreamedDeviceKind::PhysicalController; } // direction secant over the full derive ring: raw displacement newest-oldest @@ -4964,8 +5076,7 @@ bool CustomHeadsetDeviceProvider::HandleDeviceAdded(const char *&pchDeviceSerial std::string serial = pchDeviceSerialNumber ? pchDeviceSerialNumber : ""; // SamsungVST-Controller-* on the patched APK, VRLINKQ2_Controller_* on // the stock one. "Controller" excludes the VRLINKQ_Hand_* hand trackers. - bool streamedController = (serial.rfind("SamsungVST-Controller", 0) == 0) - || (serial.rfind("VRLINK", 0) == 0 && serial.find("Controller") != std::string::npos); + bool streamedController = PhysicalGalaxyControllerSerial(serial.c_str()); if(streamedController){ GalaxyXRControllerShim* controllerShim = new GalaxyXRControllerShim(serial); shims.insert(controllerShim); diff --git a/CustomHeadsetOpenVR/src/Driver/DeviceProvider.h b/CustomHeadsetOpenVR/src/Driver/DeviceProvider.h index 01b9ff3..a6ef05e 100644 --- a/CustomHeadsetOpenVR/src/Driver/DeviceProvider.h +++ b/CustomHeadsetOpenVR/src/Driver/DeviceProvider.h @@ -170,13 +170,20 @@ class CustomHeadsetDeviceProvider : public vr::IServerTrackedDeviceProvider static void ComputeRingSecant(const VelFixState &state, bool useSmoothed, double secantVel[3], double secantAng[3]); // cached device classes (Prop_DeviceClass_Int32), resolved on first pose std::map deviceClasses = {}; - // streamed-controller identity cache (serial prefix VRLINK*/SamsungVST* - // = vrlink device). the velocity fix must never touch lighthouse - // devices: their native velocity is correct and mixed sessions - // (knuckles + playspace override) are a supported setup. queried once - // per id OUTSIDE any lock, then cached. - std::map streamedControllerCache = {}; + // Strict vrlink device classification. A VRLINK prefix alone is not + // enough: native hand devices use VRLINKQ_Hand_* serials and must never + // enter any physical-controller pose/filter path. Unknown devices pass + // through untouched. Queried outside locks and cached only after the + // serial property is readable. + enum class StreamedDeviceKind : int { + Other = 0, + PhysicalController = 1, + NativeHand = 2, + }; + std::map streamedDeviceKindCache = {}; std::mutex streamedIdentityLock; + StreamedDeviceKind GetStreamedDeviceKind(uint32_t openVRID); + bool IsNativeHand(uint32_t openVRID); bool IsStreamedController(uint32_t openVRID); // derive-mode adaptive smoothing state (pure math under its own lock; // never calls out — lock discipline) @@ -570,6 +577,7 @@ class CustomHeadsetDeviceProvider : public vr::IServerTrackedDeviceProvider // throw died (snap back? dropout? zero?) with direct evidence. struct InputComponentInfo { vr::PropertyContainerHandle_t container = 0; + uint32_t openVRID = vr::k_unTrackedDeviceIndexInvalid; std::string name; bool lastValue = false; bool haveValue = false; @@ -593,6 +601,15 @@ class CustomHeadsetDeviceProvider : public vr::IServerTrackedDeviceProvider int tunerRole = 0; float tunerScalar = 0; bool tunerBool = false; + // Temporary native-hand diagnostics. These are observational only: + // the original IVRDriverInput call is made before this tap. + bool nativeHand = false; + bool diagnostic = false; + bool diagHaveBool = false; + bool diagLastBool = false; + bool diagHaveScalar = false; + float diagLastScalar = 0; + double diagLastLogTime = 0; }; std::map inputComponents = {}; // gate for tuner input capture on the hot component-update path @@ -683,10 +700,10 @@ class CustomHeadsetDeviceProvider : public vr::IServerTrackedDeviceProvider std::atomic alignerGripActive {false}; double alignerGripCm[2][3] = {}; public: - void OnInputComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle); - void OnBooleanComponentUpdated(vr::VRInputComponentHandle_t handle, bool value); - void OnScalarComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle); - void OnScalarComponentUpdated(vr::VRInputComponentHandle_t handle, float value); + void OnInputComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle, vr::EVRInputError error); + void OnBooleanComponentUpdated(vr::VRInputComponentHandle_t handle, bool value, double timeOffset, vr::EVRInputError error); + void OnScalarComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle, vr::EVRInputError error); + void OnScalarComponentUpdated(vr::VRInputComponentHandle_t handle, float value, double timeOffset, vr::EVRInputError error); void OnPoseComponentCreated(vr::PropertyContainerHandle_t container, const char* name, vr::VRInputComponentHandle_t handle); void OnSkeletonComponentCreated(vr::PropertyContainerHandle_t container, const char* name, const char* skeletonPath, vr::VRInputComponentHandle_t handle); bool HandleSkeletonUpdate(vr::VRInputComponentHandle_t handle, const vr::VRBoneTransform_t* bones, uint32_t count, vr::VRBoneTransform_t* outBones); diff --git a/CustomHeadsetOpenVR/src/Driver/Hooking/InterfaceHookInjector.cpp b/CustomHeadsetOpenVR/src/Driver/Hooking/InterfaceHookInjector.cpp index bfa8950..d3270fb 100644 --- a/CustomHeadsetOpenVR/src/Driver/Hooking/InterfaceHookInjector.cpp +++ b/CustomHeadsetOpenVR/src/Driver/Hooking/InterfaceHookInjector.cpp @@ -3,6 +3,7 @@ #include "InterfaceHookInjector.h" #include "../DeviceProvider.h" #include "../EyeTrackingTap.h" +#include static CustomHeadsetDeviceProvider *Driver = nullptr; @@ -52,23 +53,78 @@ static Hook UpdateSkeletonComponentHook004("IVRDriverInput004::UpdateSkeletonComponent"); +// Do not hand vrserver any DriverPose_t but the caller's own. +// +// The detour copied the caller's pose, ran the handler over the copy and +// forwarded that copy. Doing so stops SteamVR promoting vrlink's native hand +// devices to a controller role: Prop_ControllerRoleHint_Int32 stays correct +// while GetControllerRoleForTrackedDeviceIndex returns Invalid for the whole +// hand session, so /user/hand/left|right resolve to no device and every hand +// binding is dead - while index_pinch still reaches IVRDriverInput and returns +// VRInputError_None, which is why nothing looks wrong at the driver boundary. +// +// Bisected live on SteamVR 2.17.9 / Steam Link 2.0.20, reading +// GetTrackedDeviceIndexForControllerRole across the hand/controller swap: +// +// forward a stack copy for every device -> hands broken +// detour not installed at all -> hands work +// forward the caller's object for every device -> hands work +// run the handler over the caller's object (const_cast) -> hands broken +// forward the copy only for devices we modified -> hands broken +// forward a persistent per-device static buffer -> hands broken +// +// Every object other than the caller's own breaks it, whatever its lifetime, +// and for any device - forwarding a copy for the CONTROLLERS alone still takes +// the hands down. Writing into the caller's struct breaks it too. Why vrserver +// behaves this way is not established; only that it reproducibly does. +// +// Until that is understood, correctness wins over the pose corrections: the +// handler still runs, so its estimator state, aligner and diagnostics stay +// live, but vrserver always gets the pose it gave us. THIS MEANS THE PHYSICAL +// CONTROLLER POSE CORRECTIONS (grip convention, Kalman/CA velocity, trims) DO +// NOT REACH SteamVR. See the pull request's test matrix and trade-off notes. +static void PoseAbiWarn(uint32_t unPoseStructSize) +{ + static std::atomic reported{ false }; + if (!reported.exchange(true, std::memory_order_relaxed)) + { + DriverLog("PoseABI: caller struct=%u ours=%u%s", unPoseStructSize, + (unsigned)sizeof(vr::DriverPose_t), + unPoseStructSize == (uint32_t)sizeof(vr::DriverPose_t) ? "" : " - MISMATCH, handler skipped"); + } +} + static void DetourTrackedDevicePoseUpdated005(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize) { - //TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice); + PoseAbiWarn(unPoseStructSize); + if (unPoseStructSize != sizeof(vr::DriverPose_t)) + { + TrackedDevicePoseUpdatedHook005.originalFunc(_this, unWhichDevice, newPose, unPoseStructSize); + return; + } + // the handler works on a private copy purely for its own state and + // diagnostics; the copy is deliberately discarded, see above auto pose = newPose; if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose)) { - TrackedDevicePoseUpdatedHook005.originalFunc(_this, unWhichDevice, pose, unPoseStructSize); + TrackedDevicePoseUpdatedHook005.originalFunc(_this, unWhichDevice, newPose, unPoseStructSize); } } static void DetourTrackedDevicePoseUpdated006(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize) { - //TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice); + PoseAbiWarn(unPoseStructSize); + if (unPoseStructSize != sizeof(vr::DriverPose_t)) + { + TrackedDevicePoseUpdatedHook006.originalFunc(_this, unWhichDevice, newPose, unPoseStructSize); + return; + } + // the handler works on a private copy purely for its own state and + // diagnostics; the copy is deliberately discarded, see above auto pose = newPose; if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose)) { - TrackedDevicePoseUpdatedHook006.originalFunc(_this, unWhichDevice, pose, unPoseStructSize); + TrackedDevicePoseUpdatedHook006.originalFunc(_this, unWhichDevice, newPose, unPoseStructSize); } } @@ -90,32 +146,30 @@ static void DetourTrackedDeviceAdded006(vr::IVRServerDriverHost *_this, const ch static vr::EVRInputError DetourCreateBooleanComponent004(vr::IVRDriverInput *_this, vr::PropertyContainerHandle_t ulContainer, const char *pchName, vr::VRInputComponentHandle_t *pHandle) { auto error = CreateBooleanComponentHook004.originalFunc(_this, ulContainer, pchName, pHandle); - if(pHandle){ - Driver->OnInputComponentCreated(ulContainer, pchName, *pHandle); - } + Driver->OnInputComponentCreated(ulContainer, pchName, + pHandle ? *pHandle : vr::k_ulInvalidInputComponentHandle, error); return error; } static vr::EVRInputError DetourUpdateBooleanComponent004(vr::IVRDriverInput *_this, vr::VRInputComponentHandle_t ulComponent, bool bNewValue, double fTimeOffset) { auto error = UpdateBooleanComponentHook004.originalFunc(_this, ulComponent, bNewValue, fTimeOffset); - Driver->OnBooleanComponentUpdated(ulComponent, bNewValue); + Driver->OnBooleanComponentUpdated(ulComponent, bNewValue, fTimeOffset, error); return error; } static vr::EVRInputError DetourCreateScalarComponent004(vr::IVRDriverInput *_this, vr::PropertyContainerHandle_t ulContainer, const char *pchName, vr::VRInputComponentHandle_t *pHandle, vr::EVRScalarType eType, vr::EVRScalarUnits eUnits) { auto error = CreateScalarComponentHook004.originalFunc(_this, ulContainer, pchName, pHandle, eType, eUnits); - if(pHandle){ - Driver->OnScalarComponentCreated(ulContainer, pchName, *pHandle); - } + Driver->OnScalarComponentCreated(ulContainer, pchName, + pHandle ? *pHandle : vr::k_ulInvalidInputComponentHandle, error); return error; } static vr::EVRInputError DetourUpdateScalarComponent004(vr::IVRDriverInput *_this, vr::VRInputComponentHandle_t ulComponent, float fNewValue, double fTimeOffset) { auto error = UpdateScalarComponentHook004.originalFunc(_this, ulComponent, fNewValue, fTimeOffset); - Driver->OnScalarComponentUpdated(ulComponent, fNewValue); + Driver->OnScalarComponentUpdated(ulComponent, fNewValue, fTimeOffset, error); return error; } @@ -284,4 +338,4 @@ void DisableHooks() { IHook::DestroyAll(); // MH_Uninitialize(); -} \ No newline at end of file +}