Working on refactoring the xbox hardware access and wiring up test code

This commit is contained in:
Brad Davis 2015-10-10 22:34:11 -07:00
parent d29c7ef267
commit 14f511350d
38 changed files with 1815 additions and 1172 deletions

View file

@ -8,84 +8,6 @@
#include "Endpoint.h"
#include <mutex>
#include <input-plugins/UserInputMapper.h>
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<UserInputMapper>();
// 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;
}
}

View file

@ -12,26 +12,45 @@
#include <list>
#include <memory>
#include <functional>
#include <input-plugins/UserInputMapper.h>
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<Endpoint>;
using List = std::list<Pointer>;
using Pair = std::pair<Pointer, Pointer>;
using ReadLambda = std::function<float()>;
using WriteLambda = std::function<void(float)>;
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

View file

@ -11,7 +11,7 @@
#include <QtCore/QObject>
#include <QtScript/QScriptValue>
namespace Controllers {
namespace controller {
Filter::Pointer Filter::parse(const QJsonObject& json) {
// FIXME parse the json object and determine the instance type to create

View file

@ -19,7 +19,7 @@
class QJsonObject;
namespace Controllers {
namespace controller {
// Encapsulates part of a filter chain
class Filter {

View file

@ -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")

View file

@ -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 <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(controllers)
#endif

View file

@ -1,63 +1,5 @@
#include "Mapping.h"
namespace Controllers {
namespace controller {
}
// class MappingsStack {
// std::list<Mapping> _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;
// }
// }
// };

View file

@ -11,6 +11,7 @@
#define hifi_Controllers_Mapping_h
#include <map>
#include <unordered_map>
#include <QtCore/QString>
@ -18,19 +19,15 @@
#include "Filter.h"
#include "Route.h"
namespace Controllers {
using ValueMap = std::map<Endpoint::Pointer, float>;
namespace controller {
class Mapping {
public:
// Map of source channels to route lists
using Map = std::map<Endpoint::Pointer, Route::List>;
using Pointer = std::shared_ptr<Mapping>;
using List = std::list<Pointer>;
Map _channelMappings;
ValueMap _lastValues;
void parse(const QString& json);
QString serialize();
@ -38,4 +35,4 @@ namespace Controllers {
}
#endif
#endif

View file

@ -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 <mutex>
#include <set>
#include <QtCore/QRegularExpression>
#include <GLMHelpers.h>
#include <DependencyManager.h>
#include <input-plugins/UserInputMapper.h>
#include <input-plugins/InputPlugin.h>
#include <input-plugins/KeyboardMouseDevice.h>
#include <plugins/PluginManager.h>
#include "impl/MappingBuilderProxy.h"
#include "Logging.h"
namespace Controllers {
void NewControllerScriptingInterface::update() {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
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<Mapping>());
QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
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<UserInputMapper>();
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<LambdaEndpoint>([=] {
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<VirtualEndpoint>(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<VirtualEndpoint>();
}
}
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<Mapping>();
_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<UserInputMapper>();
//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>();
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<JSEndpoint>(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<CompositeEndpoint>(first, second);
_compositeEndpoints[pair] = result;
} else {
result = iterator->second;
}
return result;
}
} // namespace controllers
// class MappingsStack {
// std::list<Mapping> _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"

View file

@ -10,21 +10,78 @@
#ifndef hifi_Controllers_NewControllerScriptingInterface_h
#define hifi_Controllers_NewControllerScriptingInterface_h
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtQml/QJSValue>
#include <QtScript/QScriptValue>
#include <input-plugins/UserInputMapper.h>
#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<QString, Mapping::Pointer>;
using MappingStack = std::list<Mapping::Pointer>;
using InputToEndpointMap = std::map<UserInputMapper::Input, Endpoint::Pointer>;
using EndpointSet = std::unordered_set<Endpoint::Pointer>;
using ValueMap = std::map<Endpoint::Pointer, float>;
using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
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;
};
}

View file

@ -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

View file

@ -15,7 +15,7 @@
class QJsonObject;
namespace Controllers {
namespace controller {
/*
* encapsulates a source, destination and filters to apply

View file

@ -12,22 +12,33 @@
#include <QtCore/QDebug>
#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<QString, Endpoint::Pointer> ENDPOINTS;
if (!ENDPOINTS.contains(endpoint)) {
ENDPOINTS[endpoint] = std::make_shared<Endpoint>();
}
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));
}
}

View file

@ -13,20 +13,30 @@
#include <QtCore/QString>
#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;
};

View file

@ -12,17 +12,47 @@
#include <GLMHelpers.h>
#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<float>::max() };
const float _interval;
};
QObject* RouteBuilderProxy::pulse(float interval) {
Filter::Pointer filter = std::make_shared<PulseFilter>(interval);
addFilter(filter);
return this;
}
void RouteBuilderProxy::addFilter(Filter::Lambda lambda) {
Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda);
addFilter(filterPointer);

View file

@ -12,19 +12,28 @@
#include <QtCore/QObject>
#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;
};
}

View file

@ -22,8 +22,8 @@ InputPluginList getInputPlugins() {
InputPlugin* PLUGIN_POOL[] = {
new KeyboardMouseDevice(),
new SDL2Manager(),
new SixenseManager(),
new ViveControllerManager(),
//new SixenseManager(),
//new ViveControllerManager(),
nullptr
};

View file

@ -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<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> 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);
}

View file

@ -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);

View file

@ -10,6 +10,11 @@
//
#include "KeyboardMouseDevice.h"
#include <QtGUI/QKeyEvent>
#include <QtGUI/QMouseEvent>
#include <QtGUI/QTouchEvent>
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<QTouchEvent::TouchPoint>& points) const {
glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) {
glm::vec2 averagePoint(0.0f);
if (points.count() > 0) {
for (auto& point : points) {

View file

@ -12,11 +12,15 @@
#ifndef hifi_KeyboardMouseDevice_h
#define hifi_KeyboardMouseDevice_h
#include <QTouchEvent>
#include <chrono>
#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<QTouchEvent::TouchPoint>& points) const;
std::chrono::high_resolution_clock _clock;
std::chrono::high_resolution_clock::time_point _lastTouchTime;
};

View file

@ -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<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> 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);
}

View file

@ -17,6 +17,8 @@
#include "InputDevice.h"
#include "StandardControls.h"
typedef std::shared_ptr<StandardController> 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:
};

View file

@ -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
};
}

View file

@ -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<QString> UserInputMapper::getDeviceNames() {
QVector<QString> 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<ActionToInputsMap::iterator, ActionToInputsMap::iterator> 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<InputChannel> 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::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
InputChannels allChannels;
getInputChannels(allChannels);
QVector<InputChannel> 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::Action> UserInputMapper::getAllActions() const {
QVector<Action> actions;
for (auto i = 0; i < NUM_ACTIONS; i++) {
actions.append(Action(i));
}
return actions;
}
QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
QVector<InputChannel> inputChannels;
std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> 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<QString> UserInputMapper::getActionNames() const {
QVector<QString> 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>();
_standardController->registerToUserInputMapper(*this);
}
//
// 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<QString> UserInputMapper::getDeviceNames() {
QVector<QString> 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<ActionToInputsMap::iterator, ActionToInputsMap::iterator> 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<InputChannel> 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::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
InputChannels allChannels;
getInputChannels(allChannels);
QVector<InputChannel> 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::Action> UserInputMapper::getAllActions() const {
QVector<Action> actions;
for (auto i = 0; i < NUM_ACTIONS; i++) {
actions.append(Action(i));
}
return actions;
}
QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
QVector<InputChannel> inputChannels;
std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> 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<QString> UserInputMapper::getActionNames() const {
QVector<QString> 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>();
_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;
}
}

View file

@ -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<InputPair>(); };
ResetBindings resetDeviceBindings = [] () -> bool { return true; };
float getValue(const Input& input, int timestamp = 0) const;
typedef std::shared_ptr<DeviceProxy> 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,
};

View file

@ -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)
}
}
}

View file

@ -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
}
}
}

View file

@ -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()
}
}
}

View file

@ -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()
}
}
}

View file

@ -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
}
}
}

View file

@ -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()
}
}
}

View file

@ -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
}
}

View file

@ -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 ]
}
}

View file

@ -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 ]
}
}

View file

@ -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'
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -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 <unordered_map>
#include <memory>
#include <cstdio>
#include <mutex>
#include <set>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QDir>
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
#include <QtCore/QLoggingCategory>
#include <QtCore/QRegularExpression>
#include <QtGui/QResizeEvent>
#include <QtGui/QWindow>
#include <QtGui/QGuiApplication>
#include <QtGui/QImage>
#include <QtQuick/QQuickItem>
//
// 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 <unordered_map>
#include <memory>
#include <cstdio>
#include <mutex>
#include <set>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QDir>
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
#include <QtCore/QLoggingCategory>
#include <QtGui/QResizeEvent>
#include <QtGui/QWindow>
#include <QtGui/QGuiApplication>
#include <QtGui/QImage>
#include <QtQuick/QQuickItem>
#include <QtQml/QQmlApplicationEngine>
#include <QtQml/QQmlContext>
#include <controllers/NewControllerScriptingInterface.h>
#include <DependencyManager.h>
#include <plugins/PluginManager.h>
#include <input-plugins/InputPlugin.h>
#include <input-plugins/KeyboardMouseDevice.h>
#include <input-plugins/UserInputMapper.h>
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<UserInputMapper>()->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<UserInputMapper>()->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<UserInputMapper>();
PluginManager::getInstance()->getInputPlugins();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
QString name = inputPlugin->getName();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (name == KeyboardMouseDevice::NAME) {
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);
}
}
// Register our component type with QML.
qmlRegisterType<AppHook>("com.highfidelity.test", 1, 0, "AppHook");
//qmlRegisterType<NewControllerScriptingInterface>("com.highfidelity.test", 1, 0, "NewControllers");
QGuiApplication app(argc, argv);
#include <plugins/Plugin.h>
#include <plugins/PluginContainer.h>
#include <plugins/PluginManager.h>
#include <input-plugins/InputPlugin.h>
#include <input-plugins/KeyboardMouseDevice.h>
#include <controllers/NewControllerScriptingInterface.h>
#include <DependencyManager.h>
#include <input-plugins/UserInputMapper.h>
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<void(bool)> 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<QQuickWindow *>(topLevel);
//
//QSurfaceFormat surfaceFormat = window->requestedFormat();
//window->setFormat(surfaceFormat);
//window->show();
//
//rc = app.exec();
//
//delete component;
//return rc;
//}
#include "main.moc"
{
DependencyManager::set<UserInputMapper>();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
QString name = inputPlugin->getName();
inputPlugin->activate();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (name == KeyboardMouseDevice::NAME) {
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(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"