diff options
Diffstat (limited to 'qt/tools')
-rw-r--r-- | qt/tools/.cvsignore | 11 | ||||
-rw-r--r-- | qt/tools/Makefile.am | 10 | ||||
-rw-r--r-- | qt/tools/dbuscpp2xml.cpp | 409 | ||||
-rw-r--r-- | qt/tools/dbusidl2cpp.cpp | 960 |
4 files changed, 1390 insertions, 0 deletions
diff --git a/qt/tools/.cvsignore b/qt/tools/.cvsignore new file mode 100644 index 00000000..f6454f28 --- /dev/null +++ b/qt/tools/.cvsignore @@ -0,0 +1,11 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +*.bb +*.bbg +*.da +*.gcov +*.moc diff --git a/qt/tools/Makefile.am b/qt/tools/Makefile.am new file mode 100644 index 00000000..a79aafd6 --- /dev/null +++ b/qt/tools/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES=-I$(top_srcdir)/qt $(DBUS_CLIENT_CFLAGS) $(DBUS_QT_CFLAGS) -DDBUS_COMPILATION +bin_PROGRAMS = dbusidl2cpp dbuscpp2xml + +dbusidl2cpp_SOURCES = dbusidl2cpp.cpp +dbusidl2cpp_LDFLAGS = -no-undefined +dbusidl2cpp_LDADD = $(DBUS_QT_LIBS) ../src/libdbus-qt4-1.la + +dbuscpp2xml_SOURCES = dbuscpp2xml.cpp +dbuscpp2xml_LDFLAGS = -no-undefined +dbuscpp2xml_LDADD = $(DBUS_QT_LIBS) ../src/libdbus-qt4-1.la diff --git a/qt/tools/dbuscpp2xml.cpp b/qt/tools/dbuscpp2xml.cpp new file mode 100644 index 00000000..dd08b5fd --- /dev/null +++ b/qt/tools/dbuscpp2xml.cpp @@ -0,0 +1,409 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <QByteArray> +#include <QString> +#include <QVarLengthArray> +#include <QFile> +#include <QProcess> +#include <QMetaObject> +#include <QList> +#include <QRegExp> + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "../src/qdbusconnection.h" // for the Export* flags + +// copied from dbus-protocol.h: +static const char docTypeHeader[] = + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"; + +// in qdbusxmlgenerator.cpp +extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +#define PROGRAMNAME "dbuscpp2xml" +#define PROGRAMVERSION "0.1" +#define PROGRAMCOPYRIGHT "Copyright (C) 2006 Trolltech AS. All rights reserved." + +static const char cmdlineOptions[] = "psmaPSMAo:hV"; +static const char *outputFile; +static int flags; + +static const char help[] = + "Usage: " PROGRAMNAME " [options...] [files...]\n" + "Parses the C++ source or header file containing a QObject-derived class and\n" + "produces the D-Bus Introspection XML." + "\n" + "Options:\n" + " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n" + " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n" + " -a Output all scriptable contents (equivalent to -psm)\n" + " -A Output all contents (equivalent to -PSM)\n" + " -o <filename> Write the output to file <filename>\n" + " -h Show this information\n" + " -V Show the program version and quit.\n" + "\n"; + +class MocParser +{ + void parseError(); + QByteArray readLine(); + void loadIntData(uint *&data); + void loadStringData(char *&stringdata); + + QIODevice *input; + const char *filename; + int line; +public: + ~MocParser(); + void parse(const char *filename, QIODevice *input, int lineNumber = 0); + + QList<QMetaObject> objects; +}; + +void MocParser::parseError() +{ + fprintf(stderr, PROGRAMNAME ": error parsing input file '%s' line %d \n", filename, line); + exit(1); +} + +QByteArray MocParser::readLine() +{ + ++line; + return input->readLine(); +} + +void MocParser::loadIntData(uint *&data) +{ + data = 0; // initialise + QVarLengthArray<uint> array; + QRegExp rx("(\\d+|0x[0-9abcdef]+)", Qt::CaseInsensitive); + + while (!input->atEnd()) { + QString line = QLatin1String(readLine()); + int pos = line.indexOf("//"); + if (pos != -1) + line.truncate(pos); // drop comments + + if (line == "};\n") { + // end of data + data = new uint[array.count()]; + memcpy(data, array.data(), array.count() * sizeof(*data)); + return; + } + + pos = 0; + while ((pos = rx.indexIn(line, pos)) != -1) { + QString num = rx.cap(1); + if (num.startsWith("0x")) + array.append(num.mid(2).toUInt(0, 16)); + else + array.append(num.toUInt()); + pos += rx.matchedLength(); + } + } + + parseError(); +} + +void MocParser::loadStringData(char *&stringdata) +{ + stringdata = 0; + QVarLengthArray<char, 1024> array; + + while (!input->atEnd()) { + QByteArray line = readLine(); + if (line == "};\n") { + // end of data + stringdata = new char[array.count()]; + memcpy(stringdata, array.data(), array.count() * sizeof(*stringdata)); + return; + } + + int start = line.indexOf('"'); + if (start == -1) + parseError(); + + int len = line.length() - 1; + line.truncate(len); // drop ending \n + if (line.at(len - 1) != '"') + parseError(); + + --len; + ++start; + for ( ; start < len; ++start) + if (line.at(start) == '\\') { + // parse escaped sequence + ++start; + if (start == len) + parseError(); + + QChar c(QLatin1Char(line.at(start))); + if (!c.isDigit()) { + switch (c.toLatin1()) { + case 'a': + array.append('\a'); + break; + case 'b': + array.append('\b'); + break; + case 'f': + array.append('\f'); + break; + case 'n': + array.append('\n'); + break; + case 'r': + array.append('\r'); + break; + case 't': + array.append('\t'); + break; + case 'v': + array.append('\v'); + break; + case '\\': + case '?': + case '\'': + case '"': + array.append(c.toLatin1()); + break; + + case 'x': + if (start + 2 <= len) + parseError(); + array.append(char(line.mid(start + 1, 2).toInt(0, 16))); + break; + + default: + array.append(c.toLatin1()); + fprintf(stderr, PROGRAMNAME ": warning: invalid escape sequence '\\%c' found in input", + c.toLatin1()); + } + } else { + // octal + QRegExp octal("([0-7]+)"); + if (octal.indexIn(QLatin1String(line), start) == -1) + parseError(); + array.append(char(octal.cap(1).toInt(0, 8))); + } + } else { + array.append(line.at(start)); + } + } + + parseError(); +} + +void MocParser::parse(const char *fname, QIODevice *io, int lineNumber) +{ + filename = fname; + input = io; + line = lineNumber; + + while (!input->atEnd()) { + QByteArray line = readLine(); + if (line.startsWith("static const uint qt_meta_data_")) { + // start of new class data + uint *data; + loadIntData(data); + + // find the start of the string data + do { + line = readLine(); + if (input->atEnd()) + parseError(); + } while (!line.startsWith("static const char qt_meta_stringdata_")); + + char *stringdata; + loadStringData(stringdata); + + QMetaObject mo; + mo.d.superdata = &QObject::staticMetaObject; + mo.d.stringdata = stringdata; + mo.d.data = data; + mo.d.extradata = 0; + objects.append(mo); + } + } + + fname = 0; + input = 0; +} + +MocParser::~MocParser() +{ + foreach (QMetaObject mo, objects) { + delete const_cast<char *>(mo.d.stringdata); + delete const_cast<uint *>(mo.d.data); + } +} + +static void showHelp() +{ + printf("%s", help); + exit(0); +} + +static void showVersion() +{ + printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION); + printf("D-Bus QObject-to-XML converter\n"); + exit(0); +} + +static void parseCmdLine(int argc, char **argv) +{ + int c; + opterr = true; + while ((c = getopt(argc, argv, cmdlineOptions)) != -1) + switch (c) + { + case 'p': + flags |= QDBusConnection::ExportProperties; + break; + + case 's': + flags |= QDBusConnection::ExportSignals; + break; + + case 'm': + flags |= QDBusConnection::ExportSlots; + break; + + case 'a': + flags |= QDBusConnection::ExportContents; + break; + + case 'P': + flags |= QDBusConnection::ExportAllProperties; + break; + + case 'S': + flags |= QDBusConnection::ExportAllSignals; + break; + + case 'M': + flags |= QDBusConnection::ExportAllSlots; + break; + + case 'A': + flags |= QDBusConnection::ExportAllContents; + break; + + case 'o': + outputFile = optarg; + break; + + case 'h': + showHelp(); + break; + + case 'V': + showVersion(); + break; + + case '?': + exit(1); + default: + abort(); + } + + if (flags == 0) + flags = QDBusConnection::ExportAllContents; +} + +int main(int argc, char **argv) +{ + MocParser parser; + parseCmdLine(argc, argv); + + for (int i = optind; i < argc; ++i) { + FILE *in = fopen(argv[i], "r"); + if (in == 0) { + fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n", + argv[i], strerror(errno)); + return 1; + } + + QFile f; + f.open(in, QIODevice::ReadOnly); + f.readLine(); + + QByteArray line = f.readLine(); + if (line.contains("Meta object code from reading C++ file")) + // this is a moc-generated file + parser.parse(argv[i], &f, 3); + else { + // run moc on this file + QProcess proc; + proc.start("moc", QStringList() << QFile::encodeName(argv[i])); + + if (!proc.waitForStarted()) { + fprintf(stderr, PROGRAMNAME ": could not execute moc! Aborting.\n"); + return 1; + } + + proc.closeWriteChannel(); + + if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit || + proc.exitCode() != 0) { + // output the moc errors: + fprintf(stderr, "%s", proc.readAllStandardError().constData()); + fprintf(stderr, PROGRAMNAME ": exit code %d from moc. Aborting\n", proc.exitCode()); + return 1; + } + fprintf(stderr, "%s", proc.readAllStandardError().constData()); + + parser.parse(argv[i], &proc, 1); + } + + f.close(); + fclose(in); + } + + FILE *output = stdout; + if (outputFile != 0) { + output = fopen(outputFile, "w"); + if (output == 0) { + fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s", + outputFile, strerror(errno)); + return 1; + } + } + + fprintf(output, "%s<node>\n", docTypeHeader); + foreach (QMetaObject mo, parser.objects) { + QString xml = qDBusGenerateMetaObjectXml(QString(), &mo, &QObject::staticMetaObject, + flags); + fprintf(output, "%s", qPrintable(xml)); + } + fprintf(output, "</node>\n"); + + if (output != stdout) + fclose(output); +} + diff --git a/qt/tools/dbusidl2cpp.cpp b/qt/tools/dbusidl2cpp.cpp new file mode 100644 index 00000000..091685b4 --- /dev/null +++ b/qt/tools/dbusidl2cpp.cpp @@ -0,0 +1,960 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qfile.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qtextstream.h> +#include <QtCore/qset.h> + +#include <dbus/qdbus.h> +#include "../src/qdbusmetaobject_p.h" +#include "../src/qdbusintrospection_p.h" + +#define PROGRAMNAME "dbusidl2cpp" +#define PROGRAMVERSION "0.4" +#define PROGRAMCOPYRIGHT "Copyright (C) 2006 Trolltech AS. All rights reserved." + +#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply" + +static const char cmdlineOptions[] = "a:c:hmNp:vV"; +static const char *globalClassName; +static const char *proxyFile; +static const char *adaptorFile; +static const char *inputFile; +static bool skipNamespaces; +static bool verbose; +static bool includeMocs; +static QStringList wantedInterfaces; + +static const char help[] = + "Usage: " PROGRAMNAME " [options...] [idl-or-xml-file] [interfaces...]\n" + "Produces the C++ code to implement the interfaces defined in the input file.\n" + "If no options are given, the code is written to the standard output.\n" + "\n" + "Options:\n" + " -a <filename> Write the adaptor code to <filename>\n" + " -c <classname> Use <classname> as the class name for the generated classes\n" + " -h Show this information\n" + " -m Generate #include \"filename.moc\" statements in the .cpp files\n" + " -N Don't use namespaces\n" + " -p <filename> Write the proxy code to <filename>\n" + " -v Be verbose.\n" + " -V Show the program version and quit.\n" + "\n" + "If the file name given to the options -a and -p does not end in .cpp or .h, the\n" + "program will automatically append the suffixes and produce both files.\n"; + +static const char includeList[] = + "#include <QtCore/QByteArray>\n" + "#include <QtCore/QList>\n" + "#include <QtCore/QMap>\n" + "#include <QtCore/QString>\n" + "#include <QtCore/QStringList>\n" + "#include <QtCore/QVariant>\n"; + +static const char forwardDeclarations[] = + "class QByteArray;\n" + "template<class T> class QList;\n" + "template<class Key, class Value> class QMap;\n" + "class QString;\n" + "class QStringList;\n" + "class QVariant;\n"; + +static void showHelp() +{ + printf("%s", help); + exit(0); +} + +static void showVersion() +{ + printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION); + printf("D-Bus binding tool for Qt\n"); + exit(0); +} + +static void parseCmdLine(int argc, char **argv) +{ + int c; + opterr = true; + while ((c = getopt(argc, argv, cmdlineOptions)) != -1) + switch (c) + { + case 'a': + adaptorFile = optarg; + break; + + case 'c': + globalClassName = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'm': + includeMocs = true; + break; + + case 'N': + skipNamespaces = true; + break; + + case 'h': + showHelp(); + break; + + case 'V': + showVersion(); + break; + + case 'p': + proxyFile = optarg; + break; + + case '?': + exit(1); + default: + abort(); + } + + if (optind != argc) + inputFile = argv[optind++]; + + while (optind != argc) + wantedInterfaces << QString::fromLocal8Bit(argv[optind++]); +} + +static QDBusIntrospection::Interfaces readInput() +{ + QFile input(QFile::decodeName(inputFile)); + if (inputFile && QLatin1String("-") != inputFile) + input.open(QIODevice::ReadOnly); + else + input.open(stdin, QIODevice::ReadOnly); + + QByteArray data = input.readAll(); + + // check if the input is already XML + data = data.trimmed(); + if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") || + data.startsWith("<node") || data.startsWith("<interface")) + // already XML + return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data)); + + fprintf(stderr, "Cannot process input. Stop.\n"); + exit(1); +} + +static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces) +{ + if (!wantedInterfaces.isEmpty()) { + QDBusIntrospection::Interfaces::Iterator it = interfaces.begin(); + while (it != interfaces.end()) + if (!wantedInterfaces.contains(it.key())) + it = interfaces.erase(it); + else + ++it; + } +} + +// produce a header name from the file name +static QString header(const char *name) +{ + if (!name || (name[0] == '-' && name[1] == '\0')) + return QString(); + + QString retval = QFile::decodeName(name); + if (!retval.endsWith(".h") && !retval.endsWith(".cpp") && !retval.endsWith(".cc")) + retval.append(".h"); + + return retval; +} + +// produce a cpp name from the file name +static QString cpp(const char *name) +{ + if (!name || (name[0] == '-' && name[1] == '\0')) + return QString(); + + QString retval = QFile::decodeName(name); + if (!retval.endsWith(".h") && !retval.endsWith(".cpp") && !retval.endsWith(".cc")) + retval.append(".cpp"); + + return retval; +} + +static QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost) +{ + ts << "/*" << endl + << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << endl + << " * when processing input file " << (inputFile ? inputFile : "<stdin>") << endl + << " *" << endl + << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << endl + << " *" << endl + << " * This is an auto-generated file." << endl; + + if (changesWillBeLost) + ts << " * Do not edit! All changes made to it will be lost." << endl; + + ts << " */" << endl + << endl; + + return ts; +} + +enum ClassType { Proxy, Adaptor }; +static QString classNameForInterface(const QString &interface, ClassType classType) +{ + if (globalClassName) + return QLatin1String(globalClassName); + + QStringList parts = interface.split('.'); + + QString retval; + if (classType == Proxy) + foreach (QString part, parts) { + part[0] = part[0].toUpper(); + retval += part; + } + else { + retval = parts.last(); + retval[0] = retval[0].toUpper(); + } + + if (classType == Proxy) + retval += "Interface"; + else + retval += "Adaptor"; + + return retval; +} + +static QByteArray qtTypeName(const QString &signature) +{ + QVariant::Type type = QDBusUtil::signatureToType(signature); + if (type == QVariant::Invalid) + qFatal("Got unknown type `%s'", qPrintable(signature)); + + return QVariant::typeToName(type); +} + +static QString nonConstRefArg(const QByteArray &arg) +{ + return QLatin1String(arg + " &"); +} + +static QString templateArg(const QByteArray &arg) +{ + if (!arg.endsWith('>')) + return QLatin1String(arg); + + return QLatin1String(arg + ' '); +} + +static QString constRefArg(const QByteArray &arg) +{ + if (!arg.startsWith('Q')) + return QLatin1String(arg + ' '); + else + return QString("const %1 &").arg( QLatin1String(arg) ); +} + +static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs, + const QDBusIntrospection::Arguments &outputArgs = + QDBusIntrospection::Arguments()) +{ + QStringList retval; + for (int i = 0; i < inputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = inputArgs.at(i); + QString name = arg.name; + if (name.isEmpty()) + name = QString("in%1").arg(i); + while (retval.contains(name)) + name += "_"; + retval << name; + } + for (int i = 0; i < outputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = outputArgs.at(i); + QString name = arg.name; + if (name.isEmpty()) + name = QString("out%1").arg(i); + while (retval.contains(name)) + name += "_"; + retval << name; + } + return retval; +} + +static void writeArgList(QTextStream &ts, const QStringList &argNames, + const QDBusIntrospection::Arguments &inputArgs, + const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments()) +{ + // input args: + bool first = true; + int argPos = 0; + for (int i = 0; i < inputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = inputArgs.at(i); + QString type = constRefArg(qtTypeName(arg.type)); + + if (!first) + ts << ", "; + ts << type << argNames.at(argPos++); + first = false; + } + + argPos++; + + // output args + // yes, starting from 1 + for (int i = 1; i < outputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = outputArgs.at(i); + QString name = arg.name; + + if (!first) + ts << ", "; + ts << nonConstRefArg(qtTypeName(arg.type)) << argNames.at(argPos++); + first = false; + } +} + +static QString propertyGetter(const QDBusIntrospection::Property &property) +{ + QString getter = property.annotations.value("com.trolltech.QtDBus.propertyGetter"); + if (getter.isEmpty()) { + getter = property.name; + getter[0] = getter[0].toLower(); + } + return getter; +} + +static QString propertySetter(const QDBusIntrospection::Property &property) +{ + QString setter = property.annotations.value("com.trolltech.QtDBus.propertySetter"); + if (setter.isEmpty()) { + setter = "set" + property.name; + setter[3] = setter[3].toUpper(); + } + return setter; +} + +static QString stringify(const QString &data) +{ + QString retval; + int i; + for (i = 0; i < data.length(); ++i) { + retval += '\"'; + for ( ; i < data.length() && data[i] != QChar('\n'); ++i) + if (data[i] == '\"') + retval += "\\\""; + else + retval += data[i]; + retval += "\\n\"\n"; + } + return retval; +} + +static void writeProxy(const char *proxyFile, const QDBusIntrospection::Interfaces &interfaces) +{ + // open the file + QString headerName = header(proxyFile); + QFile file(headerName); + if (!headerName.isEmpty()) + file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); + else + file.open(stdout, QIODevice::WriteOnly | QIODevice::Text); + QTextStream hs(&file); + + QString cppName = cpp(proxyFile); + QByteArray cppData; + QTextStream cs(&cppData); + + // write the header: + writeHeader(hs, true); + + // include guards: + QString includeGuard; + if (!headerName.isEmpty()) { + includeGuard = headerName.toUpper().replace(QChar('.'), QChar('_')); + int pos = includeGuard.lastIndexOf('/'); + if (pos != -1) + includeGuard = includeGuard.mid(pos + 1); + } else { + includeGuard = QString("QDBUSIDL2CPP_PROXY"); + } + includeGuard = QString("%1_%2%3") + .arg(includeGuard) + .arg(getpid()) + .arg(QDateTime::currentDateTime().toTime_t()); + hs << "#ifndef " << includeGuard << endl + << "#define " << includeGuard << endl + << endl; + + // include our stuff: + hs << "#include <QtCore/QObject>" << endl + << includeList + << "#include <dbus/qdbus.h>" << endl + << endl; + + if (cppName != headerName) { + writeHeader(cs, false); + cs << "#include \"" << headerName << "\"" << endl + << endl; + } + + foreach (const QDBusIntrospection::Interface *interface, interfaces) { + QString className = classNameForInterface(interface->name, Proxy); + + // comment: + hs << "/*" << endl + << " * Proxy class for interface " << interface->name << endl + << " */" << endl; + cs << "/*" << endl + << " * Implementation of interface class " << className << endl + << " */" << endl + << endl; + + // class header: + hs << "class " << className << ": public QDBusAbstractInterface" << endl + << "{" << endl + << " Q_OBJECT" << endl; + + // the interface name + hs << "public:" << endl + << " static inline const char *staticInterfaceName()" << endl + << " { return \"" << interface->name << "\"; }" << endl + << endl; + + // constructors/destructors: + hs << "public:" << endl + << " explicit " << className << "(QDBusAbstractInterfacePrivate *p);" << endl + << endl + << " ~" << className << "();" << endl + << endl; + cs << className << "::" << className << "(QDBusAbstractInterfacePrivate *p)" << endl + << " : QDBusAbstractInterface(p)" << endl + << "{" << endl + << "}" << endl + << endl + << className << "::~" << className << "()" << endl + << "{" << endl + << "}" << endl + << endl; + + // properties: + foreach (const QDBusIntrospection::Property &property, interface->properties) { + QByteArray type = qtTypeName(property.type); + QString templateType = templateArg(type); + QString constRefType = constRefArg(type); + QString getter = propertyGetter(property); + QString setter = propertySetter(property); + + hs << " Q_PROPERTY(" << type << " " << property.name; + + // getter: + if (property.access != QDBusIntrospection::Property::Write) + // it's readble + hs << " READ " << getter; + + // setter + if (property.access != QDBusIntrospection::Property::Read) + // it's writeable + hs << " WRITE " << setter; + + hs << ")" << endl; + + // getter: + if (property.access != QDBusIntrospection::Property::Write) { + hs << " inline " << type << " " << getter << "() const" << endl; + if (type != "QVariant") + hs << " { return qvariant_cast< " << type << " >(internalPropGet(\"" + << property.name << "\")); }" << endl; + else + hs << " { return internalPropGet(\"" << property.name << "\"); }" << endl; + } + + // setter: + if (property.access != QDBusIntrospection::Property::Read) { + hs << " inline void " << setter << "(" << constRefArg(type) << "value)" << endl + << " { internalPropSet(\"" << property.name + << "\", qVariantFromValue(value)); }" << endl; + } + + hs << endl; + } + + // methods: + hs << "public Q_SLOTS: // METHODS" << endl; + foreach (const QDBusIntrospection::Method &method, interface->methods) { + bool isAsync = method.annotations.value(ANNOTATION_NO_WAIT) == "true"; + if (isAsync && !method.outputArgs.isEmpty()) { + fprintf(stderr, "warning: method %s in interface %s is marked 'async' but has output arguments.\n", + qPrintable(method.name), qPrintable(interface->name)); + continue; + } + + hs << " inline "; + + if (method.annotations.value("org.freedesktop.DBus.Deprecated") == "true") + hs << "Q_DECL_DEPRECATED "; + + if (isAsync) + hs << "Q_ASYNC void "; + else if (method.outputArgs.isEmpty()) + hs << "QDBusReply<void> "; + else { + hs << "QDBusReply<" << templateArg(qtTypeName(method.outputArgs.first().type)) << "> "; + } + + hs << method.name << "("; + + QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs); + writeArgList(hs, argNames, method.inputArgs, method.outputArgs); + + hs << ")" << endl + << " {" << endl; + + if (method.outputArgs.count() > 1) + hs << " QDBusMessage reply = call(QLatin1String(\""; + else if (!isAsync) + hs << " return call(QLatin1String(\""; + else + hs << " call(NoWaitForReply, QLatin1String(\""; + + // rebuild the method input signature: + QString signature = QChar('.'); + foreach (const QDBusIntrospection::Argument &arg, method.inputArgs) + signature += arg.type; + if (signature.length() == 1) + signature.clear(); + hs << method.name << signature << "\")"; + + int argPos = 0; + for (int i = 0; i < method.inputArgs.count(); ++i) + hs << ", " << argNames.at(argPos++); + + // close the QDBusIntrospection::call call + hs << ");" << endl; + + argPos++; + if (method.outputArgs.count() > 1) { + hs << " if (reply.type() == QDBusMessage::ReplyMessage) {" << endl; + + // yes, starting from 1 + for (int i = 1; i < method.outputArgs.count(); ++i) + hs << " " << argNames.at(argPos++) << " = qvariant_cast<" + << templateArg(qtTypeName(method.outputArgs.at(i).type)) + << ">(reply.at(" << i << "));" << endl; + hs << " }" << endl + << " return reply;" << endl; + } + + // close the function: + hs << " }" << endl + << endl; + } + + hs << "Q_SIGNALS: // SIGNALS" << endl; + foreach (const QDBusIntrospection::Signal &signal, interface->signals_) { + hs << " "; + if (signal.annotations.value("org.freedesktop.DBus.Deprecated") == "true") + hs << "Q_DECL_DEPRECATED "; + + hs << "void " << signal.name << "("; + + QStringList argNames = makeArgNames(signal.outputArgs); + writeArgList(hs, argNames, signal.outputArgs); + + hs << ");" << endl; // finished for header + } + + // close the class: + hs << "};" << endl + << endl; + } + + if (!skipNamespaces) { + QStringList last; + QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin(); + do + { + QStringList current; + QString name; + if (it != interfaces.constEnd()) { + current = it->constData()->name.split('.'); + name = current.takeLast(); + } + + int i = 0; + while (i < current.count() && i < last.count() && current.at(i) == last.at(i)) + ++i; + + // i parts matched + // close last.count() - i namespaces: + for (int j = i; j < last.count(); ++j) + hs << QString((last.count() - j - 1 + i) * 2, ' ') << "}" << endl; + + // open current.count() - i namespaces + for (int j = i; j < current.count(); ++j) + hs << QString(j * 2, ' ') << "namespace " << current.at(j) << " {" << endl; + + // add this class: + if (!name.isEmpty()) { + hs << QString(current.count() * 2, ' ') + << "typedef ::" << classNameForInterface(it->constData()->name, Proxy) + << " " << name << ";" << endl; + } + + if (it == interfaces.constEnd()) + break; + ++it; + last = current; + } while (true); + } + + // close the include guard + hs << "#endif" << endl; + + if (includeMocs) + cs << endl + << "#include \"" << proxyFile << ".moc\"" << endl; + + cs.flush(); + hs.flush(); + if (headerName == cppName) + file.write(cppData); + else { + // write to cpp file + QFile f(cppName); + f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); + f.write(cppData); + } +} + +static void writeAdaptor(const char *adaptorFile, const QDBusIntrospection::Interfaces &interfaces) +{ + // open the file + QString headerName = header(adaptorFile); + QFile file(headerName); + if (!headerName.isEmpty()) + file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); + else + file.open(stdout, QIODevice::WriteOnly | QIODevice::Text); + QTextStream hs(&file); + + QString cppName = cpp(adaptorFile); + QByteArray cppData; + QTextStream cs(&cppData); + + // write the headers + writeHeader(hs, false); + + // include guards: + QString includeGuard; + if (!headerName.isEmpty()) { + includeGuard = headerName.toUpper().replace(QChar('.'), QChar('_')); + int pos = includeGuard.lastIndexOf('/'); + if (pos != -1) + includeGuard = includeGuard.mid(pos + 1); + } else { + includeGuard = QString("QDBUSIDL2CPP_ADAPTOR"); + } + includeGuard = QString("%1_%2%3") + .arg(includeGuard) + .arg(getpid()) + .arg(QDateTime::currentDateTime().toTime_t()); + hs << "#ifndef " << includeGuard << endl + << "#define " << includeGuard << endl + << endl; + + // include our stuff: + hs << "#include <QtCore/QObject>" << endl; + if (cppName == headerName) + hs << "#include <QtCore/QMetaObject>" << endl + << "#include <QtCore/QVariant>" << endl; + hs << "#include <dbus/qdbus.h>" << endl; + + if (cppName != headerName) { + writeHeader(cs, false); + cs << "#include \"" << headerName << "\"" << endl + << "#include <QtCore/QMetaObject>" << endl + << includeList + << endl; + hs << forwardDeclarations; + } else { + hs << includeList; + } + + hs << endl; + + foreach (const QDBusIntrospection::Interface *interface, interfaces) { + QString className = classNameForInterface(interface->name, Adaptor); + + // comment: + hs << "/*" << endl + << " * Adaptor class for interface " << interface->name << endl + << " */" << endl; + cs << "/*" << endl + << " * Implementation of adaptor class " << className << endl + << " */" << endl + << endl; + + // class header: + hs << "class " << className << ": public QDBusAbstractAdaptor" << endl + << "{" << endl + << " Q_OBJECT" << endl + << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << endl + << " Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << endl + << stringify(interface->introspection) + << " \"\")" << endl + << "public:" << endl + << " " << className << "(QObject *parent);" << endl + << " virtual ~" << className << "();" << endl + << endl; + + // constructor/destructor + cs << className << "::" << className << "(QObject *parent)" << endl + << " : QDBusAbstractAdaptor(parent)" << endl + << "{" << endl + << " // constructor" << endl + << " setAutoRelaySignals(true);" << endl + << "}" << endl + << endl + << className << "::~" << className << "()" << endl + << "{" << endl + << " // destructor" << endl + << "}" << endl + << endl; + + hs << "public: // PROPERTIES" << endl; + foreach (const QDBusIntrospection::Property &property, interface->properties) { + QByteArray type = qtTypeName(property.type); + QString constRefType = constRefArg(type); + QString getter = propertyGetter(property); + QString setter = propertySetter(property); + + hs << " Q_PROPERTY(" << type << " " << property.name; + if (property.access != QDBusIntrospection::Property::Write) + hs << " READ " << getter; + if (property.access != QDBusIntrospection::Property::Read) + hs << " WRITE " << setter; + hs << ")" << endl; + + // getter: + if (property.access != QDBusIntrospection::Property::Write) { + hs << " " << type << " " << getter << "() const;" << endl; + cs << type << " " + << className << "::" << getter << "() const" << endl + << "{" << endl + << " // get the value of property " << property.name << endl + << " return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));" << endl + << "}" << endl + << endl; + } + + // setter + if (property.access != QDBusIntrospection::Property::Read) { + hs << " void " << setter << "(" << constRefType << "value);" << endl; + cs << "void " << className << "::" << setter << "(" << constRefType << "value)" << endl + << "{" << endl + << " // set the value of property " << property.name << endl + << " parent()->setProperty(\"" << property.name << "\", value);" << endl + << "}" << endl + << endl; + } + + hs << endl; + } + + hs << "public Q_SLOTS: // METHODS" << endl; + foreach (const QDBusIntrospection::Method &method, interface->methods) { + bool isAsync = method.annotations.value(ANNOTATION_NO_WAIT) == "true"; + if (isAsync && !method.outputArgs.isEmpty()) { + fprintf(stderr, "warning: method %s in interface %s is marked 'async' but has output arguments.\n", + qPrintable(method.name), qPrintable(interface->name)); + continue; + } + + hs << " "; + if (method.annotations.value("org.freedesktop.DBus.Deprecated") == "true") + hs << "Q_DECL_DEPRECATED "; + + QByteArray returnType; + if (isAsync) { + hs << "Q_ASYNC void "; + cs << "void "; + } else if (method.outputArgs.isEmpty()) { + hs << "void "; + cs << "void "; + } else { + returnType = qtTypeName(method.outputArgs.first().type); + hs << returnType << " "; + cs << returnType << " "; + } + + QString name = method.name; + hs << name << "("; + cs << className << "::" << name << "("; + + QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs); + writeArgList(hs, argNames, method.inputArgs, method.outputArgs); + writeArgList(cs, argNames, method.inputArgs, method.outputArgs); + + hs << ");" << endl; // finished for header + cs << ")" << endl + << "{" << endl + << " // handle method call " << interface->name << "." << method.name << endl; + + // create the return type + int j = method.inputArgs.count(); + if (!returnType.isEmpty()) + cs << " " << returnType << " " << argNames.at(j) << ";" << endl; + + // make the call + if (method.inputArgs.count() <= 10 && method.outputArgs.count() <= 1) { + // we can use QMetaObject::invokeMethod + static const char invoke[] = " QMetaObject::invokeMethod(parent(), \""; + cs << invoke << name << "\""; + + if (!method.outputArgs.isEmpty()) + cs << ", Q_RETURN_ARG(" + << qtTypeName(method.outputArgs.at(0).type) + << ", " + << argNames.at(method.inputArgs.count()) + << ")"; + + for (int i = 0; i < method.inputArgs.count(); ++i) + cs << ", Q_ARG(" + << qtTypeName(method.inputArgs.at(i).type) + << ", " + << argNames.at(i) + << ")"; + + cs << ");" << endl; + } + + cs << endl + << " // Alternative:" << endl + << " //"; + if (!method.outputArgs.isEmpty()) + cs << argNames.at(method.inputArgs.count()) << " = "; + cs << "static_cast<YourObjectType *>(parent())->" << name << "("; + + int argPos = 0; + bool first = true; + for (int i = 0; i < method.inputArgs.count(); ++i) { + cs << (first ? "" : ", ") << argNames.at(argPos++); + first = false; + } + ++argPos; // skip retval, if any + for (int i = 1; i < method.outputArgs.count(); ++i) { + cs << (first ? "" : ", ") << argNames.at(argPos++); + first = false; + } + + cs << ");" << endl; + if (!method.outputArgs.isEmpty()) + cs << " return " << argNames.at(method.inputArgs.count()) << ";" << endl; + cs << "}" << endl + << endl; + } + + hs << "Q_SIGNALS: // SIGNALS" << endl; + foreach (const QDBusIntrospection::Signal &signal, interface->signals_) { + hs << " "; + if (signal.annotations.value("org.freedesktop.DBus.Deprecated") == "true") + hs << "Q_DECL_DEPRECATED "; + + hs << "void " << signal.name << "("; + + QStringList argNames = makeArgNames(signal.outputArgs); + writeArgList(hs, argNames, signal.outputArgs); + + hs << ");" << endl; // finished for header + } + + // close the class: + hs << "};" << endl + << endl; + } + + // close the include guard + hs << "#endif" << endl; + + if (includeMocs) + cs << endl + << "#include \"" << adaptorFile << ".moc\"" << endl; + + cs.flush(); + hs.flush(); + if (headerName == cppName) + file.write(cppData); + else { + // write to cpp file + QFile f(cppName); + f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); + f.write(cppData); + } +} + +int main(int argc, char **argv) +{ + parseCmdLine(argc, argv); + + QDBusIntrospection::Interfaces interfaces = readInput(); + cleanInterfaces(interfaces); + + writeProxy(proxyFile, interfaces); + + if (adaptorFile) + writeAdaptor(adaptorFile, interfaces); + + return 0; +} + +/*! + \page dbusidl2cpp.html + \title QtDBus IDL compiler (dbusidl2cpp) + + The QtDBus IDL compiler is a tool that can be used to parse interface descriptions and produce + static code representing those interfaces, which can then be used to make calls to remote + objects or implement said interfaces. + + \c dbusidl2dcpp has two modes of operation, that correspond to the two possible outputs it can + produce: the interface (proxy) class or the adaptor class.The latter consists of both a C++ + header and a source file, which are meant to be edited and adapted to your needs. + + The \c dbusidl2dcpp tool is not meant to be run every time you compile your + application. Instead, it's meant to be used when developing the code or when the interface + changes. + + The adaptor classes generated by \c dbusidl2cpp are just a skeleton that must be completed. It + generates, by default, calls to slots with the same name on the object the adaptor is attached + to. However, you may modify those slots or the property accessor functions to suit your needs. +*/ |