diff --git a/Readme.md b/Readme.md index b74090e6..5fafe248 100644 --- a/Readme.md +++ b/Readme.md @@ -117,6 +117,7 @@ Allows to configure a digital button binding. Available binding types are: - **OpenVR**: Remap to another OpenVR controller button. - **Keyboard**: Remap to a keyboard key. - **Suspend Redirect Mode**: Allows to temporarily suspend controller redirect mode. +- **Toggle Touchpad Emulation**: Enables/disables the touchpad emulation mode for all analog axes. ##### OpenVR @@ -145,6 +146,16 @@ Allows to configure an analog input. Available binding types are: - **Disabled**: Disables the analog input. - **OpenVR**: Remap to another OpenVR controller axis. +The touchpad emulation mode for this analog axis can also be configured here. Touchpad emulation mode tries to emulate the behaviour of a touchpad with a joystick. The primary purpose of this option is to make Fallout 4 VR playable with Oculus Rift controllers, but it can also be used for other games. + +Available touch emulation modes: + +- **Position Based**: This modes assumes that the joystick can only move further away from the center position. All newer positions smaller can the last known position are ignored. The saved highest position is reset when the center position has been reached. The idea is to ignore input events caused by the joystick snapping back to center position. As soon as the center position is reached it is immediately send to the application in a position update. This helps with movement controls as otherwise any movement in an application is not reset when the joystick is let go by the user. + +- **Position Based (Deferred Zero Update)**: This modes works exactly the same as the mode above with one small difference. The position update when the center position is reached is not immediately send but only when the joystick starts moving again. This helps with applications that have problems with the above mode but messes up movement controls. + +**Button Press Deadzone Fix**: Some applications ignore touchpad/joystick clicks when the position is exactly the center position. This fix can help in this case by slightly offsetting the position when a click has been registered exactly at center position. + ##### OpenVR ![Analog Binding Page - OpenVR](docs/screenshots/AnalogBindingOpenVRPage.png) diff --git a/client_overlay/Debug/moc_DigitalInputRemappingController.cpp b/client_overlay/Debug/moc_DigitalInputRemappingController.cpp deleted file mode 100644 index 3119b4d7..00000000 --- a/client_overlay/Debug/moc_DigitalInputRemappingController.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/**************************************************************************** -** Meta object code from reading C++ file 'DigitalInputRemappingController.h' -** -** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.1) -** -** WARNING! All changes made in this file will be lost! -*****************************************************************************/ - -#include "../src/tabcontrollers/DigitalInputRemappingController.h" -#include -#include -#if !defined(Q_MOC_OUTPUT_REVISION) -#error "The header file 'DigitalInputRemappingController.h' doesn't include ." -#elif Q_MOC_OUTPUT_REVISION != 67 -#error "This file was generated using the moc from 5.9.1. It" -#error "cannot be used with the include files from this version of Qt." -#error "(The moc has changed too much.)" -#endif - -QT_BEGIN_MOC_NAMESPACE -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED -struct qt_meta_stringdata_inputemulator__DigitalInputRemappingController_t { - QByteArrayData data[46]; - char stringdata0[981]; -}; -#define QT_MOC_LITERAL(idx, ofs, len) \ - Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ - qptrdiff(offsetof(qt_meta_stringdata_inputemulator__DigitalInputRemappingController_t, stringdata0) + ofs \ - - idx * sizeof(QByteArrayData)) \ - ) -static const qt_meta_stringdata_inputemulator__DigitalInputRemappingController_t qt_meta_stringdata_inputemulator__DigitalInputRemappingController = { - { -QT_MOC_LITERAL(0, 0, 46), // "inputemulator::DigitalInputRe..." -QT_MOC_LITERAL(1, 47, 31), // "configureDigitalBindingFinished" -QT_MOC_LITERAL(2, 79, 0), // "" -QT_MOC_LITERAL(3, 80, 31), // "finishConfigureBinding_Original" -QT_MOC_LITERAL(4, 112, 31), // "finishConfigureBinding_Disabled" -QT_MOC_LITERAL(5, 144, 29), // "finishConfigureBinding_OpenVR" -QT_MOC_LITERAL(6, 174, 12), // "controllerId" -QT_MOC_LITERAL(7, 187, 8), // "ButtonId" -QT_MOC_LITERAL(8, 196, 10), // "toggleMode" -QT_MOC_LITERAL(9, 207, 15), // "toggleThreshold" -QT_MOC_LITERAL(10, 223, 11), // "autoTrigger" -QT_MOC_LITERAL(11, 235, 16), // "triggerFrequency" -QT_MOC_LITERAL(12, 252, 31), // "finishConfigureBinding_keyboard" -QT_MOC_LITERAL(13, 284, 12), // "shiftPressed" -QT_MOC_LITERAL(14, 297, 11), // "ctrlPressed" -QT_MOC_LITERAL(15, 309, 10), // "altPressed" -QT_MOC_LITERAL(16, 320, 8), // "keyIndex" -QT_MOC_LITERAL(17, 329, 42), // "finishConfigureBinding_suspen..." -QT_MOC_LITERAL(18, 372, 27), // "startConfigureNormalBinding" -QT_MOC_LITERAL(19, 400, 30), // "startConfigureLongPressBinding" -QT_MOC_LITERAL(20, 431, 32), // "startConfigureDoublePressBinding" -QT_MOC_LITERAL(21, 464, 22), // "getNormalBindingStatus" -QT_MOC_LITERAL(22, 487, 18), // "isLongPressEnabled" -QT_MOC_LITERAL(23, 506, 21), // "getLongPressThreshold" -QT_MOC_LITERAL(24, 528, 25), // "getLongPressBindingStatus" -QT_MOC_LITERAL(25, 554, 27), // "isLongPressImmediateRelease" -QT_MOC_LITERAL(26, 582, 20), // "isDoublePressEnabled" -QT_MOC_LITERAL(27, 603, 23), // "getDoublePressThreshold" -QT_MOC_LITERAL(28, 627, 27), // "getDoublePressBindingStatus" -QT_MOC_LITERAL(29, 655, 29), // "isDoublePressImmediateRelease" -QT_MOC_LITERAL(30, 685, 17), // "getButtonMaxCount" -QT_MOC_LITERAL(31, 703, 13), // "getButtonName" -QT_MOC_LITERAL(32, 717, 2), // "id" -QT_MOC_LITERAL(33, 720, 12), // "withDefaults" -QT_MOC_LITERAL(34, 733, 14), // "getBindingType" -QT_MOC_LITERAL(35, 748, 28), // "getBindingOpenVRControllerId" -QT_MOC_LITERAL(36, 777, 24), // "getBindingOpenVRButtonId" -QT_MOC_LITERAL(37, 802, 19), // "touchAsClickEnabled" -QT_MOC_LITERAL(38, 822, 19), // "isToggleModeEnabled" -QT_MOC_LITERAL(39, 842, 19), // "toggleModeThreshold" -QT_MOC_LITERAL(40, 862, 20), // "isAutoTriggerEnabled" -QT_MOC_LITERAL(41, 883, 20), // "autoTriggerFrequency" -QT_MOC_LITERAL(42, 904, 20), // "keyboardShiftEnabled" -QT_MOC_LITERAL(43, 925, 19), // "keyboardCtrlEnabled" -QT_MOC_LITERAL(44, 945, 18), // "keyboardAltEnabled" -QT_MOC_LITERAL(45, 964, 16) // "keyboardKeyIndex" - - }, - "inputemulator::DigitalInputRemappingController\0" - "configureDigitalBindingFinished\0\0" - "finishConfigureBinding_Original\0" - "finishConfigureBinding_Disabled\0" - "finishConfigureBinding_OpenVR\0" - "controllerId\0ButtonId\0toggleMode\0" - "toggleThreshold\0autoTrigger\0" - "triggerFrequency\0finishConfigureBinding_keyboard\0" - "shiftPressed\0ctrlPressed\0altPressed\0" - "keyIndex\0finishConfigureBinding_suspendRedirectMode\0" - "startConfigureNormalBinding\0" - "startConfigureLongPressBinding\0" - "startConfigureDoublePressBinding\0" - "getNormalBindingStatus\0isLongPressEnabled\0" - "getLongPressThreshold\0getLongPressBindingStatus\0" - "isLongPressImmediateRelease\0" - "isDoublePressEnabled\0getDoublePressThreshold\0" - "getDoublePressBindingStatus\0" - "isDoublePressImmediateRelease\0" - "getButtonMaxCount\0getButtonName\0id\0" - "withDefaults\0getBindingType\0" - "getBindingOpenVRControllerId\0" - "getBindingOpenVRButtonId\0touchAsClickEnabled\0" - "isToggleModeEnabled\0toggleModeThreshold\0" - "isAutoTriggerEnabled\0autoTriggerFrequency\0" - "keyboardShiftEnabled\0keyboardCtrlEnabled\0" - "keyboardAltEnabled\0keyboardKeyIndex" -}; -#undef QT_MOC_LITERAL - -static const uint qt_meta_data_inputemulator__DigitalInputRemappingController[] = { - - // content: - 7, // revision - 0, // classname - 0, 0, // classinfo - 33, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 1, // signalCount - - // signals: name, argc, parameters, tag, flags - 1, 0, 179, 2, 0x06 /* Public */, - - // slots: name, argc, parameters, tag, flags - 3, 0, 180, 2, 0x0a /* Public */, - 4, 0, 181, 2, 0x0a /* Public */, - 5, 6, 182, 2, 0x0a /* Public */, - 12, 8, 195, 2, 0x0a /* Public */, - 17, 0, 212, 2, 0x0a /* Public */, - - // methods: name, argc, parameters, tag, flags - 18, 0, 213, 2, 0x02 /* Public */, - 19, 0, 214, 2, 0x02 /* Public */, - 20, 0, 215, 2, 0x02 /* Public */, - 21, 0, 216, 2, 0x02 /* Public */, - 22, 0, 217, 2, 0x02 /* Public */, - 23, 0, 218, 2, 0x02 /* Public */, - 24, 0, 219, 2, 0x02 /* Public */, - 25, 0, 220, 2, 0x02 /* Public */, - 26, 0, 221, 2, 0x02 /* Public */, - 27, 0, 222, 2, 0x02 /* Public */, - 28, 0, 223, 2, 0x02 /* Public */, - 29, 0, 224, 2, 0x02 /* Public */, - 30, 0, 225, 2, 0x02 /* Public */, - 31, 2, 226, 2, 0x02 /* Public */, - 31, 1, 231, 2, 0x22 /* Public | MethodCloned */, - 34, 0, 234, 2, 0x02 /* Public */, - 35, 0, 235, 2, 0x02 /* Public */, - 36, 0, 236, 2, 0x02 /* Public */, - 37, 0, 237, 2, 0x02 /* Public */, - 38, 0, 238, 2, 0x02 /* Public */, - 39, 0, 239, 2, 0x02 /* Public */, - 40, 0, 240, 2, 0x02 /* Public */, - 41, 0, 241, 2, 0x02 /* Public */, - 42, 0, 242, 2, 0x02 /* Public */, - 43, 0, 243, 2, 0x02 /* Public */, - 44, 0, 244, 2, 0x02 /* Public */, - 45, 0, 245, 2, 0x02 /* Public */, - - // signals: parameters - QMetaType::Void, - - // slots: parameters - QMetaType::Void, - QMetaType::Void, - QMetaType::Void, QMetaType::Int, QMetaType::Int, QMetaType::Bool, QMetaType::Int, QMetaType::Bool, QMetaType::Int, 6, 7, 8, 9, 10, 11, - QMetaType::Void, QMetaType::Bool, QMetaType::Bool, QMetaType::Bool, QMetaType::ULong, QMetaType::Bool, QMetaType::Int, QMetaType::Bool, QMetaType::Int, 13, 14, 15, 16, 8, 9, 10, 11, - QMetaType::Void, - - // methods: parameters - QMetaType::Void, - QMetaType::Void, - QMetaType::Void, - QMetaType::QString, - QMetaType::Bool, - QMetaType::UInt, - QMetaType::QString, - QMetaType::Bool, - QMetaType::Bool, - QMetaType::UInt, - QMetaType::QString, - QMetaType::Bool, - QMetaType::Int, - QMetaType::QString, QMetaType::Int, QMetaType::Bool, 32, 33, - QMetaType::QString, QMetaType::Int, 32, - QMetaType::Int, - QMetaType::Int, - QMetaType::Int, - QMetaType::Bool, - QMetaType::Bool, - QMetaType::Int, - QMetaType::Bool, - QMetaType::Int, - QMetaType::Bool, - QMetaType::Bool, - QMetaType::Bool, - QMetaType::UInt, - - 0 // eod -}; - -void inputemulator::DigitalInputRemappingController::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) -{ - if (_c == QMetaObject::InvokeMetaMethod) { - DigitalInputRemappingController *_t = static_cast(_o); - Q_UNUSED(_t) - switch (_id) { - case 0: _t->configureDigitalBindingFinished(); break; - case 1: _t->finishConfigureBinding_Original(); break; - case 2: _t->finishConfigureBinding_Disabled(); break; - case 3: _t->finishConfigureBinding_OpenVR((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2])),(*reinterpret_cast< bool(*)>(_a[3])),(*reinterpret_cast< int(*)>(_a[4])),(*reinterpret_cast< bool(*)>(_a[5])),(*reinterpret_cast< int(*)>(_a[6]))); break; - case 4: _t->finishConfigureBinding_keyboard((*reinterpret_cast< bool(*)>(_a[1])),(*reinterpret_cast< bool(*)>(_a[2])),(*reinterpret_cast< bool(*)>(_a[3])),(*reinterpret_cast< ulong(*)>(_a[4])),(*reinterpret_cast< bool(*)>(_a[5])),(*reinterpret_cast< int(*)>(_a[6])),(*reinterpret_cast< bool(*)>(_a[7])),(*reinterpret_cast< int(*)>(_a[8]))); break; - case 5: _t->finishConfigureBinding_suspendRedirectMode(); break; - case 6: _t->startConfigureNormalBinding(); break; - case 7: _t->startConfigureLongPressBinding(); break; - case 8: _t->startConfigureDoublePressBinding(); break; - case 9: { QString _r = _t->getNormalBindingStatus(); - if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); } break; - case 10: { bool _r = _t->isLongPressEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 11: { uint _r = _t->getLongPressThreshold(); - if (_a[0]) *reinterpret_cast< uint*>(_a[0]) = std::move(_r); } break; - case 12: { QString _r = _t->getLongPressBindingStatus(); - if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); } break; - case 13: { bool _r = _t->isLongPressImmediateRelease(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 14: { bool _r = _t->isDoublePressEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 15: { uint _r = _t->getDoublePressThreshold(); - if (_a[0]) *reinterpret_cast< uint*>(_a[0]) = std::move(_r); } break; - case 16: { QString _r = _t->getDoublePressBindingStatus(); - if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); } break; - case 17: { bool _r = _t->isDoublePressImmediateRelease(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 18: { int _r = _t->getButtonMaxCount(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 19: { QString _r = _t->getButtonName((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< bool(*)>(_a[2]))); - if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); } break; - case 20: { QString _r = _t->getButtonName((*reinterpret_cast< int(*)>(_a[1]))); - if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); } break; - case 21: { int _r = _t->getBindingType(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 22: { int _r = _t->getBindingOpenVRControllerId(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 23: { int _r = _t->getBindingOpenVRButtonId(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 24: { bool _r = _t->touchAsClickEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 25: { bool _r = _t->isToggleModeEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 26: { int _r = _t->toggleModeThreshold(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 27: { bool _r = _t->isAutoTriggerEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 28: { int _r = _t->autoTriggerFrequency(); - if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; - case 29: { bool _r = _t->keyboardShiftEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 30: { bool _r = _t->keyboardCtrlEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 31: { bool _r = _t->keyboardAltEnabled(); - if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); } break; - case 32: { uint _r = _t->keyboardKeyIndex(); - if (_a[0]) *reinterpret_cast< uint*>(_a[0]) = std::move(_r); } break; - default: ; - } - } else if (_c == QMetaObject::IndexOfMethod) { - int *result = reinterpret_cast(_a[0]); - void **func = reinterpret_cast(_a[1]); - { - typedef void (DigitalInputRemappingController::*_t)(); - if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&DigitalInputRemappingController::configureDigitalBindingFinished)) { - *result = 0; - return; - } - } - } -} - -const QMetaObject inputemulator::DigitalInputRemappingController::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_inputemulator__DigitalInputRemappingController.data, - qt_meta_data_inputemulator__DigitalInputRemappingController, qt_static_metacall, nullptr, nullptr} -}; - - -const QMetaObject *inputemulator::DigitalInputRemappingController::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; -} - -void *inputemulator::DigitalInputRemappingController::qt_metacast(const char *_clname) -{ - if (!_clname) return nullptr; - if (!strcmp(_clname, qt_meta_stringdata_inputemulator__DigitalInputRemappingController.stringdata0)) - return static_cast(const_cast< DigitalInputRemappingController*>(this)); - return QObject::qt_metacast(_clname); -} - -int inputemulator::DigitalInputRemappingController::qt_metacall(QMetaObject::Call _c, int _id, void **_a) -{ - _id = QObject::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - if (_id < 33) - qt_static_metacall(this, _c, _id, _a); - _id -= 33; - } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { - if (_id < 33) - *reinterpret_cast(_a[0]) = -1; - _id -= 33; - } - return _id; -} - -// SIGNAL 0 -void inputemulator::DigitalInputRemappingController::configureDigitalBindingFinished() -{ - QMetaObject::activate(this, &staticMetaObject, 0, nullptr); -} -QT_WARNING_POP -QT_END_MOC_NAMESPACE diff --git a/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml b/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml index bbc80c3b..0cf9398d 100644 --- a/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceAnalogInputRemappingPage.qml @@ -33,7 +33,17 @@ MyStackViewPage { } openvrAxisComboBox.model = _axisNames bindingTypeComboBox.currentIndex = AnalogInputRemappingController.getBindingType() - if (bindingTypeComboBox.currentIndex == 2) { + if (bindingTypeComboBox.currentIndex == 0) { + openVRControllerComboBox.currentIndex = 0 + openvrAxisComboBox.currentIndex = 0 + openvrXAxisInvertToggle.checked = false + openvrYAxisInvertToggle.checked = false + openvrAxisSwapToggle.checked = false + deadzonesRangeSlider.first.value = 0.0 + deadzonesRangeSlider.second.value = 1.0 + touchpadEmulationComboBox.currentIndex = AnalogInputRemappingController.getBindingTouchpadEmulationMode() + buttonDeadzoneFixToggle.checked = AnalogInputRemappingController.getBindingButtonPressDeadzoneFix() + } else if (bindingTypeComboBox.currentIndex == 2) { var controllerId = AnalogInputRemappingController.getBindingOpenVRControllerId() var index = 0 for (var i = 0; i < _controllerIds.length; i++) { @@ -50,6 +60,8 @@ MyStackViewPage { openvrAxisSwapToggle.checked = AnalogInputRemappingController.isBindingOpenVRAxesSwapped() deadzonesRangeSlider.first.value = AnalogInputRemappingController.getBindingDeadzoneLower() deadzonesRangeSlider.second.value = AnalogInputRemappingController.getBindingDeadzoneUpper() + touchpadEmulationComboBox.currentIndex = AnalogInputRemappingController.getBindingTouchpadEmulationMode() + buttonDeadzoneFixToggle.checked = AnalogInputRemappingController.getBindingButtonPressDeadzoneFix() } else { openVRControllerComboBox.currentIndex = 0 openvrAxisComboBox.currentIndex = 0 @@ -58,176 +70,216 @@ MyStackViewPage { openvrAxisSwapToggle.checked = false deadzonesRangeSlider.first.value = 0.0 deadzonesRangeSlider.second.value = 1.0 + touchpadEmulationComboBox.currentIndex = 0 + buttonDeadzoneFixToggle.checked = false } } content: ColumnLayout { - spacing: 64 - - RowLayout { - MyText { - Layout.preferredWidth: 200 - text: "Binding Type:" - } - MyComboBox { - id: bindingTypeComboBox - Layout.maximumWidth: 910 - Layout.minimumWidth: 910 - Layout.preferredWidth: 910 - Layout.fillWidth: true - model: [ - "No Remapping", - "Disabled", - "OpenVR" - ] - onCurrentIndexChanged: { - if (currentIndex == 2) { - openvrConfigContainer.visible = true - deadzoneConfigContainer.visible = true - } else { - openvrConfigContainer.visible = false - deadzoneConfigContainer.visible = false - } - } - } - } - - ColumnLayout { - id: openvrConfigContainer - visible: false - spacing: 18 + ColumnLayout { + spacing: 64 RowLayout { MyText { Layout.preferredWidth: 200 - text: "Controller:" + text: "Binding Type:" } MyComboBox { - id: openVRControllerComboBox + id: bindingTypeComboBox Layout.maximumWidth: 910 Layout.minimumWidth: 910 Layout.preferredWidth: 910 Layout.fillWidth: true - model: [] + model: [ + "No Remapping", + "Disabled", + "OpenVR" + ] onCurrentIndexChanged: { + if (currentIndex == 0) { + openvrConfigContainer.visible = false + deadzoneConfigContainer.visible = false + touchpadEmulationContainer.visible = true + } else if (currentIndex == 2) { + openvrConfigContainer.visible = true + deadzoneConfigContainer.visible = true + touchpadEmulationContainer.visible = true + } else { + openvrConfigContainer.visible = false + deadzoneConfigContainer.visible = false + touchpadEmulationContainer.visible = false + } } } } - RowLayout { - MyText { - Layout.preferredWidth: 200 - text: "Axis:" - } - MyComboBox { - id: openvrAxisComboBox - Layout.maximumWidth: 910 - Layout.minimumWidth: 910 - Layout.preferredWidth: 910 - Layout.fillWidth: true - model: [] - onCurrentIndexChanged: { + ColumnLayout { + id: openvrConfigContainer + visible: false + spacing: 18 + + RowLayout { + MyText { + Layout.preferredWidth: 200 + text: "Controller:" + } + MyComboBox { + id: openVRControllerComboBox + Layout.maximumWidth: 910 + Layout.minimumWidth: 910 + Layout.preferredWidth: 910 + Layout.fillWidth: true + model: [] + onCurrentIndexChanged: { + } } } - } - RowLayout { - Item { - Layout.preferredWidth: 200 - } - MyToggleButton { - id: openvrXAxisInvertToggle - Layout.preferredWidth: 300 - text: "Invert X Axis" - } - MyToggleButton { - id: openvrYAxisInvertToggle - Layout.preferredWidth: 300 - text: "Invert Y Axis" + RowLayout { + MyText { + Layout.preferredWidth: 200 + text: "Axis:" + } + MyComboBox { + id: openvrAxisComboBox + Layout.maximumWidth: 910 + Layout.minimumWidth: 910 + Layout.preferredWidth: 910 + Layout.fillWidth: true + model: [] + onCurrentIndexChanged: { + } + } } - MyToggleButton { - id: openvrAxisSwapToggle - Layout.preferredWidth: 300 - text: "Swap X/Y" + + RowLayout { + Item { + Layout.preferredWidth: 200 + } + MyToggleButton { + id: openvrXAxisInvertToggle + Layout.preferredWidth: 300 + text: "Invert X Axis" + } + MyToggleButton { + id: openvrYAxisInvertToggle + Layout.preferredWidth: 300 + text: "Invert Y Axis" + } + MyToggleButton { + id: openvrAxisSwapToggle + Layout.preferredWidth: 300 + text: "Swap X/Y" + } } } - } - ColumnLayout { - id: deadzoneConfigContainer - visible: false - spacing: 18 + ColumnLayout { + id: deadzoneConfigContainer + visible: false + spacing: 18 - RowLayout { - spacing: 16 + RowLayout { + spacing: 16 - MyText { - text: "Dead Zone:" - Layout.preferredWidth: 180 - Layout.rightMargin: 12 - } + MyText { + text: "Dead Zone:" + Layout.preferredWidth: 180 + Layout.rightMargin: 12 + } - MyTextField { - id: lowerDeadzonesText - text: "0.0" - Layout.preferredWidth: 150 - Layout.rightMargin: 10 - horizontalAlignment: Text.AlignHCenter - function onInputEvent(input) { - var val = parseFloat(input) - if (!isNaN(val)) { - if (val < 0.0) { - val = 0.0 - } else if (val > 1.0) { - val = 1.0 + MyTextField { + id: lowerDeadzonesText + text: "0.0" + Layout.preferredWidth: 150 + Layout.rightMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val < 0.0) { + val = 0.0 + } else if (val > 1.0) { + val = 1.0 + } + var v = val.toFixed(2) + deadzonesRangeSlider.first.value = v + } else { + lowerDeadzonesText.text = deadzonesRangeSlider.first.value.toFixed(2) } - var v = val.toFixed(2) - deadzonesRangeSlider.first.value = v - } else { - lowerDeadzonesText.text = deadzonesRangeSlider.first.value.toFixed(2) } } - } - MyRangeSlider { - id: deadzonesRangeSlider - from: 0.0 - to: 1.0 - stepSize: 0.01 - first.value: 0.0 - second.value: 1.0 - Layout.fillWidth: true - first.onValueChanged: { - lowerDeadzonesText.text = first.value.toFixed(2) - } - second.onValueChanged: { - upperDeadzonesText.text = second.value.toFixed(2) + MyRangeSlider { + id: deadzonesRangeSlider + from: 0.0 + to: 1.0 + stepSize: 0.01 + first.value: 0.0 + second.value: 1.0 + Layout.fillWidth: true + first.onValueChanged: { + lowerDeadzonesText.text = first.value.toFixed(2) + } + second.onValueChanged: { + upperDeadzonesText.text = second.value.toFixed(2) + } } - } - MyTextField { - id: upperDeadzonesText - text: "1.0" - Layout.preferredWidth: 150 - Layout.leftMargin: 10 - horizontalAlignment: Text.AlignHCenter - function onInputEvent(input) { - var val = parseFloat(input) - if (!isNaN(val)) { - if (val < 0.0) { - val = 0.0 - } else if (val > 1.0) { - val = 1.0 + MyTextField { + id: upperDeadzonesText + text: "1.0" + Layout.preferredWidth: 150 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val < 0.0) { + val = 0.0 + } else if (val > 1.0) { + val = 1.0 + } + var v = val.toFixed(2) + deadzonesRangeSlider.second.value = v + } else { + upperDeadzonesText.text = deadzonesRangeSlider.second.value.toFixed(2) } - var v = val.toFixed(2) - deadzonesRangeSlider.second.value = v - } else { - upperDeadzonesText.text = deadzonesRangeSlider.second.value.toFixed(2) } } } } } + ColumnLayout { + id: touchpadEmulationContainer + spacing: 4 + Layout.topMargin: 64 + + RowLayout { + MyText { + Layout.preferredWidth: 370 + text: "Joystick Touchpad Emulation:" + } + MyComboBox { + id: touchpadEmulationComboBox + Layout.maximumWidth: 480 + Layout.minimumWidth: 480 + Layout.preferredWidth: 480 + Layout.fillWidth: true + model: [ + "Disabled", + "Position Based", + "Position Based (Deferred Zero Update)" + ] + } + } + + MyToggleButton { + id: buttonDeadzoneFixToggle + text: "Button Press Deadzone Fix" + } + } + Item { Layout.fillWidth: true @@ -242,8 +294,10 @@ MyStackViewPage { Layout.preferredWidth: 200 text: "Save" onClicked: { + var touchpadEmulation = touchpadEmulationComboBox.currentIndex + var deadzoneFix = buttonDeadzoneFixToggle.checked if (bindingTypeComboBox.currentIndex == 0) { - AnalogInputRemappingController.finishConfigure_Original() + AnalogInputRemappingController.finishConfigure_Original(touchpadEmulation, deadzoneFix) } else if (bindingTypeComboBox.currentIndex == 1) { AnalogInputRemappingController.finishConfigure_Disabled() } else if (bindingTypeComboBox.currentIndex == 2) { @@ -257,7 +311,7 @@ MyStackViewPage { var swapAxes = openvrAxisSwapToggle.checked var lowerDeadzone = deadzonesRangeSlider.first.value var upperDeadzone = deadzonesRangeSlider.second.value - AnalogInputRemappingController.finishConfigure_OpenVR(controllerId, mappedAxisId, invertXAxis, invertYAxis, swapAxes, lowerDeadzone, upperDeadzone) + AnalogInputRemappingController.finishConfigure_OpenVR(controllerId, mappedAxisId, invertXAxis, invertYAxis, swapAxes, lowerDeadzone, upperDeadzone, touchpadEmulation, deadzoneFix) } goBack() } diff --git a/client_overlay/bin/win64/res/qml/DeviceDigitalBindingPage.qml b/client_overlay/bin/win64/res/qml/DeviceDigitalBindingPage.qml index 68456184..a1e9cf30 100644 --- a/client_overlay/bin/win64/res/qml/DeviceDigitalBindingPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceDigitalBindingPage.qml @@ -95,7 +95,8 @@ MyStackViewPage { "Disabled", "OpenVR", "Keyboard", - "Suspend Redirect Mode" + "Suspend Redirect Mode", + "Toggle Touchpad Emulation" ] onCurrentIndexChanged: { if (currentIndex == 2) { @@ -419,6 +420,8 @@ MyStackViewPage { DigitalInputRemappingController.finishConfigureBinding_keyboard(shift, ctrl, alt, keyIndex, toggleMode, toggleDelay, autoTrigger, autoTriggerFreq) } else if (bindingTypeComboBox.currentIndex == 4) { DigitalInputRemappingController.finishConfigureBinding_suspendRedirectMode() + } else if (bindingTypeComboBox.currentIndex == 5) { + DigitalInputRemappingController.finishConfigureBinding_toggleTouchpadEmulationFix() } goBack() } diff --git a/client_overlay/src/overlaycontroller.cpp b/client_overlay/src/overlaycontroller.cpp index 67b89c5f..c5747308 100644 --- a/client_overlay/src/overlaycontroller.cpp +++ b/client_overlay/src/overlaycontroller.cpp @@ -666,6 +666,9 @@ QString OverlayController::digitalBindingToString(const vrinputemulator::Digital case vrinputemulator::DigitalBindingType::SuspendRedirectMode: status = "Suspend Redirect Mode"; break; + case vrinputemulator::DigitalBindingType::ToggleTouchpadEmulationFix: + status = "Toggle Touchpad Emulation"; + break; default: status = ""; break; @@ -699,6 +702,9 @@ QString OverlayController::analogBindingToString(const vrinputemulator::AnalogBi status = ""; break; } + if (binding.touchpadEmulationMode > 0 || binding.buttonPressDeadzoneFix) { + status.append(";Touchpad Emulation"); + } return status; } diff --git a/client_overlay/src/overlaycontroller.h b/client_overlay/src/overlaycontroller.h index daae70b5..1445915d 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.1 beta1"; + static constexpr const char* applicationVersionString = "v1.2"; private: vr::VROverlayHandle_t m_ulOverlayHandle = vr::k_ulOverlayHandleInvalid; diff --git a/client_overlay/src/tabcontrollers/AnalogInputRemappingController.cpp b/client_overlay/src/tabcontrollers/AnalogInputRemappingController.cpp index 85975fc8..d116b39d 100644 --- a/client_overlay/src/tabcontrollers/AnalogInputRemappingController.cpp +++ b/client_overlay/src/tabcontrollers/AnalogInputRemappingController.cpp @@ -127,8 +127,26 @@ float AnalogInputRemappingController::getBindingDeadzoneUpper() { } } -void AnalogInputRemappingController::finishConfigure_Original() { +unsigned AnalogInputRemappingController::getBindingTouchpadEmulationMode() { + if (m_currentRemapping.valid) { + return m_currentRemapping.binding.touchpadEmulationMode; + } else { + return false; + } +} + +bool AnalogInputRemappingController::getBindingButtonPressDeadzoneFix() { + if (m_currentRemapping.valid) { + return m_currentRemapping.binding.buttonPressDeadzoneFix; + } else { + return false; + } +} + +void AnalogInputRemappingController::finishConfigure_Original(unsigned touchpadEmulationMode, bool updateOnButtonEvent) { m_currentRemapping.binding.type = vrinputemulator::AnalogBindingType::NoRemapping; + m_currentRemapping.binding.touchpadEmulationMode = touchpadEmulationMode; + m_currentRemapping.binding.buttonPressDeadzoneFix = updateOnButtonEvent; parent->deviceManipulationTabController.finishConfigureAnalogInputRemapping(m_currentDeviceIndex, m_currentAxisId); } @@ -137,7 +155,7 @@ void AnalogInputRemappingController::finishConfigure_Disabled() { parent->deviceManipulationTabController.finishConfigureAnalogInputRemapping(m_currentDeviceIndex, m_currentAxisId); } -void AnalogInputRemappingController::finishConfigure_OpenVR(int controllerId, int axisId, bool invertXAxis, bool invertYAxis, bool swapAxes, float lowerDeadzone, float upperDeadzone) { +void AnalogInputRemappingController::finishConfigure_OpenVR(int controllerId, int axisId, bool invertXAxis, bool invertYAxis, bool swapAxes, float lowerDeadzone, float upperDeadzone, unsigned touchpadEmulationMode, bool updateOnButtonEvent) { m_currentRemapping.binding.type = vrinputemulator::AnalogBindingType::OpenVR; if (controllerId < 0) { m_currentRemapping.binding.data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; @@ -150,6 +168,8 @@ void AnalogInputRemappingController::finishConfigure_OpenVR(int controllerId, in m_currentRemapping.binding.swapAxes = swapAxes; m_currentRemapping.binding.lowerDeadzone = lowerDeadzone; m_currentRemapping.binding.upperDeadzone = upperDeadzone; + m_currentRemapping.binding.touchpadEmulationMode = touchpadEmulationMode; + m_currentRemapping.binding.buttonPressDeadzoneFix = updateOnButtonEvent; parent->deviceManipulationTabController.finishConfigureAnalogInputRemapping(m_currentDeviceIndex, m_currentAxisId); } diff --git a/client_overlay/src/tabcontrollers/AnalogInputRemappingController.h b/client_overlay/src/tabcontrollers/AnalogInputRemappingController.h index 81576c80..da6ca8ed 100644 --- a/client_overlay/src/tabcontrollers/AnalogInputRemappingController.h +++ b/client_overlay/src/tabcontrollers/AnalogInputRemappingController.h @@ -41,12 +41,15 @@ class AnalogInputRemappingController : public QObject { Q_INVOKABLE float getBindingDeadzoneLower(); Q_INVOKABLE float getBindingDeadzoneUpper(); + Q_INVOKABLE unsigned getBindingTouchpadEmulationMode(); + Q_INVOKABLE bool getBindingButtonPressDeadzoneFix(); + const vrinputemulator::AnalogInputRemapping& currentRemapping() { return m_currentRemapping; } public slots: - void finishConfigure_Original(); + void finishConfigure_Original(unsigned touchpadEmulationMode, bool updateOnButtonEvent); void finishConfigure_Disabled(); - void finishConfigure_OpenVR(int controllerId, int axisId, bool invertXAxis, bool invertYAxis, bool swapAxes, float lowerDeadzone, float upperDeadzone); + void finishConfigure_OpenVR(int controllerId, int axisId, bool invertXAxis, bool invertYAxis, bool swapAxes, float lowerDeadzone, float upperDeadzone, unsigned touchpadEmulationMode, bool updateOnButtonEvent); private: OverlayController* parent; diff --git a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp index 114e42ae..b6e99ee7 100644 --- a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp +++ b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp @@ -375,6 +375,8 @@ void DeviceManipulationTabController::reloadDeviceManipulationProfiles() { p.remapping.binding.swapAxes = r["swapAxes"].toBool(); p.remapping.binding.lowerDeadzone = r["lowerDeadzone"].toFloat(); p.remapping.binding.upperDeadzone = r["upperDeadzone"].toFloat(); + p.remapping.binding.buttonPressDeadzoneFix = r["buttonPressDeadzoneFix"].toBool(); + p.remapping.binding.touchpadEmulationMode = r["touchpadEmulationMode"].toUInt(); entry.analogRemappingProfiles[key.toInt()] = p; } } @@ -489,6 +491,8 @@ void DeviceManipulationTabController::saveDeviceManipulationProfiles() { profile["lowerDeadzone"] = ap.remapping.binding.lowerDeadzone; profile["upperDeadzone"] = ap.remapping.binding.upperDeadzone; profile["swapAxes"] = ap.remapping.binding.swapAxes; + profile["buttonPressDeadzoneFix"] = ap.remapping.binding.buttonPressDeadzoneFix; + profile["touchpadEmulationMode"] = ap.remapping.binding.touchpadEmulationMode; analogProfiles[QString::number(i2)] = profile; } } @@ -593,6 +597,8 @@ void DeviceManipulationTabController::addDeviceManipulationProfile(QString name, p.remapping.binding.lowerDeadzone = r.binding.lowerDeadzone; p.remapping.binding.upperDeadzone = r.binding.upperDeadzone; p.remapping.binding.swapAxes = r.binding.swapAxes; + p.remapping.binding.buttonPressDeadzoneFix = r.binding.buttonPressDeadzoneFix; + p.remapping.binding.touchpadEmulationMode = r.binding.touchpadEmulationMode; } } } catch (const std::exception& e) { @@ -638,6 +644,30 @@ void DeviceManipulationTabController::applyDeviceManipulationProfile(unsigned in parent->vrInputEmulator().setDigitalInputRemapping(device->openvrId, i, vrinputemulator::DigitalInputRemapping()); } else { auto& p = *it; + + // We need to correct some potentially false data to not mess up the button state machine + if (p.second.remapping.binding.type == vrinputemulator::DigitalBindingType::NoRemapping + || p.second.remapping.binding.type == vrinputemulator::DigitalBindingType::Disabled + || p.second.remapping.binding.type == vrinputemulator::DigitalBindingType::SuspendRedirectMode + || p.second.remapping.binding.type == vrinputemulator::DigitalBindingType::ToggleTouchpadEmulationFix) { + p.second.remapping.binding.toggleEnabled = false; + p.second.remapping.binding.autoTriggerEnabled = false; + } + if (p.second.remapping.doublePressEnabled && (p.second.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::NoRemapping + || p.second.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::Disabled + || p.second.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::SuspendRedirectMode + || p.second.remapping.doublePressBinding.type == vrinputemulator::DigitalBindingType::ToggleTouchpadEmulationFix)) { + p.second.remapping.doublePressBinding.toggleEnabled = false; + p.second.remapping.doublePressBinding.autoTriggerEnabled = false; + } + if (p.second.remapping.longPressEnabled && (p.second.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::NoRemapping + || p.second.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::Disabled + || p.second.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::SuspendRedirectMode + || p.second.remapping.longPressBinding.type == vrinputemulator::DigitalBindingType::ToggleTouchpadEmulationFix)) { + p.second.remapping.longPressBinding.toggleEnabled = false; + p.second.remapping.longPressBinding.autoTriggerEnabled = false; + } + 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 { @@ -872,6 +902,9 @@ QString DeviceManipulationTabController::getAnalogAxisStatus(unsigned deviceInde if (deviceIndex < deviceInfos.size()) { auto remapping = parent->vrInputEmulator().getAnalogInputRemapping(deviceInfos[deviceIndex]->openvrId, axisId); status = parent->analogBindingToString(remapping.binding, remapping.binding.data.openvr.controllerId != deviceInfos[deviceIndex]->openvrId); + if (status.size() > 60) { + status = status.left(60).append("..."); + } } return status; } @@ -1153,7 +1186,7 @@ void DeviceManipulationTabController::triggerHapticPulse(unsigned index) { // When I use a thread everything works in debug modus, but as soon as I switch to release mode I get a segmentation fault // Hard to debug since it works in debug modus and therefore I cannot use a debugger :-( for (unsigned i = 0; i < 50; ++i) { - parent->vrInputEmulator().triggerHapticPulse(deviceInfos[index]->openvrId, 0, 2000, true); + parent->vrInputEmulator().triggerHapticPulse(deviceInfos[index]->openvrId, 0, 1000, true); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } /*if (identifyThread.joinable()) { diff --git a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp index b79d1732..64b24ebe 100644 --- a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp +++ b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.cpp @@ -275,16 +275,27 @@ void DigitalInputRemappingController::enableTouchAsClick(bool enable, bool notif void DigitalInputRemappingController::finishConfigureBinding_Original() { m_currentBinding->type = vrinputemulator::DigitalBindingType::NoRemapping; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); + m_currentBinding->toggleEnabled = false; + m_currentBinding->toggleDelay = 0; + m_currentBinding->autoTriggerEnabled = false; + m_currentBinding->autoTriggerFrequency = 1; emit configureDigitalBindingFinished(); } void DigitalInputRemappingController::finishConfigureBinding_Disabled() { m_currentBinding->type = vrinputemulator::DigitalBindingType::Disabled; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); + m_currentBinding->toggleEnabled = false; + m_currentBinding->toggleDelay = 0; + m_currentBinding->autoTriggerEnabled = false; + m_currentBinding->autoTriggerFrequency = 1; emit configureDigitalBindingFinished(); } void DigitalInputRemappingController::finishConfigureBinding_OpenVR(int controllerId, int ButtonId, bool toggleMode, int toggleThreshold, bool autoTrigger, int triggerFrequency) { m_currentBinding->type = vrinputemulator::DigitalBindingType::OpenVR; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); if (controllerId < 0) { m_currentBinding->data.openvr.controllerId = vr::k_unTrackedDeviceIndexInvalid; } else { @@ -300,6 +311,7 @@ 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; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); m_currentBinding->data.keyboard.shiftPressed = shiftPressed; m_currentBinding->data.keyboard.ctrlPressed = ctrlPressed; m_currentBinding->data.keyboard.altPressed = altPressed; @@ -313,6 +325,22 @@ void DigitalInputRemappingController::finishConfigureBinding_keyboard(bool shift void DigitalInputRemappingController::finishConfigureBinding_suspendRedirectMode() { m_currentBinding->type = vrinputemulator::DigitalBindingType::SuspendRedirectMode; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); + m_currentBinding->toggleEnabled = false; + m_currentBinding->toggleDelay = 0; + m_currentBinding->autoTriggerEnabled = false; + m_currentBinding->autoTriggerFrequency = 1; + emit configureDigitalBindingFinished(); +} + + +void DigitalInputRemappingController::finishConfigureBinding_toggleTouchpadEmulationFix() { + m_currentBinding->type = vrinputemulator::DigitalBindingType::ToggleTouchpadEmulationFix; + memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data)); + m_currentBinding->toggleEnabled = false; + m_currentBinding->toggleDelay = 0; + m_currentBinding->autoTriggerEnabled = false; + m_currentBinding->autoTriggerFrequency = 1; emit configureDigitalBindingFinished(); } diff --git a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.h b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.h index 5848b181..074b6184 100644 --- a/client_overlay/src/tabcontrollers/DigitalInputRemappingController.h +++ b/client_overlay/src/tabcontrollers/DigitalInputRemappingController.h @@ -78,6 +78,7 @@ public slots: void finishConfigureBinding_OpenVR(int controllerId, int ButtonId, bool toggleMode, int toggleThreshold, bool autoTrigger, int triggerFrequency); void finishConfigureBinding_keyboard(bool shiftPressed, bool ctrlPressed, bool altPressed, unsigned long keyIndex, bool toggleMode, int toggleThreshold, bool autoTrigger, int triggerFrequency); void finishConfigureBinding_suspendRedirectMode(); + void finishConfigureBinding_toggleTouchpadEmulationFix(); signals: /*void longPressEnabledChanged(bool enable); diff --git a/docs/screenshots/AnalogBindingPage.png b/docs/screenshots/AnalogBindingPage.png index 6dd541d6..80acff77 100644 Binary files a/docs/screenshots/AnalogBindingPage.png and b/docs/screenshots/AnalogBindingPage.png differ diff --git a/docs/screenshots/DigitalBindingPage.png b/docs/screenshots/DigitalBindingPage.png index 82ea9eba..42acfe3d 100644 Binary files a/docs/screenshots/DigitalBindingPage.png and b/docs/screenshots/DigitalBindingPage.png differ diff --git a/driver_vrinputemulator/driver_vrinputemulator.vcxproj b/driver_vrinputemulator/driver_vrinputemulator.vcxproj index 0c88b855..01d39b62 100644 --- a/driver_vrinputemulator/driver_vrinputemulator.vcxproj +++ b/driver_vrinputemulator/driver_vrinputemulator.vcxproj @@ -142,7 +142,7 @@ Windows true ..\openvr\lib\win64;..\third-party\boost_1_63_0\lib64-msvc-14.0;..\third-party\MinHook\lib;%(AdditionalLibraryDirectories) - libMinHook-x64-v141-mtd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + libMinHook-x64-v141-mtd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;%(AdditionalDependencies) @@ -182,7 +182,7 @@ true true ..\openvr\lib\win64;..\third-party\boost_1_63_0\lib64-msvc-14.0;..\third-party\MinHook\lib;%(AdditionalLibraryDirectories) - libMinHook-x64-v141-mtd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + libMinHook-x64-v141-mtd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;%(AdditionalDependencies) diff --git a/driver_vrinputemulator/resources/sounds/License.txt b/driver_vrinputemulator/resources/sounds/License.txt new file mode 100644 index 00000000..e206de50 --- /dev/null +++ b/driver_vrinputemulator/resources/sounds/License.txt @@ -0,0 +1,2 @@ +audiocue.wav is licensed under the Creative Commons Attribution license (Find out more under https://creativecommons.org/licenses/by/4.0/legalcode). +It has been downloaded from https://notificationsounds.com/notification-sounds/plucky-550, and the only changes done to it was the removal of silence. \ No newline at end of file diff --git a/driver_vrinputemulator/resources/sounds/audiocue.wav b/driver_vrinputemulator/resources/sounds/audiocue.wav new file mode 100644 index 00000000..00693120 Binary files /dev/null and b/driver_vrinputemulator/resources/sounds/audiocue.wav differ diff --git a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp index 20ed28d9..5e4ce89f 100644 --- a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp +++ b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp @@ -890,6 +890,10 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver } break; + case ipc::RequestType::InputRemapping_SetTouchpadEmulationFixEnabled: { + OpenvrDeviceManipulationInfo::setTouchpadEmulationFixFlag(message.msg.ir_SetTouchPadEmulationFixEnabled.enable); + } break; + default: LOG(ERROR) << "Error in ipc server receive loop: Unknown message type (" << (int)message.type << ")"; break; diff --git a/driver_vrinputemulator/src/driver_deviceinfo.cpp b/driver_vrinputemulator/src/driver_deviceinfo.cpp index 9c64a866..1ab31c8b 100644 --- a/driver_vrinputemulator/src/driver_deviceinfo.cpp +++ b/driver_vrinputemulator/src/driver_deviceinfo.cpp @@ -2,6 +2,12 @@ #include "stdafx.h" #include "driver_vrinputemulator.h" #include +#undef WIN32_LEAN_AND_MEAN +#undef NOSOUND +#include +// According to windows documentation mmsystem.h should be automatically included with Windows.h when WIN32_LEAN_AND_MEAN and NOSOUND are not defined +// But it doesn't work so I have to include it manually +#include namespace vrinputemulator { @@ -14,6 +20,7 @@ namespace driver { lhs[2] += rhs.v[2]; +bool OpenvrDeviceManipulationInfo::touchpadEmulationEnabledFlag = true; void OpenvrDeviceManipulationInfo::setDigitalInputRemapping(uint32_t buttonId, const DigitalInputRemapping& remapping) { @@ -39,21 +46,15 @@ DigitalInputRemapping OpenvrDeviceManipulationInfo::getDigitalInputRemapping(uin void OpenvrDeviceManipulationInfo::setAnalogInputRemapping(uint32_t axisId, const AnalogInputRemapping& remapping) { - if (remapping.valid) { + if (axisId < 5) { m_analogInputRemapping[axisId].remapping = remapping; - } else { - auto it = m_analogInputRemapping.find(axisId); - if (it != m_analogInputRemapping.end()) { - m_analogInputRemapping.erase(it); - } } } AnalogInputRemapping OpenvrDeviceManipulationInfo::getAnalogInputRemapping(uint32_t axisId) { - auto it = m_analogInputRemapping.find(axisId); - if (it != m_analogInputRemapping.end()) { - return it->second.remapping; + if (axisId < 5) { + return m_analogInputRemapping[axisId].remapping; } else { return AnalogInputRemapping(); } @@ -172,14 +173,14 @@ void OpenvrDeviceManipulationInfo::handleButtonEvent(vr::IVRServerDriverHost* dr switch (buttonInfo.state) { case 0: { if (!buttonInfo.remapping.doublePressEnabled && !buttonInfo.remapping.longPressEnabled) { + //LOG(INFO) << "buttonInfo.state = 0: sendDigitalBinding - EventType: " << (int)eventType; sendDigitalBinding(buttonInfo.remapping.binding, unWhichDevice, eventType, eButtonId, eventTimeOffset, &buttonInfo.bindings[0]); - LOG(INFO) << "buttonInfo.state = 0: sendDigitalBinding"; } else if (eventType == ButtonEventType::ButtonPressed) { if (buttonInfo.remapping.longPressEnabled) { buttonInfo.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(buttonInfo.remapping.longPressThreshold); } buttonInfo.state = 1; - LOG(INFO) << "buttonInfo.state = 0: => 1"; + //LOG(INFO) << "buttonInfo.state = 0: => 1"; } } break; case 1: { @@ -187,12 +188,12 @@ void OpenvrDeviceManipulationInfo::handleButtonEvent(vr::IVRServerDriverHost* dr if (buttonInfo.remapping.doublePressEnabled) { buttonInfo.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(buttonInfo.remapping.doublePressThreshold); buttonInfo.state = 3; - LOG(INFO) << "buttonInfo.state = 1: => 3"; + //LOG(INFO) << "buttonInfo.state = 1: => 3"; } else { sendDigitalBinding(buttonInfo.remapping.binding, m_openvrId, ButtonEventType::ButtonPressed, eButtonId, 0.0, &buttonInfo.bindings[0]); buttonInfo.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(100); buttonInfo.state = 4; - LOG(INFO) << "buttonInfo.state = 1: => 4"; + //LOG(INFO) << "buttonInfo.state = 1: => 4"; } } } break; @@ -200,7 +201,7 @@ void OpenvrDeviceManipulationInfo::handleButtonEvent(vr::IVRServerDriverHost* dr if (eventType == ButtonEventType::ButtonUnpressed) { sendDigitalBinding(buttonInfo.remapping.longPressBinding, unWhichDevice, eventType, eButtonId, eventTimeOffset, &buttonInfo.bindings[1]); buttonInfo.state = 0; - LOG(INFO) << "buttonInfo.state = 2: sendDigitalBinding, => 0"; + //LOG(INFO) << "buttonInfo.state = 2: sendDigitalBinding, => 0"; } } break; case 3: { @@ -210,20 +211,20 @@ void OpenvrDeviceManipulationInfo::handleButtonEvent(vr::IVRServerDriverHost* dr buttonInfo.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(100); } buttonInfo.state = 5; - LOG(INFO) << "buttonInfo.state = 3: sendDigitalBinding, => 5"; + //LOG(INFO) << "buttonInfo.state = 3: sendDigitalBinding, => 5"; } } break; case 5: { if (eventType == ButtonEventType::ButtonUnpressed) { sendDigitalBinding(buttonInfo.remapping.doublePressBinding, unWhichDevice, eventType, eButtonId, eventTimeOffset, &buttonInfo.bindings[2]); buttonInfo.state = 0; - LOG(INFO) << "buttonInfo.state = 5: sendDigitalBinding, => 0"; + //LOG(INFO) << "buttonInfo.state = 5: sendDigitalBinding, => 0"; } } break; case 6: { if (eventType == ButtonEventType::ButtonUnpressed) { buttonInfo.state = 0; - LOG(INFO) << "buttonInfo.state = 6: => 0"; + //LOG(INFO) << "buttonInfo.state = 6: => 0"; } } break; default: { @@ -269,7 +270,7 @@ void OpenvrDeviceManipulationInfo::RunFrame() { r.second.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(100); } r.second.state = 2; - LOG(INFO) << "buttonInfo.state = 1: sendDigitalBinding, => 2"; + //LOG(INFO) << "buttonInfo.state = 1: sendDigitalBinding, => 2"; } } } break; @@ -279,7 +280,7 @@ void OpenvrDeviceManipulationInfo::RunFrame() { if (r.second.timeout <= now) { sendDigitalBinding(r.second.remapping.longPressBinding, m_openvrId, ButtonEventType::ButtonUnpressed, (vr::EVRButtonId)r.first, 0.0, &r.second.bindings[1]); r.second.state = 6; - LOG(INFO) << "buttonInfo.state = 2: sendDigitalBinding, => 6"; + //LOG(INFO) << "buttonInfo.state = 2: sendDigitalBinding, => 6"; } } } break; @@ -289,7 +290,7 @@ void OpenvrDeviceManipulationInfo::RunFrame() { sendDigitalBinding(r.second.remapping.binding, m_openvrId, ButtonEventType::ButtonPressed, (vr::EVRButtonId)r.first, 0.0, &r.second.bindings[0]); r.second.timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(100); r.second.state = 4; - LOG(INFO) << "buttonInfo.state = 3: sendDigitalBinding, => 4"; + //LOG(INFO) << "buttonInfo.state = 3: sendDigitalBinding, => 4"; } } break; case 4: { @@ -297,7 +298,7 @@ void OpenvrDeviceManipulationInfo::RunFrame() { if (r.second.timeout <= now) { sendDigitalBinding(r.second.remapping.binding, m_openvrId, ButtonEventType::ButtonUnpressed, (vr::EVRButtonId)r.first, 0.0, &r.second.bindings[0]); r.second.state = 0; - LOG(INFO) << "buttonInfo.state = 4: sendDigitalBinding, => 0"; + //LOG(INFO) << "buttonInfo.state = 4: sendDigitalBinding, => 0"; } } break; case 5: { @@ -306,7 +307,7 @@ void OpenvrDeviceManipulationInfo::RunFrame() { if (r.second.timeout <= now) { sendDigitalBinding(r.second.remapping.doublePressBinding, m_openvrId, ButtonEventType::ButtonUnpressed, (vr::EVRButtonId)r.first, 0.0, &r.second.bindings[2]); r.second.state = 6; - LOG(INFO) << "buttonInfo.state = 5: sendDigitalBinding, => 6"; + //LOG(INFO) << "buttonInfo.state = 5: sendDigitalBinding, => 6"; } } } break; @@ -351,18 +352,12 @@ void OpenvrDeviceManipulationInfo::RunFrameDigitalBinding(vrinputemulator::Digit void OpenvrDeviceManipulationInfo::handleAxisEvent(vr::IVRServerDriverHost* driver, _DetourTrackedDeviceAxisUpdated_t origFunc, uint32_t& unWhichDevice, uint32_t unWhichAxis, const vr::VRControllerAxis_t& axisState) { std::lock_guard lock(_mutex); - auto axisIt = m_analogInputRemapping.find(unWhichAxis); - if (axisIt != m_analogInputRemapping.end()) { - auto& axisInfo = axisIt->second; - if (axisInfo.remapping.valid) { - sendAnalogBinding(axisInfo.remapping.binding, unWhichDevice, unWhichAxis, axisState, &axisInfo.bindings[0]); - } else { - if (m_deviceMode == 1 || (m_deviceMode == 3 && !m_redirectSuspended) /*|| m_deviceMode == 5*/) { - //nop - } else { - sendAxisEvent(unWhichDevice, unWhichAxis, axisState); - } - } + AnalogInputRemappingInfo* axisInfo = nullptr; + if (unWhichAxis < 5) { + axisInfo = m_analogInputRemapping + unWhichAxis; + } + if (axisInfo && axisInfo->remapping.valid) { + sendAnalogBinding(axisInfo->remapping.binding, unWhichDevice, unWhichAxis, axisState, &axisInfo->binding); } else { if (m_deviceMode == 1 || (m_deviceMode == 3 && !m_redirectSuspended) /*|| m_deviceMode == 5*/) { //nop @@ -480,7 +475,22 @@ void OpenvrDeviceManipulationInfo::sendDigitalBinding(vrinputemulator::DigitalBi } break; case DigitalBindingType::SuspendRedirectMode: { if (eventType == ButtonEventType::ButtonPressed) { + //_vibrationCue(); // Better not let it interfere with an haptic event triggered by an application + _audioCue(); suspendRedirectMode(); + bindingInfo->pressedState = true; + } else if (eventType == ButtonEventType::ButtonUnpressed) { + bindingInfo->pressedState = false; + } + } break; + case DigitalBindingType::ToggleTouchpadEmulationFix: { + if (eventType == ButtonEventType::ButtonPressed) { + //_vibrationCue(); // Better not let it interfere with an haptic event triggered by an application + _audioCue(); + OpenvrDeviceManipulationInfo::setTouchpadEmulationFixFlag(!OpenvrDeviceManipulationInfo::getTouchpadEmulationFixFlag()); + bindingInfo->pressedState = true; + } else if (eventType == ButtonEventType::ButtonUnpressed) { + bindingInfo->pressedState = false; } } break; default: { @@ -491,9 +501,46 @@ void OpenvrDeviceManipulationInfo::sendDigitalBinding(vrinputemulator::DigitalBi } +void OpenvrDeviceManipulationInfo::_audioCue() { + auto audiofile = CServerDriver::getInstallDirectory().append("\\resources\\sounds\\audiocue.wav"); + PlaySoundA(audiofile.c_str(), NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT); +} + + +void OpenvrDeviceManipulationInfo::_vibrationCue() { + if (_vibrationCueTheadHandle != NULL) { + DWORD threadExitCode = 0; + if (GetExitCodeThread(_vibrationCueTheadHandle, &threadExitCode) != 0 && threadExitCode == STILL_ACTIVE) { + return; + } + CloseHandle(_vibrationCueTheadHandle); + _vibrationCueTheadHandle = NULL; + } + static struct _vibrationCueThreadArgs { + OpenvrDeviceManipulationInfo* deviceInfo; + } threadArgs; + threadArgs.deviceInfo = this; + _vibrationCueTheadHandle = CreateThread( + NULL, // default security attributes + 0, // use default stack size + [](LPVOID lpParam)->DWORD { + _vibrationCueThreadArgs* args = (_vibrationCueThreadArgs*)lpParam; + for (unsigned i = 0; i < 10; ++i) { + args->deviceInfo->m_triggerHapticPulseFunc(args->deviceInfo->controllerComponent(), 0, 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + return 0; + }, + &threadArgs, // argument to thread function + 0, // use default creation flags + NULL + ); +} + + void OpenvrDeviceManipulationInfo::sendAnalogBinding(vrinputemulator::AnalogBinding& binding, uint32_t unWhichDevice, uint32_t axisId, const vr::VRControllerAxis_t& axisState, OpenvrDeviceManipulationInfo::AnalogInputRemappingInfo::BindingInfo* bindingInfo) { if (binding.type == AnalogBindingType::NoRemapping) { - sendAxisEvent(unWhichDevice, axisId, axisState); + sendAxisEvent(unWhichDevice, axisId, axisState, false, bindingInfo); } else if (binding.type == AnalogBindingType::Disabled) { // nop } else { @@ -562,6 +609,15 @@ void OpenvrDeviceManipulationInfo::sendAnalogBinding(vrinputemulator::AnalogBind } +void OpenvrDeviceManipulationInfo::_buttonPressDeadzoneFix(vr::EVRButtonId eButtonId) { + auto& axisInfo = m_analogInputRemapping[eButtonId - vr::k_EButton_Axis0]; + if (axisInfo.remapping.valid && axisInfo.remapping.binding.buttonPressDeadzoneFix) { + if (axisInfo.binding.lastSendAxisState.x == 0.0f && axisInfo.binding.lastSendAxisState.y == 0.0f) { + CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, m_openvrId, eButtonId - vr::k_EButton_Axis0, {0.01f, 0.01f}); + } + } +} + void OpenvrDeviceManipulationInfo::sendButtonEvent(uint32_t& unWhichDevice, ButtonEventType eventType, vr::EVRButtonId eButtonId, double eventTimeOffset, bool directMode, DigitalInputRemappingInfo::BindingInfo* binding) { if (!directMode) { if (unWhichDevice == m_openvrId && ((m_deviceMode == 2 && !m_redirectSuspended) || m_deviceMode == 4)) { @@ -578,15 +634,25 @@ void OpenvrDeviceManipulationInfo::sendButtonEvent(uint32_t& unWhichDevice, Butt case ButtonEventType::ButtonPressed: { if (binding) { if (!binding->touchedState) { + if (eButtonId >= vr::k_EButton_Axis0 && eButtonId <= vr::k_EButton_Axis4) { + auto& axisInfo = m_analogInputRemapping[eButtonId - vr::k_EButton_Axis0]; + axisInfo.binding.touchedState = true; + } CServerDriver::openvrTrackedDeviceButtonTouched(m_driverHost, unWhichDevice, eButtonId, eventTimeOffset); binding->touchedState = true; binding->touchedAutoset = true; } if (!binding->pressedState) { + if (eButtonId >= vr::k_EButton_Axis0 && eButtonId <= vr::k_EButton_Axis4) { + _buttonPressDeadzoneFix(eButtonId); + } CServerDriver::openvrTrackedDeviceButtonPressed(m_driverHost, unWhichDevice, eButtonId, eventTimeOffset); binding->pressedState = true; } } else { + if (eButtonId >= vr::k_EButton_Axis0 && eButtonId <= vr::k_EButton_Axis4) { + _buttonPressDeadzoneFix(eButtonId); + } CServerDriver::openvrTrackedDeviceButtonPressed(m_driverHost, unWhichDevice, eButtonId, eventTimeOffset); } } break; @@ -702,7 +768,50 @@ void OpenvrDeviceManipulationInfo::sendAxisEvent(uint32_t& unWhichDevice, uint32 } } } - CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, unWhichDevice, unWhichAxis, axisState); + if (unWhichAxis < 5) { + if (m_analogInputRemapping[unWhichAxis].remapping.valid && touchpadEmulationEnabledFlag) { + auto newState = axisState; + auto& lastSeenState = m_analogInputRemapping[unWhichAxis].binding.lastSeenAxisState; + auto& lastSendState = m_analogInputRemapping[unWhichAxis].binding.lastSendAxisState; + bool suppress = false; + + if (m_analogInputRemapping[unWhichAxis].remapping.binding.touchpadEmulationMode == 1) { + if (axisState.x != 0.0f && vrmath::signum(axisState.x) == vrmath::signum(lastSendState.x) && abs(axisState.x) < abs(lastSendState.x)) { + newState.x = lastSendState.x; + } + if (axisState.y != 0.0f && vrmath::signum(axisState.y) == vrmath::signum(lastSendState.y) && abs(axisState.y) < abs(lastSendState.y)) { + newState.y = lastSendState.y; + } + + } else if (m_analogInputRemapping[unWhichAxis].remapping.binding.touchpadEmulationMode == 2) { + // Joystick was already in neutral position but we haven't send it yet, and now it moved away from neutral position + // => send neutral position before we do anything else since some menus use this information to reset input handling. + if (lastSeenState.x == 0.0f && lastSeenState.y == 0.0f && (lastSendState.x != 0.0f || lastSendState.y != 0.0f) && (axisState.x != 0.0f || axisState.y != 0.0f)) { + CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, unWhichDevice, unWhichAxis, {0.0f, 0.0f}); + lastSendState = {0.0f, 0.0f}; + } + if (axisState.x == 0.0f || (vrmath::signum(axisState.x) == vrmath::signum(lastSendState.x) && abs(axisState.x) < abs(lastSendState.x))) { + newState.x = lastSendState.x; + } + if (axisState.y == 0.0f || (vrmath::signum(axisState.y) == vrmath::signum(lastSendState.y) && abs(axisState.y) < abs(lastSendState.y))) { + newState.y = lastSendState.y; + } + } + + lastSeenState = axisState; + if (!suppress) { + CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, unWhichDevice, unWhichAxis, newState); + lastSendState = newState; + } + } else { + CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, unWhichDevice, unWhichAxis, axisState); + m_analogInputRemapping[unWhichAxis].binding.lastSeenAxisState = axisState; + m_analogInputRemapping[unWhichAxis].binding.lastSendAxisState = axisState; + } + + } else { + CServerDriver::openvrTrackedDeviceAxisUpdated(m_driverHost, unWhichDevice, unWhichAxis, axisState); + } } diff --git a/driver_vrinputemulator/src/driver_server.cpp b/driver_vrinputemulator/src/driver_server.cpp index 322083fa..b17caed6 100644 --- a/driver_vrinputemulator/src/driver_server.cpp +++ b/driver_vrinputemulator/src/driver_server.cpp @@ -37,6 +37,7 @@ namespace driver { CServerDriver* CServerDriver::singleton = nullptr; +std::string CServerDriver::installDir; std::map> CServerDriver::_openvrDeviceInfos; OpenvrDeviceManipulationInfo* CServerDriver::_openvrIdToDeviceInfoMap[vr::k_unMaxTrackedDeviceCount]; // index == openvrId @@ -366,6 +367,15 @@ vr::EVRInitError CServerDriver::Init(vr::IVRDriverContext *pDriverContext) { LOG(TRACE) << "CServerDriver::Init()"; VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); + // Read installation directory + vr::ETrackedPropertyError tpeError; + installDir = vr::VRProperties()->GetStringProperty(pDriverContext->GetDriverHandle(), vr::Prop_InstallPath_String, &tpeError); + if (tpeError == vr::TrackedProp_Success) { + LOG(INFO) << "Install Dir:" << installDir; + } else { + LOG(INFO) << "Could not get Install Dir: " << vr::VRPropertiesRaw()->GetPropErrorNameFromEnum(tpeError); + } + // Read vrsettings char buffer[vr::k_unMaxPropertyStringSize]; vr::EVRSettingsError peError; diff --git a/driver_vrinputemulator/src/driver_vrinputemulator.h b/driver_vrinputemulator/src/driver_vrinputemulator.h index 11885a8b..6497d89c 100644 --- a/driver_vrinputemulator/src/driver_vrinputemulator.h +++ b/driver_vrinputemulator/src/driver_vrinputemulator.h @@ -210,12 +210,17 @@ class OpenvrDeviceManipulationInfo { std::map m_digitalInputRemapping; struct AnalogInputRemappingInfo { - // currently no internal state needed struct BindingInfo { - } bindings[1]; + bool pressedState = false; + bool touchedState = false; + vr::VRControllerAxis_t lastSeenAxisState = { 0, 0 }; + vr::VRControllerAxis_t lastSendAxisState = { 0, 0 }; + } binding; AnalogInputRemapping remapping; }; - std::map m_analogInputRemapping; + AnalogInputRemappingInfo m_analogInputRemapping[5]; + + static bool touchpadEmulationEnabledFlag; bool m_redirectSuspended = false; OpenvrDeviceManipulationInfo* m_redirectRef = nullptr; @@ -230,9 +235,15 @@ class OpenvrDeviceManipulationInfo { vr::IVRProperties* m_vrproperties = nullptr; vr::PropertyContainerHandle_t m_propertyContainerHandle = vr::k_ulInvalidPropertyContainer; + HANDLE _vibrationCueTheadHandle = NULL; + void sendDigitalBinding(vrinputemulator::DigitalBinding& binding, uint32_t unWhichDevice, ButtonEventType eventType, vr::EVRButtonId eButtonId, double eventTimeOffset, DigitalInputRemappingInfo::BindingInfo* bindingInfo = nullptr); void sendAnalogBinding(vrinputemulator::AnalogBinding& binding, uint32_t unWhichDevice, uint32_t axisId, const vr::VRControllerAxis_t& axisState, AnalogInputRemappingInfo::BindingInfo* bindingInfo = nullptr); + void _buttonPressDeadzoneFix(vr::EVRButtonId eButtonId); + void _vibrationCue(); + void _audioCue(); + public: OpenvrDeviceManipulationInfo() {} OpenvrDeviceManipulationInfo(vr::ITrackedDeviceServerDriver* driver, vr::ETrackedDeviceClass eDeviceClass, uint32_t openvrId, vr::IVRServerDriverHost* driverHost) @@ -317,6 +328,13 @@ class OpenvrDeviceManipulationInfo { void RunFrameDigitalBinding(vrinputemulator::DigitalBinding& binding, vr::EVRButtonId eButtonId, DigitalInputRemappingInfo::BindingInfo& bindingInfo); void suspendRedirectMode(); + + static bool getTouchpadEmulationFixFlag() { + return touchpadEmulationEnabledFlag; + } + static void setTouchpadEmulationFixFlag(bool flag) { + touchpadEmulationEnabledFlag = flag; + } }; @@ -365,6 +383,8 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { static CServerDriver* getInstance() { return singleton; } + static std::string getInstallDirectory() { return installDir; } + uint32_t virtualDevices_getDeviceCount(); CTrackedDeviceDriver* virtualDevices_getDevice(uint32_t unWhichDevice); @@ -423,6 +443,8 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { private: static CServerDriver* singleton; + static std::string installDir; + //// virtual devices related //// std::recursive_mutex _virtualDevicesMutex; uint32_t m_virtualDeviceCount = 0; diff --git a/installer/installer.nsi b/installer/installer.nsi index 66e7aae7..e0d44148 100644 --- a/installer/installer.nsi +++ b/installer/installer.nsi @@ -127,6 +127,9 @@ Section "Install" SecInstall File "${DRIVER_BASEDIR}\resources\driver.vrresources" SetOutPath "$vrRuntimePath\drivers\00vrinputemulator\resources\settings" File "${DRIVER_BASEDIR}\resources\settings\default.vrsettings" + SetOutPath "$vrRuntimePath\drivers\00vrinputemulator\resources\sounds" + File "${DRIVER_BASEDIR}\resources\sounds\audiocue.wav" + File "${DRIVER_BASEDIR}\resources\sounds\License.txt" SetOutPath "$vrRuntimePath\drivers\00vrinputemulator\bin\win64" File "${DRIVER_BASEDIR}\bin\x64\driver_00vrinputemulator.dll" @@ -168,9 +171,12 @@ Section "Uninstall" Delete "$vrRuntimePath2\drivers\00vrinputemulator\driver.vrdrivermanifest" Delete "$vrRuntimePath2\drivers\00vrinputemulator\resources\driver.vrresources" Delete "$vrRuntimePath2\drivers\00vrinputemulator\resources\settings\default.vrsettings" + Delete "$vrRuntimePath2\drivers\00vrinputemulator\resources\sounds\audiocue.wav" + Delete "$vrRuntimePath2\drivers\00vrinputemulator\resources\sounds\License.txt" Delete "$vrRuntimePath2\drivers\00vrinputemulator\bin\win64\driver_00vrinputemulator.dll" Delete "$vrRuntimePath2\drivers\00vrinputemulator\bin\win64\driver_vrinputemulator.log" RMdir "$vrRuntimePath2\drivers\00vrinputemulator\resources\settings" + RMdir "$vrRuntimePath2\drivers\00vrinputemulator\resources\sounds" RMdir "$vrRuntimePath2\drivers\00vrinputemulator\resources\" RMdir "$vrRuntimePath2\drivers\00vrinputemulator\bin\win64\" RMdir "$vrRuntimePath2\drivers\00vrinputemulator\bin\" diff --git a/lib_vrinputemulator/include/ipc_protocol.h b/lib_vrinputemulator/include/ipc_protocol.h index 78954fdf..690fa027 100644 --- a/lib_vrinputemulator/include/ipc_protocol.h +++ b/lib_vrinputemulator/include/ipc_protocol.h @@ -54,7 +54,8 @@ enum class RequestType : uint32_t { InputRemapping_SetDigitalRemapping, InputRemapping_GetDigitalRemapping, InputRemapping_SetAnalogRemapping, - InputRemapping_GetAnalogRemapping + InputRemapping_GetAnalogRemapping, + InputRemapping_SetTouchpadEmulationFixEnabled }; @@ -328,6 +329,12 @@ struct Request_InputRemapping_GetAnalogRemapping { uint32_t axisId; }; +struct Request_InputRemapping_SetTouchpadEmulationFixEnabled { + uint32_t clientId; + uint32_t messageId; // Used to associate with Reply + bool enable; +}; + struct Request { @@ -370,6 +377,7 @@ struct Request { Request_InputRemapping_GetDigitalRemapping ir_GetDigitalRemapping; Request_InputRemapping_SetAnalogRemapping ir_SetAnalogRemapping; Request_InputRemapping_GetAnalogRemapping ir_GetAnalogRemapping; + Request_InputRemapping_SetTouchpadEmulationFixEnabled ir_SetTouchPadEmulationFixEnabled; MsgUnion() {} } msg; }; diff --git a/lib_vrinputemulator/include/openvr_math.h b/lib_vrinputemulator/include/openvr_math.h index 3e81b97e..43170e6a 100644 --- a/lib_vrinputemulator/include/openvr_math.h +++ b/lib_vrinputemulator/include/openvr_math.h @@ -86,6 +86,10 @@ inline vr::HmdVector3d_t operator/(const vr::HmdVector3d_t& lhs, const double rh namespace vrmath { + template int signum(T v) { + return (v > (T)0) ? 1 : ((v < (T)0) ? -1 : 0); + } + inline vr::HmdQuaternion_t quaternionFromRotationAxis(double rot, double ux, double uy, double uz) { auto ha = rot / 2; return{ diff --git a/lib_vrinputemulator/include/vrinputemulator_types.h b/lib_vrinputemulator/include/vrinputemulator_types.h index 428b8bb5..ef8f0924 100644 --- a/lib_vrinputemulator/include/vrinputemulator_types.h +++ b/lib_vrinputemulator/include/vrinputemulator_types.h @@ -76,7 +76,8 @@ namespace vrinputemulator { Disabled = 1, OpenVR = 2, Keyboard = 3, - SuspendRedirectMode = 4 + SuspendRedirectMode = 4, + ToggleTouchpadEmulationFix = 5 }; @@ -99,11 +100,11 @@ namespace vrinputemulator { BindingUnion() {} } data; - bool toggleEnabled; - uint32_t toggleDelay; + bool toggleEnabled = false; + uint32_t toggleDelay = 0; - bool autoTriggerEnabled; - uint32_t autoTriggerFrequency; + bool autoTriggerEnabled = false; + uint32_t autoTriggerFrequency = 1; DigitalBinding() {} }; @@ -149,6 +150,8 @@ namespace vrinputemulator { bool swapAxes = false; float lowerDeadzone = 0.0; float upperDeadzone = 1.0; + unsigned touchpadEmulationMode = 0; // 0 .. Disabled, 1 .. Position Based, 2 .. Position Based (Deferred Zero Updates) + bool buttonPressDeadzoneFix = false; AnalogBinding(AnalogBindingType type = AnalogBindingType::NoRemapping) : type(type) {} };