/**************************************************************************** ** ** Copyright (C) 2019 Luxoft Sweden AB ** Copyright (C) 2018 Pelagicore AG ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtIvi module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL-QTAS$ ** Commercial License Usage ** Licensees holding valid commercial Qt Automotive Suite licenses may use ** this file in accordance with the commercial license agreement provided ** with the Software or, alternatively, in accordance with the terms ** contained in a written agreement between you and The Qt Company. For ** licensing terms and conditions see https://www.qt.io/terms-conditions. ** For further information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ** SPDX-License-Identifier: LGPL-3.0 ** ****************************************************************************/ #include "qivisimulationproxy.h" #include "qivisimulationengine.h" #include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcIviSimulationEngine, "qt.ivi.simulationengine"); namespace qtivi_private { QIviSimulationProxyBase::QIviSimulationProxyBase(QMetaObject *staticMetaObject, QObject *instance, const QHash &methodMap, QObject *parent) : QObject(parent) , m_noSimulationEngine(false) , m_instance(instance) , m_staticMetaObject(staticMetaObject) , m_methodMap(methodMap) { } const QMetaObject *QIviSimulationProxyBase::metaObject() const { // Copied from moc_ class code // A dynamicMetaObject is created when the type is used from QML and new functions/properties // are added. This makes sure that we can access these from C++ as well return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : m_staticMetaObject; } void *QIviSimulationProxyBase::qt_metacast(const char *classname) { if (!classname) return nullptr; return m_instance->qt_metacast(classname); } int QIviSimulationProxyBase::qt_metacall(QMetaObject::Call call, int methodId, void **a) { if (m_noSimulationEngine) return -1; // Special handling for the artificial "Base" property. // All other properties are forwarded to the qt_metacall generated by moc which translates the // absolute id's back to relative ones if (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) { int basePropertyIndex = m_staticMetaObject->indexOfProperty("Base"); if (methodId == basePropertyIndex) { void *_v = a[0]; *reinterpret_cast< QObject**>(_v) = m_instance; return -1; } } if (call == QMetaObject::InvokeMetaMethod) { // When a forwarded signal from the registered instance gets in. Directly call the signal here as well if (sender() == m_instance) { // The static MetaObject uses local ids, so we need to subtract the offset QMetaObject::activate(this, m_staticMetaObject, methodId - m_staticMetaObject->methodOffset(), a); return 0; } // As we don't derive from the MetaObject of m_instance, we need to use the methodMap to // translate our methodId to the methodId for m_instance return m_instance->qt_metacall(call, m_methodMap.key(methodId), a); } return m_instance->qt_metacall(call, methodId, a); } void QIviSimulationProxyBase::classBegin() { } void QIviSimulationProxyBase::componentComplete() { setProperty("Base", QVariant::fromValue(m_instance)); } QMetaObject QIviSimulationProxyBase::buildObject(const QMetaObject *metaObject, QHash &methodMap, QIviSimulationProxyBase::StaticMetacallFunction metaCallFunction) { QMetaObjectBuilder builder; const QString name = QString(QStringLiteral("QIviSimulationProxy_%1")).arg(QLatin1String(metaObject->className())); builder.setClassName(qPrintable(name)); builder.setSuperClass(&QObject::staticMetaObject); builder.setStaticMetacallFunction(metaCallFunction); // Build the MetaObject ourself // This is needed as QML uses the static_metacall for reading the properties and every QMetaObject // has its own set. But as we need to intercept this to forward it to the registered instance, we // build our MetaObject completely and have one static_metacall function for all properties const QMetaObject *mo = metaObject; //Search for the QObject base class to know which offset we need to start building from const QMetaObject *superClass = mo->superClass(); while (qstrcmp(superClass->className(), "QObject") != 0) { superClass = superClass->superClass(); } const int methodOffset = superClass->methodCount(); const int propertyOffset = superClass->propertyCount(); //Fill the mapping for all QObject methods. for (int i=0; i Proxy"; for (int index = methodOffset; index < mo->methodCount(); ++index) { QMetaMethod mm = mo->method(index); if (mm.methodType() == QMetaMethod::Signal) { auto mb = builder.addMethod(mm); qCDebug(qLcIviSimulationEngine) << index << "->" << methodOffset + mb.index(); methodMap.insert(index, methodOffset + mb.index()); } } //Add all other methods qCDebug(qLcIviSimulationEngine) << "Method Mapping: Original -> Proxy"; for (int index = methodOffset; index < mo->methodCount(); ++index) { QMetaMethod mm = mo->method(index); if (mm.methodType() != QMetaMethod::Signal) { auto mb = builder.addMethod(mm); qCDebug(qLcIviSimulationEngine) << index << "->" << methodOffset + mb.index(); methodMap.insert(index, methodOffset + mb.index()); } } //Add all properties for (int index = propertyOffset; index < mo->propertyCount(); ++index) { QMetaProperty prop = mo->property(index); builder.addProperty(prop); } //Add a Base property which works like a attached property builder.addProperty("Base", "QObject *"); //Debugging output if (qLcIviSimulationEngine().isDebugEnabled()) { qCDebug(qLcIviSimulationEngine) << "Original Object:"; for (int i=0; i < mo->methodCount(); i++) { QMetaMethod method = mo->method(i); qCDebug(qLcIviSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature(); } for (int i=0; i < mo->propertyCount(); i++) { QMetaProperty prop = mo->property(i); qCDebug(qLcIviSimulationEngine) << "property:" << prop.propertyIndex() << prop.name(); QMetaMethod method = prop.notifySignal(); qCDebug(qLcIviSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature(); } qCDebug(qLcIviSimulationEngine) << "Proxy Object:"; mo = builder.toMetaObject(); for (int i=0; i < mo->methodCount(); i++) { QMetaMethod method = mo->method(i); qCDebug(qLcIviSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature(); } for (int i=0; i < mo->propertyCount(); i++) { QMetaProperty prop = mo->property(i); qCDebug(qLcIviSimulationEngine) << "property:" << prop.propertyIndex() << prop.name(); QMetaMethod method = prop.notifySignal(); qCDebug(qLcIviSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature(); } } return *builder.toMetaObject(); } bool QIviSimulationProxyBase::callQmlMethod(const char *function, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { if (m_noSimulationEngine) return false; //Prevent recursion static bool recursionGuard = false; if (recursionGuard) return false; recursionGuard = true; bool functionExecuted = false; const QMetaObject *mo = metaObject(); // Only invoke the functions declared in QML. // Once a function/property is added to a type a new MetaObject gets created which contains // _QML_ in the name. if (QString::fromLatin1(mo->className()).contains(QLatin1String("_QML_"))) { for (int i=mo->methodOffset(); imethodCount(); i++) { //qDebug() << "CHECKING FOR: " << function << mo->method(i).name(); if (mo->method(i).name() != function) continue; //qDebug() << "EXECUTING"; functionExecuted = QMetaObject::invokeMethod(this, function, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); break; } } recursionGuard = false; return functionExecuted; } void QIviSimulationProxyBase::setup(QIviSimulationEngine *engine) { if (engine != qmlEngine(this)) { qmlWarning(this) << "QIviSimulationProxy can only be used in the same Engine it is registered in"; m_noSimulationEngine = true; return; } // Connect all signals from the instance to the signals of this metaobject. // This is needed to relay the signals from the instance to this instance and to QML const QMetaObject *mo = m_instance->metaObject(); for (int i=0; imethodCount(); i++) { QMetaMethod mm = mo->method(i); if (mm.methodType() != QMetaMethod::Signal) continue; connect(m_instance, mm, this, m_staticMetaObject->method(m_methodMap.value(i))); } } } //namespace QT_END_NAMESPACE