From c835165ed3357b8fac986a6dd5cab631c21f322b Mon Sep 17 00:00:00 2001 From: matzman666 Date: Tue, 24 Oct 2017 21:26:22 +0200 Subject: [PATCH] Version 1.1 Release Candidate 1 --- VRInputEmulator.sln | 3 + .../win64/res/qml/DeviceAnalogAxisEntry.qml | 2 +- .../qml/DeviceAnalogInputRemappingPage.qml | 4 +- .../res/qml/DeviceInputRemappingPage.qml | 7 - .../win64/res/qml/DeviceManipulationPage.qml | 101 +++-- .../win64/res/qml/MotionCompensationPage.qml | 28 +- client_overlay/src/overlaycontroller.cpp | 74 +++- client_overlay/src/overlaycontroller.h | 4 +- .../DeviceManipulationTabController.cpp | 386 +++++++++++++++--- .../DeviceManipulationTabController.h | 33 +- .../DigitalInputRemappingController.cpp | 32 +- .../src/com/shm/driver_ipc_shm.cpp | 27 +- .../src/driver_deviceinfo.cpp | 33 +- driver_vrinputemulator/src/driver_server.cpp | 142 ++++++- .../src/driver_vrinputemulator.h | 53 ++- lib_vrinputemulator/include/ipc_protocol.h | 5 +- lib_vrinputemulator/include/vrinputemulator.h | 4 + .../include/vrinputemulator_types.h | 5 +- lib_vrinputemulator/src/vrinputemulator.cpp | 100 +++-- 19 files changed, 818 insertions(+), 225 deletions(-) diff --git a/VRInputEmulator.sln b/VRInputEmulator.sln index 3d607073..c9e3e16d 100644 --- a/VRInputEmulator.sln +++ b/VRInputEmulator.sln @@ -6,6 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_vrinputemulator", "lib_vrinputemulator\lib_vrinputemulator.vcxproj", "{05AC9994-2B63-4DE5-ABF3-95CE346F3A64}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver_vrinputemulator", "driver_vrinputemulator\driver_vrinputemulator.vcxproj", "{AF6FBE95-527D-499B-9ABD-3A47E9E84C8A}" + ProjectSection(ProjectDependencies) = postProject + {05AC9994-2B63-4DE5-ABF3-95CE346F3A64} = {05AC9994-2B63-4DE5-ABF3-95CE346F3A64} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client_leapmotion", "client_leapmotion\client_leapmotion.vcxproj", "{329C6147-E62D-456F-8860-A2DD91B113B5}" EndProject diff --git a/client_overlay/bin/win64/res/qml/DeviceAnalogAxisEntry.qml b/client_overlay/bin/win64/res/qml/DeviceAnalogAxisEntry.qml index c67fa62d..c44981fb 100644 --- a/client_overlay/bin/win64/res/qml/DeviceAnalogAxisEntry.qml +++ b/client_overlay/bin/win64/res/qml/DeviceAnalogAxisEntry.qml @@ -29,7 +29,7 @@ RowLayout { Connections { target: DeviceManipulationTabController onConfigureAnalogInputRemappingFinished: { - axisStatus = DeviceManipulationTabController.getAnalogAxisName(deviceIndex, axisId); + axisStatus = DeviceManipulationTabController.getAnalogAxisStatus(deviceIndex, axisId); configButton.text = axisStatus } } diff --git a/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml b/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml index f6760ede..4b110d4f 100644 --- a/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml @@ -21,7 +21,9 @@ MyStackViewPage { var _controllerNames = [""] _controllerIds = [] for (var i = 0; i < DeviceManipulationTabController.getDeviceCount(); i++) { - _controllerNames.push(DeviceManipulationTabController.getDeviceSerial(i)) + var deviceId = DeviceManipulationTabController.getDeviceId(i) + var deviceName = deviceId.toString() + ": " + DeviceManipulationTabController.getDeviceSerial(i) + _controllerNames.push(deviceName) _controllerIds.push(DeviceManipulationTabController.getDeviceId(i)) } openVRControllerComboBox.model = _controllerNames diff --git a/client_overlay/bin/win64/res/qml/DeviceInputRemappingPage.qml b/client_overlay/bin/win64/res/qml/DeviceInputRemappingPage.qml index d960d1e5..f0812ef8 100644 --- a/client_overlay/bin/win64/res/qml/DeviceInputRemappingPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceInputRemappingPage.qml @@ -170,13 +170,6 @@ MyStackViewPage { Component.onCompleted: { } - Connections { - target: DeviceManipulationTabController - onConfigureDigitalInputRemappingFinished: { - - } - } - } } diff --git a/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml b/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml index 97b7ad53..7154dc9b 100644 --- a/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml @@ -46,28 +46,6 @@ MyStackViewPage { dialogWidth: 600 dialogHeight: 400 dialogContentItem: ColumnLayout { - RowLayout { - Layout.topMargin: 16 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - Item { - Layout.fillWidth: true - } - MyComboBox { - id: deviceManipulationNewProfileType - Layout.maximumWidth: 555 - Layout.minimumWidth: 555 - Layout.preferredWidth: 555 - Layout.fillWidth: true - model: [ - "Device Offsets" - ] - currentIndex: 0 - } - Item { - Layout.fillWidth: true - } - } RowLayout { Layout.topMargin: 16 Layout.leftMargin: 16 @@ -86,22 +64,37 @@ MyStackViewPage { } } } + ColumnLayout { + Layout.topMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + MyToggleButton { + id: includeDeviceOffsetsToggle + text: "Include Device Offsets" + } + MyToggleButton { + id: includeInputRemapping + text: "Include Input Remapping" + } + } } onClosed: { if (okClicked) { if (deviceManipulationNewProfileName.text == "") { deviceManipulationMessageDialog.showMessage("Create New Profile", "ERROR: Empty profile name.") - } else if (deviceManipulationNewProfileType.currentIndex == 0) { - DeviceManipulationTabController.addDeviceManipulationProfile(deviceManipulationNewProfileName.text, deviceIndex, true, false) + } else if (!includeDeviceOffsetsToggle.checked && !includeInputRemapping.checked) { + deviceManipulationMessageDialog.showMessage("Create New Profile", "ERROR: Nothing to include selected.") } else { - DeviceManipulationTabController.addDeviceManipulationProfile(deviceManipulationNewProfileName.text, deviceIndex, false, true) + DeviceManipulationTabController.addDeviceManipulationProfile(deviceManipulationNewProfileName.text, deviceIndex, + includeDeviceOffsetsToggle.checked, includeInputRemapping.checked) } } } function openPopup(device) { deviceManipulationNewProfileName.text = "" - deviceManipulationNewProfileType.currentIndex = 0 + includeDeviceOffsetsToggle.checked = false + includeInputRemapping.checked = false deviceIndex = device open() } @@ -128,11 +121,11 @@ MyStackViewPage { Layout.fillWidth: true model: [] onCurrentIndexChanged: { + deviceModeSelectionComboBox.currentIndex = 0 if (currentIndex >= 0) { DeviceManipulationTabController.updateDeviceInfo(currentIndex); fetchDeviceInfo() - } - deviceModeSelectionComboBox.currentIndex = 0 + } } } @@ -183,8 +176,9 @@ MyStackViewPage { var deviceCount = DeviceManipulationTabController.getDeviceCount() for (var i = 0; i < deviceCount; i++) { var deviceMode = DeviceManipulationTabController.getDeviceMode(i) - if (deviceMode == 0 && i != deviceSelectionComboBox.currentIndex) { - var deviceName = DeviceManipulationTabController.getDeviceSerial(i) + var deviceId = DeviceManipulationTabController.getDeviceId(i) + if (i != deviceSelectionComboBox.currentIndex) { + var deviceName =deviceId + ": " + DeviceManipulationTabController.getDeviceSerial(i) var deviceClass = DeviceManipulationTabController.getDeviceClass(i) if (deviceClass == 1) { deviceName += " (HMD)" @@ -232,13 +226,14 @@ MyStackViewPage { if (deviceModeSelectionComboBox.currentIndex == 2 || deviceModeSelectionComboBox.currentIndex == 3) { var targetId = deviceModeTargetSelectionComboBox.deviceIndices[deviceModeTargetSelectionComboBox.currentIndex] if (targetId >= 0) { - DeviceManipulationTabController.setDeviceMode(deviceSelectionComboBox.currentIndex, deviceModeSelectionComboBox.currentIndex, targetId) - deviceModeSelectionComboBox.currentIndex = 0 - deviceModeTargetSelectionComboBox.currentIndex = -1 + if (!DeviceManipulationTabController.setDeviceMode(deviceSelectionComboBox.currentIndex, deviceModeSelectionComboBox.currentIndex, targetId)) { + deviceManipulationMessageDialog.showMessage("Set Device Mode", "Could not set device mode: " + DeviceManipulationTabController.getDeviceModeErrorString()) + } } } else { - DeviceManipulationTabController.setDeviceMode(deviceSelectionComboBox.currentIndex, deviceModeSelectionComboBox.currentIndex, 0) - deviceModeSelectionComboBox.currentIndex = 0 + if (!DeviceManipulationTabController.setDeviceMode(deviceSelectionComboBox.currentIndex, deviceModeSelectionComboBox.currentIndex, 0)) { + deviceManipulationMessageDialog.showMessage("Set Device Mode", "Could not set device mode: " + DeviceManipulationTabController.getDeviceModeErrorString()) + } } } } @@ -481,8 +476,11 @@ MyStackViewPage { if (index >= 0) { var deviceMode = DeviceManipulationTabController.getDeviceMode(index) var deviceState = DeviceManipulationTabController.getDeviceState(index) + var deviceClass = DeviceManipulationTabController.getDeviceClass(index) var statusText = "" if (deviceMode == 0) { // default + deviceModeSelectionComboBox.currentIndex = 0 + deviceModeSelectionComboBox.enabled = true if (deviceState == 0) { statusText = "Default" } else if (deviceState == 1) { @@ -491,12 +489,21 @@ MyStackViewPage { statusText = "Default (Unknown state " + deviceState.toString() + ")" } } else if (deviceMode == 1) { // fake disconnection + deviceModeSelectionComboBox.currentIndex = 1 if (deviceState == 0 || deviceState == 1) { statusText = "Disabled" } else { statusText = "Disabled (Unknown state " + deviceState.toString() + ")" } } else if (deviceMode == 2) { // redirect source + deviceModeSelectionComboBox.currentIndex = 2 + var refIndex = DeviceManipulationTabController.getDeviceModeRefDeviceIndex(index) + for (var i = 0; i < deviceModeTargetSelectionComboBox.deviceIndices.length; i++) { + if (refIndex == deviceModeTargetSelectionComboBox.deviceIndices[i]) { + deviceModeTargetSelectionComboBox.currentIndex = i + break + } + } if (deviceState == 0) { statusText = "Redirect Source" } else if (deviceState == 1) { @@ -505,6 +512,14 @@ MyStackViewPage { statusText = "Redirect Source (Unknown state " + deviceState.toString() + ")" } } else if (deviceMode == 3) { // redirect target + deviceModeSelectionComboBox.currentIndex = 2 + var refIndex = DeviceManipulationTabController.getDeviceModeRefDeviceIndex(index) + for (var i = 0; i < deviceModeTargetSelectionComboBox.deviceIndices.length; i++) { + if (refIndex == deviceModeTargetSelectionComboBox.deviceIndices[i]) { + deviceModeTargetSelectionComboBox.currentIndex = i + break + } + } if (deviceState == 0) { statusText = "Redirect Target" } else if (deviceState == 1) { @@ -513,6 +528,14 @@ MyStackViewPage { statusText = "Redirect Target (Unknown state " + deviceState.toString() + ")" } } else if (deviceMode == 4) { // swap mode + deviceModeSelectionComboBox.currentIndex = 3 + var refIndex = DeviceManipulationTabController.getDeviceModeRefDeviceIndex(index) + for (var i = 0; i < deviceModeTargetSelectionComboBox.deviceIndices.length; i++) { + if (refIndex == deviceModeTargetSelectionComboBox.deviceIndices[i]) { + deviceModeTargetSelectionComboBox.currentIndex = i + break + } + } if (deviceState == 0) { statusText = "Swapped" } else if (deviceState == 1) { @@ -520,7 +543,8 @@ MyStackViewPage { } else { statusText = "Swapped (Unknown state " + deviceState.toString() + ")" } - } else if (deviceMode == 5) { // motion compensation + } else if (deviceMode == 5) { // motion compens4tion + deviceModeSelectionComboBox.currentIndex = 4 if (deviceState == 0) { statusText = "Motion Compensation" } else { @@ -530,6 +554,11 @@ MyStackViewPage { statusText = "Unknown Mode " + deviceMode.toString() } deviceStatusText.text = statusText + if (deviceClass == 2 || deviceClass == 3) { + deviceIdentifyButton.enabled = true + } else { + deviceIdentifyButton.enabled = false + } } } diff --git a/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml b/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml index d7bccd51..8cc910de 100644 --- a/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml +++ b/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml @@ -182,21 +182,17 @@ MyStackViewPage { MySlider { id: movingAverageWindowSlider - // There seems to be a bug which causes the handle to snap to the wrong position when 'from' is not 0 - from: 0 - to: 19 + from: 1 + to: 20 stepSize: 1 value: 2 Layout.fillWidth: true onPositionChanged: { - var val = (1 + ( this.position * this.to)).toFixed(0) - if (activeFocus) { - //ChaperoneTabController.setHeight(val, false); - } + var val = (this.from + this.position * (this.to-this.from)).toFixed(0) movingAverageWindowText.text = val } onValueChanged: { - //ChaperoneTabController.setHeight(value.toFixed(0), false) + DeviceManipulationTabController.setMotionCompensationMovingAverageWindow(value.toFixed(0), false) } } @@ -216,18 +212,11 @@ MyStackViewPage { horizontalAlignment: Text.AlignHCenter function onInputEvent(input) { var val = parseFloat(input) - if (!isNaN(val)) { - if (val < 1) { - val = 1 - } - var v = val.toFixed(0) - 1 - if (v <= movingAverageWindowSlider.to) { - movingAverageWindowSlider.value = v - } else { - //ChaperoneTabController.setHeight(v, false) - } + if (!isNaN(val) && val >= 0.0) { + DeviceManipulationTabController.setMotionCompensationMovingAverageWindow(val.toFixed(0)) + } else { + movingAverageWindowText.text = DeviceManipulationTabController.getMotionCompensationMovingAverageWindow().toFixed(0) } - //text = ChaperoneTabController.height.toFixed(2) } } } @@ -246,6 +235,7 @@ MyStackViewPage { deviceSelectionComboBox.currentIndex = DeviceManipulationTabController.getMotionCompensationVelAccMode() kalmanProcessNoiseInputField.text = DeviceManipulationTabController.getMotionCompensationKalmanProcessNoise().toFixed(2) kalmanObservationNoiseInputField.text = DeviceManipulationTabController.getMotionCompensationKalmanObservationNoise().toFixed(2) + movingAverageWindowSlider.value = DeviceManipulationTabController.getMotionCompensationMovingAverageWindow().toFixed(0) setupFinished = true } diff --git a/client_overlay/src/overlaycontroller.cpp b/client_overlay/src/overlaycontroller.cpp index b64a877c..0c09f6c6 100644 --- a/client_overlay/src/overlaycontroller.cpp +++ b/client_overlay/src/overlaycontroller.cpp @@ -629,9 +629,9 @@ QString OverlayController::digitalBindingToString(const vrinputemulator::Digital status = "Disabled"; break; case vrinputemulator::DigitalBindingType::OpenVR: - status = openvrButtonToString(binding.binding.openvr.controllerId, binding.binding.openvr.buttonId); - if (printOptController && binding.binding.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid) { - status.append(" [R:").append(QString::number(binding.binding.openvr.controllerId)).append("]"); + status = openvrButtonToString(binding.data.openvr.controllerId, binding.data.openvr.buttonId); + if (printOptController && binding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid) { + status.append(" [R:").append(QString::number(binding.data.openvr.controllerId)).append("]"); } if (binding.toggleEnabled) { status.append(" [T]"); @@ -641,17 +641,17 @@ QString OverlayController::digitalBindingToString(const vrinputemulator::Digital } break; case vrinputemulator::DigitalBindingType::Keyboard: - if (binding.binding.keyboard.shiftPressed) { + if (binding.data.keyboard.shiftPressed) { status.append("SHIFT + "); } - if (binding.binding.keyboard.altPressed) { + if (binding.data.keyboard.altPressed) { status.append("ALT + "); } - if (binding.binding.keyboard.ctrlPressed) { + if (binding.data.keyboard.ctrlPressed) { status.append("CTRL + "); } for (auto& k : _keyboardVirtualCodes) { - if (k.second == binding.binding.keyboard.keyCode) { + if (k.second == binding.data.keyboard.keyCode) { status.append(QString::fromStdString(k.first)); break; } @@ -674,6 +674,35 @@ QString OverlayController::digitalBindingToString(const vrinputemulator::Digital } +QString OverlayController::analogBindingToString(const vrinputemulator::AnalogBinding& binding, bool printOptController) { + QString status; + switch (binding.type) { + case vrinputemulator::AnalogBindingType::NoRemapping: + status = "No Remapping"; + break; + case vrinputemulator::AnalogBindingType::Disabled: + status = "Disabled"; + break; + case vrinputemulator::AnalogBindingType::OpenVR: + status = openvrAxisToString(binding.data.openvr.controllerId, binding.data.openvr.axisId); + if (printOptController && binding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid) { + status.append(" [R:").append(QString::number(binding.data.openvr.controllerId)).append("]"); + } + if (binding.invertXAxis || binding.invertYAxis) { + status.append(" [I]"); + } + if (binding.swapAxes) { + status.append(" [S]"); + } + break; + default: + status = ""; + break; + } + return status; +} + + QString OverlayController::openvrButtonToString(unsigned deviceId, unsigned buttonId) { QString name; auto nameIt = _openVRButtonNames.find(buttonId); @@ -708,6 +737,37 @@ QString OverlayController::openvrButtonToString(unsigned deviceId, unsigned butt return name; } + +QString OverlayController::openvrAxisToString(unsigned deviceId, unsigned axisId) { + QString name("Axis"); + name.append(QString::number(axisId)); + if (deviceId != vr::k_unTrackedDeviceIndexInvalid) { + vr::ETrackedPropertyError pError; + auto axisType = vr::VRSystem()->GetInt32TrackedDeviceProperty(deviceId, (vr::ETrackedDeviceProperty)((int)vr::Prop_Axis0Type_Int32 + axisId), &pError); + if (pError == vr::TrackedProp_Success && axisType != vr::k_eControllerAxis_None) { + name.append(" ("); + switch (axisType) { + case vr::k_eControllerAxis_Trigger: + name.append("Trigger)"); + break; + case vr::k_eControllerAxis_TrackPad: + name.append("TrackPad)"); + break; + case vr::k_eControllerAxis_Joystick: + name.append("Joystick)"); + break; + default: + LOG(INFO) << "AxisType: " << axisType; + name.append(")"); + break; + } + name.append(")"); + } + } + return name; +} + + unsigned OverlayController::keyboardVirtualCodeCount() { return (unsigned)_keyboardVirtualCodes.size(); } diff --git a/client_overlay/src/overlaycontroller.h b/client_overlay/src/overlaycontroller.h index a18580c4..bbeb3da1 100644 --- a/client_overlay/src/overlaycontroller.h +++ b/client_overlay/src/overlaycontroller.h @@ -40,7 +40,7 @@ class OverlayController : public QObject { public: static constexpr const char* applicationKey = "matzman666.VRInputEmulator"; static constexpr const char* applicationName = "VR Input Emulator"; - static constexpr const char* applicationVersionString = "v1.0.3"; + static constexpr const char* applicationVersionString = "v1.1"; private: vr::VROverlayHandle_t m_ulOverlayHandle = vr::k_ulOverlayHandleInvalid; @@ -112,7 +112,9 @@ class OverlayController : public QObject { QString digitalBindingToString(const vrinputemulator::DigitalBinding& binding, bool printOptController); + QString analogBindingToString(const vrinputemulator::AnalogBinding& binding, bool printOptController); QString openvrButtonToString(unsigned deviceId, unsigned buttonId); + QString openvrAxisToString(unsigned deviceId, unsigned axisId); Q_INVOKABLE unsigned keyboardVirtualCodeCount(); Q_INVOKABLE QString keyboardVirtualCodeNameFromIndex(unsigned index); diff --git a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp index 7de5fc3b..114e42ae 100644 --- a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp +++ b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp @@ -4,6 +4,7 @@ #include "../overlaycontroller.h" #include #include +#include #include // application namespace @@ -182,6 +183,19 @@ int DeviceManipulationTabController::getDeviceMode(unsigned index) { } } +int DeviceManipulationTabController::getDeviceModeRefDeviceIndex(unsigned index) { + int retval = (int)vr::k_unTrackedDeviceIndexInvalid; + if (index < deviceInfos.size()) { + for (unsigned i = 0; i < deviceInfos.size(); i++) { + if (deviceInfos[i]->openvrId == deviceInfos[index]->refDeviceId) { + retval = i; + break; + } + } + } + return retval; +} + bool DeviceManipulationTabController::deviceOffsetsEnabled(unsigned index) { if (index < deviceInfos.size()) { return deviceInfos[index]->deviceOffsetsEnabled; @@ -250,6 +264,10 @@ double DeviceManipulationTabController::getMotionCompensationKalmanObservationNo return motionCompensationKalmanObservationNoise; } +unsigned DeviceManipulationTabController::getMotionCompensationMovingAverageWindow() { + return motionCompensationMovingAverageWindow; +} + #define DEVICEMANIPULATIONSETTINGS_GETTRANSLATIONVECTOR(name) { \ double valueX = settings->value(#name ## "_x", 0.0).toDouble(); \ @@ -271,6 +289,7 @@ void DeviceManipulationTabController::reloadDeviceManipulationSettings() { motionCompensationVelAccMode = (vrinputemulator::MotionCompensationVelAccMode)settings->value("motionCompensationVelAccMode", 0).toUInt(); motionCompensationKalmanProcessNoise = settings->value("motionCompensationKalmanProcessNoise", 0.1).toDouble(); motionCompensationKalmanObservationNoise = settings->value("motionCompensationKalmanObservationNoise", 0.1).toDouble(); + motionCompensationMovingAverageWindow = settings->value("motionCompensationMovingAverageWindow", 3).toUInt(); settings->endGroup(); } @@ -294,6 +313,71 @@ void DeviceManipulationTabController::reloadDeviceManipulationProfiles() { DEVICEMANIPULATIONSETTINGS_GETTRANSLATIONVECTOR(driverTranslationOffset); DEVICEMANIPULATIONSETTINGS_GETROTATIONVECTOR(driverRotationOffset); } + + entry.includesInputRemapping = settings->value("includesInputRemapping", false).toBool(); + if (entry.includesInputRemapping) { + + auto digitalRemappings = settings->value("digitalInputRemappings", false).toMap(); + + auto loadDigitalBinding = [](vrinputemulator::DigitalBinding& binding, QString& serial, QVariantMap& data) { + binding.type = (vrinputemulator::DigitalBindingType)data["type"].toUInt(); + if (binding.type == vrinputemulator::DigitalBindingType::OpenVR) { + if (data.contains("controllerSerial")) { + serial = data["controllerSerial"].toString(); + } + binding.data.openvr.buttonId = data["buttonId"].toUInt(); + } else if (binding.type == vrinputemulator::DigitalBindingType::OpenVR) { + binding.data.keyboard.shiftPressed = data["shiftPressed"].toBool(); + binding.data.keyboard.altPressed = data["altPressed"].toBool(); + binding.data.keyboard.ctrlPressed = data["ctrlPressed"].toBool(); + binding.data.keyboard.keyCode = data["keyCode"].toUInt(); + } + binding.toggleEnabled = data["toggleEnabled"].toBool(); + binding.toggleDelay = data["toggleDelay"].toUInt(); + binding.autoTriggerEnabled = data["autoTriggerEnabled"].toBool(); + binding.autoTriggerFrequency = data["autoTriggerFrequency"].toUInt(); + }; + + for (auto key : digitalRemappings.keys()) { + auto r = digitalRemappings[key].toMap(); + DigitalInputRemappingProfile p; + loadDigitalBinding(p.remapping.binding, p.normalBindingControllerSerial, r["binding"].toMap()); + p.remapping.touchAsClick = r.contains("touchAsClick") ? r["touchAsClick"].toBool() : false; + p.remapping.longPressEnabled = r.contains("longPressEnabled") ? r["longPressEnabled"].toBool() : false; + if (p.remapping.longPressEnabled) { + loadDigitalBinding(p.remapping.longPressBinding, p.longBindingControllerSerial, r["longPressBinding"].toMap()); + p.remapping.longPressThreshold = r.contains("longPressThreshold") ? r["longPressThreshold"].toUInt() : 1000u; + p.remapping.longPressImmediateRelease = r.contains("longPressImmediateRelease") ? r["longPressImmediateRelease"].toBool() : false; + } + p.remapping.doublePressEnabled = r.contains("doublePressEnabled") ? r["doublePressEnabled"].toBool() : false; + if (p.remapping.doublePressEnabled) { + loadDigitalBinding(p.remapping.doublePressBinding, p.doubleBindingControllerSerial, r["doublePressBinding"].toMap()); + p.remapping.doublePressThreshold = r.contains("doublePressThreshold") ? r["doublePressThreshold"].toUInt() : 1000u; + p.remapping.doublePressImmediateRelease = r.contains("doublePressImmediateRelease") ? r["doublePressImmediateRelease"].toBool() : false; + } + entry.digitalRemappingProfiles[key.toInt()] = p; + } + + auto analogRemappings = settings->value("analogInputRemappings", false).toMap(); + + for (auto key : analogRemappings.keys()) { + auto r = analogRemappings[key].toMap(); + AnalogInputRemappingProfile p; + p.remapping.binding.type = (vrinputemulator::AnalogBindingType)(r.contains("type") ? r["type"].toUInt() : (unsigned)vrinputemulator::AnalogBindingType::NoRemapping); + if (p.remapping.binding.type == vrinputemulator::AnalogBindingType::OpenVR) { + p.remapping.binding.data.openvr.axisId = r["axisId"].toUInt(); + if (r.contains("controllerSerial")) { + p.controllerSerial = r["controllerSerial"].toString(); + } + } + p.remapping.binding.invertXAxis = r["invertXAxis"].toBool(); + p.remapping.binding.invertYAxis = r["invertYAxis"].toBool(); + p.remapping.binding.swapAxes = r["swapAxes"].toBool(); + p.remapping.binding.lowerDeadzone = r["lowerDeadzone"].toFloat(); + p.remapping.binding.upperDeadzone = r["upperDeadzone"].toFloat(); + entry.analogRemappingProfiles[key.toInt()] = p; + } + } } settings->endArray(); settings->endGroup(); @@ -305,6 +389,7 @@ void DeviceManipulationTabController::saveDeviceManipulationSettings() { settings->setValue("motionCompensationVelAccMode", (unsigned)motionCompensationVelAccMode); settings->setValue("motionCompensationKalmanProcessNoise", motionCompensationKalmanProcessNoise); settings->setValue("motionCompensationKalmanObservationNoise", motionCompensationKalmanObservationNoise); + settings->setValue("motionCompensationMovingAverageWindow", motionCompensationMovingAverageWindow); settings->endGroup(); settings->sync(); } @@ -331,6 +416,27 @@ void DeviceManipulationTabController::saveDeviceManipulationProfiles() { settings->beginGroup("deviceManipulationSettings"); settings->beginWriteArray("deviceManipulationProfiles"); unsigned i = 0; + + auto saveDigitalBinding = [this](const vrinputemulator::DigitalBinding& binding, const QString& serial)->QVariantMap { + QVariantMap data; + data["type"] = (int)binding.type; + if (binding.type == vrinputemulator::DigitalBindingType::OpenVR) { + data["buttonId"] = binding.data.keyboard.keyCode; + if (!serial.isEmpty()) { + data["controllerSerial"] = serial; + } + } else if (binding.type == vrinputemulator::DigitalBindingType::OpenVR) { + data["shiftPressed"] = binding.data.keyboard.shiftPressed; + data["altPressed"] = binding.data.keyboard.altPressed; + data["ctrlPressed"] = binding.data.keyboard.ctrlPressed; + data["keyCode"] = binding.data.keyboard.keyCode; + } + data["toggleEnabled"] = binding.toggleEnabled; + data["autoTriggerEnabled"] = binding.autoTriggerEnabled; + data["autoTriggerFrequency"] = binding.autoTriggerFrequency; + return data; + }; + for (auto& p : deviceManipulationProfiles) { settings->setArrayIndex(i); settings->setValue("profileName", QString::fromStdString(p.profileName)); @@ -344,6 +450,50 @@ void DeviceManipulationTabController::saveDeviceManipulationProfiles() { DEVICEMANIPULATIONSETTINGS_WRITETRANSLATIONVECTOR(driverTranslationOffset); DEVICEMANIPULATIONSETTINGS_WRITEROTATIONVECTOR(driverRotationOffset); } + settings->setValue("includesInputRemapping", p.includesInputRemapping); + if (p.includesInputRemapping) { + QVariantMap digitalProfiles; + for (auto dp : p.digitalRemappingProfiles) { + QVariantMap profile; + profile["binding"] = saveDigitalBinding(dp.second.remapping.binding, dp.second.normalBindingControllerSerial); + profile["touchAsClick"] = dp.second.remapping.touchAsClick; + profile["longPressEnabled"] = dp.second.remapping.longPressEnabled; + if (dp.second.remapping.longPressEnabled) { + profile["longPressBinding"] = saveDigitalBinding(dp.second.remapping.longPressBinding, dp.second.longBindingControllerSerial); + profile["longPressThreshold"] = dp.second.remapping.longPressThreshold; + profile["longPressImmediateRelease"] = dp.second.remapping.longPressImmediateRelease; + } + profile["doublePressEnabled"] = dp.second.remapping.doublePressEnabled; + if (dp.second.remapping.doublePressEnabled) { + profile["doublePressBinding"] = saveDigitalBinding(dp.second.remapping.doublePressBinding, dp.second.doubleBindingControllerSerial); + profile["doublePressThreshold"] = dp.second.remapping.doublePressThreshold; + profile["doublePressImmediateRelease"] = dp.second.remapping.doublePressImmediateRelease; + } + digitalProfiles[QString::number(dp.first)] = profile; + } + settings->setValue("digitalInputRemappings", digitalProfiles); + QVariantMap analogProfiles; + for (unsigned i2 = 0; i2 < 5; i2++) { + auto& ap = p.analogRemappingProfiles[i2]; + if (ap.remapping.valid) { + QVariantMap profile; + profile["type"] = (int)ap.remapping.binding.type; + if (ap.remapping.binding.type == vrinputemulator::AnalogBindingType::OpenVR) { + profile["axisId"] = ap.remapping.binding.data.openvr.axisId; + if (!ap.controllerSerial.isEmpty()) { + profile["controllerSerial"] = ap.controllerSerial; + } + } + profile["invertXAxis"] = ap.remapping.binding.invertXAxis; + profile["invertYAxis"] = ap.remapping.binding.invertYAxis; + profile["lowerDeadzone"] = ap.remapping.binding.lowerDeadzone; + profile["upperDeadzone"] = ap.remapping.binding.upperDeadzone; + profile["swapAxes"] = ap.remapping.binding.swapAxes; + analogProfiles[QString::number(i2)] = profile; + } + } + settings->setValue("analogInputRemappings", analogProfiles); + } i++; } settings->endArray(); @@ -363,7 +513,7 @@ QString DeviceManipulationTabController::getDeviceManipulationProfileName(unsign } } -void DeviceManipulationTabController::addDeviceManipulationProfile(QString name, unsigned deviceIndex, bool includesDeviceOffsets, bool includesMotionCompensationSettings) { +void DeviceManipulationTabController::addDeviceManipulationProfile(QString name, unsigned deviceIndex, bool includesDeviceOffsets, bool includesInputRemapping) { if (deviceIndex >= deviceInfos.size()) { return; } @@ -391,6 +541,64 @@ void DeviceManipulationTabController::addDeviceManipulationProfile(QString name, profile->driverTranslationOffset = device->deviceTranslationOffset; profile->driverRotationOffset = device->deviceRotationOffset; } + profile->includesInputRemapping = includesInputRemapping; + if (includesInputRemapping) { + try { + auto _getDeviceSerialFromId = [&](uint32_t deviceId)->std::string { + std::string retval; + for (auto& i : deviceInfos) { + if (i->openvrId == deviceId) { + retval = i->serial; + break; + } + } + return retval; + }; + for (unsigned i = 0; i < vr::k_EButton_Max; i++) { + auto r = parent->vrInputEmulator().getDigitalInputRemapping(device->openvrId, i); + if (r.valid) { + auto& p = profile->digitalRemappingProfiles[i]; + p.remapping = r; + if ( p.remapping.binding.type == vrinputemulator::DigitalBindingType::OpenVR + && p.remapping.binding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid + && p.remapping.binding.data.openvr.controllerId != device->openvrId ) { + p.normalBindingControllerSerial = QString::fromStdString(_getDeviceSerialFromId(p.remapping.binding.data.openvr.controllerId)); + } + if ( p.remapping.longPressEnabled && p.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::OpenVR + && p.remapping.longPressBinding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid + && p.remapping.longPressBinding.data.openvr.controllerId != device->openvrId) { + p.longBindingControllerSerial = QString::fromStdString(_getDeviceSerialFromId(p.remapping.longPressBinding.data.openvr.controllerId)); + } + if (p.remapping.doublePressEnabled && p.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::OpenVR + && p.remapping.doublePressBinding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid + && p.remapping.doublePressBinding.data.openvr.controllerId != device->openvrId) { + p.doubleBindingControllerSerial = QString::fromStdString(_getDeviceSerialFromId(p.remapping.doublePressBinding.data.openvr.controllerId)); + } + } + } + for (unsigned i = 0; i < 5; i++) { + auto r = parent->vrInputEmulator().getAnalogInputRemapping(device->openvrId, i); + auto& p = profile->analogRemappingProfiles[i]; + p.remapping.valid = r.valid; + if (r.valid) { + p.remapping.binding.type = r.binding.type; + if (r.binding.type == vrinputemulator::AnalogBindingType::OpenVR) { + p.remapping.binding.data.openvr.axisId = r.binding.data.openvr.axisId; + if (r.binding.data.openvr.controllerId != vr::k_unTrackedDeviceIndexInvalid && r.binding.data.openvr.controllerId != device->openvrId) { + p.controllerSerial = QString::fromStdString(_getDeviceSerialFromId(r.binding.data.openvr.controllerId)); + } + } + p.remapping.binding.invertXAxis = r.binding.invertXAxis; + p.remapping.binding.invertYAxis = r.binding.invertYAxis; + p.remapping.binding.lowerDeadzone = r.binding.lowerDeadzone; + p.remapping.binding.upperDeadzone = r.binding.upperDeadzone; + p.remapping.binding.swapAxes = r.binding.swapAxes; + } + } + } catch (const std::exception& e) { + LOG(ERROR) << "Exception while adding a new digital input remapping profile: " << e.what(); + } + } saveDeviceManipulationProfiles(); OverlayController::appSettings()->sync(); emit deviceManipulationProfilesChanged(); @@ -412,8 +620,52 @@ void DeviceManipulationTabController::applyDeviceManipulationProfile(unsigned in setDriverTranslationOffset(deviceIndex, profile.driverTranslationOffset.v[0], profile.driverTranslationOffset.v[1], profile.driverTranslationOffset.v[2], false); enableDeviceOffsets(deviceIndex, profile.deviceOffsetsEnabled, false); updateDeviceInfo(deviceIndex); - emit deviceInfoChanged(deviceIndex); } + if (profile.includesInputRemapping) { + auto _getDeviceIdFromSerial = [&](const QString& serial)->uint32_t { + uint32_t retval = vr::k_unTrackedDeviceIndexInvalid; + for (auto i : deviceInfos) { + if (i->serial.compare(serial.toStdString()) == 0) { + retval = i->openvrId; + break; + } + } + return retval; + }; + for (unsigned i = 0; i < vr::k_EButton_Max; i++) { + auto it = profile.digitalRemappingProfiles.find(i); + if (it == profile.digitalRemappingProfiles.end()) { + parent->vrInputEmulator().setDigitalInputRemapping(device->openvrId, i, vrinputemulator::DigitalInputRemapping()); + } else { + auto& p = *it; + if (p.second.remapping.binding.type == vrinputemulator::DigitalBindingType::OpenVR && !p.second.normalBindingControllerSerial.isEmpty()) { + p.second.remapping.binding.data.openvr.controllerId = _getDeviceIdFromSerial(p.second.normalBindingControllerSerial); + } else { + p.second.remapping.binding.data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; + } + if (p.second.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::OpenVR && !p.second.longBindingControllerSerial.isEmpty()) { + p.second.remapping.longPressBinding.data.openvr.controllerId = _getDeviceIdFromSerial(p.second.longBindingControllerSerial); + } else { + p.second.remapping.longPressBinding.data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; + } + if (p.second.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::OpenVR && !p.second.doubleBindingControllerSerial.isEmpty()) { + p.second.remapping.doublePressBinding.data.openvr.controllerId = _getDeviceIdFromSerial(p.second.doubleBindingControllerSerial); + } else { + p.second.remapping.doublePressBinding.data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; + } + parent->vrInputEmulator().setDigitalInputRemapping(device->openvrId, p.first, p.second.remapping); + } + } + for (unsigned i = 0; i < 5; i++) { + if (profile.analogRemappingProfiles[i].remapping.valid + && profile.analogRemappingProfiles[i].remapping.binding.type == vrinputemulator::AnalogBindingType::OpenVR + && !profile.analogRemappingProfiles[i].controllerSerial.isEmpty()) { + profile.analogRemappingProfiles[i].remapping.binding.data.openvr.controllerId = _getDeviceIdFromSerial(profile.analogRemappingProfiles[i].controllerSerial); + } + parent->vrInputEmulator().setAnalogInputRemapping(device->openvrId, i, profile.analogRemappingProfiles[i].remapping); + } + } + emit deviceInfoChanged(deviceIndex); } } @@ -461,6 +713,17 @@ void DeviceManipulationTabController::setMotionCompensationKalmanObservationNois } } +void DeviceManipulationTabController::setMotionCompensationMovingAverageWindow(unsigned window, bool notify) { + if (motionCompensationMovingAverageWindow != window) { + motionCompensationMovingAverageWindow = window; + parent->vrInputEmulator().setMotionCompensationMovingAverageWindow(motionCompensationMovingAverageWindow); + saveDeviceManipulationSettings(); + if (notify) { + emit motionCompensationMovingAverageWindowChanged(motionCompensationMovingAverageWindow); + } + } +} + unsigned DeviceManipulationTabController::getDigitalButtonCount(unsigned deviceIndex) { unsigned count = 0; if (deviceIndex < deviceInfos.size()) { @@ -517,15 +780,37 @@ QString DeviceManipulationTabController::getDigitalButtonName(unsigned deviceInd } QString DeviceManipulationTabController::getDigitalButtonStatus(unsigned deviceIndex, unsigned buttonId) { - QString status = -1; + QString status; if (deviceIndex < deviceInfos.size()) { auto remapping = parent->vrInputEmulator().getDigitalInputRemapping(deviceInfos[deviceIndex]->openvrId, buttonId); - status = parent->digitalBindingToString(remapping.binding, remapping.binding.binding.openvr.controllerId != deviceInfos[deviceIndex]->openvrId); - if (remapping.longPressEnabled) { - status.append(" [LP]"); - } - if (remapping.doublePressEnabled) { - status.append(" [DP]"); + if (!remapping.doublePressEnabled && !remapping.longPressEnabled) { + status = parent->digitalBindingToString(remapping.binding, remapping.binding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId); + } else { + status.append("Regular: "); + if (remapping.binding.type == vrinputemulator::DigitalBindingType::NoRemapping) { + status.append("Original"); + } else { + status.append(parent->digitalBindingToString(remapping.binding, remapping.binding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId)); + } + if (remapping.longPressEnabled) { + status.append("; Long: "); + if (remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::NoRemapping) { + status.append("Original"); + } else { + status.append(parent->digitalBindingToString(remapping.longPressBinding, remapping.longPressBinding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId)); + } + } + if (remapping.doublePressEnabled) { + status.append("; Double: "); + if (remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::NoRemapping) { + status.append("Original"); + } else { + status.append(parent->digitalBindingToString(remapping.doublePressBinding, remapping.doublePressBinding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId)); + } + } + if (status.size() > 60) { + status = status.left(60).append("..."); + } } } return status; @@ -575,48 +860,18 @@ int DeviceManipulationTabController::getAnalogAxisId(unsigned deviceIndex, unsig } QString DeviceManipulationTabController::getAnalogAxisName(unsigned deviceIndex, unsigned axisId) { - QString name("Axis"); - name.append(QString::number(axisId)).append(" ("); - vr::ETrackedPropertyError pError; - auto axisType = vr::VRSystem()->GetInt32TrackedDeviceProperty(deviceInfos[deviceIndex]->openvrId, (vr::ETrackedDeviceProperty)((int)vr::Prop_Axis0Type_Int32 + axisId), &pError); - if (pError == vr::TrackedProp_Success) { - switch (axisType) { - case vr::k_eControllerAxis_Trigger: - name.append("Trigger)"); - break; - case vr::k_eControllerAxis_TrackPad: - name.append("TrackPad)"); - break; - case vr::k_eControllerAxis_Joystick: - name.append("Joystick)"); - break; - default: - name.append(")"); - break; - } + if (deviceIndex < deviceInfos.size()) { + return parent->openvrAxisToString(deviceInfos[deviceIndex]->openvrId, axisId); } else { - LOG(ERROR) << "Could not get axis type for device " << deviceInfos[deviceIndex]->serial; + return parent->openvrAxisToString(vr::k_unTrackedDeviceIndexInvalid, axisId); } - return name; } QString DeviceManipulationTabController::getAnalogAxisStatus(unsigned deviceIndex, unsigned axisId) { - /*ÜQString status = ""; + QString status; if (deviceIndex < deviceInfos.size()) { - auto remapping = parent->vrInputEmulator().getAnalogInputRemapping(deviceInfos[deviceIndex]->openvrId, buttonId); - status = parent->digitalBindingToString(remapping.binding, remapping.binding.binding.openvr.controllerId != deviceInfos[deviceIndex]->openvrId); - if (remapping.longPressEnabled) { - status.append(" [LP]"); - } - if (remapping.doublePressEnabled) { - status.append(" [DP]"); - } - } - return status;*/ - - QString status = -1; - if (deviceIndex < deviceInfos.size()) { - status = ""; + auto remapping = parent->vrInputEmulator().getAnalogInputRemapping(deviceInfos[deviceIndex]->openvrId, axisId); + status = parent->analogBindingToString(remapping.binding, remapping.binding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId); } return status; } @@ -790,7 +1045,8 @@ void DeviceManipulationTabController::setDriverTranslationOffset(unsigned index, } // 0 .. normal, 1 .. disable, 2 .. redirect mode, 3 .. swap mode, 4 ... motion compensation -void DeviceManipulationTabController::setDeviceMode(unsigned index, unsigned mode, unsigned targedIndex, bool notify) { +bool DeviceManipulationTabController::setDeviceMode(unsigned index, unsigned mode, unsigned targedIndex, bool notify) { + bool retval = true; try { switch (mode) { case 0: @@ -809,20 +1065,54 @@ void DeviceManipulationTabController::setDeviceMode(unsigned index, unsigned mod if (motionCompensationVelAccMode == vrinputemulator::MotionCompensationVelAccMode::KalmanFilter) { parent->vrInputEmulator().setMotionCompensationKalmanProcessNoise(motionCompensationKalmanProcessNoise); parent->vrInputEmulator().setMotionCompensationKalmanObservationNoise(motionCompensationKalmanObservationNoise); + } else if (motionCompensationVelAccMode == vrinputemulator::MotionCompensationVelAccMode::LinearApproximation) { + parent->vrInputEmulator().setMotionCompensationMovingAverageWindow(motionCompensationMovingAverageWindow); } parent->vrInputEmulator().setDeviceMotionCompensationMode(deviceInfos[index]->openvrId, motionCompensationVelAccMode); break; default: + retval = false; + m_deviceModeErrorString = "Unknown Device Mode"; LOG(ERROR) << "Unkown device mode"; break; } + } catch (vrinputemulator::vrinputemulator_exception& e) { + retval = false; + switch (e.errorcode) { + case (int)vrinputemulator::ipc::ReplyStatus::Ok: { + m_deviceModeErrorString = "Not an error"; + } break; + case (int)vrinputemulator::ipc::ReplyStatus::AlreadyInUse: { + m_deviceModeErrorString = "Device already in use"; + } break; + case (int)vrinputemulator::ipc::ReplyStatus::InvalidId: { + m_deviceModeErrorString = "Invalid Id"; + } break; + case (int)vrinputemulator::ipc::ReplyStatus::NotFound: { + m_deviceModeErrorString = "Device not found"; + } break; + case (int)vrinputemulator::ipc::ReplyStatus::NotTracking: { + m_deviceModeErrorString = "Device not tracking"; + } break; + default: { + m_deviceModeErrorString = "Unknown error"; + } break; + } + LOG(ERROR) << "Exception caught while setting device mode: " << e.what(); } catch (std::exception& e) { + retval = false; + m_deviceModeErrorString = "Unknown exception"; LOG(ERROR) << "Exception caught while setting device mode: " << e.what(); } if (notify) { updateDeviceInfo(index); emit deviceInfoChanged(index); } + return retval; +} + +QString DeviceManipulationTabController::getDeviceModeErrorString() { + return m_deviceModeErrorString; } @@ -836,6 +1126,10 @@ bool DeviceManipulationTabController::updateDeviceInfo(unsigned index) { deviceInfos[index]->deviceMode = info.deviceMode; retval = true; } + if (deviceInfos[index]->refDeviceId != info.refDeviceId) { + deviceInfos[index]->refDeviceId = info.refDeviceId; + retval = true; + } if (deviceInfos[index]->deviceOffsetsEnabled != info.offsetsEnabled) { deviceInfos[index]->deviceOffsetsEnabled = info.offsetsEnabled; retval = true; diff --git a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h index 6d96802b..ca69da37 100644 --- a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h +++ b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h @@ -14,6 +14,22 @@ namespace inputemulator { class OverlayController; +struct DigitalInputRemappingProfile { + QString normalBindingControllerSerial; + QString longBindingControllerSerial; + QString doubleBindingControllerSerial; + vrinputemulator::DigitalInputRemapping remapping; + + DigitalInputRemappingProfile() : remapping(true) {} +}; + +struct AnalogInputRemappingProfile { + QString controllerSerial; + vrinputemulator::AnalogInputRemapping remapping; + + AnalogInputRemappingProfile() : remapping(true) {} +}; + struct DeviceManipulationProfile { std::string profileName; @@ -25,6 +41,9 @@ struct DeviceManipulationProfile { vr::HmdVector3d_t driverFromHeadRotationOffset; vr::HmdVector3d_t driverTranslationOffset; vr::HmdVector3d_t driverRotationOffset; + bool includesInputRemapping = false; + std::map digitalRemappingProfiles; + AnalogInputRemappingProfile analogRemappingProfiles[5]; }; @@ -34,6 +53,7 @@ struct DeviceInfo { uint32_t openvrId = 0; int deviceStatus = 0; // 0 .. Normal, 1 .. Disconnected/Suspended int deviceMode = 0; // 0 .. Default, 1 .. Fake Disconnected, 2 .. Redirect Source, 3 .. Redirect Target, 4 .. Motion Compensation + uint32_t refDeviceId = 0; bool deviceOffsetsEnabled; vr::HmdVector3d_t worldFromDriverRotationOffset; vr::HmdVector3d_t worldFromDriverTranslationOffset; @@ -62,6 +82,9 @@ class DeviceManipulationTabController : public QObject { vrinputemulator::MotionCompensationVelAccMode motionCompensationVelAccMode = vrinputemulator::MotionCompensationVelAccMode::Disabled; double motionCompensationKalmanProcessNoise = 0.1; double motionCompensationKalmanObservationNoise = 0.1; + unsigned motionCompensationMovingAverageWindow = 3; + + QString m_deviceModeErrorString; unsigned settingsUpdateCounter = 0; @@ -81,6 +104,7 @@ class DeviceManipulationTabController : public QObject { Q_INVOKABLE int getDeviceClass(unsigned index); Q_INVOKABLE int getDeviceState(unsigned index); Q_INVOKABLE int getDeviceMode(unsigned index); + Q_INVOKABLE int getDeviceModeRefDeviceIndex(unsigned index); Q_INVOKABLE bool deviceOffsetsEnabled(unsigned index); Q_INVOKABLE double getWorldFromDriverRotationOffset(unsigned index, unsigned axis); Q_INVOKABLE double getWorldFromDriverTranslationOffset(unsigned index, unsigned axis); @@ -91,6 +115,7 @@ class DeviceManipulationTabController : public QObject { Q_INVOKABLE unsigned getMotionCompensationVelAccMode(); Q_INVOKABLE double getMotionCompensationKalmanProcessNoise(); Q_INVOKABLE double getMotionCompensationKalmanObservationNoise(); + Q_INVOKABLE unsigned getMotionCompensationMovingAverageWindow(); void reloadDeviceManipulationSettings(); void reloadDeviceManipulationProfiles(); @@ -121,6 +146,9 @@ class DeviceManipulationTabController : public QObject { Q_INVOKABLE void startConfigureAnalogInputRemapping(unsigned deviceIndex, unsigned axisId); Q_INVOKABLE void finishConfigureAnalogInputRemapping(unsigned deviceIndex, unsigned axisId); + Q_INVOKABLE bool setDeviceMode(unsigned index, unsigned mode, unsigned targedIndex, bool notify = true); + Q_INVOKABLE QString getDeviceModeErrorString(); + public slots: void enableDeviceOffsets(unsigned index, bool enable, bool notify = true); @@ -130,17 +158,17 @@ public slots: void setDriverFromHeadTranslationOffset(unsigned index, double yaw, double pitch, double roll, bool notify = true); void setDriverRotationOffset(unsigned index, double x, double y, double z, bool notify = true); void setDriverTranslationOffset(unsigned index, double yaw, double pitch, double roll, bool notify = true); - void setDeviceMode(unsigned index, unsigned mode, unsigned targedIndex, bool notify = true); void triggerHapticPulse(unsigned index); void setDeviceRenderModel(unsigned deviceIndex, unsigned renderModelIndex); - void addDeviceManipulationProfile(QString name, unsigned deviceIndex, bool includesDeviceOffsets, bool includesMotionCompensationSettings); + void addDeviceManipulationProfile(QString name, unsigned deviceIndex, bool includesDeviceOffsets, bool includesInputRemapping); void applyDeviceManipulationProfile(unsigned index, unsigned deviceIndex); void deleteDeviceManipulationProfile(unsigned index); void setMotionCompensationVelAccMode(unsigned mode, bool notify = true); void setMotionCompensationKalmanProcessNoise(double variance, bool notify = true); void setMotionCompensationKalmanObservationNoise(double variance, bool notify = true); + void setMotionCompensationMovingAverageWindow(unsigned window, bool notify = true); signals: void deviceCountChanged(unsigned deviceCount); @@ -150,6 +178,7 @@ public slots: void motionCompensationVelAccModeChanged(unsigned mode); void motionCompensationKalmanProcessNoiseChanged(double variance); void motionCompensationKalmanObservationNoiseChanged(double variance); + void motionCompensationMovingAverageWindowChanged(unsigned window); void configureDigitalInputRemappingFinished(); void configureAnalogInputRemappingFinished(); diff --git a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp index 8cfeb696..b79d1732 100644 --- a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp +++ b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp @@ -56,7 +56,7 @@ void DigitalInputRemappingController::startConfigureDoublePressBinding() { } QString DigitalInputRemappingController::getNormalBindingStatus() { - return parent->digitalBindingToString(m_currentRemapping.binding, m_currentRemapping.binding.binding.openvr.controllerId != m_currentDeviceId); + return parent->digitalBindingToString(m_currentRemapping.binding, m_currentRemapping.binding.data.openvr.controllerId != m_currentDeviceId); } bool DigitalInputRemappingController::isLongPressEnabled() { @@ -68,7 +68,7 @@ unsigned DigitalInputRemappingController::getLongPressThreshold() { } QString DigitalInputRemappingController::getLongPressBindingStatus() { - return parent->digitalBindingToString(m_currentRemapping.longPressBinding, m_currentRemapping.longPressBinding.binding.openvr.controllerId != m_currentDeviceId); + return parent->digitalBindingToString(m_currentRemapping.longPressBinding, m_currentRemapping.longPressBinding.data.openvr.controllerId != m_currentDeviceId); } bool DigitalInputRemappingController::isLongPressImmediateRelease() { @@ -84,7 +84,7 @@ unsigned DigitalInputRemappingController::getDoublePressThreshold() { } QString DigitalInputRemappingController::getDoublePressBindingStatus() { - return parent->digitalBindingToString(m_currentRemapping.doublePressBinding, m_currentRemapping.doublePressBinding.binding.openvr.controllerId != m_currentDeviceId); + return parent->digitalBindingToString(m_currentRemapping.doublePressBinding, m_currentRemapping.doublePressBinding.data.openvr.controllerId != m_currentDeviceId); } bool DigitalInputRemappingController::isDoublePressImmediateRelease() { @@ -101,7 +101,7 @@ int DigitalInputRemappingController::getBindingType() { int DigitalInputRemappingController::getBindingOpenVRControllerId() { if (m_currentBinding) { - return m_currentBinding->binding.openvr.controllerId; + return m_currentBinding->data.openvr.controllerId; } else { return -1; } @@ -109,7 +109,7 @@ int DigitalInputRemappingController::getBindingOpenVRControllerId() { int DigitalInputRemappingController::getBindingOpenVRButtonId() { if (m_currentBinding) { - return m_currentBinding->binding.openvr.buttonId; + return m_currentBinding->data.openvr.buttonId; } else { return -1; } @@ -157,7 +157,7 @@ int DigitalInputRemappingController::autoTriggerFrequency() { bool DigitalInputRemappingController::keyboardShiftEnabled() { if (m_currentBinding) { - return m_currentBinding->binding.keyboard.shiftPressed; + return m_currentBinding->data.keyboard.shiftPressed; } else { return false; } @@ -165,7 +165,7 @@ bool DigitalInputRemappingController::keyboardShiftEnabled() { bool DigitalInputRemappingController::keyboardCtrlEnabled() { if (m_currentBinding) { - return m_currentBinding->binding.keyboard.ctrlPressed; + return m_currentBinding->data.keyboard.ctrlPressed; } else { return false; } @@ -173,7 +173,7 @@ bool DigitalInputRemappingController::keyboardCtrlEnabled() { bool DigitalInputRemappingController::keyboardAltEnabled() { if (m_currentBinding) { - return m_currentBinding->binding.keyboard.altPressed; + return m_currentBinding->data.keyboard.altPressed; } else { return false; } @@ -181,7 +181,7 @@ bool DigitalInputRemappingController::keyboardAltEnabled() { unsigned DigitalInputRemappingController::keyboardKeyIndex() { if (m_currentBinding) { - return parent->keyboardVirtualCodeIndexFromId(m_currentBinding->binding.keyboard.keyCode); + return parent->keyboardVirtualCodeIndexFromId(m_currentBinding->data.keyboard.keyCode); } else { return false; } @@ -286,11 +286,11 @@ void DigitalInputRemappingController::finishConfigureBinding_Disabled() { void DigitalInputRemappingController::finishConfigureBinding_OpenVR(int controllerId, int ButtonId, bool toggleMode, int toggleThreshold, bool autoTrigger, int triggerFrequency) { m_currentBinding->type = vrinputemulator::DigitalBindingType::OpenVR; if (controllerId < 0) { - m_currentBinding->binding.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; + m_currentBinding->data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; } else { - m_currentBinding->binding.openvr.controllerId = controllerId; + m_currentBinding->data.openvr.controllerId = controllerId; } - m_currentBinding->binding.openvr.buttonId = ButtonId; + m_currentBinding->data.openvr.buttonId = ButtonId; m_currentBinding->toggleEnabled = toggleMode; m_currentBinding->toggleDelay = toggleThreshold; m_currentBinding->autoTriggerEnabled = autoTrigger; @@ -300,10 +300,10 @@ void DigitalInputRemappingController::finishConfigureBinding_OpenVR(int controll void DigitalInputRemappingController::finishConfigureBinding_keyboard(bool shiftPressed, bool ctrlPressed, bool altPressed, unsigned long keyIndex, bool toggleMode, int toggleThreshold, bool autoTrigger, int triggerFrequency) { m_currentBinding->type = vrinputemulator::DigitalBindingType::Keyboard; - m_currentBinding->binding.keyboard.shiftPressed = shiftPressed; - m_currentBinding->binding.keyboard.ctrlPressed = ctrlPressed; - m_currentBinding->binding.keyboard.altPressed = altPressed; - m_currentBinding->binding.keyboard.keyCode = parent->keyboardVirtualCodeIdFromIndex(keyIndex); + m_currentBinding->data.keyboard.shiftPressed = shiftPressed; + m_currentBinding->data.keyboard.ctrlPressed = ctrlPressed; + m_currentBinding->data.keyboard.altPressed = altPressed; + m_currentBinding->data.keyboard.keyCode = parent->keyboardVirtualCodeIdFromIndex(keyIndex); m_currentBinding->toggleEnabled = toggleMode; m_currentBinding->toggleDelay = toggleThreshold; m_currentBinding->autoTriggerEnabled = autoTrigger; diff --git a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp index 7b419e7f..20ed28d9 100644 --- a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp +++ b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp @@ -472,6 +472,12 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver resp.msg.dm_deviceInfo.deviceId = message.msg.vd_GenericDeviceIdMessage.deviceId; resp.msg.dm_deviceInfo.deviceMode = info->deviceMode(); resp.msg.dm_deviceInfo.deviceClass = info->deviceClass(); + auto ref = info->redirectRef(); + if (ref) { + resp.msg.dm_deviceInfo.refDeviceId = ref->openvrId(); + } else { + resp.msg.dm_deviceInfo.refDeviceId = (uint32_t)vr::k_unTrackedDeviceIndexInvalid; + } resp.msg.dm_deviceInfo.offsetsEnabled = info->areOffsetsEnabled(); resp.msg.dm_deviceInfo.redirectSuspended = info->redirectSuspended(); } @@ -713,8 +719,13 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver resp.status = ipc::ReplyStatus::NotFound; } else { OpenvrDeviceManipulationInfo* infoTarget = driver->deviceManipulation_getInfo(message.msg.dm_RedirectMode.targetId); - if (info && (info->deviceMode() == 0 || info->deviceMode() == 1) - && infoTarget && (infoTarget->deviceMode() == 0 || infoTarget->deviceMode() == 1)) { + if (info && infoTarget) { + if (info->deviceMode() > 0) { + info->setDefaultMode(); + } + if (infoTarget->deviceMode() > 0) { + infoTarget->setDefaultMode(); + } info->setRedirectMode(false, infoTarget); infoTarget->setRedirectMode(true, info); resp.status = ipc::ReplyStatus::Ok; @@ -744,8 +755,13 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver resp.status = ipc::ReplyStatus::NotFound; } else { OpenvrDeviceManipulationInfo* infoTarget = driver->deviceManipulation_getInfo(message.msg.dm_SwapMode.targetId); - if (info && (info->deviceMode() == 0 || info->deviceMode() == 1) - && infoTarget && (infoTarget->deviceMode() == 0 || infoTarget->deviceMode() == 1)) { + if (info && infoTarget) { + if (info->deviceMode() > 0) { + info->setDefaultMode(); + } + if (infoTarget->deviceMode() > 0) { + infoTarget->setDefaultMode(); + } info->setSwapMode(infoTarget); infoTarget->setSwapMode(info); resp.status = ipc::ReplyStatus::Ok; @@ -858,6 +874,9 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver if (message.msg.dm_SetMotionCompensationProperties.kalmanFilterObservationNoiseValid) { serverDriver->setMotionCompensationKalmanObservationVariance(message.msg.dm_SetMotionCompensationProperties.kalmanFilterObservationNoise); } + if (message.msg.dm_SetMotionCompensationProperties.movingAverageWindowValid) { + serverDriver->setMotionCompensationMovingAverageWindow(message.msg.dm_SetMotionCompensationProperties.movingAverageWindow); + } resp.status = ipc::ReplyStatus::Ok; } else { resp.status = ipc::ReplyStatus::UnknownError; diff --git a/driver_vrinputemulator/src/driver_deviceinfo.cpp b/driver_vrinputemulator/src/driver_deviceinfo.cpp index 27ce3c69..9c64a866 100644 --- a/driver_vrinputemulator/src/driver_deviceinfo.cpp +++ b/driver_vrinputemulator/src/driver_deviceinfo.cpp @@ -17,8 +17,14 @@ namespace driver { void OpenvrDeviceManipulationInfo::setDigitalInputRemapping(uint32_t buttonId, const DigitalInputRemapping& remapping) { - auto& entry = m_digitalInputRemapping[buttonId]; - entry.remapping = remapping; + if (remapping.valid) { + m_digitalInputRemapping[buttonId].remapping = remapping; + } else { + auto it = m_digitalInputRemapping.find(buttonId); + if (it != m_digitalInputRemapping.end()) { + m_digitalInputRemapping.erase(it); + } + } } @@ -33,8 +39,14 @@ DigitalInputRemapping OpenvrDeviceManipulationInfo::getDigitalInputRemapping(uin void OpenvrDeviceManipulationInfo::setAnalogInputRemapping(uint32_t axisId, const AnalogInputRemapping& remapping) { - auto& entry = m_analogInputRemapping[axisId]; - entry.remapping = remapping; + if (remapping.valid) { + m_analogInputRemapping[axisId].remapping = remapping; + } else { + auto it = m_analogInputRemapping.find(axisId); + if (it != m_analogInputRemapping.end()) { + m_analogInputRemapping.erase(it); + } + } } @@ -65,6 +77,7 @@ void OpenvrDeviceManipulationInfo::handleNewDevicePose(vr::IVRServerDriverHost* auto serverDriver = CServerDriver::getInstance(); if (serverDriver) { if (pose.poseIsValid && pose.result == vr::TrackingResult_Running_OK) { + serverDriver->_setMotionCompensationStatus(MotionCompensationStatus::Running); if (!serverDriver->_isMotionCompensationZeroPoseValid()) { serverDriver->_setMotionCompensationZeroPose(pose); serverDriver->sendReplySetMotionCompensationMode(true); @@ -73,7 +86,10 @@ void OpenvrDeviceManipulationInfo::handleNewDevicePose(vr::IVRServerDriverHost* } } else { if (!serverDriver->_isMotionCompensationZeroPoseValid()) { + setDefaultMode(); serverDriver->sendReplySetMotionCompensationMode(false); + } else { + serverDriver->_setMotionCompensationStatus(MotionCompensationStatus::MotionRefNotTracking); } } } @@ -446,8 +462,8 @@ void OpenvrDeviceManipulationInfo::sendDigitalBinding(vrinputemulator::DigitalBi if (m_deviceMode == 1 || (m_deviceMode == 3 && !m_redirectSuspended) /*|| m_deviceMode == 5*/) { //nop } else { - vr::EVRButtonId button = (vr::EVRButtonId)binding.binding.openvr.buttonId; - uint32_t deviceId = binding.binding.openvr.controllerId; + vr::EVRButtonId button = (vr::EVRButtonId)binding.data.openvr.buttonId; + uint32_t deviceId = binding.data.openvr.controllerId; if (deviceId >= 999) { deviceId = m_openvrId; } @@ -458,8 +474,8 @@ void OpenvrDeviceManipulationInfo::sendDigitalBinding(vrinputemulator::DigitalBi if (m_deviceMode == 1 || (m_deviceMode == 3 && !m_redirectSuspended) /*|| m_deviceMode == 5*/) { //nop } else { - sendKeyboardEvent(eventType, binding.binding.keyboard.shiftPressed, binding.binding.keyboard.ctrlPressed, - binding.binding.keyboard.altPressed, (WORD)binding.binding.keyboard.keyCode, bindingInfo); + sendKeyboardEvent(eventType, binding.data.keyboard.shiftPressed, binding.data.keyboard.ctrlPressed, + binding.data.keyboard.altPressed, (WORD)binding.data.keyboard.keyCode, bindingInfo); } } break; case DigitalBindingType::SuspendRedirectMode: { @@ -758,6 +774,7 @@ int OpenvrDeviceManipulationInfo::setMotionCompensationMode() { if (res == 0 && serverDriver) { serverDriver->enableMotionCompensation(true); serverDriver->setMotionCompensationRefDevice(this); + serverDriver->_setMotionCompensationStatus(MotionCompensationStatus::WaitingForZeroRef); m_deviceMode = 5; } return 0; diff --git a/driver_vrinputemulator/src/driver_server.cpp b/driver_vrinputemulator/src/driver_server.cpp index 8ea75367..322083fa 100644 --- a/driver_vrinputemulator/src/driver_server.cpp +++ b/driver_vrinputemulator/src/driver_server.cpp @@ -666,6 +666,7 @@ void CServerDriver::setMotionCompensationVelAccMode(MotionCompensationVelAccMode d.second->setLastPoseTime(-1); d.second->kalmanFilter().setProcessNoise(m_motionCompensationKalmanProcessVariance); d.second->kalmanFilter().setObservationNoise(m_motionCompensationKalmanObservationVariance); + d.second->velMovingAverage().resize(m_motionCompensationMovingAverageWindow); } _motionCompensationVelAccMode = velAccMode; } @@ -689,6 +690,15 @@ void CServerDriver::setMotionCompensationKalmanObservationVariance(double varian } } +void CServerDriver::setMotionCompensationMovingAverageWindow(unsigned window) { + m_motionCompensationMovingAverageWindow = window; + if (_motionCompensationVelAccMode == MotionCompensationVelAccMode::LinearApproximation) { + for (auto d : _openvrDeviceInfos) { + d.second->velMovingAverage().resize(window); + } + } +} + void CServerDriver::_disableMotionCompensationOnAllDevices() { for (auto d : _openvrDeviceInfos) { if (d.second->deviceMode() == 5) { @@ -748,20 +758,20 @@ bool CServerDriver::_applyMotionCompensation(vr::DriverPose_t& pose, OpenvrDevic // Velocity / Acceleration Compensation vr::HmdVector3d_t compensatedPoseWorldVel; bool compensatedPoseWorldVelValid = false; + bool setVelToZero = false; + bool setAccToZero = false; + bool setAngVelToZero = false; + bool setAngAccToZero = false; + + auto now = std::chrono::duration_cast (std::chrono::system_clock::now().time_since_epoch()).count(); if (_motionCompensationVelAccMode == MotionCompensationVelAccMode::SetZero) { - pose.vecVelocity[0] = 0.0; - pose.vecVelocity[1] = 0.0; - pose.vecVelocity[2] = 0.0; - pose.vecAcceleration[0] = 0.0; - pose.vecAcceleration[1] = 0.0; - pose.vecAcceleration[2] = 0.0; - pose.vecAngularVelocity[0] = 0.0; - pose.vecAngularVelocity[1] = 0.0; - pose.vecAngularVelocity[2] = 0.0; - pose.vecAngularAcceleration[0] = 0.0; - pose.vecAngularAcceleration[1] = 0.0; - pose.vecAngularAcceleration[2] = 0.0; + setVelToZero = true; + setAccToZero = true; + setAngVelToZero = true; + setAngAccToZero = true; + } else if (_motionCompensationVelAccMode == MotionCompensationVelAccMode::SubstractMotionRef) { + // We translate the motion ref vel/acc values into driver space and directly substract them if (_motionCompensationRefVelAccValid) { auto tmpRot = pose.qWorldFromDriverRotation * pose.qRotation; auto tmpRotInv = vrmath::quaternionConjugate(tmpRot); @@ -782,19 +792,36 @@ bool CServerDriver::_applyMotionCompensation(vr::DriverPose_t& pose, OpenvrDevic pose.vecAngularAcceleration[1] -= tmpRotAcc.v[1]; pose.vecAngularAcceleration[2] -= tmpRotAcc.v[2]; } + } else if (_motionCompensationVelAccMode == MotionCompensationVelAccMode::KalmanFilter) { - auto now = std::chrono::duration_cast (std::chrono::system_clock::now().time_since_epoch()).count(); + // The Kalman filter uses app space coordinates auto lastTime = deviceInfo->getLastPoseTime(); if (lastTime >= 0.0) { double tdiff = ((double)(now - lastTime) / 1.0E6) + (pose.poseTimeOffset - deviceInfo->getLastPoseTimeOffset()); - if (tdiff > 0.0001) { + if (tdiff < 0.0001) { // Sometimes we get a very small or even negative time difference between current and last pose + // In this case we just take the velocities and accelerations from last time + auto& lastPose = deviceInfo->lastDriverPose(); + pose.vecVelocity[0] = lastPose.vecVelocity[0]; + pose.vecVelocity[1] = lastPose.vecVelocity[1]; + pose.vecVelocity[2] = lastPose.vecVelocity[2]; + pose.vecAcceleration[0] = lastPose.vecAcceleration[0]; + pose.vecAcceleration[1] = lastPose.vecAcceleration[1]; + pose.vecAcceleration[2] = lastPose.vecAcceleration[2]; + pose.vecAngularVelocity[0] = lastPose.vecAngularVelocity[0]; + pose.vecAngularVelocity[1] = lastPose.vecAngularVelocity[1]; + pose.vecAngularVelocity[2] = lastPose.vecAngularVelocity[2]; + pose.vecAngularAcceleration[0] = lastPose.vecAngularAcceleration[0]; + pose.vecAngularAcceleration[1] = lastPose.vecAngularAcceleration[1]; + pose.vecAngularAcceleration[2] = lastPose.vecAngularAcceleration[2]; + } else { deviceInfo->kalmanFilter().update(compensatedPoseWorldPos, tdiff); - compensatedPoseWorldPos = deviceInfo->kalmanFilter().getUpdatedPositionEstimate(); + //compensatedPoseWorldPos = deviceInfo->kalmanFilter().getUpdatedPositionEstimate(); // Better to use the original values compensatedPoseWorldVel = deviceInfo->kalmanFilter().getUpdatedVelocityEstimate(); compensatedPoseWorldVelValid = true; - } else { - // Ignore pose when timediff is very small or even negative - return false; + // Kalman filter only gives us velocity, so set the rest to zero + setAccToZero = true; + setAngVelToZero = true; + setAngAccToZero = true; } } else { deviceInfo->kalmanFilter().init( @@ -804,10 +831,66 @@ bool CServerDriver::_applyMotionCompensation(vr::DriverPose_t& pose, OpenvrDevic ); deviceInfo->kalmanFilter().setProcessNoise(m_motionCompensationKalmanProcessVariance); deviceInfo->kalmanFilter().setObservationNoise(m_motionCompensationKalmanObservationVariance); + // Kalman Filter is not ready yet, so set everything to zero + setVelToZero = true; + setAccToZero = true; + setAngVelToZero = true; + setAngAccToZero = true; + } + + } else if (_motionCompensationVelAccMode == MotionCompensationVelAccMode::LinearApproximation) { + // Linear approximation uses driver space coordinates + if (deviceInfo->lastDriverPoseValid()) { + auto& lastPose = deviceInfo->lastDriverPose(); + double tdiff = ((double)(now - deviceInfo->getLastPoseTime()) / 1.0E6) + (pose.poseTimeOffset - lastPose.poseTimeOffset); + if (tdiff < 0.0001) { // Sometimes we get a very small or even negative time difference between current and last pose + // In this case we just take the velocities and accelerations from last time + pose.vecVelocity[0] = lastPose.vecVelocity[0]; + pose.vecVelocity[1] = lastPose.vecVelocity[1]; + pose.vecVelocity[2] = lastPose.vecVelocity[2]; + pose.vecAcceleration[0] = lastPose.vecAcceleration[0]; + pose.vecAcceleration[1] = lastPose.vecAcceleration[1]; + pose.vecAcceleration[2] = lastPose.vecAcceleration[2]; + pose.vecAngularVelocity[0] = lastPose.vecAngularVelocity[0]; + pose.vecAngularVelocity[1] = lastPose.vecAngularVelocity[1]; + pose.vecAngularVelocity[2] = lastPose.vecAngularVelocity[2]; + pose.vecAngularAcceleration[0] = lastPose.vecAngularAcceleration[0]; + pose.vecAngularAcceleration[1] = lastPose.vecAngularAcceleration[1]; + pose.vecAngularAcceleration[2] = lastPose.vecAngularAcceleration[2]; + } else { + vr::HmdVector3d_t p; + p.v[0] = (pose.vecPosition[0] - lastPose.vecPosition[0]) / tdiff; + if (p.v[0] > -0.01 && p.v[0] < 0.01) { // Set very small values to zero to avoid jitter + p.v[0] = 0.0; + } + p.v[1] = (pose.vecPosition[1] - lastPose.vecPosition[1]) / tdiff; + if (p.v[1] > -0.01 && p.v[1] < 0.01) { + p.v[1] = 0.0; + } + p.v[2] = (pose.vecPosition[2] - lastPose.vecPosition[2]) / tdiff; + if (p.v[2] > -0.01 && p.v[2] < 0.01) { + p.v[2] = 0.0; + } + deviceInfo->velMovingAverage().push(p); + auto vel = deviceInfo->velMovingAverage().average(); + pose.vecVelocity[0] = vel.v[0]; + pose.vecVelocity[1] = vel.v[1]; + pose.vecVelocity[2] = vel.v[2]; + // Predicting acceleration values leads to a very jittery experience. + // Also, the lighthouse driver does not send acceleration values any way, so why care? + setAccToZero = true; + setAngVelToZero = true; + setAngAccToZero = true; + } + } else { + // Linear approximation is not ready yet, so set everything to zero + setVelToZero = true; + setAccToZero = true; + setAngVelToZero = true; + setAngAccToZero = true; } - deviceInfo->setLastPoseTime(now); - deviceInfo->setLastPoseTimeOffset(pose.poseTimeOffset); } + deviceInfo->setLastDriverPose(pose, now); // convert back to driver space pose.qRotation = pose.qWorldFromDriverRotation * compensatedPoseWorldRot; @@ -820,6 +903,25 @@ bool CServerDriver::_applyMotionCompensation(vr::DriverPose_t& pose, OpenvrDevic pose.vecVelocity[0] = adjPoseDriverVel.v[0]; pose.vecVelocity[1] = adjPoseDriverVel.v[1]; pose.vecVelocity[2] = adjPoseDriverVel.v[2]; + } else if (setVelToZero) { + pose.vecVelocity[0] = 0.0; + pose.vecVelocity[1] = 0.0; + pose.vecVelocity[2] = 0.0; + } + if (setAccToZero) { + pose.vecAcceleration[0] = 0.0; + pose.vecAcceleration[1] = 0.0; + pose.vecAcceleration[2] = 0.0; + } + if (setAngVelToZero) { + pose.vecAngularVelocity[0] = 0.0; + pose.vecAngularVelocity[1] = 0.0; + pose.vecAngularVelocity[2] = 0.0; + } + if (setAngAccToZero) { + pose.vecAngularAcceleration[0] = 0.0; + pose.vecAngularAcceleration[1] = 0.0; + pose.vecAngularAcceleration[2] = 0.0; } return true; diff --git a/driver_vrinputemulator/src/driver_vrinputemulator.h b/driver_vrinputemulator/src/driver_vrinputemulator.h index d7578850..11885a8b 100644 --- a/driver_vrinputemulator/src/driver_vrinputemulator.h +++ b/driver_vrinputemulator/src/driver_vrinputemulator.h @@ -12,6 +12,7 @@ #include #include "utils/DevicePropertyValueVisitor.h" #include "com/shm/driver_ipc_shm.h" +#include // driver namespace @@ -102,71 +103,61 @@ struct DeviceInputMapping { }; - -template -class RingBuffer { +class MovingAverageRingBuffer { public: - RingBuffer() noexcept : _buffer(nullptr), _bufferSize(0) {} - RingBuffer(unsigned size) noexcept : _buffer(new T[size]), _bufferSize(size) {} - ~RingBuffer() noexcept { + MovingAverageRingBuffer() noexcept : _buffer(new vr::HmdVector3d_t[1]), _bufferSize(1) {} + MovingAverageRingBuffer(unsigned size) noexcept : _buffer(new vr::HmdVector3d_t[size > 0 ? size : 1]), _bufferSize(size > 0 ? size : 1) {} + ~MovingAverageRingBuffer() noexcept { std::lock_guard lock(_mutex); if (_buffer) { delete[] _buffer; _buffer = nullptr; - _bufferSize = 0; - _dataStart = 0; - _dataSize = 0; + _bufferSize = _dataStart = _dataSize = 0; } } void resize(unsigned size) noexcept { std::lock_guard lock(_mutex); + if (size == 0) { + size = 1; + } if (_buffer) { delete[] _buffer; } - _buffer = new T[size]; + _buffer = new vr::HmdVector3d_t[size]; _bufferSize = size; - _dataStart = 0; - _dataSize = 0; + _dataSize = _dataStart = 0; } unsigned bufferSize() noexcept { return _bufferSize; } unsigned dataSize() noexcept { return _dataSize; } - const T& operator[](unsigned index) noexcept { - if (index < _dataSize) { - return _buffer[(_dataStart + _index) % _bufferSize]; - } else { - return T(); - } - } - - void push(const T& value) { + void push(const vr::HmdVector3d_t& value) { if (_dataSize < _bufferSize) { _buffer[(_dataStart + _dataSize) % _bufferSize] = value; _dataSize++; } else { _buffer[_dataStart] = value; - dataStart++; + _dataStart = (_dataStart+1)%_bufferSize; } } - const T& average() { + vr::HmdVector3d_t average() { if (_dataSize > 0) { - T sum = T(); + vr::HmdVector3d_t sum = {0.0, 0.0, 0.0}; for (unsigned i = 0; i < _dataSize; i++) { - sum += _buffer[(_dataStart + i) % _bufferSize]; + sum = sum + _buffer[(_dataStart + i) % _bufferSize]; } return sum / _dataSize; } else { - return T(); + return vr::HmdVector3d_t(); } } private: std::mutex _mutex; - T* _buffer; + vr::HmdVector3d_t* _buffer; unsigned _bufferSize; unsigned _dataStart = 0; unsigned _dataSize = 0; @@ -232,7 +223,7 @@ class OpenvrDeviceManipulationInfo { long long m_lastPoseTime = -1; bool m_lastPoseValid = false; vr::DriverPose_t m_lastPose; - RingBuffer m_velMovingAverageBuffer; + MovingAverageRingBuffer m_velMovingAverageBuffer; double m_lastPoseTimeOffset = 0.0; PosKalmanFilter m_kalmanFilter; @@ -303,6 +294,7 @@ class OpenvrDeviceManipulationInfo { bool triggerHapticPulse(uint32_t unAxisId, uint16_t usPulseDurationMicroseconds, bool directMode = false); PosKalmanFilter& kalmanFilter() { return m_kalmanFilter; } + MovingAverageRingBuffer& velMovingAverage() { return m_velMovingAverageBuffer; } long long getLastPoseTime() { return m_lastPoseTime; } void setLastPoseTime(long long time) { m_lastPoseTime = time; } double getLastPoseTimeOffset() { return m_lastPoseTimeOffset; } @@ -409,6 +401,8 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { /* Motion Compensation API */ void enableMotionCompensation(bool enable); + MotionCompensationStatus motionCompensationStatus() { return _motionCompensationStatus; } + void _setMotionCompensationStatus(MotionCompensationStatus status) { _motionCompensationStatus = status; } void setMotionCompensationRefDevice(OpenvrDeviceManipulationInfo* device); OpenvrDeviceManipulationInfo* getMotionCompensationRefDevice(); void setMotionCompensationVelAccMode(MotionCompensationVelAccMode velAccMode); @@ -416,6 +410,8 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { void setMotionCompensationKalmanProcessVariance(double variance); double motionCompensationKalmanObservationVariance() { return m_motionCompensationKalmanObservationVariance; } void setMotionCompensationKalmanObservationVariance(double variance); + double motionCompensationMovingAverageWindow() { return m_motionCompensationMovingAverageWindow; } + void setMotionCompensationMovingAverageWindow(unsigned window); void _disableMotionCompensationOnAllDevices(); bool _isMotionCompensationZeroPoseValid(); void _setMotionCompensationZeroPose(const vr::DriverPose_t& pose); @@ -451,6 +447,7 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { MotionCompensationVelAccMode _motionCompensationVelAccMode = MotionCompensationVelAccMode::Disabled; double m_motionCompensationKalmanProcessVariance = 0.1; double m_motionCompensationKalmanObservationVariance = 0.1; + unsigned m_motionCompensationMovingAverageWindow = 3; bool _motionCompensationZeroPoseValid = false; vr::HmdVector3d_t _motionCompensationZeroPos; diff --git a/lib_vrinputemulator/include/ipc_protocol.h b/lib_vrinputemulator/include/ipc_protocol.h index 1fcdba64..78954fdf 100644 --- a/lib_vrinputemulator/include/ipc_protocol.h +++ b/lib_vrinputemulator/include/ipc_protocol.h @@ -4,7 +4,7 @@ #include -#define IPC_PROTOCOL_VERSION 1 +#define IPC_PROTOCOL_VERSION 2 namespace vrinputemulator { namespace ipc { @@ -294,6 +294,8 @@ struct Request_DeviceManipulation_SetMotionCompensationProperties { double kalmanFilterProcessNoise; bool kalmanFilterObservationNoiseValid; double kalmanFilterObservationNoise; + bool movingAverageWindowValid; + unsigned movingAverageWindow; }; struct Request_InputRemapping_SetDigitalRemapping { @@ -411,6 +413,7 @@ struct Reply_DeviceManipulation_GetDeviceInfo { uint32_t deviceId; vr::ETrackedDeviceClass deviceClass; int deviceMode; + uint32_t refDeviceId; bool offsetsEnabled; bool redirectSuspended; }; diff --git a/lib_vrinputemulator/include/vrinputemulator.h b/lib_vrinputemulator/include/vrinputemulator.h index 436a4cec..977536e2 100644 --- a/lib_vrinputemulator/include/vrinputemulator.h +++ b/lib_vrinputemulator/include/vrinputemulator.h @@ -88,7 +88,10 @@ namespace vr { namespace vrinputemulator { class vrinputemulator_exception : public std::runtime_error { +public: + const int errorcode = 0; using std::runtime_error::runtime_error; + vrinputemulator_exception(const std::string& msg, int code) : std::runtime_error(msg), errorcode(code) {} }; class vrinputemulator_connectionerror : public vrinputemulator_exception { @@ -186,6 +189,7 @@ class VRInputEmulator { void setMotionVelAccCompensationMode(MotionCompensationVelAccMode velAccMode, bool modal = true); void setMotionCompensationKalmanProcessNoise(double variance, bool modal = true); void setMotionCompensationKalmanObservationNoise(double variance, bool modal = true); + void setMotionCompensationMovingAverageWindow(unsigned window, bool modal = true); void triggerHapticPulse(uint32_t deviceId, uint32_t axisId, uint16_t durationMicroseconds, bool directMode, bool modal = true); diff --git a/lib_vrinputemulator/include/vrinputemulator_types.h b/lib_vrinputemulator/include/vrinputemulator_types.h index a6950600..428b8bb5 100644 --- a/lib_vrinputemulator/include/vrinputemulator_types.h +++ b/lib_vrinputemulator/include/vrinputemulator_types.h @@ -56,6 +56,7 @@ namespace vrinputemulator { uint32_t deviceId; vr::ETrackedDeviceClass deviceClass; int deviceMode; + uint32_t refDeviceId; bool offsetsEnabled; bool redirectSuspended; }; @@ -96,7 +97,7 @@ namespace vrinputemulator { } keyboard; BindingUnion() {} - } binding; + } data; bool toggleEnabled; uint32_t toggleDelay; @@ -115,7 +116,7 @@ namespace vrinputemulator { bool touchAsClick = false; bool longPressEnabled = false; - uint32_t longPressThreshold = 1000; + uint32_t longPressThreshold = 1000u; DigitalBinding longPressBinding; bool longPressImmediateRelease = false; diff --git a/lib_vrinputemulator/src/vrinputemulator.cpp b/lib_vrinputemulator/src/vrinputemulator.cpp index 2ab5673d..f8d6852d 100644 --- a/lib_vrinputemulator/src/vrinputemulator.cpp +++ b/lib_vrinputemulator/src/vrinputemulator.cpp @@ -1288,6 +1288,7 @@ void VRInputEmulator::getDeviceInfo(uint32_t deviceId, DeviceInfo & info) { info.deviceId = resp.msg.dm_deviceInfo.deviceId; info.deviceClass = resp.msg.dm_deviceInfo.deviceClass; info.deviceMode = resp.msg.dm_deviceInfo.deviceMode; + info.refDeviceId = resp.msg.dm_deviceInfo.refDeviceId; info.offsetsEnabled = resp.msg.dm_deviceInfo.offsetsEnabled; info.redirectSuspended = resp.msg.dm_deviceInfo.redirectSuspended; } else if (resp.status == ipc::ReplyStatus::InvalidId) { @@ -1459,13 +1460,13 @@ void VRInputEmulator::setDeviceSwapMode(uint32_t deviceId, uint32_t target, bool ss << "Error while setting swap mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1477,6 +1478,7 @@ void VRInputEmulator::setDeviceSwapMode(uint32_t deviceId, uint32_t target, bool void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, MotionCompensationVelAccMode velAccMode, bool modal) { + bool retval = false; if (_ipcServerQueue) { ipc::Request message(ipc::RequestType::DeviceManipulation_MotionCompensationMode); memset(&message.msg, 0, sizeof(message.msg)); @@ -1503,13 +1505,13 @@ void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, MotionC ss << "Error while setting motion compensation mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1549,13 +1551,13 @@ void VRInputEmulator::setMotionVelAccCompensationMode(MotionCompensationVelAccMo ss << "Error while setting motion compensation properties: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1594,13 +1596,13 @@ void VRInputEmulator::setMotionCompensationKalmanProcessNoise(double variance, b ss << "Error while setting motion compensation properties: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1639,13 +1641,59 @@ void VRInputEmulator::setMotionCompensationKalmanObservationNoise(double varianc ss << "Error while setting motion compensation properties: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); + } + } else { + _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); + } + } else { + throw vrinputemulator_connectionerror("No active connection."); + } +} + +void VRInputEmulator::setMotionCompensationMovingAverageWindow(unsigned window, bool modal) { + if (_ipcServerQueue) { + ipc::Request message(ipc::RequestType::DeviceManipulation_SetMotionCompensationProperties); + memset(&message.msg, 0, sizeof(message.msg)); + message.msg.dm_SetMotionCompensationProperties.clientId = m_clientId; + message.msg.dm_SetMotionCompensationProperties.messageId = 0; + message.msg.dm_SetMotionCompensationProperties.velAccCompensationModeValid = false; + message.msg.dm_SetMotionCompensationProperties.kalmanFilterProcessNoiseValid = false; + message.msg.dm_SetMotionCompensationProperties.kalmanFilterObservationNoiseValid = false; + message.msg.dm_SetMotionCompensationProperties.movingAverageWindowValid = true; + message.msg.dm_SetMotionCompensationProperties.movingAverageWindow = window; + if (modal) { + uint32_t messageId = _ipcRandomDist(_ipcRandomDevice); + message.msg.dm_SetMotionCompensationProperties.messageId = messageId; + std::promise respPromise; + auto respFuture = respPromise.get_future(); + { + std::lock_guard lock(_mutex); + _ipcPromiseMap.insert({ messageId, std::move(respPromise) }); + } + _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); + auto resp = respFuture.get(); + { + std::lock_guard lock(_mutex); + _ipcPromiseMap.erase(messageId); + } + std::stringstream ss; + ss << "Error while setting motion compensation properties: "; + if (resp.status == ipc::ReplyStatus::InvalidId) { + ss << "Invalid device id"; + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); + } else if (resp.status == ipc::ReplyStatus::NotFound) { + ss << "Device not found"; + throw vrinputemulator_notfound(ss.str(), (int)resp.status); + } else if (resp.status != ipc::ReplyStatus::Ok) { + ss << "Error code " << (int)resp.status; + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1685,13 +1733,13 @@ void VRInputEmulator::triggerHapticPulse(uint32_t deviceId, uint32_t axisId, uin ss << "Error while enabling device offsets: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1729,13 +1777,13 @@ void VRInputEmulator::setDigitalInputRemapping(uint32_t deviceId, uint32_t butto ss << "Error while setting digital input remapping: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1768,7 +1816,7 @@ DigitalInputRemapping VRInputEmulator::getDigitalInputRemapping(uint32_t deviceI if (resp.status != ipc::ReplyStatus::Ok) { std::stringstream ss; ss << "Error while getting digital input remapping: Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } return resp.msg.ir_getDigitalRemapping.remapData; } else { @@ -1804,13 +1852,13 @@ void VRInputEmulator::setAnalogInputRemapping(uint32_t deviceId, uint32_t axisId ss << "Error while setting analog input remapping: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; - throw vrinputemulator_invalidid(ss.str()); + throw vrinputemulator_invalidid(ss.str(), (int)resp.status); } else if (resp.status == ipc::ReplyStatus::NotFound) { ss << "Device not found"; - throw vrinputemulator_notfound(ss.str()); + throw vrinputemulator_notfound(ss.str(), (int)resp.status); } else if (resp.status != ipc::ReplyStatus::Ok) { ss << "Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } } else { _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); @@ -1843,7 +1891,7 @@ AnalogInputRemapping VRInputEmulator::getAnalogInputRemapping(uint32_t deviceId, if (resp.status != ipc::ReplyStatus::Ok) { std::stringstream ss; ss << "Error while getting analog input remapping: Error code " << (int)resp.status; - throw vrinputemulator_exception(ss.str()); + throw vrinputemulator_exception(ss.str(), (int)resp.status); } return resp.msg.ir_getAnalogRemapping.remapData; } else {