static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
{
    QString retval;

    // start with properties:
    if (flags & (QDBusConnection::ExportScriptableProperties |
                 QDBusConnection::ExportNonScriptableProperties)) {
        for (int i = propOffset; i < mo->propertyCount(); ++i) {
            static const char *accessvalues[] = {0, "read", "write", "readwrite"};

            QMetaProperty mp = mo->property(i);

            if (!((mp.isScriptable() && (flags & QDBusConnection::ExportScriptableProperties)) ||
                  (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties))))
                continue;

            int access = 0;
            if (mp.isReadable())
                access |= 1;
            if (mp.isWritable())
                access |= 2;

            int typeId = qDBusNameToTypeId(mp.typeName());
            if (!typeId)
                continue;
            const char *signature = QDBusMetaType::typeToSignature(typeId);
            if (!signature)
                continue;

            retval += QString::fromLatin1("    <property name=\"%1\" type=\"%2\" access=\"%3\"")
                      .arg(QLatin1String(mp.name()))
                      .arg(QLatin1String(signature))
                      .arg(QLatin1String(accessvalues[access]));

            if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
                const char *typeName = QVariant::typeToName(QVariant::Type(typeId));
                retval += QString::fromLatin1(">\n      <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"%3\"/>\n    </property>\n")
                          .arg(typeNameToXml(typeName));
            } else {
                retval += QLatin1String("/>\n");
            }
        }
    }

    // now add methods:
    for (int i = methodOffset; i < mo->methodCount(); ++i) {
        QMetaMethod mm = mo->method(i);
        QByteArray signature = mm.signature();
        int paren = signature.indexOf('(');

        bool isSignal;
        if (mm.methodType() == QMetaMethod::Signal)
            // adding a signal
            isSignal = true;
        else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public)
            isSignal = false;
        else
            continue;           // neither signal nor public slot

        if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals |
                                   QDBusConnection::ExportNonScriptableSignals)))
            continue;           // we're not exporting any signals
        if (!isSignal && !(flags & (QDBusConnection::ExportScriptableSlots |
                                    QDBusConnection::ExportNonScriptableSlots)))
            continue;           // we're not exporting any slots

        QString xml = QString::fromLatin1("    <%1 name=\"%2\">\n")
                      .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
                      .arg(QLatin1String(signature.left(paren)));

        // check the return type first
        int typeId = qDBusNameToTypeId(mm.typeName());
        if (typeId) {
            const char *typeName = QDBusMetaType::typeToSignature(typeId);
            if (typeName) {
                xml += QString::fromLatin1("      <arg type=\"%1\" direction=\"out\"/>\n")
                       .arg(typeNameToXml(typeName));

                // do we need to describe this argument?
                if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
                    xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
                           .arg(typeNameToXml(mm.typeName()));
            } else
                continue;
        }
        else if (*mm.typeName())
            continue;           // wasn't a valid type

        QList<QByteArray> names = mm.parameterNames();
        QList<int> types;
        int inputCount = qDBusParametersForMethod(mm, types);
        if (inputCount == -1)
            continue;           // invalid form
        if (isSignal && inputCount + 1 != types.count())
            continue;           // signal with output arguments?
        if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
            continue;           // signal with QDBusMessage argument?
        if (isSignal && mm.attributes() & QMetaMethod::Cloned)
            continue;           // cloned signal?

        int j;
        bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
        for (j = 1; j < types.count(); ++j) {
            // input parameter for a slot or output for a signal
            if (types.at(j) == QDBusMetaTypeId::message) {
                isScriptable = true;
                continue;
            }

            QString name;
            if (!names.at(j - 1).isEmpty())
                name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));

            bool isOutput = isSignal || j > inputCount;

            const char *signature = QDBusMetaType::typeToSignature(types.at(j));
            xml += QString::fromLatin1("      <arg %1type=\"%2\" direction=\"%3\"/>\n")
                   .arg(name)
                   .arg(QLatin1String(signature))
                   .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));

            // do we need to describe this argument?
            if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
                const char *typeName = QVariant::typeToName( QVariant::Type(types.at(j)) );
                xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
                       .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
                       .arg(isOutput ? j - 1 : j - inputCount)
                       .arg(typeNameToXml(typeName));
            }
        }

        int wantedMask;
        if (isScriptable)
            wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
                                  : QDBusConnection::ExportScriptableSlots;
        else
            wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
                                  : QDBusConnection::ExportNonScriptableSlots;
        if ((flags & wantedMask) != wantedMask)
            continue;

        if (qDBusCheckAsyncTag(mm.tag()))
            // add the no-reply annotation
            xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
                                 " value=\"true\"/>\n");

        retval += xml;
        retval += QString::fromLatin1("    </%1>\n")
                  .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
    }

    return retval;
}
static QString addFunction(const FunctionDef &mm, bool isSignal = false) {

    QString xml = QString::fromLatin1("    <%1 name=\"%2\">\n")
                  .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
                  .arg(QLatin1String(mm.name));

    // check the return type first
    int typeId = QMetaType::type(mm.normalizedType.constData());
    if (typeId != QMetaType::Void) {
        if (typeId) {
            const char *typeName = QDBusMetaType::typeToSignature(typeId);
            if (typeName) {
                xml += QString::fromLatin1("      <arg type=\"%1\" direction=\"out\"/>\n")
                        .arg(typeNameToXml(typeName));

                    // do we need to describe this argument?
                    if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
                        xml += QString::fromLatin1("      <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
                            .arg(typeNameToXml(mm.normalizedType.constData()));
            } else {
                return QString();
            }
        } else if (!mm.normalizedType.isEmpty()) {
            return QString();           // wasn't a valid type
        }
    }
    QList<ArgumentDef> names = mm.arguments;
    QVector<int> types;
    QString errorMsg;
    int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
    if (inputCount == -1) {
        qWarning() << qPrintable(errorMsg);
        return QString();           // invalid form
    }
    if (isSignal && inputCount + 1 != types.count())
        return QString();           // signal with output arguments?
    if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message())
        return QString();           // signal with QDBusMessage argument?

    bool isScriptable = mm.isScriptable;
    for (int j = 1; j < types.count(); ++j) {
        // input parameter for a slot or output for a signal
        if (types.at(j) == QDBusMetaTypeId::message()) {
            isScriptable = true;
            continue;
        }

        QString name;
        if (!names.at(j - 1).name.isEmpty())
            name = QString::fromLatin1("name=\"%1\" ").arg(QString::fromLatin1(names.at(j - 1).name));

        bool isOutput = isSignal || j > inputCount;

        const char *signature = QDBusMetaType::typeToSignature(types.at(j));
        xml += QString::fromLatin1("      <arg %1type=\"%2\" direction=\"%3\"/>\n")
                .arg(name)
                .arg(QLatin1String(signature))
                .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));

        // do we need to describe this argument?
        if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
            const char *typeName = QMetaType::typeName(types.at(j));
            xml += QString::fromLatin1("      <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
                    .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
                    .arg(isOutput && !isSignal ? j - inputCount : j - 1)
                    .arg(typeNameToXml(typeName));
        }
    }

    int wantedMask;
    if (isScriptable)
        wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
                              : QDBusConnection::ExportScriptableSlots;
    else
        wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
                              : QDBusConnection::ExportNonScriptableSlots;
    if ((flags & wantedMask) != wantedMask)
        return QString();

    if (qDBusCheckAsyncTag(mm.tag.constData()))
        // add the no-reply annotation
        xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
                              " value=\"true\"/>\n");

    QString retval = xml;
    retval += QString::fromLatin1("    </%1>\n")
              .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));

    return retval;
}