diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index fa8cf48b9b..48ad8f0879 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -1,4 +1,12 @@ ControllerTest = function() { + var standard = Controller.Standard; + var actions = Controller.Actions; + this.mappingEnabled = false; + this.mapping = Controller.newMapping(); + this.mapping.from(standard.RX).to(actions.StepYaw); + this.mapping.enable(); + this.mappingEnabled = true; + print("Actions"); for (var prop in Controller.Actions) { @@ -24,6 +32,9 @@ ControllerTest = function() { } ControllerTest.prototype.onCleanup = function() { + if (this.mappingEnabled) { + this.mapping.disable(); + } } diff --git a/interface/resources/qml/ScrollingGraph.qml b/interface/resources/qml/ScrollingGraph.qml index b5eaac6f89..26ca9a61ff 100644 --- a/interface/resources/qml/ScrollingGraph.qml +++ b/interface/resources/qml/ScrollingGraph.qml @@ -3,12 +3,12 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -Item { +Rectangle { id: root property int size: 64 width: size height: size - + color: 'black' property int controlId: 0 property real value: 0.5 property int scrollWidth: 1 @@ -16,7 +16,7 @@ Item { property real max: 1.0 property bool log: false property real range: max - min - property color color: 'blue' + property color lineColor: 'yellow' property bool bar: false property real lastHeight: -1 property string label: "" @@ -49,19 +49,21 @@ Item { Text { anchors.top: parent.top text: root.label - + color: 'white' } Text { anchors.right: parent.right anchors.top: parent.top text: root.max + color: 'white' } Text { anchors.right: parent.right anchors.bottom: parent.bottom text: root.min + color: 'white' } function scroll() { @@ -92,7 +94,7 @@ Item { ctx.beginPath(); ctx.lineWidth = 1 - ctx.strokeStyle = root.color + ctx.strokeStyle = root.lineColor ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) ctx.stroke() ctx.restore() diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index a5deaed159..f1b8640c02 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -20,8 +20,20 @@ HifiControls.VrDialog { property var standard: Controller.Standard property var hydra: null property var testMapping: null + property bool testMappingEnabled: false property var xbox: null + function buildMapping() { + testMapping = Controller.newMapping(); + testMapping.from(standard.RY).invert().to(actions.Pitch); + testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); + testMapping.from(standard.RX).to(actions.StepYaw); + } + + function toggleMapping() { + testMapping.enable(!testMappingEnabled); + testMappingEnabled = !testMappingEnabled; + } Component.onCompleted: { enabled = true @@ -49,110 +61,18 @@ HifiControls.VrDialog { Row { spacing: 8 - Button { - text: "Standard Mapping" - onClicked: { - var mapping = Controller.newMapping("Default"); - mapping.from(standard.LX).to(actions.TranslateX); - mapping.from(standard.LY).to(actions.TranslateZ); - mapping.from(standard.RY).to(actions.Pitch); - mapping.from(standard.RX).to(actions.Yaw); - mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD); - mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD); - mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT); - mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT); - mapping.from(standard.X).to(actions.VERTICAL_DOWN); - mapping.from(standard.Y).to(actions.VERTICAL_UP); - mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN); - mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT); - mapping.from(standard.B).to(actions.ACTION1); - mapping.from(standard.A).to(actions.ACTION2); - mapping.from(standard.RB).to(actions.SHIFT); - mapping.from(standard.Back).to(actions.TOGGLE_MUTE); - mapping.from(standard.Start).to(actions.CONTEXT_MENU); - Controller.enableMapping("Default"); - enabled = false; - text = "Standard Built" - } - } Button { - text: root.xbox ? "XBox Mapping" : "XBox not found" - property bool built: false - enabled: root.xbox && !built + text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping") onClicked: { - var mapping = Controller.newMapping(); - mapping.from(xbox.A).to(standard.A); - mapping.from(xbox.B).to(standard.B); - mapping.from(xbox.X).to(standard.X); - mapping.from(xbox.Y).to(standard.Y); - mapping.from(xbox.Up).to(standard.DU); - mapping.from(xbox.Down).to(standard.DD); - mapping.from(xbox.Left).to(standard.DL); - mapping.from(xbox.Right).to(standard.Right); - mapping.from(xbox.LB).to(standard.LB); - mapping.from(xbox.RB).to(standard.RB); - mapping.from(xbox.LS).to(standard.LS); - mapping.from(xbox.RS).to(standard.RS); - mapping.from(xbox.Start).to(standard.Start); - mapping.from(xbox.Back).to(standard.Back); - mapping.from(xbox.LY).to(standard.LY); - mapping.from(xbox.LX).to(standard.LX); - mapping.from(xbox.RY).to(standard.RY); - mapping.from(xbox.RX).to(standard.RX); - mapping.from(xbox.LT).to(standard.LT); - mapping.from(xbox.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "XBox Built" + + if (!root.testMapping) { + root.buildMapping() + } else { + root.toggleMapping(); + } } } - - Button { - text: root.hydra ? "Hydra Mapping" : "Hydra Not Found" - property bool built: false - enabled: root.hydra && !built - onClicked: { - var mapping = Controller.newMapping(); - mapping.from(hydra.LY).invert().to(standard.LY); - mapping.from(hydra.LX).to(standard.LX); - mapping.from(hydra.RY).invert().to(standard.RY); - mapping.from(hydra.RX).to(standard.RX); - mapping.from(hydra.LT).to(standard.LT); - mapping.from(hydra.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "Hydra Built" - } - } - - Button { - text: "Test Mapping" - onClicked: { - var mapping = Controller.newMapping(); - // Inverting a value - mapping.from(standard.RY).invert().to(standard.RY); - mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - testMapping = mapping; - enabled = false - text = "Built" - } - } - - Button { - text: "Enable Mapping" - onClicked: root.testMapping.enable() - } - - Button { - text: "Disable Mapping" - onClicked: root.testMapping.disable() - } - - Button { - text: "Enable Mapping" - onClicked: print(Controller.getValue(root.xbox.LY)); - } } Row { @@ -170,25 +90,32 @@ HifiControls.VrDialog { ScrollingGraph { controlId: Controller.Actions.Yaw label: "Yaw" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 } ScrollingGraph { - controlId: Controller.Actions.YAW_LEFT + controlId: Controller.Actions.YawLeft label: "Yaw Left" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 } ScrollingGraph { - controlId: Controller.Actions.YAW_RIGHT + controlId: Controller.Actions.YawRight label: "Yaw Right" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 + } + ScrollingGraph { + controlId: Controller.Actions.StepYaw + label: "StepYaw" + min: -2.0 + max: 2.0 + size: 64 } } } diff --git a/interface/resources/qml/controller/Standard.qml b/interface/resources/qml/controller/Standard.qml index cee79fe50c..45e4febfa2 100644 --- a/interface/resources/qml/controller/Standard.qml +++ b/interface/resources/qml/controller/Standard.qml @@ -29,7 +29,7 @@ Item { // Left primary ToggleButton { x: parent.width - width; y: parent.height - height; - controlId: root.device.RB + controlId: root.device.RightPrimaryThumb width: 16 * root.scale; height: 16 * root.scale } } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 7954ab5522..b0d2d24edf 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -42,6 +42,12 @@ namespace controller { makeAxisPair(Action::ROLL, "Roll"), makeAxisPair(Action::PITCH, "Pitch"), makeAxisPair(Action::YAW, "Yaw"), + makeAxisPair(Action::STEP_YAW, "StepYaw"), + makeAxisPair(Action::STEP_PITCH, "StepPitch"), + makeAxisPair(Action::STEP_ROLL, "StepRoll"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), @@ -67,7 +73,6 @@ namespace controller { makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), - // Deprecated aliases // FIXME remove after we port all scripts makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 77a772de9e..47f04141f3 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -27,6 +27,16 @@ enum class Action { ROTATE_Y, YAW = ROTATE_Y, ROTATE_Z, ROLL = ROTATE_Z, + STEP_YAW, + // FIXME does this have a use case? + STEP_PITCH, + // FIXME does this have a use case? + STEP_ROLL, + + STEP_TRANSLATE_X, + STEP_TRANSLATE_Y, + STEP_TRANSLATE_Z, + TRANSLATE_CAMERA_Z, NUM_COMBINED_AXES, diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d80952a5d9..ae806ed613 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -29,7 +29,6 @@ namespace controller { // Default contruct allocate the poutput size with the current hardcoded action channels controller::UserInputMapper::UserInputMapper() { - _activeMappings.push_back(_defaultMapping); _standardController = std::make_shared<StandardController>(); registerDevice(new ActionsDevice()); registerDevice(_standardController.get()); @@ -317,6 +316,7 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) { } void UserInputMapper::registerDevice(InputDevice* device) { + Locker locker(_lock); if (device->_deviceID == Input::INVALID_DEVICE) { device->_deviceID = getFreeDeviceID(); } @@ -354,13 +354,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { auto mapping = loadMapping(device->getDefaultMappingConfig()); if (mapping) { _mappingsByDevice[deviceID] = mapping; - auto& defaultRoutes = _defaultMapping->routes; - - // New routes for a device get injected IN FRONT of existing routes. Routes - // are processed in order so this ensures that the standard -> action processing - // takes place after all of the hardware -> standard or hardware -> action processing - // because standard -> action is the first set of routes added. - defaultRoutes.insert(defaultRoutes.begin(), mapping->routes.begin(), mapping->routes.end()); + enableMapping(mapping); } emit hardwareChanged(); @@ -368,6 +362,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { // FIXME remove the associated device mappings void UserInputMapper::removeDevice(int deviceID) { + Locker locker(_lock); auto proxyEntry = _registeredDevices.find(deviceID); if (_registeredDevices.end() == proxyEntry) { qCWarning(controllers) << "Attempted to remove unknown device " << deviceID; @@ -376,15 +371,7 @@ void UserInputMapper::removeDevice(int deviceID) { auto proxy = proxyEntry->second; auto mappingsEntry = _mappingsByDevice.find(deviceID); if (_mappingsByDevice.end() != mappingsEntry) { - const auto& mapping = mappingsEntry->second; - const auto& deviceRoutes = mapping->routes; - std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end()); - - auto& defaultRoutes = _defaultMapping->routes; - std::remove_if(defaultRoutes.begin(), defaultRoutes.end(), [&](Route::Pointer route)->bool { - return routeSet.count(route) != 0; - }); - + disableMapping(mappingsEntry->second); _mappingsByDevice.erase(mappingsEntry); } @@ -395,6 +382,7 @@ void UserInputMapper::removeDevice(int deviceID) { DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + Locker locker(_lock); auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { return (device->second); @@ -404,6 +392,7 @@ DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { } QString UserInputMapper::getDeviceName(uint16 deviceID) { + Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { return _registeredDevices[deviceID]->_name; } @@ -411,6 +400,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) { } int UserInputMapper::findDevice(QString name) const { + Locker locker(_lock); for (auto device : _registeredDevices) { if (device.second->_name == name) { return device.first; @@ -420,6 +410,7 @@ int UserInputMapper::findDevice(QString name) const { } QVector<QString> UserInputMapper::getDeviceNames() { + Locker locker(_lock); QVector<QString> result; for (auto device : _registeredDevices) { QString deviceName = device.second->_name.split(" (")[0]; @@ -433,6 +424,7 @@ int UserInputMapper::findAction(const QString& actionName) const { } Input UserInputMapper::findDeviceInput(const QString& inputName) const { + Locker locker(_lock); // Split the full input name as such: deviceName.inputName auto names = inputName.split('.'); @@ -472,6 +464,7 @@ void fixBisectedAxis(float& full, float& negative, float& positive) { } void UserInputMapper::update(float deltaTime) { + Locker locker(_lock); // Reset the axis state for next loop for (auto& channel : _actionStates) { channel = 0.0f; @@ -505,11 +498,13 @@ void UserInputMapper::update(float deltaTime) { } Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { + Locker locker(_lock); auto iterator = _registeredDevices.find(deviceID); return iterator->second->getAvailabeInputs(); } QVector<Action> UserInputMapper::getAllActions() const { + Locker locker(_lock); QVector<Action> actions; for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { actions.append(Action(i)); @@ -518,6 +513,7 @@ QVector<Action> UserInputMapper::getAllActions() const { } QString UserInputMapper::getActionName(Action action) const { + Locker locker(_lock); for (auto actionPair : getActionInputs()) { if (actionPair.first.channel == toInt(action)) { return actionPair.second; @@ -528,6 +524,7 @@ QString UserInputMapper::getActionName(Action action) const { QVector<QString> UserInputMapper::getActionNames() const { + Locker locker(_lock); QVector<QString> result; for (auto actionPair : getActionInputs()) { result << actionPair.second; @@ -645,74 +642,87 @@ void UserInputMapper::runMappings() { } // Now process the current values for each level of the stack - for (auto& mapping : _activeMappings) { - for (const auto& route : mapping->routes) { - if (route->conditional) { - if (!route->conditional->satisfied()) { - continue; - } - } + for (const auto& route : _deviceRoutes) { + if (!route) { + continue; + } + applyRoute(route); + } - auto source = route->source; - if (_overrides.count(source)) { - source = _overrides[source]; - } + for (const auto& route : _standardRoutes) { + if (!route) { + continue; + } + applyRoute(route); + } - // Endpoints can only be read once (though a given mapping can route them to - // multiple places). Consider... If the default is to wire the A button to JUMP - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when - // I press the button. The exception is if I'm wiring a control back to itself - // in order to adjust my interface, like inverting the Y axis on an analog stick - if (!source->readable()) { - continue; - } +} - auto input = source->getInput(); - float value = source->value(); - if (value != 0.0) { - int i = 0; - } - - auto destination = route->destination; - // THis could happen if the route destination failed to create - // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!destination) { - continue; - } - - // FIXME?, should come before or after the override logic? - if (!destination->writeable()) { - continue; - } - - // Only consume the input if the route isn't a loopback. - // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); - // Each time we loop back we re-write the override - if (loopback) { - _overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput()); - } - - // Fetch the value, may have been overriden by previous loopback routes - if (source->isPose()) { - Pose value = getPose(source); - // no filters yet for pose - destination->apply(value, Pose(), source); - } else { - // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); - - // Apply each of the filters. - for (const auto& filter : route->filters) { - value = filter->apply(value); - } - - destination->apply(value, 0, source); - } +void UserInputMapper::applyRoute(const Route::Pointer& route) { + if (route->conditional) { + if (!route->conditional->satisfied()) { + return; } } + auto source = route->source; + if (_overrides.count(source)) { + source = _overrides[source]; + } + + // Endpoints can only be read once (though a given mapping can route them to + // multiple places). Consider... If the default is to wire the A button to JUMP + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (!source->readable()) { + return; + } + + + auto input = source->getInput(); + float value = source->value(); + if (value != 0.0) { + int i = 0; + } + + auto destination = route->destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + return; + } + + // FIXME?, should come before or after the override logic? + if (!destination->writeable()) { + return; + } + + // Only consume the input if the route isn't a loopback. + // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` + bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); + // Each time we loop back we re-write the override + if (loopback) { + _overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput()); + } + + // Fetch the value, may have been overriden by previous loopback routes + if (source->isPose()) { + Pose value = getPose(source); + // no filters yet for pose + destination->apply(value, Pose(), source); + } else { + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); + + // Apply each of the filters. + for (const auto& filter : route->filters) { + value = filter->apply(value); + } + + destination->apply(value, 0, source); + } } Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { @@ -744,6 +754,7 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { } Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const { + Locker locker(_lock); auto iterator = _endpointsByInput.find(inputId); if (_endpointsByInput.end() == iterator) { qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); @@ -767,6 +778,7 @@ Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { + Locker locker(_lock); if (_mappingsByName.count(mappingName)) { qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; } @@ -799,8 +811,8 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { // return result; //} - void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { + Locker locker(_lock); qCDebug(controllers) << "Attempting to enable mapping " << mappingName; auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { @@ -810,18 +822,14 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { auto mapping = iterator->second; if (enable) { - _activeMappings.push_front(mapping); + enableMapping(mapping); } else { - auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping); - if (_activeMappings.end() == activeIterator) { - qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName; - return; - } - _activeMappings.erase(activeIterator); + disableMapping(mapping); } } float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { + Locker locker(_lock); auto valuesIterator = _overrides.find(endpoint); if (_overrides.end() != valuesIterator) { return valuesIterator->second->value(); @@ -854,6 +862,7 @@ Pose UserInputMapper::getPose(const Input& input) const { } Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { + Locker locker(_lock); if (jsonFile.isEmpty()) { return Mapping::Pointer(); } @@ -1102,6 +1111,36 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { return parseMapping(doc.object()); } + +void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { + Locker locker(_lock); + // New routes for a device get injected IN FRONT of existing routes. Routes + // are processed in order so this ensures that the standard -> action processing + // takes place after all of the hardware -> standard or hardware -> action processing + // because standard -> action is the first set of routes added. + for (auto route : mapping->routes) { + if (route->source->getInput().device == STANDARD_DEVICE) { + _standardRoutes.push_front(route); + } else { + _deviceRoutes.push_front(route); + } + } +} + +void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { + Locker locker(_lock); + const auto& deviceRoutes = mapping->routes; + std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end()); + + // FIXME this seems to result in empty route pointers... need to find a better way to remove them. + std::remove_if(_deviceRoutes.begin(), _deviceRoutes.end(), [&](Route::Pointer route)->bool { + return routeSet.count(route) != 0; + }); + std::remove_if(_standardRoutes.begin(), _standardRoutes.end(), [&](Route::Pointer route)->bool { + return routeSet.count(route) != 0; + }); +} + } #include "UserInputMapper.moc" diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 345bba8c2b..70cd227e05 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -15,6 +15,7 @@ #include <unordered_set> #include <functional> #include <memory> +#include <mutex> #include <QtQml/QJSValue> #include <QtScript/QScriptValue> @@ -119,10 +120,8 @@ namespace controller { void hardwareChanged(); protected: - virtual void runMappings(); // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } - InputDevice::Pointer _standardController; DevicesMap _registeredDevices; uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; @@ -142,6 +141,11 @@ namespace controller { friend class RouteBuilderProxy; friend class MappingBuilderProxy; + + void runMappings(); + void applyRoute(const Route::Pointer& route); + void enableMapping(const Mapping::Pointer& mapping); + void disableMapping(const Mapping::Pointer& mapping); Endpoint::Pointer endpointFor(const QJSValue& endpoint); Endpoint::Pointer endpointFor(const QScriptValue& endpoint); Endpoint::Pointer endpointFor(const Input& endpoint) const; @@ -163,9 +167,14 @@ namespace controller { EndpointOverrideMap _overrides; MappingNameMap _mappingsByName; - Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") }; MappingDeviceMap _mappingsByDevice; - MappingStack _activeMappings; + + Route::List _deviceRoutes; + Route::List _standardRoutes; + + using Locker = std::unique_lock<std::recursive_mutex>; + + mutable std::recursive_mutex _lock; }; }