diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index 515effd15c..88c4ae2daa 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -11,6 +11,16 @@ // Assumes you only have the default keyboard connected +Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print(deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); +}); + +Object.keys(Controller.Actions).forEach(function (actionName) { + print(actionName + ":" + Controller.Actions[actionName]); +}); + // Resets every device to its default key bindings: Controller.resetAllDeviceBindings(); diff --git a/interface/src/devices/DeviceTracker.h b/interface/src/devices/DeviceTracker.h index b28e22b06d..543e9bab1a 100644 --- a/interface/src/devices/DeviceTracker.h +++ b/interface/src/devices/DeviceTracker.h @@ -91,6 +91,9 @@ public: /// Get the name assigned to the Device when registered after creation, or "Unknown" if it hasn't been registered which should not happen. const Name& getName() const { return _name; } + typedef std::map< Name, ID > Map; + static const Map& getDevices() { return Singleton::get()->_devicesMap; } + protected: DeviceTracker(); virtual ~DeviceTracker(); @@ -103,7 +106,6 @@ private: void assignIDAndName( const ID id, const Name& name ) { _ID = id; _name = name; } typedef std::vector< DeviceTracker* > Vector; - typedef std::map< Name, ID > Map; struct SingletonData { Map _devicesMap; Vector _devicesVector; diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 6f36a6267f..4db482b6d4 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -111,7 +111,7 @@ void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::Input inputPair.second = QString(object.property("inputName").toVariant().toString()); } -void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) { +void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); qScriptRegisterSequenceMetaType >(engine); qScriptRegisterSequenceMetaType >(engine); @@ -119,6 +119,8 @@ void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); + + wireUpControllers(engine); } void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { @@ -381,6 +383,38 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { return qApp->getUiSize(); } +QString ControllerScriptingInterface::sanatizeName(const QString& name) { + QString cleanName { name }; + cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"}); + return cleanName; +} + +void ControllerScriptingInterface::wireUpControllers(ScriptEngine* engine) { + + // Controller.Hardware.* + auto devices = DependencyManager::get()->getDevices(); + for(const auto& deviceMapping : devices) { + auto device = deviceMapping.second.get(); + auto deviceName = sanatizeName(device->getName()); + auto deviceInputs = device->getAvailabeInputs(); + for (const auto& inputMapping : deviceInputs) { + auto input = inputMapping.first; + auto inputName = sanatizeName(inputMapping.second); + QString deviceInputName { "Controller.Hardware." + deviceName + "." + inputName }; + engine->registerValue(deviceInputName, input.getID()); + } + } + + // Controller.Actions.* + auto actionNames = DependencyManager::get()->getActionNames(); + int actionNumber = 0; + for (const auto& actionName : actionNames) { + QString safeActionName { "Controller.Actions." + sanatizeName(actionName) }; + engine->registerValue(safeActionName, actionNumber); + actionNumber++; + } +} + AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { // This is where we retreive the Device Tracker category and then the sub tracker within it //TODO C++11 auto icIt = _inputControllers.find(0); diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 6956619bf0..aa0526accb 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -56,7 +56,7 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface public: ControllerScriptingInterface(); - virtual void registerControllerTypes(QScriptEngine* engine); + virtual void registerControllerTypes(ScriptEngine* engine); void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } @@ -150,6 +150,8 @@ public slots: virtual void releaseInputController(AbstractInputController* input); private: + QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript + const PalmData* getPrimaryPalm() const; const PalmData* getPalm(int palmIndex) const; int getNumberOfActivePalms() const; @@ -164,6 +166,9 @@ private: typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap; InputControllerMap _inputControllers; + + void wireUpControllers(ScriptEngine* engine); + }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index 1b9b87684c..5c51db9410 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -319,4 +319,6 @@ void UserInputMapper::createActionNames() { _actionNames[SHIFT] = "SHIFT"; _actionNames[ACTION1] = "ACTION1"; _actionNames[ACTION2] = "ACTION2"; + _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; + _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; } diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 2657f335a0..1ad4294e0c 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -109,7 +109,8 @@ public: class DeviceProxy { public: DeviceProxy(QString name) { _name = name; } - + const QString& getName() const { return _name; } + QString _name; ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; @@ -234,12 +235,14 @@ public: UserInputMapper(); + typedef std::map DevicesMap; + DevicesMap getDevices() { return _registeredDevices; } + signals: void actionEvent(int action, float state); protected: - typedef std::map DevicesMap; DevicesMap _registeredDevices; uint16 _nextFreeDeviceID = 1; diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index b44e13f012..d6a6b51b62 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -24,6 +24,8 @@ #include "TouchEvent.h" #include "WheelEvent.h" +class ScriptEngine; + class AbstractInputController : public QObject { Q_OBJECT @@ -52,7 +54,7 @@ class AbstractControllerScriptingInterface : public QObject { Q_OBJECT public slots: - virtual void registerControllerTypes(QScriptEngine* engine) = 0; + virtual void registerControllerTypes(ScriptEngine* engine) = 0; virtual bool isPrimaryButtonPressed() const = 0; virtual glm::vec2 getPrimaryJoystickPosition() const = 0; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3906d68c42..76590f266b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -277,10 +277,6 @@ void ScriptEngine::init() { registerAvatarTypes(this); registerAudioMetaTypes(this); - if (_controllerScriptingInterface) { - _controllerScriptingInterface->registerControllerTypes(this); - } - qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); @@ -323,6 +319,43 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); + + if (_controllerScriptingInterface) { + _controllerScriptingInterface->registerControllerTypes(this); + } + + +} + +void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { + if (QThread::currentThread() != thread()) { +#ifdef THREAD_DEBUGGING + qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; +#endif + QMetaObject::invokeMethod(this, "registerValue", + Q_ARG(const QString&, valueName), + Q_ARG(QScriptValue, value)); + return; + } + + QStringList pathToValue = valueName.split("."); + int partsToGo = pathToValue.length(); + QScriptValue partObject = globalObject(); + + for (const auto& pathPart : pathToValue) { + partsToGo--; + if (!partObject.property(pathPart).isValid()) { + if (partsToGo > 0) { + //QObject *object = new QObject; + QScriptValue partValue = newArray(); //newQObject(object, QScriptEngine::ScriptOwnership); + qDebug() << "partValue[" << pathPart<<"].isArray() :" << partValue.isArray(); + partObject.setProperty(pathPart, partValue); + } else { + partObject.setProperty(pathPart, value); + } + } + partObject = partObject.property(pathPart); + } } void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { @@ -339,9 +372,13 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; #endif - if (object) { - QScriptValue value = newQObject(object); - globalObject().setProperty(name, value); + if (!globalObject().property(name).isValid()) { + if (object) { + QScriptValue value = newQObject(object); + globalObject().setProperty(name, value); + } else { + globalObject().setProperty(name, QScriptValue()); + } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3ff397e98d..1d3986143a 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -84,6 +84,9 @@ public: Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); + /// registers a global object by name + Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); + /// evaluate some code in the context of the ScriptEngine and return the result Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); // this is also used by the script tool widget