diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp index dddacc5ae5..3f1d12b9de 100644 --- a/libraries/controllers/src/controllers/Endpoint.cpp +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -8,84 +8,6 @@ #include "Endpoint.h" -#include -#include +namespace controller { -namespace Controllers { - - // Ex: xbox.RY, xbox.A .... - class HardwareEndpoint : public Endpoint { - public: - virtual float value() override { - // ... - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - // Default does nothing, but in theory this could be something like vibration - // mapping.from(xbox.X).to(xbox.Vibrate) - } - }; - - // Ex: Standard.RY, Action.Yaw - class VirtualEndpoint : public Endpoint { - public: - virtual void apply(float newValue) { - if (newValue != _lastValue) { - _lastValue = newValue; - } - } - - virtual float value() { - return _lastValue; - } - - float _lastValue; - }; - - float currentTime() { - return 0; - } - /* - * A function which provides input - */ - class FunctionEndpoint : public Endpoint { - public: - - virtual float value() override { - float now = currentTime(); - float delta = now - _lastCalled; - float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); - _lastCalled = now; - return result; - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - if (newValue != oldValue) { - //_outputFunction.call(newValue, oldValue, source); - } - } - - float _lastValue{ NAN }; - float _lastCalled{ 0 }; - QScriptValue _outputFunction; - QScriptValue _inputFunction; - QScriptValue _object; - }; - - - - // FIXME how do we handle dynamic changes in connected hardware? - const Endpoint::List& Endpoint::getHardwareEndpoints() { - static Endpoint::List ACTIVE_HARDWARE_ENDPOINTS; - static std::once_flag once; - std::call_once(once, [&] { - auto userInputMapper = DependencyManager::get(); - // TODO populate ACTIVE_HARDWARE with all the connected devices - // For each connected device - // for each input channel - // build a HardwareEndpoint instance around the input channel and add it to the list - }); - - return ACTIVE_HARDWARE_ENDPOINTS; - } } diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 48cdf015fa..bea33517f5 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -12,26 +12,45 @@ #include #include +#include + +#include class QScriptValue; -namespace Controllers { +namespace controller { /* * Encapsulates a particular input / output, * i.e. Hydra.Button0, Standard.X, Action.Yaw */ class Endpoint { public: - virtual float value() { return 0; } // = 0; - virtual void apply(float newValue, float oldValue, const Endpoint& source) {} // = 0; - using Pointer = std::shared_ptr; using List = std::list; + using Pair = std::pair; + using ReadLambda = std::function; + using WriteLambda = std::function; - static const List& getHardwareEndpoints(); - static Pointer getEndpoint(const QScriptValue& value); + Endpoint(const UserInputMapper::Input& id) : _id(id) {} + virtual float value() = 0; + virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; + const UserInputMapper::Input& getId() { return _id; } + protected: + UserInputMapper::Input _id; }; + class LambdaEndpoint : public Endpoint { + public: + LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) + : Endpoint(UserInputMapper::Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } + + virtual float value() override { return _readLambda(); } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _writeLambda(newValue); } + + private: + ReadLambda _readLambda; + WriteLambda _writeLambda; + }; } #endif diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index e0c6adfcac..17715eceff 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -11,7 +11,7 @@ #include #include -namespace Controllers { +namespace controller { Filter::Pointer Filter::parse(const QJsonObject& json) { // FIXME parse the json object and determine the instance type to create diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index de58dc3647..f3978e2c0a 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -19,7 +19,7 @@ class QJsonObject; -namespace Controllers { +namespace controller { // Encapsulates part of a filter chain class Filter { diff --git a/libraries/controllers/src/controllers/Logging.cpp b/libraries/controllers/src/controllers/Logging.cpp new file mode 100644 index 0000000000..ae6b523a45 --- /dev/null +++ b/libraries/controllers/src/controllers/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Logging.h" + +Q_LOGGING_CATEGORY(controllers, "hifi.controllers") diff --git a/libraries/controllers/src/controllers/Logging.h b/libraries/controllers/src/controllers/Logging.h new file mode 100644 index 0000000000..d74ddae59f --- /dev/null +++ b/libraries/controllers/src/controllers/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Controllers_Logging_h +#define hifi_Controllers_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(controllers) + +#endif diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index dd1ec14d1e..0063a1d24a 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -1,63 +1,5 @@ #include "Mapping.h" -namespace Controllers { +namespace controller { } -// class MappingsStack { -// std::list _stack; -// ValueMap _lastValues; -// -// void update() { -// EndpointList hardwareInputs = getHardwareEndpoints(); -// ValueMap currentValues; -// -// for (auto input : hardwareInputs) { -// currentValues[input] = input->value(); -// } -// -// // Now process the current values for each level of the stack -// for (auto& mapping : _stack) { -// update(mapping, currentValues); -// } -// -// _lastValues = currentValues; -// } -// -// void update(Mapping& mapping, ValueMap& values) { -// ValueMap updates; -// EndpointList consumedEndpoints; -// for (const auto& entry : values) { -// Endpoint* endpoint = entry.first; -// if (!mapping._channelMappings.count(endpoint)) { -// continue; -// } -// -// const Mapping::List& routes = mapping._channelMappings[endpoint]; -// consumedEndpoints.push_back(endpoint); -// for (const auto& route : routes) { -// float lastValue = 0; -// if (mapping._lastValues.count(endpoint)) { -// lastValue = mapping._lastValues[endpoint]; -// } -// float value = entry.second; -// for (const auto& filter : route._filters) { -// value = filter->apply(value, lastValue); -// } -// updates[route._destination] = value; -// } -// } -// -// // Update the last seen values -// mapping._lastValues = values; -// -// // Remove all the consumed inputs -// for (auto endpoint : consumedEndpoints) { -// values.erase(endpoint); -// } -// -// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) -// for (const auto& entry : updates) { -// values[entry.first] = entry.second; -// } -// } -// }; diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 4154701478..5b54a1745b 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -11,6 +11,7 @@ #define hifi_Controllers_Mapping_h #include +#include #include @@ -18,19 +19,15 @@ #include "Filter.h" #include "Route.h" -namespace Controllers { - - using ValueMap = std::map; +namespace controller { class Mapping { public: // Map of source channels to route lists using Map = std::map; using Pointer = std::shared_ptr; - using List = std::list; Map _channelMappings; - ValueMap _lastValues; void parse(const QString& json); QString serialize(); @@ -38,4 +35,4 @@ namespace Controllers { } -#endif \ No newline at end of file +#endif diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index bc915ba1a6..f5d6276b91 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -1,100 +1,330 @@ -#include "NewControllerScriptingInterface.h" +// +// Created by Bradley Austin Davis 2015/10/09 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "NewControllerScriptingInterface.h" #include #include +#include + #include #include #include +#include +#include +#include #include "impl/MappingBuilderProxy.h" +#include "Logging.h" -namespace Controllers { - void NewControllerScriptingInterface::update() { - auto userInputMapper = DependencyManager::get(); - static float last = secTimestampNow(); - float now = secTimestampNow(); - userInputMapper->update(now - last); - last = now; +static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1; + +namespace controller { + + + class VirtualEndpoint : public Endpoint { + public: + VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + + private: + float _currentValue{ 0.0f }; + }; + + + class JSEndpoint : public Endpoint { + public: + JSEndpoint(const QJSValue& callable) + : Endpoint(UserInputMapper::Input(-1)), _callable(callable) {} + + virtual float value() { + float result = (float)_callable.call().toNumber();; + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QJSValueList({ QJSValue(newValue) })); + } + + private: + QJSValue _callable; + }; + + class CompositeEndpoint : public Endpoint, Endpoint::Pair { + public: + CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) + : Endpoint(UserInputMapper::Input(-1)), Pair(first, second) { } + + virtual float value() { + float result = first->value() * -1.0 + second->value(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + // Composites are read only + } + + private: + Endpoint::Pointer _first; + Endpoint::Pointer _second; + }; + + QString sanatizeName(const QString& name) { + QString cleanName{ name }; + cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); + return cleanName; } - QObject* NewControllerScriptingInterface::newMapping() { - qDebug() << "Creating new Mapping proxy"; - return new MappingBuilderProxy(std::make_shared()); + QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) { + auto userInputMapper = DependencyManager::get(); + QVariantMap deviceMap; + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + const auto inputName = sanatizeName(inputMapping.second); + qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() + << QString::number(input.getID(), 16) << ": " << inputName; + deviceMap.insert(inputName, input.getID()); + } + return deviceMap; + } + + NewControllerScriptingInterface::NewControllerScriptingInterface() { + auto userInputMapper = DependencyManager::get(); + auto devices = userInputMapper->getDevices(); + for (const auto& deviceMapping : devices) { + auto device = deviceMapping.second.get(); + auto deviceName = sanatizeName(device->getName()); + qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; + // Expose the IDs to JS + _hardware.insert(deviceName, createDeviceMap(device)); + + // Create the endpoints + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + // Ignore aliases + if (_endpoints.count(input)) { + continue; + } + _endpoints[input] = std::make_shared([=] { + auto deviceProxy = userInputMapper->getDeviceProxy(input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(input, 0); + }); + } + } + + qCDebug(controllers) << "Setting up standard controller abstraction"; + auto standardDevice = userInputMapper->getStandardDevice(); + // Expose the IDs to JS + _standard = createDeviceMap(standardDevice.get()); + // Create the endpoints + for (const auto& inputMapping : standardDevice->getAvailabeInputs()) { + const auto& standardInput = inputMapping.first; + // Ignore aliases + if (_endpoints.count(standardInput)) { + continue; + } + _endpoints[standardInput] = std::make_shared(standardInput); + } + + auto actionNames = userInputMapper->getActionNames(); + int actionNumber = 0; + qCDebug(controllers) << "Setting up standard actions"; + for (const auto& actionName : actionNames) { + UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); + qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); + // Expose the IDs to JS + _actions.insert(sanatizeName(actionName), actionInput.getID()); + + // Create the endpoints + // FIXME action endpoints need to accumulate values, and have them cleared at each frame + _endpoints[actionInput] = std::make_shared(); + } + } + + QObject* NewControllerScriptingInterface::newMapping(const QString& mappingName) { + if (_mappingsByName.count(mappingName)) { + qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; + } + qDebug() << "Creating new Mapping " << mappingName; + Mapping::Pointer mapping = std::make_shared(); + _mappingsByName[mappingName] = mapping; + return new MappingBuilderProxy(*this, mapping); + } + + void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) { + auto iterator = _mappingsByName.find(mappingName); + if (_mappingsByName.end() == iterator) { + qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; + return; + } + + auto mapping = iterator->second; + if (enable) { + _activeMappings.push_front(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); + } } float NewControllerScriptingInterface::getValue(const int& source) { - //UserInputMapper::Input input; input._id = source; - //auto userInputMapper = DependencyManager::get(); - //auto deviceProxy = userInputMapper->getDeviceProxy(input); - //return deviceProxy->getButton(input, 0) ? 1.0 : 0.0; + // return (sin(secTimestampNow()) + 1.0f) / 2.0f; + UserInputMapper::Input input(source); + auto iterator = _endpoints.find(input); + if (_endpoints.end() == iterator) { + return 0.0; + } - return (sin(secTimestampNow()) + 1.0f) / 2.0f; + const auto& endpoint = iterator->second; + return getValue(endpoint); + } + + float NewControllerScriptingInterface::getValue(const Endpoint::Pointer& endpoint) { + auto valuesIterator = _overrideValues.find(endpoint); + if (_overrideValues.end() != valuesIterator) { + return valuesIterator->second; + } + + return endpoint->value(); + } + + + void NewControllerScriptingInterface::update() { + static float last = secTimestampNow(); + float now = secTimestampNow(); + float delta = now - last; + last = now; + + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + inputPlugin->pluginUpdate(delta, false); + } + + auto userInputMapper = DependencyManager::get(); + userInputMapper->update(delta); + + _overrideValues.clear(); + EndpointSet readEndpoints; + EndpointSet writtenEndpoints; + // Now process the current values for each level of the stack + for (auto& mapping : _activeMappings) { + for (const auto& mappingEntry : mapping->_channelMappings) { + const auto& source = mappingEntry.first; + + // 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 (readEndpoints.count(source)) { + continue; + } + + // Apply the value to all the routes + const auto& routes = mappingEntry.second; + + for (const auto& route : routes) { + const auto& destination = route->_destination; + + if (writtenEndpoints.count(destination)) { + continue; + } + + // Standard controller destinations can only be can only be used once. + if (userInputMapper->getStandardDeviceID() == destination->getId().getDevice()) { + writtenEndpoints.insert(destination); + } + + // 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 == destination; + if (!loopback) { + readEndpoints.insert(source); + } + + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); + + // Apply each of the filters. + const auto& filters = route->_filters; + for (const auto& filter : route->_filters) { + value = filter->apply(value); + } + + if (loopback) { + _overrideValues[source] = value; + } else { + destination->apply(value, 0, source); + } + } + } + } + } + + + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QJSValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(UserInputMapper::Input(endpoint.toInt())); + } + + if (endpoint.isCallable()) { + auto result = std::make_shared(endpoint); + return result; + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); + } + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QScriptValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(UserInputMapper::Input(endpoint.toInt32())); + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); + } + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { + auto iterator = _endpoints.find(inputId); + if (_endpoints.end() == iterator) { + qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); + return Endpoint::Pointer(); + } + return iterator->second; + } + + Endpoint::Pointer NewControllerScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { + EndpointPair pair(first, second); + Endpoint::Pointer result; + auto iterator = _compositeEndpoints.find(pair); + if (_compositeEndpoints.end() == iterator) { + result = std::make_shared(first, second); + _compositeEndpoints[pair] = result; + } else { + result = iterator->second; + } + return result; } } // namespace controllers - - - -// class MappingsStack { -// std::list _stack; -// ValueMap _lastValues; -// -// void update() { -// EndpointList hardwareInputs = getHardwareEndpoints(); -// ValueMap currentValues; -// -// for (auto input : hardwareInputs) { -// currentValues[input] = input->value(); -// } -// -// // Now process the current values for each level of the stack -// for (auto& mapping : _stack) { -// update(mapping, currentValues); -// } -// -// _lastValues = currentValues; -// } -// -// void update(Mapping& mapping, ValueMap& values) { -// ValueMap updates; -// EndpointList consumedEndpoints; -// for (const auto& entry : values) { -// Endpoint* endpoint = entry.first; -// if (!mapping._channelMappings.count(endpoint)) { -// continue; -// } -// -// const Mapping::List& routes = mapping._channelMappings[endpoint]; -// consumedEndpoints.push_back(endpoint); -// for (const auto& route : routes) { -// float lastValue = 0; -// if (mapping._lastValues.count(endpoint)) { -// lastValue = mapping._lastValues[endpoint]; -// } -// float value = entry.second; -// for (const auto& filter : route._filters) { -// value = filter->apply(value, lastValue); -// } -// updates[route._destination] = value; -// } -// } -// -// // Update the last seen values -// mapping._lastValues = values; -// -// // Remove all the consumed inputs -// for (auto endpoint : consumedEndpoints) { -// values.erase(endpoint); -// } -// -// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) -// for (const auto& entry : updates) { -// values[entry.first] = entry.second; -// } -// } -// }; //var mapping = Controller.newMapping(); //mapping.map(hydra.LeftButton0, actions.ContextMenu); //mapping.map(hydra.LeftButton0).to(xbox.RT); @@ -134,5 +364,3 @@ namespace Controllers { // mappingSnap.from(hydra.LX).to(function(newValue, oldValue) { // timeSinceLastYaw += deltaTime - -#include "NewControllerScriptingInterface.moc" diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index c4c42a2245..6bf0bda40d 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -10,21 +10,78 @@ #ifndef hifi_Controllers_NewControllerScriptingInterface_h #define hifi_Controllers_NewControllerScriptingInterface_h +#include +#include +#include +#include + #include #include +#include +#include + +#include + #include "Mapping.h" class QScriptValue; -namespace Controllers { +namespace controller { class NewControllerScriptingInterface : public QObject { Q_OBJECT - + Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) + Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) + Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL) + public: - Q_INVOKABLE void update(); - Q_INVOKABLE QObject* newMapping(); + NewControllerScriptingInterface(); Q_INVOKABLE float getValue(const int& source); + + Q_INVOKABLE void update(); + Q_INVOKABLE QObject* newMapping(const QString& mappingName); + Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); + Q_INVOKABLE void disableMapping(const QString& mappingName) { + enableMapping(mappingName, false); + } + + + const QVariantMap& getHardware() { return _hardware; } + const QVariantMap& getActions() { return _actions; } + const QVariantMap& getStandard() { return _standard; } + + private: + + // FIXME move to unordered set / map + using MappingMap = std::map; + using MappingStack = std::list; + using InputToEndpointMap = std::map; + using EndpointSet = std::unordered_set; + using ValueMap = std::map; + using EndpointPair = std::pair; + using EndpointPairMap = std::map; + + void update(Mapping::Pointer& mapping, EndpointSet& consumed); + float getValue(const Endpoint::Pointer& endpoint); + Endpoint::Pointer endpointFor(const QJSValue& endpoint); + Endpoint::Pointer endpointFor(const QScriptValue& endpoint); + Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); + Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + + friend class MappingBuilderProxy; + friend class RouteBuilderProxy; + private: + uint16_t _nextFunctionId; + InputToEndpointMap _endpoints; + EndpointPairMap _compositeEndpoints; + + ValueMap _overrideValues; + MappingMap _mappingsByName; + MappingStack _activeMappings; + + QVariantMap _hardware; + QVariantMap _actions; + QVariantMap _standard; }; } diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/Route.cpp index 29fb9b04eb..b9116d2813 100644 --- a/libraries/controllers/src/controllers/Route.cpp +++ b/libraries/controllers/src/controllers/Route.cpp @@ -12,10 +12,11 @@ #include "Endpoint.h" #include "Filter.h" +#include "Logging.h" class QJsonObject; -namespace Controllers { +namespace controller { /* * encapsulates a source, destination and filters to apply diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index 9459369d18..01770a87d1 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -15,7 +15,7 @@ class QJsonObject; -namespace Controllers { +namespace controller { /* * encapsulates a source, destination and filters to apply diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index e16fa511db..4e2c6a4d8c 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -12,22 +12,33 @@ #include #include "RouteBuilderProxy.h" +#include "../NewControllerScriptingInterface.h" +#include "../Logging.h" -namespace Controllers { +namespace controller { -QObject* MappingBuilderProxy::from(const QString& source) { - qDebug() << "Creating new Route builder proxy from " << source; +QObject* MappingBuilderProxy::from(const QJSValue& source) { + qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); + auto sourceEndpoint = _parent.endpointFor(source); + return from(sourceEndpoint); +} + +QObject* MappingBuilderProxy::from(const QScriptValue& source) { + qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); + auto sourceEndpoint = _parent.endpointFor(source); + return from(sourceEndpoint); +} + +QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { auto route = Route::Pointer(new Route()); - route->_source = endpointFor(source); - return new RouteBuilderProxy(this, route); + route->_source = source; + return new RouteBuilderProxy(_parent, _mapping, route); } -Endpoint::Pointer MappingBuilderProxy::endpointFor(const QString& endpoint) { - static QHash ENDPOINTS; - if (!ENDPOINTS.contains(endpoint)) { - ENDPOINTS[endpoint] = std::make_shared(); - } - return ENDPOINTS[endpoint]; +QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& source2) { + auto source1Endpoint = _parent.endpointFor(source1); + auto source2Endpoint = _parent.endpointFor(source2); + return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 6dac38b21e..b5e02bbfdf 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -13,20 +13,30 @@ #include #include "../Mapping.h" +#include "../Endpoint.h" -namespace Controllers { +class QJSValue; +class QScriptValue; + +namespace controller { + +class NewControllerScriptingInterface; class MappingBuilderProxy : public QObject { Q_OBJECT public: - MappingBuilderProxy(Mapping::Pointer mapping) - : _mapping(mapping) { } + MappingBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping) + : _parent(parent), _mapping(mapping) { } - Q_INVOKABLE QObject* from(const QString& fromEndpoint); + Q_INVOKABLE QObject* from(const QJSValue& source); + Q_INVOKABLE QObject* from(const QScriptValue& source); + Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); protected: + QObject* from(const Endpoint::Pointer& source); + friend class RouteBuilderProxy; - Endpoint::Pointer endpointFor(const QString& endpoint); + NewControllerScriptingInterface& _parent; Mapping::Pointer _mapping; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d1659573e4..e6b67e9ca6 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -12,17 +12,47 @@ #include #include "MappingBuilderProxy.h" +#include "../NewControllerScriptingInterface.h" +#include "../Logging.h" -namespace Controllers { +namespace controller { -void RouteBuilderProxy::to(const QString& destination) { - qDebug() << "Completed route: " << destination; +void RouteBuilderProxy::to(const QJSValue& destination) { + qCDebug(controllers) << "Completing route " << destination.toString(); + auto destinationEndpoint = _parent.endpointFor(destination); + return to(destinationEndpoint); +} + +void RouteBuilderProxy::to(const QScriptValue& destination) { + qCDebug(controllers) << "Completing route " << destination.toString(); + auto destinationEndpoint = _parent.endpointFor(destination); + return to(destinationEndpoint); +} + +void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { auto sourceEndpoint = _route->_source; - auto& mapping = _parent->_mapping; - mapping->_channelMappings[sourceEndpoint].push_back(_route); + _route->_destination = destination; + _mapping->_channelMappings[sourceEndpoint].push_back(_route); deleteLater(); } +QObject* RouteBuilderProxy::filter(const QJSValue& expression) { + if (expression.isCallable()) { + addFilter([=](float value) { + QJSValue originalExpression = expression; + QJSValueList params({ QJSValue(value) }); + auto result = originalExpression.call(params); + return (float)(result.toNumber()); + }); + } + return this; +} + +QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { + return this; +} + + QObject* RouteBuilderProxy::clamp(float min, float max) { addFilter([=](float value) { return glm::clamp(value, min, max); @@ -45,7 +75,7 @@ QObject* RouteBuilderProxy::deadZone(float min) { assert(min < 1.0f); float scale = 1.0f / (1.0f - min); addFilter([=](float value) { - if (value < min) { + if (abs(value) < min) { return 0.0f; } return (value - min) * scale; @@ -68,6 +98,40 @@ QObject* RouteBuilderProxy::constrainToPositiveInteger() { return this; } + +class PulseFilter : public Filter { +public: + PulseFilter(float interval) : _interval(interval) {} + + virtual float apply(float value) const override { + float result = 0.0; + + if (0.0 != value) { + float now = secTimestampNow(); + float delta = now - _lastEmitTime; + if (delta >= _interval) { + _lastEmitTime = now; + result = value; + } + } + + return result; + } + +private: + mutable float _lastEmitTime{ -std::numeric_limits::max() }; + const float _interval; +}; + + +QObject* RouteBuilderProxy::pulse(float interval) { + Filter::Pointer filter = std::make_shared(interval); + addFilter(filter); + return this; +} + + + void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); addFilter(filterPointer); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 516712b969..63cd106edb 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -12,19 +12,28 @@ #include #include "../Filter.h" #include "../Route.h" +#include "../Mapping.h" -namespace Controllers { +class QJSValue; +class QScriptValue; -class MappingBuilderProxy; +namespace controller { + +class NewControllerScriptingInterface; class RouteBuilderProxy : public QObject { Q_OBJECT public: - RouteBuilderProxy(MappingBuilderProxy* parent, Route::Pointer route) - : _parent(parent), _route(route) { } + RouteBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) + : _parent(parent), _mapping(mapping), _route(route) { } - Q_INVOKABLE void to(const QString& destination); + Q_INVOKABLE void to(const QJSValue& destination); + Q_INVOKABLE void to(const QScriptValue& destination); + + Q_INVOKABLE QObject* filter(const QJSValue& expression); + Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); + Q_INVOKABLE QObject* pulse(float interval); Q_INVOKABLE QObject* scale(float multiplier); Q_INVOKABLE QObject* invert(); Q_INVOKABLE QObject* deadZone(float min); @@ -32,10 +41,13 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToPositiveInteger(); private: + void to(const Endpoint::Pointer& destination); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); + Mapping::Pointer _mapping; Route::Pointer _route; - MappingBuilderProxy* _parent; + + NewControllerScriptingInterface& _parent; }; } diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 227bd12e1b..b52dd3f658 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,8 +22,8 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - new SixenseManager(), - new ViveControllerManager(), + //new SixenseManager(), + //new ViveControllerManager(), nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index d0e2705e98..5c6f43c604 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -15,6 +15,8 @@ #include "Joystick.h" +#include "StandardControls.h" + const float CONTROLLER_THRESHOLD = 0.3f; #ifdef HAVE_SDL2 @@ -55,39 +57,14 @@ void Joystick::focusOutEvent() { }; #ifdef HAVE_SDL2 + void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; - - switch (axis) { - case SDL_CONTROLLER_AXIS_LEFTX: - _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_LEFTY: - _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_RIGHTX: - _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_RIGHTY: - _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS; - break; - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS; - break; - default: - break; - } + _axisStateMap[makeInput((Controllers::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; } void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { - auto input = makeInput((SDL_GameControllerButton) event.button); + auto input = makeInput((Controllers::StandardButtonChannel)event.button); bool newValue = event.state == SDL_PRESSED; if (newValue) { _buttonPressedMap.insert(input.getChannel()); @@ -95,6 +72,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { _buttonPressedMap.erase(input.getChannel()); } } + #endif @@ -107,32 +85,57 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; -#ifdef HAVE_SDL2 - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); -#endif return availableInputs; }; proxy->resetDeviceBindings = [this, &mapper] () -> bool { @@ -150,76 +153,15 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { const float JOYSTICK_YAW_SPEED = 0.5f; const float JOYSTICK_PITCH_SPEED = 0.25f; const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); - - - // Hold front right shoulder button for precision controls - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A)); + #endif } -#ifdef HAVE_SDL2 -UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) { +UserInputMapper::Input Joystick::makeInput(Controllers::StandardButtonChannel button) { return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); } -#endif -UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) { +UserInputMapper::Input Joystick::makeInput(Controllers::StandardAxisChannel axis) { return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); } diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 2ba89da052..70949a8b83 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -21,6 +21,7 @@ #endif #include "InputDevice.h" +#include "StandardControls.h" class Joystick : public QObject, public InputDevice { Q_OBJECT @@ -31,18 +32,6 @@ class Joystick : public QObject, public InputDevice { #endif public: - enum JoystickAxisChannel { - LEFT_AXIS_X_POS = 0, - LEFT_AXIS_X_NEG, - LEFT_AXIS_Y_POS, - LEFT_AXIS_Y_NEG, - RIGHT_AXIS_X_POS, - RIGHT_AXIS_X_NEG, - RIGHT_AXIS_Y_POS, - RIGHT_AXIS_Y_NEG, - RIGHT_SHOULDER, - LEFT_SHOULDER, - }; const QString& getName() const { return _name; } @@ -55,10 +44,8 @@ public: Joystick() : InputDevice("Joystick") {} ~Joystick(); -#ifdef HAVE_SDL2 - UserInputMapper::Input makeInput(SDL_GameControllerButton button); -#endif - UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis); + UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); + UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); #ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 36ae643a8e..202a767244 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -10,6 +10,11 @@ // #include "KeyboardMouseDevice.h" +#include +#include +#include + + const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { @@ -81,7 +86,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); } -glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList& points) const { +glm::vec2 evalAverageTouchPoints(const QList& points) { glm::vec2 averagePoint(0.0f); if (points.count() > 0) { for (auto& point : points) { diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 6f703bc6f9..d96566e9d1 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -12,11 +12,15 @@ #ifndef hifi_KeyboardMouseDevice_h #define hifi_KeyboardMouseDevice_h -#include #include #include "InputDevice.h" #include "InputPlugin.h" +class QTouchEvent; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; + class KeyboardMouseDevice : public InputPlugin, public InputDevice { Q_OBJECT public: @@ -100,7 +104,6 @@ protected: glm::vec2 _lastTouch; bool _isTouching = false; - glm::vec2 evalAverageTouchPoints(const QList& points) const; std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; }; diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 4fb4a23654..040efca794 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -23,11 +23,6 @@ StandardController::~StandardController() { } void StandardController::update(float deltaTime, bool jointsCaptured) { - for (auto axisState : _axisStateMap) { - if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { - _axisStateMap[axisState.first] = 0.0f; - } - } } void StandardController::focusOutEvent() { @@ -44,119 +39,85 @@ void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_A), "Bottom Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_B), "Right Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_X), "Left Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_Y), "Top Button")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); + + // Poses + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LeftPose), "LeftPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RightPose), "RightPose")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand")); return availableInputs; }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { mapper.removeAllInputChannelsForDevice(_deviceID); this->assignDefaultInputMapping(mapper); return true; }; + mapper.registerStandardDevice(proxy); } void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left StandardController: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); - - // Right StandardController: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); - - - // Hold front right shoulder button for precision controls - // Left StandardController: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - - // Right StandardController: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(STANDARD_CONTROLLER_BUTTON_B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(STANDARD_CONTROLLER_BUTTON_A)); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardButtonChannel button) { return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardAxisChannel axis) { return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardPoseChannel pose) { return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); } diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/input-plugins/src/input-plugins/StandardController.h index f7a4215242..fa660e15b8 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/input-plugins/src/input-plugins/StandardController.h @@ -17,6 +17,8 @@ #include "InputDevice.h" +#include "StandardControls.h" + typedef std::shared_ptr StandardControllerPointer; class StandardController : public QObject, public InputDevice { @@ -24,37 +26,6 @@ class StandardController : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: - enum StandardControllerAxisChannel { - LEFT_AXIS_X_POS = 0, - LEFT_AXIS_X_NEG, - LEFT_AXIS_Y_POS, - LEFT_AXIS_Y_NEG, - RIGHT_AXIS_X_POS, - RIGHT_AXIS_X_NEG, - RIGHT_AXIS_Y_POS, - RIGHT_AXIS_Y_NEG, - RIGHT_SHOULDER, - LEFT_SHOULDER, - }; - enum StandardControllerButtonChannel { - STANDARD_CONTROLLER_BUTTON_A = 0, - STANDARD_CONTROLLER_BUTTON_B, - STANDARD_CONTROLLER_BUTTON_X, - STANDARD_CONTROLLER_BUTTON_Y, - - STANDARD_CONTROLLER_BUTTON_DPAD_UP, - STANDARD_CONTROLLER_BUTTON_DPAD_DOWN, - STANDARD_CONTROLLER_BUTTON_DPAD_LEFT, - STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT, - - STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER, - STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER, - }; - - enum StandardControllerPoseChannel { - LEFT_HAND = 0, - RIGHT_HAND, - }; const QString& getName() const { return _name; } @@ -67,9 +38,9 @@ public: StandardController() : InputDevice("Standard") {} ~StandardController(); - UserInputMapper::Input makeInput(StandardController::StandardControllerButtonChannel button); - UserInputMapper::Input makeInput(StandardController::StandardControllerAxisChannel axis); - UserInputMapper::Input makeInput(StandardController::StandardControllerPoseChannel pose); + UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); + UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); + UserInputMapper::Input makeInput(Controllers::StandardPoseChannel pose); private: }; diff --git a/libraries/input-plugins/src/input-plugins/StandardControls.h b/libraries/input-plugins/src/input-plugins/StandardControls.h new file mode 100644 index 0000000000..9b79bbdae1 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/StandardControls.h @@ -0,0 +1,55 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +namespace Controllers { + + // Needs to match order and values of SDL_GameControllerButton + enum StandardButtonChannel { + // Button quad + A = 0, + B, + X, + Y, + // Center buttons + BACK, + GUIDE, + START, + // Stick press + LS, + RS, + // Bumper press + LB, + RB, + // DPad + DU, + DD, + DL, + DR + }; + + // Needs to match order and values of SDL_GameControllerAxis + enum StandardAxisChannel { + // Left Analog stick + LX = 0, + LY, + // Right Analog stick + RX, + RY, + // Triggers + LT, + RT + }; + + // No correlation to SDL + enum StandardPoseChannel { + LeftPose = 0, + RightPose + }; + +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index fad962345c..c29acc09af 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -1,335 +1,368 @@ -// -// UserInputMapper.cpp -// input-plugins/src/input-plugins -// -// Created by Sam Gateau on 4/27/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "UserInputMapper.h" -#include "StandardController.h" - -// Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); - assignDefaulActionScales(); - createActionNames(); -} - -UserInputMapper::~UserInputMapper() { -} - - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ - proxy->_name += " (" + QString::number(deviceID) + ")"; - _registeredDevices[deviceID] = proxy; - return true; -} - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { - auto device = _registeredDevices.find(input.getDevice()); - if (device != _registeredDevices.end()) { - return (device->second); - } else { - return DeviceProxy::Pointer(); - } -} - -QString UserInputMapper::getDeviceName(uint16 deviceID) { - if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->_name; - } - return QString("unknown"); -} - - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - -int UserInputMapper::findDevice(QString name) { - for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } - } - return 0; -} - -QVector UserInputMapper::getDeviceNames() { - QVector result; - for (auto device : _registeredDevices) { - QString deviceName = device.second->_name.split(" (")[0]; - result << deviceName; - } - return result; -} - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - -void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); - _registeredDevices.erase(device); -} - -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } - - return channels; -} - -void UserInputMapper::update(float deltaTime) { - - // Reset the axis state for next loop - for (auto& channel : _actionStates) { - channel = 0.0f; - } - - for (auto& channel : _poseStates) { - channel = PoseValue(); - } - - int currentTimestamp = 0; - - for (auto& channelInput : _actionToInputsMap) { - auto& inputMapping = channelInput.second; - auto& inputID = inputMapping._input; - bool enabled = true; - - // Check if this input channel has modifiers and collect the possibilities - auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); - if (modifiersIt != _inputToModifiersMap.end()) { - Modifiers validModifiers; - bool isActiveModifier = false; - for (auto& modifier : modifiersIt->second) { - auto deviceProxy = getDeviceProxy(modifier); - if (deviceProxy->getButton(modifier, currentTimestamp)) { - validModifiers.push_back(modifier); - isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); - } - } - enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; - } - - // if enabled: default input or all modifiers on - if (enabled) { - auto deviceProxy = getDeviceProxy(inputID); - switch (inputMapping._input.getType()) { - case ChannelType::BUTTON: { - _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime - break; - } - case ChannelType::AXIS: { - _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); - break; - } - case ChannelType::POSE: { - if (!_poseStates[channelInput.first].isValid()) { - _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); - } - break; - } - default: { - break; //silence please - } - } - } else{ - // Channel input not enabled - enabled = false; - } - } - - // Scale all the channel step with the scale - static const float EPSILON = 0.01f; - for (auto i = 0; i < NUM_ACTIONS; i++) { - _actionStates[i] *= _actionScales[i]; - // Emit only on change, and emit when moving back to 0 - if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { - _lastActionStates[i] = _actionStates[i]; - emit actionEvent(i, _actionStates[i]); - } - // TODO: emit signal for pose changes - } -} - -QVector UserInputMapper::getAllActions() const { - QVector actions; - for (auto i = 0; i < NUM_ACTIONS; i++) { - actions.append(Action(i)); - } - return actions; -} - -QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { - QVector inputChannels; - std::pair ret; - ret = _actionToInputsMap.equal_range(action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - inputChannels.append(it->second); - } - return inputChannels; -} - -int UserInputMapper::findAction(const QString& actionName) const { - auto actions = getAllActions(); - for (auto action : actions) { - if (getActionName(action) == actionName) { - return action; - } - } - // If the action isn't found, return -1 - return -1; -} - -QVector UserInputMapper::getActionNames() const { - QVector result; - for (auto i = 0; i < NUM_ACTIONS; i++) { - result << _actionNames[i]; - } - return result; -} - -void UserInputMapper::assignDefaulActionScales() { - _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit - _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit - _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit - _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit - _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit - _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit - _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit - _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit - _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit - _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit - _actionScales[BOOM_IN] = 0.5f; // .5m per unit - _actionScales[BOOM_OUT] = 0.5f; // .5m per unit - _actionScales[LEFT_HAND] = 1.0f; // default - _actionScales[RIGHT_HAND] = 1.0f; // default - _actionScales[LEFT_HAND_CLICK] = 1.0f; // on - _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on - _actionStates[SHIFT] = 1.0f; // on - _actionStates[ACTION1] = 1.0f; // default - _actionStates[ACTION2] = 1.0f; // default -} - -// This is only necessary as long as the actions are hardcoded -// Eventually you can just add the string when you add the action -void UserInputMapper::createActionNames() { - _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; - _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; - _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; - _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; - _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; - _actionNames[VERTICAL_UP] = "VERTICAL_UP"; - _actionNames[YAW_LEFT] = "YAW_LEFT"; - _actionNames[YAW_RIGHT] = "YAW_RIGHT"; - _actionNames[PITCH_DOWN] = "PITCH_DOWN"; - _actionNames[PITCH_UP] = "PITCH_UP"; - _actionNames[BOOM_IN] = "BOOM_IN"; - _actionNames[BOOM_OUT] = "BOOM_OUT"; - _actionNames[LEFT_HAND] = "LEFT_HAND"; - _actionNames[RIGHT_HAND] = "RIGHT_HAND"; - _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; - _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; - _actionNames[SHIFT] = "SHIFT"; - _actionNames[ACTION1] = "ACTION1"; - _actionNames[ACTION2] = "ACTION2"; - _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; - _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; -} - -void UserInputMapper::registerStandardDevice() { - _standardController = std::make_shared(); - _standardController->registerToUserInputMapper(*this); -} \ No newline at end of file +// +// UserInputMapper.cpp +// input-plugins/src/input-plugins +// +// Created by Sam Gateau on 4/27/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "UserInputMapper.h" +#include "StandardController.h" + +const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); +const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); +const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); +const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + registerStandardDevice(); + assignDefaulActionScales(); + createActionNames(); +} + +UserInputMapper::~UserInputMapper() { +} + + +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ + proxy->_name += " (" + QString::number(deviceID) + ")"; + _registeredDevices[deviceID] = proxy; + return true; +} + +UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + auto device = _registeredDevices.find(input.getDevice()); + if (device != _registeredDevices.end()) { + return (device->second); + } else { + return DeviceProxy::Pointer(); + } +} + +QString UserInputMapper::getDeviceName(uint16 deviceID) { + if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { + return _registeredDevices[deviceID]->_name; + } + return QString("unknown"); +} + + +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + +void UserInputMapper::resetDevice(uint16 deviceID) { + auto device = _registeredDevices.find(deviceID); + if (device != _registeredDevices.end()) { + device->second->resetDeviceBindings(); + } +} + +int UserInputMapper::findDevice(QString name) { + for (auto device : _registeredDevices) { + if (device.second->_name.split(" (")[0] == name) { + return device.first; + } + } + return 0; +} + +QVector UserInputMapper::getDeviceNames() { + QVector result; + for (auto device : _registeredDevices) { + QString deviceName = device.second->_name.split(" (")[0]; + result << deviceName; + } + return result; +} + + +bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { + return addInputChannel(action, input, Input(), scale); +} + +bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { + // Check that the device is registered + if (!getDeviceProxy(input)) { + qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; + return false; + } + + auto inputChannel = InputChannel(input, modifier, action, scale); + + // Insert or replace the input to modifiers + if (inputChannel.hasModifier()) { + auto& modifiers = _inputToModifiersMap[input.getID()]; + modifiers.push_back(inputChannel._modifier); + std::sort(modifiers.begin(), modifiers.end()); + } + + // Now update the action To Inputs side of things + _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); + + return true; +} + +int UserInputMapper::addInputChannels(const InputChannels& channels) { + int nbAdded = 0; + for (auto& channel : channels) { + nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); + } + return nbAdded; +} + +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + +void UserInputMapper::removeDevice(int device) { + removeAllInputChannelsForDevice((uint16) device); + _registeredDevices.erase(device); +} + +int UserInputMapper::getInputChannels(InputChannels& channels) const { + for (auto& channel : _actionToInputsMap) { + channels.push_back(channel.second); + } + + return _actionToInputsMap.size(); +} + +QVector UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + +void UserInputMapper::update(float deltaTime) { + + // Reset the axis state for next loop + for (auto& channel : _actionStates) { + channel = 0.0f; + } + + for (auto& channel : _poseStates) { + channel = PoseValue(); + } + + int currentTimestamp = 0; + + for (auto& channelInput : _actionToInputsMap) { + auto& inputMapping = channelInput.second; + auto& inputID = inputMapping._input; + bool enabled = true; + + // Check if this input channel has modifiers and collect the possibilities + auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); + if (modifiersIt != _inputToModifiersMap.end()) { + Modifiers validModifiers; + bool isActiveModifier = false; + for (auto& modifier : modifiersIt->second) { + auto deviceProxy = getDeviceProxy(modifier); + if (deviceProxy->getButton(modifier, currentTimestamp)) { + validModifiers.push_back(modifier); + isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); + } + } + enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; + } + + // if enabled: default input or all modifiers on + if (enabled) { + auto deviceProxy = getDeviceProxy(inputID); + switch (inputMapping._input.getType()) { + case ChannelType::BUTTON: { + _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime + break; + } + case ChannelType::AXIS: { + _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); + break; + } + case ChannelType::POSE: { + if (!_poseStates[channelInput.first].isValid()) { + _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); + } + break; + } + default: { + break; //silence please + } + } + } else{ + // Channel input not enabled + enabled = false; + } + } + + // Scale all the channel step with the scale + static const float EPSILON = 0.01f; + for (auto i = 0; i < NUM_ACTIONS; i++) { + _actionStates[i] *= _actionScales[i]; + // Emit only on change, and emit when moving back to 0 + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + _lastActionStates[i] = _actionStates[i]; + emit actionEvent(i, _actionStates[i]); + } + // TODO: emit signal for pose changes + } +} + +QVector UserInputMapper::getAllActions() const { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + +int UserInputMapper::findAction(const QString& actionName) const { + auto actions = getAllActions(); + for (auto action : actions) { + if (getActionName(action) == actionName) { + return action; + } + } + // If the action isn't found, return -1 + return -1; +} + +QVector UserInputMapper::getActionNames() const { + QVector result; + for (auto i = 0; i < NUM_ACTIONS; i++) { + result << _actionNames[i]; + } + return result; +} + +void UserInputMapper::assignDefaulActionScales() { + _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit + _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit + _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit + _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit + _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit + _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit + _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit + _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit + _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit + _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit + _actionScales[BOOM_IN] = 0.5f; // .5m per unit + _actionScales[BOOM_OUT] = 0.5f; // .5m per unit + _actionScales[LEFT_HAND] = 1.0f; // default + _actionScales[RIGHT_HAND] = 1.0f; // default + _actionScales[LEFT_HAND_CLICK] = 1.0f; // on + _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on + _actionStates[SHIFT] = 1.0f; // on + _actionStates[ACTION1] = 1.0f; // default + _actionStates[ACTION2] = 1.0f; // default + _actionStates[TranslateX] = 1.0f; // default + _actionStates[TranslateY] = 1.0f; // default + _actionStates[TranslateZ] = 1.0f; // default + _actionStates[Roll] = 1.0f; // default + _actionStates[Pitch] = 1.0f; // default + _actionStates[Yaw] = 1.0f; // default +} + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; + _actionNames[LEFT_HAND] = "LEFT_HAND"; + _actionNames[RIGHT_HAND] = "RIGHT_HAND"; + _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; + _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; + _actionNames[SHIFT] = "SHIFT"; + _actionNames[ACTION1] = "ACTION1"; + _actionNames[ACTION2] = "ACTION2"; + _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; + _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; + _actionNames[TranslateX] = "TranslateX"; + _actionNames[TranslateY] = "TranslateY"; + _actionNames[TranslateZ] = "TranslateZ"; + _actionNames[Roll] = "Roll"; + _actionNames[Pitch] = "Pitch"; + _actionNames[Yaw] = "Yaw"; +} + +void UserInputMapper::registerStandardDevice() { + _standardController = std::make_shared(); + _standardController->registerToUserInputMapper(*this); +} + +float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const { + switch (input.getType()) { + case UserInputMapper::ChannelType::BUTTON: + return getButton(input, timestamp) ? 1.0f : 0.0f; + + case UserInputMapper::ChannelType::AXIS: + return getAxis(input, timestamp); + + case UserInputMapper::ChannelType::POSE: + return getPose(input, timestamp)._valid ? 1.0f : 0.0f; + + default: + return 0.0f; + } +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 1d64638ee1..304e74e8cc 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -48,8 +48,9 @@ public: union { struct { uint16 _device; // Up to 64K possible devices - uint16 _channel : 14; // 2^14 possible channel per Device + uint16 _channel : 13; // 2^13 possible channel per Device uint16 _type : 2; // 2 bits to store the Type directly in the ID + uint16 _padding : 1; // 2 bits to store the Type directly in the ID }; uint32 _id = 0; // by default Input is 0 meaning invalid }; @@ -74,13 +75,19 @@ public: // where the default initializer (a C++-11ism) for the union data above is not applied. explicit Input() : _id(0) {} explicit Input(uint32 id) : _id(id) {} - explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {} + explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)), _padding(0) {} Input(const Input& src) : _id(src._id) {} Input& operator = (const Input& src) { _id = src._id; return (*this); } bool operator ==(const Input& right) const { return _id == right._id; } bool operator < (const Input& src) const { return _id < src._id; } + + static const Input INVALID_INPUT; + static const uint16 INVALID_DEVICE; + static const uint16 INVALID_CHANNEL; + static const uint16 INVALID_TYPE; }; + // Modifiers are just button inputID typedef std::vector< Input > Modifiers; @@ -121,7 +128,7 @@ public: PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); }; AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector(); }; ResetBindings resetDeviceBindings = [] () -> bool { return true; }; - + float getValue(const Input& input, int timestamp = 0) const; typedef std::shared_ptr Pointer; }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. @@ -171,6 +178,13 @@ public: CONTEXT_MENU, TOGGLE_MUTE, + TranslateX, + TranslateY, + TranslateZ, + Roll, + Pitch, + Yaw, + NUM_ACTIONS, }; diff --git a/tests/controllers/qml/Xbox.qml b/tests/controllers/qml/Xbox.qml new file mode 100644 index 0000000000..ae66081162 --- /dev/null +++ b/tests/controllers/qml/Xbox.qml @@ -0,0 +1,99 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./xbox" +import "./controls" + +Item { + id: root + + property var device + + property real scale: 1.0 + width: 300 * scale + height: 215 * scale + + Image { + anchors.fill: parent + source: "xbox/xbox360-controller-md.png" + + LeftAnalogStick { + device: root.device + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + // Left stick press + ToggleButton { + controlId: root.device.LS + width: 16 * root.scale; height: 16 * root.scale + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + + RightAnalogStick { + device: root.device + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Right stick press + ToggleButton { + controlId: root.device.RS + width: 16 * root.scale; height: 16 * root.scale + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Left trigger + AnalogButton { + controlId: root.device.LT + width: 8; height: 64 + x: (20 * root.scale); y: (7 * root.scale) + } + + // Right trigger + AnalogButton { + controlId: root.device.RT + width: 8; height: 64 + x: (272 * root.scale); y: (7 * root.scale) + } + + // Left bumper + ToggleButton { + controlId: root.device.LB + width: 32 * root.scale; height: 16 * root.scale + x: (40 * root.scale); y: (7 * root.scale) + } + + // Right bumper + ToggleButton { + controlId: root.device.RB + width: 32 * root.scale; height: 16 * root.scale + x: (root.width - width) - (40 * root.scale); y: (7 * root.scale) + } + + DPad { + device: root.device + size: 48 * root.scale + x: (80 * root.scale); y: (71 * root.scale) + } + + XboxButtons { + device: root.device + size: 65 * root.scale + x: (206 * root.scale); y: (19 * root.scale) + } + + ToggleButton { + controlId: root.device.Back + width: 16 * root.scale; height: 12 * root.scale + x: (112 * root.scale); y: (45 * root.scale) + } + + ToggleButton { + controlId: root.device.Start + width: 16 * root.scale; height: 12 * root.scale + x: (177 * root.scale); y: (45 * root.scale) + } + } +} diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index ac171ac42c..ce8a491419 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -1,137 +1,97 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -Rectangle { - id: root - implicitHeight: column1.height + 24 - implicitWidth: column1.width + 24 - color: "lightgray" - - property real itemSize: 128 - - Component { - id: graphTemplate - Item { - implicitHeight: canvas.height + 2 + text.height - implicitWidth: canvas.width - property string text: loadText - - Canvas { - id: canvas - width: root.itemSize; height: root.itemSize; - antialiasing: false - property int controlId: control - property real value: 0.0 - property int drawWidth: 1 - - Timer { - interval: 50; running: true; repeat: true - onTriggered: { - parent.value = NewControllers.getValue(canvas.controlId) - parent.requestPaint(); - } - } - - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - - var image = ctx.getImageData(0, 0, canvas.width, canvas.height); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(image, -drawWidth, 0, canvas.width, canvas.height) - ctx.fillStyle = 'green' - // draw a filles rectangle - var height = canvas.height * canvas.value - ctx.fillRect(canvas.width - drawWidth, canvas.height - height, - drawWidth, height) - ctx.restore() - } - } - - Text { - id: text - text: parent.text - anchors.topMargin: 2 - anchors.horizontalCenter: canvas.horizontalCenter - anchors.top: canvas.bottom - font.pointSize: 12; - } - - } - } - - Column { - id: column1 - x: 12; y: 12 - spacing: 24 - Row { - spacing: 16 - Loader { - sourceComponent: graphTemplate; - property string loadText: "Key Left" - property int control: ControllerIds.Hardware.Keyboard2.Left - } - Loader { - sourceComponent: graphTemplate; - property string loadText: "DPad Up" - property int control: ControllerIds.Hardware.X360Controller1.DPadUp - } - /* - Loader { - sourceComponent: graphTemplate; - property string loadText: "Yaw Left" - property int control: ControllerIds.Actions.YAW_LEFT - } - Loader { - sourceComponent: graphTemplate; - property string loadText: "Yaw Left" - property int control: ControllerIds.Actions.YAW_LEFT - } -*/ - -// Loader { sourceComponent: graphTemplate; } -// Loader { sourceComponent: graphTemplate; } -// Loader { sourceComponent: graphTemplate; } - } - /* - Row { - spacing: 16 - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - } - Row { - spacing: 16 - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - } - */ - - - Button { - text: "Go!" - onClicked: { - // - -// var newMapping = NewControllers.newMapping(); -// console.log("Mapping Object " + newMapping); -// var routeBuilder = newMapping.from("Hello"); -// console.log("Route Builder " + routeBuilder); -// routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye"); - } - } - - Timer { - interval: 50; running: true; repeat: true - onTriggered: { - NewControllers.update(); - } - } - - } -} +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./xbox" +import "./controls" + +Column { + id: root + property var xbox: NewControllers.Hardware.X360Controller1 + property var actions: NewControllers.Actions + property var standard: NewControllers.Standard + property string mappingName: "TestMapping" + + spacing: 12 + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + NewControllers.update(); + } + } + + Row { + spacing: 8 + Button { + text: "Default Mapping" + onClicked: { + var mapping = NewControllers.newMapping("Default"); + 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); + NewControllers.enableMapping("Default"); + } + } + + Button { + text: "Build Mapping" + onClicked: { + var mapping = NewControllers.newMapping(root.mappingName); + // Inverting a value + mapping.from(xbox.RY).invert().to(standard.RY); + // Assigning a value from a function + mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); + // Constrainting a value to -1, 0, or 1, with a deadzone + mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); + mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); + } + } + + Button { + text: "Enable Mapping" + onClicked: NewControllers.enableMapping(root.mappingName) + } + + Button { + text: "Disable Mapping" + onClicked: NewControllers.disableMapping(root.mappingName) + } + } + + Row { + spacing: 8 + Xbox { device: root.xbox } + Xbox { device: root.standard } + } + + + Row { + ScrollingGraph { + controlId: NewControllers.Actions.Yaw + label: "Yaw" + min: -3.0 + max: 3.0 + size: 128 + } + } +} + diff --git a/tests/controllers/qml/controls/AnalogButton.qml b/tests/controllers/qml/controls/AnalogButton.qml new file mode 100644 index 0000000000..26f91458ac --- /dev/null +++ b/tests/controllers/qml/controls/AnalogButton.qml @@ -0,0 +1,45 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + property int controlId: 0 + property real value: 0 + property color color: 'black' + + function update() { + value = NewControllers.getValue(controlId); + canvas.requestPaint(); + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update(); + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + var fillHeight = root.value * canvas.height; + + ctx.fillStyle = 'red' + ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight); + ctx.fill(); + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/tests/controllers/qml/controls/AnalogStick.qml new file mode 100644 index 0000000000..8860aea49c --- /dev/null +++ b/tests/controllers/qml/controls/AnalogStick.qml @@ -0,0 +1,50 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + + property int halfSize: size / 2 + property var controlIds: [ 0, 0 ] + property vector2d value: Qt.vector2d(0, 0) + + function update() { + value = Qt.vector2d( + NewControllers.getValue(controlIds[0]), + NewControllers.getValue(controlIds[1]) + ); + canvas.requestPaint(); + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update() + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, width, height); + ctx.fill(); + ctx.translate(root.halfSize, root.halfSize) + ctx.lineWidth = 4 + ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1) + ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize) + ctx.stroke() + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/controls/ScrollingGraph.qml b/tests/controllers/qml/controls/ScrollingGraph.qml new file mode 100644 index 0000000000..69f919aaf1 --- /dev/null +++ b/tests/controllers/qml/controls/ScrollingGraph.qml @@ -0,0 +1,104 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + + property int controlId: 0 + property real value: 0.5 + property int scrollWidth: 1 + property real min: 0.0 + property real max: 1.0 + property bool log: false + property real range: max - min + property color color: 'blue' + property bool bar: false + property real lastHeight: -1 + property string label: "" + + function update() { + value = NewControllers.getValue(controlId); + canvas.requestPaint(); + } + + function drawHeight() { + if (value < min) { + return 0; + } + if (value > max) { + return height; + } + return ((value - min) / range) * height; + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update() + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + Text { + anchors.top: parent.top + text: root.label + + } + + Text { + anchors.right: parent.right + anchors.top: parent.top + text: root.max + } + + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: root.min + } + + function scroll() { + var ctx = canvas.getContext('2d'); + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + ctx.beginPath(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height) + ctx.restore() + } + + onPaint: { + scroll(); + var ctx = canvas.getContext('2d'); + ctx.save(); + var currentHeight = root.drawHeight(); + if (root.lastHeight == -1) { + root.lastHeight = currentHeight + } + +// var x = canvas.width - root.drawWidth; +// var y = canvas.height - drawHeight; +// ctx.fillStyle = root.color +// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1) +// ctx.fill(); +// ctx.restore() + + + ctx.beginPath(); + ctx.lineWidth = 1 + ctx.strokeStyle = root.color + ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) + ctx.stroke() + ctx.restore() + root.lastHeight = currentHeight + } + } +} + + diff --git a/tests/controllers/qml/controls/ToggleButton.qml b/tests/controllers/qml/controls/ToggleButton.qml new file mode 100644 index 0000000000..9ef54f5971 --- /dev/null +++ b/tests/controllers/qml/controls/ToggleButton.qml @@ -0,0 +1,43 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + width: size + height: size + property int size: 64 + property int controlId: 0 + property real value: 0 + property color color: 'black' + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + root.value = NewControllers.getValue(root.controlId); + canvas.requestPaint(); + } + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, width, height); + if (root.value > 0.0) { + ctx.fillStyle = root.color + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + ctx.fill(); + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/xbox/DPad.qml b/tests/controllers/qml/xbox/DPad.qml new file mode 100644 index 0000000000..8efe6c2b30 --- /dev/null +++ b/tests/controllers/qml/xbox/DPad.qml @@ -0,0 +1,43 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + + +Item { + id: root + property int size: 64 + width: size + height: size + property int spacer: size / 3 + property var device + property color color: 'black' + + ToggleButton { + controlId: device.Up + x: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Left + y: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Right + x: spacer * 2; y: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Down + x: spacer; y: spacer * 2 + width: spacer; height: spacer + } +} + + diff --git a/tests/controllers/qml/xbox/LeftAnalogStick.qml b/tests/controllers/qml/xbox/LeftAnalogStick.qml new file mode 100644 index 0000000000..ed2689e7c8 --- /dev/null +++ b/tests/controllers/qml/xbox/LeftAnalogStick.qml @@ -0,0 +1,21 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property var device + + AnalogStick { + size: size + controlIds: [ device.LX, device.LY ] + } +} + + diff --git a/tests/controllers/qml/xbox/RightAnalogStick.qml b/tests/controllers/qml/xbox/RightAnalogStick.qml new file mode 100644 index 0000000000..611b4d8f92 --- /dev/null +++ b/tests/controllers/qml/xbox/RightAnalogStick.qml @@ -0,0 +1,21 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property var device + + AnalogStick { + size: size + controlIds: [ device.RX, device.RY ] + } +} + + diff --git a/tests/controllers/qml/xbox/XboxButtons.qml b/tests/controllers/qml/xbox/XboxButtons.qml new file mode 100644 index 0000000000..4a9e87799e --- /dev/null +++ b/tests/controllers/qml/xbox/XboxButtons.qml @@ -0,0 +1,46 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property int spacer: size / 3 + property var device + property color color: 'black' + + ToggleButton { + controlId: device.Y + x: spacer + width: spacer; height: spacer + color: 'yellow' + } + + ToggleButton { + controlId: device.X + y: spacer + width: spacer; height: spacer + color: 'blue' + } + + ToggleButton { + controlId: device.B + x: spacer * 2; y: spacer + width: spacer; height: spacer + color: 'red' + } + + ToggleButton { + controlId: device.A + x: spacer; y: spacer * 2 + width: spacer; height: spacer + color: 'green' + } +} + + diff --git a/tests/controllers/qml/xbox/xbox360-controller-md.png b/tests/controllers/qml/xbox/xbox360-controller-md.png new file mode 100644 index 0000000000..bdb596455f Binary files /dev/null and b/tests/controllers/qml/xbox/xbox360-controller-md.png differ diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 9840038521..d694bcae1d 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -1,166 +1,116 @@ -// -// main.cpp -// tests/gpu-test/src -// -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include +// +// main.cpp +// tests/gpu-test/src +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include #include #include - -#include -#include -#include -#include -#include -#include - -const QString& getQmlDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; - qDebug() << "Qml Path: " << dir; - } - return dir; -} - -class AppHook : public QQuickItem { - Q_OBJECT - -public: - AppHook() { - qDebug() << "Hook Created"; - } -}; - -using namespace Controllers; - - -QString sanatizeName(const QString& name) { - QString cleanName{ name }; - cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); - return cleanName; -} - -const QVariantMap& getInputMap() { - static std::once_flag once; - static QVariantMap map; - std::call_once(once, [&] { - { - QVariantMap hardwareMap; - // Controller.Hardware.* - auto devices = DependencyManager::get()->getDevices(); - for (const auto& deviceMapping : devices) { - auto device = deviceMapping.second.get(); - auto deviceName = sanatizeName(device->getName()); - auto deviceInputs = device->getAvailabeInputs(); - QVariantMap deviceMap; - for (const auto& inputMapping : deviceInputs) { - auto input = inputMapping.first; - auto inputName = sanatizeName(inputMapping.second); - deviceMap.insert(inputName, input.getID()); - } - hardwareMap.insert(deviceName, deviceMap); - } - map.insert("Hardware", hardwareMap); - } - - // Controller.Actions.* - { - QVariantMap actionMap; - auto actionNames = DependencyManager::get()->getActionNames(); - int actionNumber = 0; - for (const auto& actionName : actionNames) { - actionMap.insert(sanatizeName(actionName), actionNumber++); - } - map.insert("Actions", actionMap); - } - }); - return map; -} - -int main(int argc, char** argv) { - DependencyManager::set(); - PluginManager::getInstance()->getInputPlugins(); - - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - QString name = inputPlugin->getName(); - auto userInputMapper = DependencyManager::get(); - if (name == KeyboardMouseDevice::NAME) { - auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); - keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); - } - } - - // Register our component type with QML. - qmlRegisterType("com.highfidelity.test", 1, 0, "AppHook"); - //qmlRegisterType("com.highfidelity.test", 1, 0, "NewControllers"); - QGuiApplication app(argc, argv); + +#include +#include +#include +#include +#include +#include + +#include +#include + +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} + +using namespace controller; + + +class PluginContainerProxy : public QObject, PluginContainer { + Q_OBJECT +public: + PluginContainerProxy() { + Plugin::setContainer(this); + } + virtual ~PluginContainerProxy() {} + virtual void addMenu(const QString& menuName) override {} + virtual void removeMenu(const QString& menuName) override {} + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } + virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} + virtual bool isOptionChecked(const QString& name) override { return false; } + virtual void setIsOptionChecked(const QString& path, bool checked) override {} + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {} + virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} + virtual void showDisplayPluginsTools() override {} + virtual void requestReset() override {} + virtual QGLWidget* getPrimarySurface() override { return nullptr; } + virtual bool isForeground() override { return true; } + virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } +}; + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface()); - engine.rootContext()->setContextProperty("ControllerIds", getInputMap()); - engine.load(getQmlDir() + "main.qml"); - app.exec(); - return 0; -} - - - -//QQmlEngine engine; -//QQmlComponent *component = new QQmlComponent(&engine); -// -//QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); -// -//component->loadUrl(QUrl("main.qml")); -// -//if (!component->isReady()) { -// qWarning("%s", qPrintable(component->errorString())); -// return -1; -//} -// -//QObject *topLevel = component->create(); -//QQuickWindow *window = qobject_cast(topLevel); -// -//QSurfaceFormat surfaceFormat = window->requestedFormat(); -//window->setFormat(surfaceFormat); -//window->show(); -// -//rc = app.exec(); -// -//delete component; -//return rc; -//} - - - -#include "main.moc" + + + { + DependencyManager::set(); + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + QString name = inputPlugin->getName(); + inputPlugin->activate(); + auto userInputMapper = DependencyManager::get(); + if (name == KeyboardMouseDevice::NAME) { + auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky + keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); + } + } + + + //new PluginContainerProxy(); + auto rootContext = engine.rootContext(); + auto controllers = new NewControllerScriptingInterface(); + rootContext->setContextProperty("NewControllers", controllers); + QVariantMap map; + map.insert("Hardware", controllers->property("Hardware")); + map.insert("Actions", controllers->property("Actions")); + rootContext->setContextProperty("ControllerIds", map); + } + engine.load(getQmlDir() + "main.qml"); + app.exec(); + return 0; +} + +#include "main.moc" +