Skip to content

Commit

Permalink
feat: inter controller communication
Browse files Browse the repository at this point in the history
  • Loading branch information
acolombier committed Dec 3, 2023
1 parent 94ba14d commit e71de08
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 2 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/controllers/controllermappingtablemodel.cpp
src/controllers/controlleroutputmappingtablemodel.cpp
src/controllers/controlpickermenu.cpp
src/controllers/controllerruntimedata.cpp
src/controllers/delegates/controldelegate.cpp
src/controllers/delegates/midibytedelegate.cpp
src/controllers/delegates/midichanneldelegate.cpp
Expand Down
1 change: 0 additions & 1 deletion src/controllers/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ class Controller : public QObject {
virtual void sendBytes(const QByteArray& data) = 0;

private: // but used by ControllerManager

virtual int open() = 0;
virtual int close() = 0;
// Requests that the device poll if it is a polling device. Returns true
Expand Down
18 changes: 17 additions & 1 deletion src/controllers/controllermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#include "controllers/controller.h"
#include "controllers/controllerlearningeventfilter.h"
#include "controllers/controllermappinginfoenumerator.h"
#include "controllers/controllerruntimedata.h"
#include "controllers/defs_controllers.h"
#include "controllers/midi/portmidienumerator.h"
#include "controllers/scripting/legacy/controllerscriptenginelegacy.h"
#include "moc_controllermanager.cpp"
#include "util/cmdlineargs.h"
#include "util/compatibility/qmutex.h"
Expand Down Expand Up @@ -89,7 +91,8 @@ ControllerManager::ControllerManager(UserSettingsPointer pConfig)
// its own event loop.
m_pControllerLearningEventFilter(new ControllerLearningEventFilter()),
m_pollTimer(this),
m_skipPoll(false) {
m_skipPoll(false),
m_pRuntimeData(std::make_shared<ControllerRuntimeData>(this)) {
qRegisterMetaType<std::shared_ptr<LegacyControllerMapping>>(
"std::shared_ptr<LegacyControllerMapping>");

Expand Down Expand Up @@ -293,6 +296,12 @@ void ControllerManager::slotSetUpDevices() {
qWarning() << "There was a problem opening" << name;
continue;
}
VERIFY_OR_DEBUG_ASSERT(pController->getScriptEngine()) {
qWarning() << "Unable to acquire the controller engine. Has the "
"controller open successfully?";
continue;
}
pController->getScriptEngine()->setRuntimeData(m_pRuntimeData);
pController->applyMapping();
}

Expand Down Expand Up @@ -385,6 +394,13 @@ void ControllerManager::openController(Controller* pController) {
// If successfully opened the device, apply the mapping and save the
// preference setting.
if (result == 0) {
VERIFY_OR_DEBUG_ASSERT(pController->getScriptEngine()) {
qWarning() << "Unable to acquire the controller engine. Has the "
"controller open successfully?";
return;
}
pController->getScriptEngine()->setRuntimeData(m_pRuntimeData);

pController->applyMapping();

// Update configuration to reflect controller is enabled.
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/controllermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Controller;
class ControllerLearningEventFilter;
class MappingInfoEnumerator;
class LegacyControllerMapping;
class ControllerRuntimeData;
class ControllerEnumerator;

/// Function to sort controllers by name
Expand Down Expand Up @@ -85,4 +86,5 @@ class ControllerManager : public QObject {
QSharedPointer<MappingInfoEnumerator> m_pMainThreadUserMappingEnumerator;
QSharedPointer<MappingInfoEnumerator> m_pMainThreadSystemMappingEnumerator;
bool m_skipPoll;
std::shared_ptr<ControllerRuntimeData> m_pRuntimeData;
};
3 changes: 3 additions & 0 deletions src/controllers/controllerruntimedata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include <controllers/controllerruntimedata.h>

#include "moc_controllerruntimedata.cpp"
34 changes: 34 additions & 0 deletions src/controllers/controllerruntimedata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <QVariant>

#include "util/assert.h"

/// ControllerRuntimeData is a wrapper that allows controllers script runtimes
/// to share arbitrary data via a the JavaScript interface. It doesn't enforce
/// any type consistency and it is the script responsibility to use this data in
/// a considerate, non-destructive way (append to lists, extend to objects,
/// ...), as well as expecting that others won't do so.
class ControllerRuntimeData : public QObject {
Q_OBJECT
public:
ControllerRuntimeData(QObject* parent)
: QObject(parent), m_value() {
}

const QVariant& get() const {
return m_value;
}

public slots:
void set(const QVariant& value) {
m_value = value;
emit updated(m_value);
}

signals:
void updated(const QVariant& value);

private:
QVariant m_value;
};
11 changes: 11 additions & 0 deletions src/controllers/scripting/controllerscriptenginebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

class Controller;
class QJSEngine;
class ControllerRuntimeData;
#ifdef MIXXX_USE_QML
class TrackCollectionManager;
#endif
Expand Down Expand Up @@ -47,12 +48,22 @@ class ControllerScriptEngineBase : public QObject {
return m_bTesting;
}

void setRuntimeData(std::shared_ptr<ControllerRuntimeData> runtimeData) {
m_pRuntimeData = std::move(runtimeData);
}

std::shared_ptr<ControllerRuntimeData> getRuntimeData() const {
return m_pRuntimeData;
}

#ifdef MIXXX_USE_QML
static void registerTrackCollectionManager(
std::shared_ptr<TrackCollectionManager> pTrackCollectionManager);
#endif

protected:
std::shared_ptr<ControllerRuntimeData> m_pRuntimeData;

virtual void shutdown();

void scriptErrorDialog(const QString& detailedError, const QString& key, bool bFatal = false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "control/controlobject.h"
#include "control/controlobjectscript.h"
#include "controllers/controllerruntimedata.h"
#include "controllers/scripting/legacy/controllerscriptenginelegacy.h"
#include "controllers/scripting/legacy/scriptconnectionjsproxy.h"
#include "mixer/playermanager.h"
Expand Down Expand Up @@ -47,6 +48,11 @@ ControllerScriptInterfaceLegacy::ControllerScriptInterfaceLegacy(
m_spinbackActive[i] = false;
m_softStartActive[i] = false;
}

connect(m_pEngine->getRuntimeData().get(),
&ControllerRuntimeData::updated,
this,
&ControllerScriptInterfaceLegacy::onRuntimeDataUpdated);
}

ControllerScriptInterfaceLegacy::~ControllerScriptInterfaceLegacy() {
Expand Down Expand Up @@ -141,6 +147,88 @@ void ControllerScriptInterfaceLegacy::setValue(
}
}

QJSValue ControllerScriptInterfaceLegacy::getRuntimeData() {
auto pJsEngine = m_pScriptEngineLegacy->jsEngine();
VERIFY_OR_DEBUG_ASSERT(pJsEngine) {
return QJSValue();
}
auto pRuntimeData = m_pScriptEngineLegacy->getRuntimeData();

if (!pRuntimeData) {
qWarning() << "Unable to fetch the runtime data";
return QJSValue();
}

return pJsEngine->toScriptValue(pRuntimeData->get());
}

void ControllerScriptInterfaceLegacy::setRuntimeData(const QJSValue& value) {
auto pJsEngine = m_pScriptEngineLegacy->jsEngine();
VERIFY_OR_DEBUG_ASSERT(pJsEngine) {
return;
}
auto pRuntimeData = m_pScriptEngineLegacy->getRuntimeData();

if (!pRuntimeData) {
qWarning() << "Unable to fetch the runtime data";
return;
}

pRuntimeData->set(value.toVariant());
qDebug() << "runtime data set successfully";
}

QJSValue ControllerScriptInterfaceLegacy::onRuntimeDataUpdate(const QJSValue& callback) {
if (!callback.isCallable()) {
m_pScriptEngineLegacy->throwJSError(
"Tried to connect runtime data update handler"
" to an invalid callback. Make sure that your code contains no "
"syntax errors.");
return QJSValue();
}
auto pJsEngine = m_pScriptEngineLegacy->jsEngine();
VERIFY_OR_DEBUG_ASSERT(pJsEngine) {
return QJSValue();
}
auto pRuntimeData = m_pScriptEngineLegacy->getRuntimeData();

if (!pRuntimeData) {
qWarning() << "Unable to fetch the runtime data";
return QJSValue();
}

ScriptConnection connection;
connection.engineJSProxy = this;
connection.controllerEngine = m_pScriptEngineLegacy;
connection.callback = callback;
connection.id = QUuid::createUuid();

m_runtimeDataConnections.append(connection);

return pJsEngine->newQObject(
new ScriptConnectionJSProxy(m_runtimeDataConnections.last()));
}

void ControllerScriptInterfaceLegacy::onRuntimeDataUpdated(const QVariant& value) {
auto pJsEngine = m_pScriptEngineLegacy->jsEngine();
const auto args = QJSValueList{
pJsEngine->toScriptValue(value),
};

for (const auto& connection : m_runtimeDataConnections) {
QJSValue func = connection.callback; // copy function because QJSValue::call is not const
QJSValue result = func.call(args);
if (result.isError()) {
if (m_pScriptEngineLegacy != nullptr) {
m_pScriptEngineLegacy->showScriptExceptionDialog(result);
}
qWarning() << "ControllerEngine: Invocation of connection " << connection.id.toString()
<< "connected to runtime data failed:"
<< result.toString();
}
}
}

double ControllerScriptInterfaceLegacy::getParameter(const QString& group, const QString& name) {
ControlObjectScript* coScript = getControlObjectScript(group, name);
if (coScript == nullptr) {
Expand Down Expand Up @@ -283,6 +371,7 @@ QJSValue ControllerScriptInterfaceLegacy::makeConnectionInternal(

bool ControllerScriptInterfaceLegacy::removeScriptConnection(
const ScriptConnection& connection) {
// TODO handle runtimeData connection
ControlObjectScript* coScript =
getControlObjectScript(connection.key.group, connection.key.item);

Expand All @@ -299,6 +388,7 @@ void ControllerScriptInterfaceLegacy::triggerScriptConnection(
return;
}

// TODO handle runtimeData connection
ControlObjectScript* coScript =
getControlObjectScript(connection.key.group, connection.key.item);
if (coScript == nullptr) {
Expand Down
10 changes: 10 additions & 0 deletions src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class ControllerScriptInterfaceLegacy : public QObject {

Q_INVOKABLE double getValue(const QString& group, const QString& name);
Q_INVOKABLE void setValue(const QString& group, const QString& name, double newValue);

Q_INVOKABLE QJSValue getRuntimeData();
Q_INVOKABLE void setRuntimeData(const QJSValue& value);
Q_INVOKABLE QJSValue onRuntimeDataUpdate(const QJSValue& callback);

Q_INVOKABLE double getParameter(const QString& group, const QString& name);
Q_INVOKABLE void setParameter(const QString& group, const QString& name, double newValue);
Q_INVOKABLE double getParameterForValue(
Expand Down Expand Up @@ -76,6 +81,9 @@ class ControllerScriptInterfaceLegacy : public QObject {
/// Handler for timers that scripts set.
virtual void timerEvent(QTimerEvent* event);

private slots:
void onRuntimeDataUpdated(const QVariant& value);

private:
QJSValue makeConnectionInternal(const QString& group,
const QString& name,
Expand Down Expand Up @@ -108,4 +116,6 @@ class ControllerScriptInterfaceLegacy : public QObject {

ControllerScriptEngineLegacy* m_pScriptEngineLegacy;
const RuntimeLoggingCategory m_logger;

QList<ScriptConnection> m_runtimeDataConnections;
};

0 comments on commit e71de08

Please sign in to comment.