diff --git a/build/depends.py b/build/depends.py index b18b41b6a7e6..de4b41e8dd37 100644 --- a/build/depends.py +++ b/build/depends.py @@ -873,6 +873,7 @@ def sources(self, build): "controllers/delegates/midibytedelegate.cpp", "controllers/delegates/midioptionsdelegate.cpp", "controllers/engine/controllerengine.cpp", + "controllers/engine/controllerenginejsproxy.cpp", "controllers/learningutils.cpp", "controllers/midi/midimessage.cpp", "controllers/midi/midiutils.cpp", diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 3c87c2b170a8..56f63d55958c 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -92,7 +92,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { protected: // The length parameter is here for backwards compatibility for when scripts // were required to specify it. - Q_INVOKABLE void send(QList data, unsigned int length = 0); + void send(QList data, unsigned int length = 0); // To be called in sub-class' open() functions after opening the device but // before starting any input polling/processing. @@ -162,10 +162,30 @@ class Controller : public QObject, ConstControllerPresetVisitor { bool m_bLearning; QTime m_userActivityInhibitTimer; + friend class ControllerJSProxy; // accesses lots of our stuff, but in the same thread friend class ControllerManager; // For testing friend class ControllerPresetValidationTest; }; +// An object of this class gets exposed to the JS engine, so the methods of this class +// constitute the api that is provided to scripts under "controller" object. +// See comments on ControllerEngineJSProxy. +class ControllerJSProxy: public QObject { + public: + ControllerJSProxy(Controller* m_pController) + : m_pController(m_pController) { + } + + // The length parameter is here for backwards compatibility for when scripts + // were required to specify it. + Q_INVOKABLE void send(QList data, unsigned int length = 0) { + m_pController->send(data, length); + } + + private: + Controller* m_pController; +}; + #endif diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index b39a36d3a470..a1f01e8cdc09 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -8,6 +8,7 @@ #include "controllers/engine/controllerengine.h" +#include "controllers/engine/controllerenginejsproxy.h" #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" @@ -192,16 +193,19 @@ void ControllerEngine::initializeScriptEngine() { // Make this ControllerEngine instance available to scripts as 'engine'. QJSValue engineGlobalObject = m_pEngine->globalObject(); - engineGlobalObject.setProperty("engine", m_pEngine->newQObject(this)); + ControllerEngineJSProxy* proxy = new ControllerEngineJSProxy(this); + engineGlobalObject.setProperty("engine", m_pEngine->newQObject(proxy)); if (m_pController) { qDebug() << "Controller in script engine is:" << m_pController->getName(); + ControllerJSProxy* controllerProxy = new ControllerJSProxy(m_pController); + // Make the Controller instance available to scripts - engineGlobalObject.setProperty("controller", m_pEngine->newQObject(m_pController)); + engineGlobalObject.setProperty("controller", m_pEngine->newQObject(controllerProxy)); // ...under the legacy name as well - engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); + engineGlobalObject.setProperty("midi", m_pEngine->newQObject(controllerProxy)); } // m_pBaClass = new ByteArrayClass(m_pEngine); diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 2711c435a6b3..2bcca34b835e 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -26,6 +26,7 @@ class Controller; class ControlObjectScript; class ControllerEngine; +class ControllerEngineJSProxy; // ScriptConnection represents a connection between // a ControlObject and a script callback function that gets executed when @@ -101,35 +102,35 @@ class ControllerEngine : public QObject { void triggerScriptConnection(const ScriptConnection conn); protected: - Q_INVOKABLE double getValue(QString group, QString name); - Q_INVOKABLE void setValue(QString group, QString name, double newValue); - Q_INVOKABLE double getParameter(QString group, QString name); - Q_INVOKABLE void setParameter(QString group, QString name, double newValue); - Q_INVOKABLE double getParameterForValue(QString group, QString name, double value); - Q_INVOKABLE void reset(QString group, QString name); - Q_INVOKABLE double getDefaultValue(QString group, QString name); - Q_INVOKABLE double getDefaultParameter(QString group, QString name); - Q_INVOKABLE QJSValue makeConnection(QString group, QString name, + double getValue(QString group, QString name); + void setValue(QString group, QString name, double newValue); + double getParameter(QString group, QString name); + void setParameter(QString group, QString name, double newValue); + double getParameterForValue(QString group, QString name, double value); + void reset(QString group, QString name); + double getDefaultValue(QString group, QString name); + double getDefaultParameter(QString group, QString name); + QJSValue makeConnection(QString group, QString name, const QJSValue callback); // DEPRECATED: Use makeConnection instead. - Q_INVOKABLE QJSValue connectControl(QString group, QString name, + QJSValue connectControl(QString group, QString name, const QJSValue passedCallback, bool disconnect = false); // Called indirectly by the objects returned by connectControl - Q_INVOKABLE void trigger(QString group, QString name); - Q_INVOKABLE void log(QString message); - Q_INVOKABLE int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); - Q_INVOKABLE void stopTimer(int timerId); - Q_INVOKABLE void scratchEnable(int deck, int intervalsPerRev, double rpm, - double alpha, double beta, bool ramp = true); - Q_INVOKABLE void scratchTick(int deck, int interval); - Q_INVOKABLE void scratchDisable(int deck, bool ramp = true); - Q_INVOKABLE bool isScratching(int deck); - Q_INVOKABLE void softTakeover(QString group, QString name, bool set); - Q_INVOKABLE void softTakeoverIgnoreNextValue(QString group, QString name); - Q_INVOKABLE void brake(int deck, bool activate, double factor=1.0, double rate=1.0); - Q_INVOKABLE void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); - Q_INVOKABLE void softStart(int deck, bool activate, double factor=1.0); + void trigger(QString group, QString name); + void log(QString message); + int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); + void stopTimer(int timerId); + void scratchEnable(int deck, int intervalsPerRev, double rpm, + double alpha, double beta, bool ramp = true); + void scratchTick(int deck, int interval); + void scratchDisable(int deck, bool ramp = true); + bool isScratching(int deck); + void softTakeover(QString group, QString name, bool set); + void softTakeoverIgnoreNextValue(QString group, QString name); + void brake(int deck, bool activate, double factor=1.0, double rate=1.0); + void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); + void softStart(int deck, bool activate, double factor=1.0); // Handler for timers that scripts set. virtual void timerEvent(QTimerEvent *event); @@ -219,6 +220,7 @@ class ControllerEngine : public QObject { QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; + friend class ControllerEngineJSProxy; friend class ControllerEngineTest; }; diff --git a/src/controllers/engine/controllerenginejsproxy.cpp b/src/controllers/engine/controllerenginejsproxy.cpp new file mode 100644 index 000000000000..d78ae71e2fa8 --- /dev/null +++ b/src/controllers/engine/controllerenginejsproxy.cpp @@ -0,0 +1,116 @@ +#include "controllerenginejsproxy.h" +#include "controllers/engine/controllerengine.h" + +ControllerEngineJSProxy::ControllerEngineJSProxy(ControllerEngine* m_pEngine) + : m_pEngine(m_pEngine) { + +} + +ControllerEngineJSProxy::~ControllerEngineJSProxy() { + +} + +double ControllerEngineJSProxy::getValue(QString group, QString name) { + return m_pEngine->getValue(group, name); +} + +void ControllerEngineJSProxy::setValue(QString group, QString name, + double newValue) { + m_pEngine->setValue(group, name, newValue); +} + +double ControllerEngineJSProxy::getParameter(QString group, QString name) { + return m_pEngine->getParameter(group, name); +} + +void ControllerEngineJSProxy::setParameter(QString group, QString name, + double newValue) { + m_pEngine->setParameter(group, name, newValue); +} + +double ControllerEngineJSProxy::getParameterForValue(QString group, + QString name, double value) { + return m_pEngine->getParameterForValue(group, name, value); +} + +void ControllerEngineJSProxy::reset(QString group, QString name) { + m_pEngine->reset(group, name); +} + +double ControllerEngineJSProxy::getDefaultValue(QString group, QString name) { + return m_pEngine->getDefaultValue(group, name); +} + +double ControllerEngineJSProxy::getDefaultParameter(QString group, + QString name) { + return m_pEngine->getDefaultParameter(group, name); +} + +QJSValue ControllerEngineJSProxy::makeConnection(QString group, QString name, + const QJSValue callback) { + return m_pEngine->makeConnection(group, name, callback); +} + +QJSValue ControllerEngineJSProxy::connectControl(QString group, QString name, + const QJSValue passedCallback, bool disconnect) { + return m_pEngine->connectControl(group, name, passedCallback, disconnect); +} + +void ControllerEngineJSProxy::trigger(QString group, QString name) { + m_pEngine->trigger(group, name); +} + +void ControllerEngineJSProxy::log(QString message) { + m_pEngine->log(message); +} + +int ControllerEngineJSProxy::beginTimer(int interval, QJSValue scriptCode, + bool oneShot) { + return m_pEngine->beginTimer(interval, scriptCode, oneShot); +} + +void ControllerEngineJSProxy::stopTimer(int timerId) { + m_pEngine->stopTimer(timerId); +} + +void ControllerEngineJSProxy::scratchEnable(int deck, int intervalsPerRev, + double rpm, double alpha, double beta, bool ramp) { + m_pEngine->scratchEnable(deck, intervalsPerRev, rpm, alpha, beta, ramp); +} + +void ControllerEngineJSProxy::scratchTick(int deck, int interval) { + m_pEngine->scratchTick(deck, interval); +} + +void ControllerEngineJSProxy::scratchDisable(int deck, bool ramp) { + m_pEngine->scratchDisable(deck, ramp); +} + +bool ControllerEngineJSProxy::isScratching(int deck) { + return m_pEngine->isScratching(deck); +} + +void ControllerEngineJSProxy::softTakeover(QString group, QString name, + bool set) { + m_pEngine->softTakeover(group, name, set); +} + +void ControllerEngineJSProxy::softTakeoverIgnoreNextValue(QString group, + QString name) { + m_pEngine->softTakeoverIgnoreNextValue(group, name); +} + +void ControllerEngineJSProxy::brake(int deck, bool activate, double factor, + double rate) { + m_pEngine->brake(deck, activate, factor, rate); +} + +void ControllerEngineJSProxy::spinback(int deck, bool activate, double factor, + double rate) { + m_pEngine->spinback(deck, activate, factor, rate); +} + +void ControllerEngineJSProxy::softStart(int deck, bool activate, + double factor) { + m_pEngine->softStart(deck, activate, factor); +} diff --git a/src/controllers/engine/controllerenginejsproxy.h b/src/controllers/engine/controllerenginejsproxy.h new file mode 100644 index 000000000000..ac3e078e9f77 --- /dev/null +++ b/src/controllers/engine/controllerenginejsproxy.h @@ -0,0 +1,59 @@ +#ifndef CONTROLLERENGINEJSPROXY_H +#define CONTROLLERENGINEJSPROXY_H + +#include +#include + +class ControllerEngine; + +// An object of this class gets exposed to the JS engine, so the methods of this class +// constitute the api that is provided to scripts under "engine" object. +// +// The implementation simply forwards its method calls to the ControllerEngine. +// We cannot expose ControllerEngine directly to the JS engine because the JS engine would take +// ownership of ControllerEngine. This is problematic when we reload a script file, because we +// destroy the existing JS engine to create a new one. Then, since the JS engine owns ControllerEngine +// it will try to delete it. See this Qt bug: https://bugreports.qt.io/browse/QTBUG-41171 +class ControllerEngineJSProxy: public QObject { + Q_OBJECT + public: + + ControllerEngineJSProxy(ControllerEngine* m_pEngine); + + virtual ~ControllerEngineJSProxy(); + + Q_INVOKABLE double getValue(QString group, QString name); + Q_INVOKABLE void setValue(QString group, QString name, double newValue); + Q_INVOKABLE double getParameter(QString group, QString name); + Q_INVOKABLE void setParameter(QString group, QString name, double newValue); + Q_INVOKABLE double getParameterForValue(QString group, QString name, double value); + Q_INVOKABLE void reset(QString group, QString name); + Q_INVOKABLE double getDefaultValue(QString group, QString name); + Q_INVOKABLE double getDefaultParameter(QString group, QString name); + Q_INVOKABLE QJSValue makeConnection(QString group, QString name, + const QJSValue callback); + // DEPRECATED: Use makeConnection instead. + Q_INVOKABLE QJSValue connectControl(QString group, QString name, + const QJSValue passedCallback, + bool disconnect = false); + // Called indirectly by the objects returned by connectControl + Q_INVOKABLE void trigger(QString group, QString name); + Q_INVOKABLE void log(QString message); + Q_INVOKABLE int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); + Q_INVOKABLE void stopTimer(int timerId); + Q_INVOKABLE void scratchEnable(int deck, int intervalsPerRev, double rpm, + double alpha, double beta, bool ramp = true); + Q_INVOKABLE void scratchTick(int deck, int interval); + Q_INVOKABLE void scratchDisable(int deck, bool ramp = true); + Q_INVOKABLE bool isScratching(int deck); + Q_INVOKABLE void softTakeover(QString group, QString name, bool set); + Q_INVOKABLE void softTakeoverIgnoreNextValue(QString group, QString name); + Q_INVOKABLE void brake(int deck, bool activate, double factor=1.0, double rate=1.0); + Q_INVOKABLE void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); + Q_INVOKABLE void softStart(int deck, bool activate, double factor=1.0); + + private: + ControllerEngine* m_pEngine; +}; + +#endif // CONTROLLERENGINEJSPROXY_H