File indexing completed on 2025-02-02 05:18:49

0001 /*
0002     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "generator.h"
0007 
0008 #include <QBuffer>
0009 #include <QByteArray>
0010 #include <QCommandLineParser>
0011 #include <QCoreApplication>
0012 #include <QDate>
0013 #include <QFile>
0014 #include <QFutureWatcher>
0015 #include <QMutexLocker>
0016 #include <QProcess>
0017 #include <QStandardPaths>
0018 #include <QTextStream>
0019 #include <QtConcurrentRun>
0020 
0021 #include <QDebug>
0022 
0023 #include <functional>
0024 
0025 namespace KWayland
0026 {
0027 namespace Tools
0028 {
0029 static QMap<QString, QString> s_clientClassNameMapping;
0030 
0031 static QString toQtInterfaceName(const QString &wlInterface)
0032 {
0033     auto it = s_clientClassNameMapping.constFind(wlInterface);
0034     if (it != s_clientClassNameMapping.constEnd()) {
0035         return it.value();
0036     } else {
0037         qWarning() << "Cannot find mapping for " << wlInterface;
0038     }
0039     return wlInterface;
0040 }
0041 
0042 static QString toCamelCase(const QString &underscoreName)
0043 {
0044     const QStringList parts = underscoreName.split(QStringLiteral("_"));
0045     if (parts.count() < 2) {
0046         return underscoreName;
0047     }
0048     auto it = parts.constBegin();
0049     QString camelCase = (*it);
0050     it++;
0051     for (; it != parts.constEnd(); ++it) {
0052         camelCase.append((*it).left(1).toUpper());
0053         camelCase.append((*it).mid(1));
0054     }
0055     return camelCase;
0056 }
0057 
0058 Argument::Argument()
0059 {
0060 }
0061 
0062 Argument::Argument(const QXmlStreamAttributes &attributes)
0063     : m_name(attributes.value(QStringLiteral("name")).toString())
0064     , m_type(parseType(attributes.value(QStringLiteral("type"))))
0065     , m_allowNull(attributes.hasAttribute(QStringLiteral("allow-null")))
0066     , m_inteface(attributes.value(QStringLiteral("interface")).toString())
0067 {
0068 }
0069 
0070 Argument::~Argument() = default;
0071 
0072 Argument::Type Argument::parseType(const QStringView type)
0073 {
0074     if (type.compare(QLatin1String("new_id")) == 0) {
0075         return Type::NewId;
0076     }
0077     if (type.compare(QLatin1String("destructor")) == 0) {
0078         return Type::Destructor;
0079     }
0080     if (type.compare(QLatin1String("object")) == 0) {
0081         return Type::Object;
0082     }
0083     if (type.compare(QLatin1String("fd")) == 0) {
0084         return Type::FileDescriptor;
0085     }
0086     if (type.compare(QLatin1String("fixed")) == 0) {
0087         return Type::Fixed;
0088     }
0089     if (type.compare(QLatin1String("uint")) == 0) {
0090         return Type::Uint;
0091     }
0092     if (type.compare(QLatin1String("int")) == 0) {
0093         return Type::Int;
0094     }
0095     if (type.compare(QLatin1String("string")) == 0) {
0096         return Type::String;
0097     }
0098 
0099     return Type::Unknown;
0100 }
0101 
0102 QString Argument::typeAsQt() const
0103 {
0104     switch (m_type) {
0105     case Type::Destructor:
0106         return QString();
0107     case Type::FileDescriptor:
0108         return QStringLiteral("int");
0109     case Type::Fixed:
0110         return QStringLiteral("qreal");
0111     case Type::Int:
0112         return QStringLiteral("qint32");
0113     case Type::NewId:
0114     case Type::Object:
0115         return toQtInterfaceName(m_inteface);
0116     case Type::String:
0117         return QStringLiteral("const QString &");
0118     case Type::Uint:
0119         return QStringLiteral("quint32");
0120     case Type::Unknown:
0121         return QString();
0122     default:
0123         Q_UNREACHABLE();
0124     }
0125 }
0126 
0127 QString Argument::typeAsServerWl() const
0128 {
0129     switch (m_type) {
0130     case Type::Destructor:
0131         return QString();
0132     case Type::FileDescriptor:
0133         return QStringLiteral("int32_t");
0134     case Type::Fixed:
0135         return QStringLiteral("wl_fixed");
0136     case Type::Int:
0137         return QStringLiteral("int32_t");
0138     case Type::Object:
0139         return QStringLiteral("wl_resource *");
0140     case Type::String:
0141         return QStringLiteral("const char *");
0142     case Type::Uint:
0143     case Type::NewId:
0144         return QStringLiteral("uint32_t");
0145     case Type::Unknown:
0146         return QString();
0147     default:
0148         Q_UNREACHABLE();
0149     }
0150 }
0151 
0152 Request::Request()
0153 {
0154 }
0155 
0156 Request::Request(const QString &name)
0157     : m_name(name)
0158 {
0159 }
0160 
0161 Request::~Request() = default;
0162 
0163 bool Request::isFactory() const
0164 {
0165     for (const auto &a : m_arguments) {
0166         if (a.type() == Argument::Type::NewId) {
0167             return true;
0168         }
0169     }
0170     return false;
0171 }
0172 
0173 Event::Event()
0174 {
0175 }
0176 
0177 Event::Event(const QString &name)
0178     : m_name(name)
0179 {
0180 }
0181 
0182 Event::~Event() = default;
0183 
0184 Interface::Interface() = default;
0185 
0186 Interface::Interface(const QXmlStreamAttributes &attributes)
0187     : m_name(attributes.value(QStringLiteral("name")).toString())
0188     , m_version(attributes.value(QStringLiteral("version")).toUInt())
0189     , m_factory(nullptr)
0190 {
0191     auto it = s_clientClassNameMapping.constFind(m_name);
0192     if (it != s_clientClassNameMapping.constEnd()) {
0193         m_clientName = it.value();
0194     } else {
0195         qWarning() << "Failed to map " << m_name << " to a KWayland name";
0196     }
0197 }
0198 
0199 Interface::~Interface() = default;
0200 
0201 Generator::Generator(QObject *parent)
0202     : QObject(parent)
0203 {
0204 }
0205 
0206 Generator::~Generator() = default;
0207 
0208 void Generator::start()
0209 {
0210     startAuthorNameProcess();
0211     startAuthorEmailProcess();
0212 
0213     startParseXml();
0214 
0215     startGenerateHeaderFile();
0216     startGenerateCppFile();
0217     startGenerateServerHeaderFile();
0218     startGenerateServerCppFile();
0219 }
0220 
0221 void Generator::startParseXml()
0222 {
0223     if (m_xmlFileName.isEmpty()) {
0224         return;
0225     }
0226     QFile xmlFile(m_xmlFileName);
0227     xmlFile.open(QIODevice::ReadOnly);
0228     m_xmlReader.setDevice(&xmlFile);
0229     while (!m_xmlReader.atEnd()) {
0230         if (!m_xmlReader.readNextStartElement()) {
0231             continue;
0232         }
0233         if (m_xmlReader.qualifiedName().compare(QLatin1String("protocol")) == 0) {
0234             parseProtocol();
0235         }
0236     }
0237 
0238     auto findFactory = [this](const QString interfaceName) -> Interface * {
0239         for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) {
0240             if ((*it).name().compare(interfaceName) == 0) {
0241                 continue;
0242             }
0243             for (auto r : (*it).requests()) {
0244                 for (auto a : r.arguments()) {
0245                     if (a.type() == Argument::Type::NewId && a.interface().compare(interfaceName) == 0) {
0246                         return &(*it);
0247                     }
0248                 }
0249             }
0250         }
0251         return nullptr;
0252     };
0253     for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) {
0254         Interface *factory = findFactory((*it).name());
0255         if (factory) {
0256             qDebug() << (*it).name() << "gets factored by" << factory->kwaylandClientName();
0257             (*it).setFactory(factory);
0258         } else {
0259             qDebug() << (*it).name() << "considered as a global";
0260             (*it).markAsGlobal();
0261         }
0262     }
0263 }
0264 
0265 void Generator::parseProtocol()
0266 {
0267     const auto attributes = m_xmlReader.attributes();
0268     const QString protocolName = attributes.value(QStringLiteral("name")).toString();
0269 
0270     if (m_baseFileName.isEmpty()) {
0271         m_baseFileName = protocolName.toLower();
0272     }
0273 
0274     while (!m_xmlReader.atEnd()) {
0275         if (!m_xmlReader.readNextStartElement()) {
0276             if (m_xmlReader.qualifiedName().compare(QLatin1String("protocol")) == 0) {
0277                 return;
0278             }
0279             continue;
0280         }
0281         if (m_xmlReader.qualifiedName().compare(QLatin1String("interface")) == 0) {
0282             m_interfaces << parseInterface();
0283         }
0284     }
0285 }
0286 
0287 Interface Generator::parseInterface()
0288 {
0289     Interface interface(m_xmlReader.attributes());
0290     while (!m_xmlReader.atEnd()) {
0291         if (!m_xmlReader.readNextStartElement()) {
0292             if (m_xmlReader.qualifiedName().compare(QLatin1String("interface")) == 0) {
0293                 break;
0294             }
0295             continue;
0296         }
0297         if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) {
0298             interface.addRequest(parseRequest());
0299         }
0300         if (m_xmlReader.qualifiedName().compare(QLatin1String("event")) == 0) {
0301             interface.addEvent(parseEvent());
0302         }
0303     }
0304     return interface;
0305 }
0306 
0307 Request Generator::parseRequest()
0308 {
0309     const auto attributes = m_xmlReader.attributes();
0310     Request request(attributes.value(QStringLiteral("name")).toString());
0311     if (attributes.value(QStringLiteral("type")).toString().compare(QLatin1String("destructor")) == 0) {
0312         request.markAsDestructor();
0313     }
0314     while (!m_xmlReader.atEnd()) {
0315         if (!m_xmlReader.readNextStartElement()) {
0316             if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) {
0317                 break;
0318             }
0319             continue;
0320         }
0321         if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) {
0322             request.addArgument(Argument(m_xmlReader.attributes()));
0323         }
0324     }
0325     return request;
0326 }
0327 
0328 Event Generator::parseEvent()
0329 {
0330     const auto attributes = m_xmlReader.attributes();
0331     Event event(attributes.value(QStringLiteral("name")).toString());
0332     while (!m_xmlReader.atEnd()) {
0333         if (!m_xmlReader.readNextStartElement()) {
0334             if (m_xmlReader.qualifiedName().compare(QLatin1String("event")) == 0) {
0335                 break;
0336             }
0337             continue;
0338         }
0339         if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) {
0340             event.addArgument(Argument(m_xmlReader.attributes()));
0341         }
0342     }
0343     return event;
0344 }
0345 
0346 void Generator::startGenerateHeaderFile()
0347 {
0348     QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
0349     connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
0350     m_finishedCounter++;
0351     watcher->setFuture(QtConcurrent::run([this] {
0352         QFile file(QStringLiteral("%1.h").arg(m_baseFileName));
0353         file.open(QIODevice::WriteOnly);
0354         m_stream.setLocalData(new QTextStream(&file));
0355         m_project.setLocalData(Project::Client);
0356         generateCopyrightHeader();
0357         generateStartIncludeGuard();
0358         generateHeaderIncludes();
0359         generateWaylandForwardDeclarations();
0360         generateStartNamespace();
0361         generateNamespaceForwardDeclarations();
0362         for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
0363             generateClass(*it);
0364         }
0365         generateEndNamespace();
0366         generateEndIncludeGuard();
0367 
0368         m_stream.setLocalData(nullptr);
0369         file.close();
0370     }));
0371 }
0372 
0373 void Generator::startGenerateCppFile()
0374 {
0375     QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
0376     connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
0377     m_finishedCounter++;
0378     watcher->setFuture(QtConcurrent::run([this] {
0379         QFile file(QStringLiteral("%1.cpp").arg(m_baseFileName));
0380         file.open(QIODevice::WriteOnly);
0381         m_stream.setLocalData(new QTextStream(&file));
0382         m_project.setLocalData(Project::Client);
0383         generateCopyrightHeader();
0384         generateCppIncludes();
0385         generateStartNamespace();
0386         for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
0387             generatePrivateClass(*it);
0388             generateClientCpp(*it);
0389             generateClientCppRequests(*it);
0390         }
0391 
0392         generateEndNamespace();
0393 
0394         m_stream.setLocalData(nullptr);
0395         file.close();
0396     }));
0397 }
0398 
0399 void Generator::startGenerateServerHeaderFile()
0400 {
0401     QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
0402     connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
0403     m_finishedCounter++;
0404     watcher->setFuture(QtConcurrent::run([this] {
0405         QFile file(QStringLiteral("%1_interface.h").arg(m_baseFileName));
0406         file.open(QIODevice::WriteOnly);
0407         m_stream.setLocalData(new QTextStream(&file));
0408         m_project.setLocalData(Project::Server);
0409         generateCopyrightHeader();
0410         generateStartIncludeGuard();
0411         generateHeaderIncludes();
0412         generateStartNamespace();
0413         generateNamespaceForwardDeclarations();
0414         if (std::any_of(m_interfaces.constBegin(), m_interfaces.constEnd(), [](const Interface &i) {
0415                 return i.isUnstableInterface();
0416             })) {
0417             // generate the unstable semantic version
0418             auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [](const Interface &i) {
0419                 return i.isGlobal();
0420             });
0421             if (it != m_interfaces.constEnd()) {
0422                 const QString templateString = QStringLiteral(
0423                     "/**\n"
0424                     " * Enum describing the interface versions the %1 can support.\n"
0425                     " *\n"
0426                     " * @since 5.XX\n"
0427                     " **/\n"
0428                     "enum class %1Version {\n"
0429                     "    /**\n"
0430                     "     * %2\n"
0431                     "     **/\n"
0432                     "     UnstableV%3\n"
0433                     "};\n\n");
0434                 *m_stream.localData() << templateString.arg((*it).kwaylandServerName())
0435                                              .arg((*it).name())
0436                                              .arg((*it).name().mid((*it).name().lastIndexOf(QStringLiteral("_v")) + 2));
0437             }
0438         }
0439         for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
0440             generateClass(*it);
0441         }
0442         generateEndNamespace();
0443         generateEndIncludeGuard();
0444 
0445         m_stream.setLocalData(nullptr);
0446         file.close();
0447     }));
0448 }
0449 
0450 void Generator::startGenerateServerCppFile()
0451 {
0452     QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
0453     connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
0454     m_finishedCounter++;
0455     watcher->setFuture(QtConcurrent::run([this] {
0456         QFile file(QStringLiteral("%1_interface.cpp").arg(m_baseFileName));
0457         file.open(QIODevice::WriteOnly);
0458         m_stream.setLocalData(new QTextStream(&file));
0459         m_project.setLocalData(Project::Server);
0460         generateCopyrightHeader();
0461         generateCppIncludes();
0462         generateStartNamespace();
0463         for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
0464             generatePrivateClass(*it);
0465             //             generateClientCpp(*it);
0466             //             generateClientCppRequests(*it);
0467         }
0468 
0469         generateEndNamespace();
0470 
0471         m_stream.setLocalData(nullptr);
0472         file.close();
0473     }));
0474 }
0475 
0476 void Generator::checkEnd()
0477 {
0478     m_finishedCounter--;
0479     if (m_finishedCounter == 0) {
0480         QCoreApplication::quit();
0481     }
0482 }
0483 
0484 static QString findGitExec()
0485 {
0486     const QString exec = QStandardPaths::findExecutable(QStringLiteral("git"));
0487     if (exec.isEmpty()) {
0488         qWarning() << "Could not find git executable in PATH.";
0489     }
0490     return exec;
0491 }
0492 
0493 void Generator::startAuthorNameProcess()
0494 {
0495     const QString exec = findGitExec();
0496     if (exec.isEmpty()) {
0497         return;
0498     }
0499     QProcess *proc = new QProcess(this);
0500     proc->setArguments(QStringList{QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.name")});
0501     proc->setProgram(exec);
0502     connect(proc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [this, proc] {
0503         QMutexLocker locker(&m_mutex);
0504         m_authorName = QString::fromLocal8Bit(proc->readAllStandardOutput()).trimmed();
0505         proc->deleteLater();
0506         m_waitCondition.wakeAll();
0507     });
0508     proc->start();
0509 }
0510 
0511 void Generator::startAuthorEmailProcess()
0512 {
0513     const QString exec = findGitExec();
0514     if (exec.isEmpty()) {
0515         return;
0516     }
0517     QProcess *proc = new QProcess(this);
0518     proc->setArguments(QStringList{QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.email")});
0519     proc->setProgram(exec);
0520     connect(proc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [this, proc] {
0521         QMutexLocker locker(&m_mutex);
0522         m_authorEmail = QString::fromLocal8Bit(proc->readAllStandardOutput()).trimmed();
0523         proc->deleteLater();
0524         m_waitCondition.wakeAll();
0525     });
0526     proc->start();
0527 }
0528 
0529 void Generator::generateCopyrightHeader()
0530 {
0531     m_mutex.lock();
0532     while (m_authorEmail.isEmpty() || m_authorName.isEmpty()) {
0533         m_waitCondition.wait(&m_mutex);
0534     }
0535     m_mutex.unlock();
0536     const QString templateString = QStringLiteral(
0537         "/****************************************************************************\n"
0538         "Copyright %1  %2 <%3>\n"
0539         "\n"
0540         "This library is free software; you can redistribute it and/or\n"
0541         "modify it under the terms of the GNU Lesser General Public\n"
0542         "License as published by the Free Software Foundation; either\n"
0543         "version 2.1 of the License, or (at your option) version 3, or any\n"
0544         "later version accepted by the membership of KDE e.V. (or its\n"
0545         "successor approved by the membership of KDE e.V.), which shall\n"
0546         "act as a proxy defined in Section 6 of version 3 of the license.\n"
0547         "\n"
0548         "This library is distributed in the hope that it will be useful,\n"
0549         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
0550         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
0551         "Lesser General Public License for more details.\n"
0552         "\n"
0553         "You should have received a copy of the GNU Lesser General Public\n"
0554         "License along with this library.  If not, see <http://www.gnu.org/licenses/>.\n"
0555         "****************************************************************************/\n");
0556     QDate date = QDate::currentDate();
0557     *m_stream.localData() << templateString.arg(date.year()).arg(m_authorName).arg(m_authorEmail);
0558 }
0559 
0560 void Generator::generateEndIncludeGuard()
0561 {
0562     *m_stream.localData() << QStringLiteral("#endif\n");
0563 }
0564 
0565 void Generator::generateStartIncludeGuard()
0566 {
0567     const QString templateString = QStringLiteral(
0568         "#ifndef KWAYLAND_%1_%2_H\n"
0569         "#define KWAYLAND_%1_%2_H\n\n");
0570 
0571     *m_stream.localData() << templateString.arg(projectToName().toUpper()).arg(m_baseFileName.toUpper());
0572 }
0573 
0574 void Generator::generateStartNamespace()
0575 {
0576     const QString templateString = QStringLiteral(
0577         "namespace KWayland\n"
0578         "{\n"
0579         "namespace %1\n"
0580         "{\n\n");
0581     *m_stream.localData() << templateString.arg(projectToName());
0582 }
0583 
0584 void Generator::generateEndNamespace()
0585 {
0586     *m_stream.localData() << QStringLiteral("\n}\n}\n\n");
0587 }
0588 
0589 void Generator::generateHeaderIncludes()
0590 {
0591     switch (m_project.localData()) {
0592     case Project::Client:
0593         *m_stream.localData() << QStringLiteral("#include <QObject>\n\n");
0594         break;
0595     case Project::Server:
0596         *m_stream.localData() << QStringLiteral(
0597             "#include \"global.h\"\n"
0598             "#include \"resource.h\"\n\n");
0599         break;
0600     default:
0601         Q_UNREACHABLE();
0602     }
0603     *m_stream.localData() << QStringLiteral("#include <KWayland/%1/kwayland%2_export.h>\n\n").arg(projectToName()).arg(projectToName().toLower());
0604 }
0605 
0606 void Generator::generateCppIncludes()
0607 {
0608     switch (m_project.localData()) {
0609     case Project::Client:
0610         *m_stream.localData() << QStringLiteral("#include \"%1.h\"\n").arg(m_baseFileName.toLower());
0611         *m_stream.localData() << QStringLiteral("#include \"event_queue.h\"\n");
0612         *m_stream.localData() << QStringLiteral("#include \"wayland_pointer_p.h\"\n\n");
0613         break;
0614     case Project::Server:
0615         *m_stream.localData() << QStringLiteral("#include \"%1_interface.h\"\n").arg(m_baseFileName.toLower());
0616         *m_stream.localData() << QStringLiteral(
0617             "#include \"display.h\"\n"
0618             "#include \"global_p.h\"\n"
0619             "#include \"resource_p.h\"\n\n");
0620         break;
0621     default:
0622         Q_UNREACHABLE();
0623     }
0624 }
0625 
0626 void Generator::generateClass(const Interface &interface)
0627 {
0628     switch (m_project.localData()) {
0629     case Project::Client:
0630         if (interface.isGlobal()) {
0631             generateClientGlobalClass(interface);
0632         } else {
0633             generateClientResourceClass(interface);
0634         }
0635         break;
0636     case Project::Server:
0637         if (interface.isGlobal()) {
0638             generateServerGlobalClass(interface);
0639         } else {
0640             generateServerResourceClass(interface);
0641         }
0642         break;
0643     default:
0644         Q_UNREACHABLE();
0645     }
0646 }
0647 
0648 void Generator::generateClientGlobalClass(const Interface &interface)
0649 {
0650     generateClientGlobalClassDoxy(interface);
0651     generateClientClassQObjectDerived(interface);
0652     generateClientGlobalClassCtor(interface);
0653     generateClientClassDtor(interface);
0654     generateClientGlobalClassSetup(interface);
0655     generateClientClassReleaseDestroy(interface);
0656     generateClientClassStart(interface);
0657     generateClientClassRequests(interface);
0658     generateClientClassCasts(interface);
0659     generateClientClassSignals(interface);
0660     generateClientGlobalClassEnd(interface);
0661 }
0662 
0663 void Generator::generateClientResourceClass(const Interface &interface)
0664 {
0665     generateClientClassQObjectDerived(interface);
0666     generateClientClassDtor(interface);
0667     generateClientResourceClassSetup(interface);
0668     generateClientClassReleaseDestroy(interface);
0669     generateClientClassRequests(interface);
0670     generateClientClassCasts(interface);
0671     generateClientResourceClassEnd(interface);
0672 }
0673 
0674 void Generator::generateServerGlobalClass(const Interface &interface)
0675 {
0676     if (interface.isUnstableInterface()) {
0677         generateServerGlobalClassUnstable(interface);
0678         return;
0679     }
0680     const QString templateString = QStringLiteral(
0681         "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
0682         "{\n"
0683         "    Q_OBJECT\n"
0684         "public:\n"
0685         "    virtual ~%1();\n"
0686         "\n"
0687         "private:\n"
0688         "    explicit %1(Display *display, QObject *parent = nullptr);\n"
0689         "    friend class Display;\n"
0690         "    class Private;\n"
0691         "};\n"
0692         "\n");
0693     *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
0694 }
0695 
0696 void Generator::generateServerGlobalClassUnstable(const Interface &interface)
0697 {
0698     const QString templateString = QStringLiteral(
0699         "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
0700         "{\n"
0701         "    Q_OBJECT\n"
0702         "public:\n"
0703         "    virtual ~%1();\n"
0704         "\n"
0705         "    /**\n"
0706         "     * @returns The interface version used by this %1\n"
0707         "     **/\n"
0708         "    %1Version interfaceVersion() const;\n"
0709         "\n"
0710         "protected:\n"
0711         "    class Private;\n"
0712         "    explicit %1(Private *d, QObject *parent = nullptr);\n"
0713         "\n"
0714         "private:\n"
0715         "    Private *d_func() const;\n"
0716         "};\n"
0717         "\n");
0718     *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
0719 }
0720 
0721 void Generator::generateServerResourceClass(const Interface &interface)
0722 {
0723     if (interface.factory()->isUnstableInterface()) {
0724         generateServerResourceClassUnstable(interface);
0725         return;
0726     }
0727     const QString templateString = QStringLiteral(
0728         "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
0729         "{\n"
0730         "    Q_OBJECT\n"
0731         "public:\n"
0732         "    virtual ~%1();\n"
0733         "\n"
0734         "private:\n"
0735         "    explicit %1(%2 *parent, wl_resource *parentResource);\n"
0736         "    friend class %2;\n"
0737         "\n"
0738         "    class Private;\n"
0739         "    Private *d_func() const;\n"
0740         "};\n"
0741         "\n");
0742     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
0743 }
0744 
0745 void Generator::generateServerResourceClassUnstable(const Interface &interface)
0746 {
0747     const QString templateString = QStringLiteral(
0748         "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
0749         "{\n"
0750         "    Q_OBJECT\n"
0751         "public:\n"
0752         "\n"
0753         "    virtual ~%1();\n"
0754         "\n"
0755         "    /**\n"
0756         "     * @returns The interface version used by this %1\n"
0757         "     **/\n"
0758         "    %2Version interfaceVersion() const;\n"
0759         "\n"
0760         "protected:\n"
0761         "    class Private;\n"
0762         "    explicit %1(Private *p, QObject *parent = nullptr);\n"
0763         "\n"
0764         "private:\n"
0765         "    Private *d_func() const;\n"
0766         "};\n"
0767         "\n");
0768     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
0769 }
0770 
0771 void Generator::generatePrivateClass(const Interface &interface)
0772 {
0773     switch (m_project.localData()) {
0774     case Project::Client:
0775         generateClientPrivateClass(interface);
0776         break;
0777     case Project::Server:
0778         if (interface.isGlobal()) {
0779             generateServerPrivateGlobalClass(interface);
0780         } else {
0781             generateServerPrivateResourceClass(interface);
0782         }
0783         break;
0784     default:
0785         Q_UNREACHABLE();
0786     }
0787 }
0788 
0789 void Generator::generateServerPrivateGlobalClass(const Interface &interface)
0790 {
0791     QString templateString = QStringLiteral(
0792         "class %1::Private : public Global::Private\n"
0793         "{\n"
0794         "public:\n"
0795         "    Private(%1 *q, Display *d);\n"
0796         "\n"
0797         "private:\n"
0798         "    void bind(wl_client *client, uint32_t version, uint32_t id) override;\n"
0799         "\n"
0800         "    static void unbind(wl_resource *resource);\n"
0801         "    static Private *cast(wl_resource *r) {\n"
0802         "        return reinterpret_cast<Private*>(wl_resource_get_user_data(r));\n"
0803         "    }\n"
0804         "\n");
0805     *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
0806 
0807     generateServerPrivateCallbackDefinitions(interface);
0808 
0809     templateString = QStringLiteral(
0810         "    %1 *q;\n"
0811         "    static const struct %2_interface s_interface;\n"
0812         "    static const quint32 s_version;\n"
0813         "};\n"
0814         "\n"
0815         "const quint32 %1::Private::s_version = %3;\n"
0816         "\n");
0817     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name()).arg(interface.version());
0818     generateServerPrivateInterfaceClass(interface);
0819     generateServerPrivateCallbackImpl(interface);
0820     generateServerPrivateGlobalCtorBindClass(interface);
0821 }
0822 
0823 void Generator::generateServerPrivateCallbackDefinitions(const Interface &interface)
0824 {
0825     for (const auto &r : interface.requests()) {
0826         if (r.isDestructor() && !interface.isGlobal()) {
0827             continue;
0828         }
0829         *m_stream.localData() << QStringLiteral("    static void %1Callback(wl_client *client, wl_resource *resource").arg(toCamelCase(r.name()));
0830         for (const auto &a : r.arguments()) {
0831             *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name());
0832         }
0833         *m_stream.localData() << QStringLiteral(");\n");
0834     }
0835     *m_stream.localData() << QStringLiteral("\n");
0836 }
0837 
0838 void Generator::generateServerPrivateCallbackImpl(const Interface &interface)
0839 {
0840     for (const auto &r : interface.requests()) {
0841         if (r.isDestructor() && !interface.isGlobal()) {
0842             continue;
0843         }
0844         *m_stream.localData() << QStringLiteral("void %2::Private::%1Callback(wl_client *client, wl_resource *resource")
0845                                      .arg(toCamelCase(r.name()))
0846                                      .arg(interface.kwaylandServerName());
0847         for (const auto &a : r.arguments()) {
0848             *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name());
0849         }
0850         *m_stream.localData() << QStringLiteral(
0851             ")\n"
0852             "{\n");
0853         if (r.isDestructor()) {
0854             *m_stream.localData() << QStringLiteral(
0855                 "    Q_UNUSED(client)\n"
0856                 "    wl_resource_destroy(resource);\n");
0857         } else {
0858             *m_stream.localData() << QStringLiteral("    // TODO: implement\n");
0859         }
0860         *m_stream.localData() << QStringLiteral(
0861             "}\n"
0862             "\n");
0863     }
0864 }
0865 
0866 void Generator::generateServerPrivateGlobalCtorBindClass(const Interface &interface)
0867 {
0868     QString templateString = QStringLiteral(
0869         "%1::Private::Private(%1 *q, Display *d)\n"
0870         "    : Global::Private(d, &%2_interface, s_version)\n"
0871         "    , q(q)\n"
0872         "{\n"
0873         "}\n"
0874         "\n"
0875         "void %1::Private::bind(wl_client *client, uint32_t version, uint32_t id)\n"
0876         "{\n"
0877         "    auto c = display->getConnection(client);\n"
0878         "    wl_resource *resource = c->createResource(&%2_interface, qMin(version, s_version), id);\n"
0879         "    if (!resource) {\n"
0880         "        wl_client_post_no_memory(client);\n"
0881         "        return;\n"
0882         "    }\n"
0883         "    wl_resource_set_implementation(resource, &s_interface, this, unbind);\n"
0884         "    // TODO: should we track?\n"
0885         "}\n"
0886         "\n"
0887         "void %1::Private::unbind(wl_resource *resource)\n"
0888         "{\n"
0889         "    Q_UNUSED(resource)\n"
0890         "    // TODO: implement?\n"
0891         "}\n"
0892         "\n");
0893     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name());
0894 }
0895 
0896 void Generator::generateServerPrivateResourceClass(const Interface &interface)
0897 {
0898     QString templateString = QStringLiteral(
0899         "class %1::Private : public Resource::Private\n"
0900         "{\n"
0901         "public:\n"
0902         "    Private(%1 *q, %2 *c, wl_resource *parentResource);\n"
0903         "    ~Private();\n"
0904         "\n"
0905         "private:\n");
0906     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
0907 
0908     generateServerPrivateCallbackDefinitions(interface);
0909 
0910     templateString = QStringLiteral(
0911         "    %1 *q_func() {\n"
0912         "        return reinterpret_cast<%1 *>(q);\n"
0913         "    }\n"
0914         "\n"
0915         "    static const struct %2_interface s_interface;\n"
0916         "};\n"
0917         "\n");
0918     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name());
0919 
0920     generateServerPrivateInterfaceClass(interface);
0921     generateServerPrivateCallbackImpl(interface);
0922     generateServerPrivateResourceCtorDtorClass(interface);
0923 }
0924 
0925 void Generator::generateServerPrivateInterfaceClass(const Interface &interface)
0926 {
0927     *m_stream.localData() << QStringLiteral("#ifndef K_DOXYGEN\n");
0928     *m_stream.localData()
0929         << QStringLiteral("const struct %2_interface %1::Private::s_interface = {\n").arg(interface.kwaylandServerName()).arg(interface.name());
0930     bool first = true;
0931     for (auto r : interface.requests()) {
0932         if (!first) {
0933             *m_stream.localData() << QStringLiteral(",\n");
0934         } else {
0935             first = false;
0936         }
0937         if (r.isDestructor() && !interface.isGlobal()) {
0938             *m_stream.localData() << QStringLiteral("    resourceDestroyedCallback");
0939         } else {
0940             *m_stream.localData() << QStringLiteral("    %1Callback").arg(toCamelCase(r.name()));
0941         }
0942     }
0943     *m_stream.localData() << QStringLiteral("\n};\n#endif\n\n");
0944 }
0945 
0946 void Generator::generateServerPrivateResourceCtorDtorClass(const Interface &interface)
0947 {
0948     QString templateString = QStringLiteral(
0949         "%1::Private::Private(%1 *q, %2 *c, wl_resource *parentResource)\n"
0950         "    : Resource::Private(q, c, parentResource, &%3_interface, &s_interface)\n"
0951         "{\n"
0952         "}\n"
0953         "\n"
0954         "%1::Private::~Private()\n"
0955         "{\n"
0956         "    if (resource) {\n"
0957         "        wl_resource_destroy(resource);\n"
0958         "        resource = nullptr;\n"
0959         "    }\n"
0960         "}\n");
0961     *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()).arg(interface.name());
0962 }
0963 
0964 void Generator::generateClientPrivateClass(const Interface &interface)
0965 {
0966     if (interface.isGlobal()) {
0967         generateClientPrivateGlobalClass(interface);
0968     } else {
0969         generateClientPrivateResourceClass(interface);
0970     }
0971 
0972     const auto events = interface.events();
0973     if (!events.isEmpty()) {
0974         *m_stream.localData() << QStringLiteral("\nprivate:\n");
0975         // generate the callbacks
0976         for (auto event : events) {
0977             const QString templateString = QStringLiteral("    static void %1Callback(void *data, %2 *%2");
0978             *m_stream.localData() << templateString.arg(event.name()).arg(interface.name());
0979             const auto arguments = event.arguments();
0980             for (auto argument : arguments) {
0981                 if (argument.interface().isNull()) {
0982                     *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name());
0983                 } else {
0984                     *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name());
0985                 }
0986             }
0987             *m_stream.localData() << ");\n";
0988         }
0989         *m_stream.localData() << QStringLiteral("\n    static const %1_listener s_listener;\n").arg(interface.name());
0990     }
0991 
0992     *m_stream.localData() << QStringLiteral("};\n\n");
0993 }
0994 
0995 void Generator::generateClientPrivateResourceClass(const Interface &interface)
0996 {
0997     const QString templateString = QStringLiteral(
0998         "class %1::Private\n"
0999         "{\n"
1000         "public:\n"
1001         "    Private(%1 *q);\n"
1002         "\n"
1003         "    void setup(%2 *arg);\n"
1004         "\n"
1005         "    WaylandPointer<%2, %2_destroy> %3;\n"
1006         "\n"
1007         "private:\n"
1008         "    %1 *q;\n");
1009 
1010     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1011 }
1012 
1013 void Generator::generateClientPrivateGlobalClass(const Interface &interface)
1014 {
1015     const QString templateString = QStringLiteral(
1016         "class %1::Private\n"
1017         "{\n"
1018         "public:\n"
1019         "    Private() = default;\n"
1020         "\n"
1021         "    void setup(%2 *arg);\n"
1022         "\n"
1023         "    WaylandPointer<%2, %2_destroy> %3;\n"
1024         "    EventQueue *queue = nullptr;\n");
1025 
1026     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1027 }
1028 
1029 void Generator::generateClientCpp(const Interface &interface)
1030 {
1031     // TODO: generate listener and callbacks
1032     const auto events = interface.events();
1033     if (!events.isEmpty()) {
1034         // listener
1035         *m_stream.localData() << QStringLiteral("const %1_listener %2::Private::s_listener = {\n").arg(interface.name()).arg(interface.kwaylandClientName());
1036         bool first = true;
1037         for (auto event : events) {
1038             if (!first) {
1039                 *m_stream.localData() << QStringLiteral(",\n");
1040             }
1041             *m_stream.localData() << QStringLiteral("    %1Callback").arg(event.name());
1042             first = false;
1043         }
1044         *m_stream.localData() << QStringLiteral("\n};\n\n");
1045 
1046         // callbacks
1047         for (auto event : events) {
1048             *m_stream.localData() << QStringLiteral("void %1::Private::%2Callback(void *data, %3 *%3")
1049                                          .arg(interface.kwaylandClientName())
1050                                          .arg(event.name())
1051                                          .arg(interface.name());
1052 
1053             const auto arguments = event.arguments();
1054             for (auto argument : arguments) {
1055                 if (argument.interface().isNull()) {
1056                     *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name());
1057                 } else {
1058                     *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name());
1059                 }
1060             }
1061 
1062             *m_stream.localData() << QStringLiteral(
1063                                          ")\n"
1064                                          "{\n"
1065                                          "    auto p = reinterpret_cast<%1::Private*>(data);\n"
1066                                          "    Q_ASSERT(p->%2 == %3);\n")
1067                                          .arg(interface.kwaylandClientName())
1068                                          .arg(interface.kwaylandClientName().toLower())
1069                                          .arg(interface.name());
1070             for (auto argument : arguments) {
1071                 *m_stream.localData() << QStringLiteral("    Q_UNUSED(%1)\n").arg(argument.name());
1072             }
1073             *m_stream.localData() << QStringLiteral("    // TODO: implement\n}\n\n");
1074         }
1075     }
1076 
1077     if (interface.isGlobal()) {
1078         // generate ctor without this pointer to Private
1079         const QString templateString = QStringLiteral(
1080             "%1::%1(QObject *parent)\n"
1081             "    : QObject(parent)\n"
1082             "    , d(new Private)\n"
1083             "{\n"
1084             "}\n");
1085         *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1086     } else {
1087         // Private ctor
1088         const QString templateString = QStringLiteral(
1089             "%1::Private::Private(%1 *q)\n"
1090             "    : q(q)\n"
1091             "{\n"
1092             "}\n"
1093             "\n"
1094             "%1::%1(QObject *parent)\n"
1095             "    : QObject(parent)\n"
1096             "    , d(new Private(this))\n"
1097             "{\n"
1098             "}\n");
1099         *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1100     }
1101 
1102     // setup call with optional add_listener
1103     const QString setupTemplate = QStringLiteral(
1104         "\n"
1105         "void %1::Private::setup(%3 *arg)\n"
1106         "{\n"
1107         "    Q_ASSERT(arg);\n"
1108         "    Q_ASSERT(!%2);\n"
1109         "    %2.setup(arg);\n");
1110     *m_stream.localData() << setupTemplate.arg(interface.kwaylandClientName()).arg(interface.kwaylandClientName().toLower()).arg(interface.name());
1111     if (!interface.events().isEmpty()) {
1112         *m_stream.localData()
1113             << QStringLiteral("    %1_add_listener(%2, &s_listener, this);\n").arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1114     }
1115     *m_stream.localData() << QStringLiteral("}\n");
1116 
1117     const QString templateString = QStringLiteral(
1118         "\n"
1119         "%1::~%1()\n"
1120         "{\n"
1121         "    release();\n"
1122         "}\n"
1123         "\n"
1124         "void %1::setup(%3 *%2)\n"
1125         "{\n"
1126         "    d->setup(%2);\n"
1127         "}\n"
1128         "\n"
1129         "void %1::release()\n"
1130         "{\n"
1131         "    d->%2.release();\n"
1132         "}\n"
1133         "\n"
1134         "void %1::destroy()\n"
1135         "{\n"
1136         "    d->%2.destroy();\n"
1137         "}\n"
1138         "\n"
1139         "%1::operator %3*() {\n"
1140         "    return d->%2;\n"
1141         "}\n"
1142         "\n"
1143         "%1::operator %3*() const {\n"
1144         "    return d->%2;\n"
1145         "}\n"
1146         "\n"
1147         "bool %1::isValid() const\n"
1148         "{\n"
1149         "    return d->%2.isValid();\n"
1150         "}\n\n");
1151 
1152     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.kwaylandClientName().toLower()).arg(interface.name());
1153     if (interface.isGlobal()) {
1154         const QString templateStringGlobal = QStringLiteral(
1155             "void %1::setEventQueue(EventQueue *queue)\n"
1156             "{\n"
1157             "    d->queue = queue;\n"
1158             "}\n"
1159             "\n"
1160             "EventQueue *%1::eventQueue()\n"
1161             "{\n"
1162             "    return d->queue;\n"
1163             "}\n\n");
1164         *m_stream.localData() << templateStringGlobal.arg(interface.kwaylandClientName());
1165     }
1166 }
1167 
1168 void Generator::generateClientGlobalClassDoxy(const Interface &interface)
1169 {
1170     const QString templateString = QStringLiteral(
1171         "/**\n"
1172         " * @short Wrapper for the %2 interface.\n"
1173         " *\n"
1174         " * This class provides a convenient wrapper for the %2 interface.\n"
1175         " *\n"
1176         " * To use this class one needs to interact with the Registry. There are two\n"
1177         " * possible ways to create the %1 interface:\n"
1178         " * @code\n"
1179         " * %1 *c = registry->create%1(name, version);\n"
1180         " * @endcode\n"
1181         " *\n"
1182         " * This creates the %1 and sets it up directly. As an alternative this\n"
1183         " * can also be done in a more low level way:\n"
1184         " * @code\n"
1185         " * %1 *c = new %1;\n"
1186         " * c->setup(registry->bind%1(name, version));\n"
1187         " * @endcode\n"
1188         " *\n"
1189         " * The %1 can be used as a drop-in replacement for any %2\n"
1190         " * pointer as it provides matching cast operators.\n"
1191         " *\n"
1192         " * @see Registry\n"
1193         " **/\n");
1194     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name());
1195 }
1196 
1197 void Generator::generateClientClassQObjectDerived(const Interface &interface)
1198 {
1199     const QString templateString = QStringLiteral(
1200         "class KWAYLANDCLIENT_EXPORT %1 : public QObject\n"
1201         "{\n"
1202         "    Q_OBJECT\n"
1203         "public:\n");
1204     *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1205 }
1206 
1207 void Generator::generateClientGlobalClassCtor(const Interface &interface)
1208 {
1209     const QString templateString = QStringLiteral(
1210         "    /**\n"
1211         "     * Creates a new %1.\n"
1212         "     * Note: after constructing the %1 it is not yet valid and one needs\n"
1213         "     * to call setup. In order to get a ready to use %1 prefer using\n"
1214         "     * Registry::create%1.\n"
1215         "     **/\n"
1216         "    explicit %1(QObject *parent = nullptr);\n");
1217     *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1218 }
1219 
1220 void Generator::generateClientClassDtor(const Interface &interface)
1221 {
1222     *m_stream.localData() << QStringLiteral("    virtual ~%1();\n\n").arg(interface.kwaylandClientName());
1223 }
1224 
1225 void Generator::generateClientClassReleaseDestroy(const Interface &interface)
1226 {
1227     const QString templateString = QStringLiteral(
1228         "    /**\n"
1229         "     * @returns @c true if managing a %2.\n"
1230         "     **/\n"
1231         "    bool isValid() const;\n"
1232         "    /**\n"
1233         "     * Releases the %2 interface.\n"
1234         "     * After the interface has been released the %1 instance is no\n"
1235         "     * longer valid and can be setup with another %2 interface.\n"
1236         "     **/\n"
1237         "    void release();\n"
1238         "    /**\n"
1239         "     * Destroys the data held by this %1.\n"
1240         "     * This method is supposed to be used when the connection to the Wayland\n"
1241         "     * server goes away. If the connection is not valid anymore, it's not\n"
1242         "     * possible to call release anymore as that calls into the Wayland\n"
1243         "     * connection and the call would fail. This method cleans up the data, so\n"
1244         "     * that the instance can be deleted or set up to a new %2 interface\n"
1245         "     * once there is a new connection available.\n"
1246         "     *\n"
1247         "     * It is suggested to connect this method to ConnectionThread::connectionDied:\n"
1248         "     * @code\n"
1249         "     * connect(connection, &ConnectionThread::connectionDied, %3, &%1::destroy);\n"
1250         "     * @endcode\n"
1251         "     *\n"
1252         "     * @see release\n"
1253         "     **/\n"
1254         "    void destroy();\n"
1255         "\n");
1256     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1257 }
1258 
1259 void Generator::generateClientGlobalClassSetup(const Interface &interface)
1260 {
1261     const QString templateString = QStringLiteral(
1262         "    /**\n"
1263         "     * Setup this %1 to manage the @p %3.\n"
1264         "     * When using Registry::create%1 there is no need to call this\n"
1265         "     * method.\n"
1266         "     **/\n"
1267         "    void setup(%2 *%3);\n");
1268     *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1269 }
1270 
1271 void Generator::generateClientResourceClassSetup(const Interface &interface)
1272 {
1273     const QString templateString = QStringLiteral(
1274         "    /**\n"
1275         "     * Setup this %1 to manage the @p %3.\n"
1276         "     * When using %4::create%1 there is no need to call this\n"
1277         "     * method.\n"
1278         "     **/\n"
1279         "    void setup(%2 *%3);\n");
1280     *m_stream.localData() << templateString.arg(interface.kwaylandClientName())
1281                                  .arg(interface.name())
1282                                  .arg(interface.kwaylandClientName().toLower())
1283                                  .arg(interface.factory()->kwaylandClientName());
1284 }
1285 
1286 void Generator::generateClientClassStart(const Interface &interface)
1287 {
1288     const QString templateString = QStringLiteral(
1289         "    /**\n"
1290         "     * Sets the @p queue to use for creating objects with this %1.\n"
1291         "     **/\n"
1292         "    void setEventQueue(EventQueue *queue);\n"
1293         "    /**\n"
1294         "     * @returns The event queue to use for creating objects with this %1.\n"
1295         "     **/\n"
1296         "    EventQueue *eventQueue();\n\n");
1297     *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1298 }
1299 
1300 void Generator::generateClientClassRequests(const Interface &interface)
1301 {
1302     const auto requests = interface.requests();
1303     const QString templateString = QStringLiteral("    void %1(%2);\n\n");
1304     const QString factoryTemplateString = QStringLiteral("    %1 *%2(%3);\n\n");
1305     for (const auto &r : requests) {
1306         if (r.isDestructor()) {
1307             continue;
1308         }
1309         QString arguments;
1310         bool first = true;
1311         QString factored;
1312         for (const auto &a : r.arguments()) {
1313             if (a.type() == Argument::Type::NewId) {
1314                 factored = a.interface();
1315                 continue;
1316             }
1317             if (!first) {
1318                 arguments.append(QStringLiteral(", "));
1319             } else {
1320                 first = false;
1321             }
1322             if (a.type() == Argument::Type::Object) {
1323                 arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1324             } else {
1325                 arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1326             }
1327         }
1328         if (factored.isEmpty()) {
1329             *m_stream.localData() << templateString.arg(toCamelCase((r.name()))).arg(arguments);
1330         } else {
1331             if (!first) {
1332                 arguments.append(QStringLiteral(", "));
1333             }
1334             arguments.append(QStringLiteral("QObject *parent = nullptr"));
1335             *m_stream.localData() << factoryTemplateString.arg(toQtInterfaceName(factored)).arg(toCamelCase(r.name())).arg(arguments);
1336         }
1337     }
1338 }
1339 
1340 void Generator::generateClientCppRequests(const Interface &interface)
1341 {
1342     const auto requests = interface.requests();
1343     const QString templateString = QStringLiteral(
1344         "void %1::%2(%3)\n"
1345         "{\n"
1346         "    Q_ASSERT(isValid());\n"
1347         "    %4_%5(d->%6%7);\n"
1348         "}\n\n");
1349     const QString factoryTemplateString = QStringLiteral(
1350         "%2 *%1::%3(%4)\n"
1351         "{\n"
1352         "    Q_ASSERT(isValid());\n"
1353         "    auto p = new %2(parent);\n"
1354         "    auto w = %5_%6(d->%7%8);\n"
1355         "    if (d->queue) {\n"
1356         "        d->queue->addProxy(w);\n"
1357         "    }\n"
1358         "    p->setup(w);\n"
1359         "    return p;\n"
1360         "}\n\n");
1361     for (const auto &r : requests) {
1362         if (r.isDestructor()) {
1363             continue;
1364         }
1365         QString arguments;
1366         QString requestArguments;
1367         bool first = true;
1368         QString factored;
1369         for (const auto &a : r.arguments()) {
1370             if (a.type() == Argument::Type::NewId) {
1371                 factored = a.interface();
1372                 continue;
1373             }
1374             if (!first) {
1375                 arguments.append(QStringLiteral(", "));
1376             } else {
1377                 first = false;
1378             }
1379             if (a.type() == Argument::Type::Object) {
1380                 arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1381                 requestArguments.append(QStringLiteral(", *%1").arg(toCamelCase(a.name())));
1382             } else {
1383                 arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1384                 QString arg = toCamelCase(a.name());
1385                 if (a.type() == Argument::Type::Fixed) {
1386                     arg = QStringLiteral("wl_fixed_from_double(%1)").arg(arg);
1387                 }
1388                 requestArguments.append(QStringLiteral(", %1").arg(arg));
1389             }
1390         }
1391         if (factored.isEmpty()) {
1392             *m_stream.localData() << templateString.arg(interface.kwaylandClientName())
1393                                          .arg(toCamelCase(r.name()))
1394                                          .arg(arguments)
1395                                          .arg(interface.name())
1396                                          .arg(r.name())
1397                                          .arg(interface.kwaylandClientName().toLower())
1398                                          .arg(requestArguments);
1399         } else {
1400             if (!first) {
1401                 arguments.append(QStringLiteral(", "));
1402             }
1403             arguments.append(QStringLiteral("QObject *parent"));
1404             *m_stream.localData() << factoryTemplateString.arg(interface.kwaylandClientName())
1405                                          .arg(toQtInterfaceName(factored))
1406                                          .arg(toCamelCase(r.name()))
1407                                          .arg(arguments)
1408                                          .arg(interface.name())
1409                                          .arg(r.name())
1410                                          .arg(interface.kwaylandClientName().toLower())
1411                                          .arg(requestArguments);
1412         }
1413     }
1414 }
1415 
1416 void Generator::generateClientClassCasts(const Interface &interface)
1417 {
1418     const QString templateString = QStringLiteral(
1419         "    operator %1*();\n"
1420         "    operator %1*() const;\n\n");
1421     *m_stream.localData() << templateString.arg(interface.name());
1422 }
1423 
1424 void Generator::generateClientGlobalClassEnd(const Interface &interface)
1425 {
1426     Q_UNUSED(interface)
1427     *m_stream.localData() << QStringLiteral("private:\n");
1428     generateClientClassDptr(interface);
1429     *m_stream.localData() << QStringLiteral("};\n\n");
1430 }
1431 
1432 void Generator::generateClientClassDptr(const Interface &interface)
1433 {
1434     Q_UNUSED(interface)
1435     *m_stream.localData() << QStringLiteral(
1436         "    class Private;\n"
1437         "    QScopedPointer<Private> d;\n");
1438 }
1439 
1440 void Generator::generateClientResourceClassEnd(const Interface &interface)
1441 {
1442     *m_stream.localData() << QStringLiteral(
1443                                  "private:\n"
1444                                  "    friend class %2;\n"
1445                                  "    explicit %1(QObject *parent = nullptr);\n")
1446                                  .arg(interface.kwaylandClientName())
1447                                  .arg(interface.factory()->kwaylandClientName());
1448     generateClientClassDptr(interface);
1449     *m_stream.localData() << QStringLiteral("};\n\n");
1450 }
1451 
1452 void Generator::generateWaylandForwardDeclarations()
1453 {
1454     for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
1455         *m_stream.localData() << QStringLiteral("struct %1;\n").arg((*it).name());
1456     }
1457     *m_stream.localData() << "\n";
1458 }
1459 
1460 void Generator::generateNamespaceForwardDeclarations()
1461 {
1462     QSet<QString> referencedObjects;
1463     for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
1464         const auto events = (*it).events();
1465         const auto requests = (*it).requests();
1466         for (const auto &e : events) {
1467             const auto args = e.arguments();
1468             for (const auto &a : args) {
1469                 if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) {
1470                     continue;
1471                 }
1472                 referencedObjects << a.interface();
1473             }
1474         }
1475         for (const auto &r : requests) {
1476             const auto args = r.arguments();
1477             for (const auto &a : args) {
1478                 if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) {
1479                     continue;
1480                 }
1481                 referencedObjects << a.interface();
1482             }
1483         }
1484     }
1485 
1486     switch (m_project.localData()) {
1487     case Project::Client:
1488         *m_stream.localData() << QStringLiteral("class EventQueue;\n");
1489         for (const auto &o : referencedObjects) {
1490             auto it = s_clientClassNameMapping.constFind(o);
1491             if (it != s_clientClassNameMapping.constEnd()) {
1492                 *m_stream.localData() << QStringLiteral("class %1;\n").arg(it.value());
1493             } else {
1494                 qWarning() << "Cannot forward declare KWayland class for interface " << o;
1495             }
1496         }
1497         *m_stream.localData() << QStringLiteral("\n");
1498         break;
1499     case Project::Server:
1500         *m_stream.localData() << QStringLiteral("class Display;\n\n");
1501         break;
1502     default:
1503         Q_UNREACHABLE();
1504     }
1505 }
1506 
1507 void Generator::generateClientClassSignals(const Interface &interface)
1508 {
1509     const QString templateString = QStringLiteral(
1510         "Q_SIGNALS:\n"
1511         "    /**\n"
1512         "     * The corresponding global for this interface on the Registry got removed.\n"
1513         "     *\n"
1514         "     * This signal gets only emitted if the %1 got created by\n"
1515         "     * Registry::create%1\n"
1516         "     **/\n"
1517         "    void removed();\n\n");
1518     *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1519 }
1520 
1521 QString Generator::projectToName() const
1522 {
1523     switch (m_project.localData()) {
1524     case Project::Client:
1525         return QStringLiteral("Client");
1526     case Project::Server:
1527         return QStringLiteral("Server");
1528     default:
1529         Q_UNREACHABLE();
1530     }
1531 }
1532 
1533 static void parseMapping()
1534 {
1535     QFile mappingFile(QStringLiteral(MAPPING_FILE));
1536     mappingFile.open(QIODevice::ReadOnly);
1537     QTextStream stream(&mappingFile);
1538     while (!stream.atEnd()) {
1539         QString line = stream.readLine();
1540         if (line.startsWith(QLatin1String("#")) || line.isEmpty()) {
1541             continue;
1542         }
1543         const QStringList parts = line.split(QStringLiteral(";"));
1544         if (parts.count() < 2) {
1545             continue;
1546         }
1547         s_clientClassNameMapping.insert(parts.first(), parts.at(1));
1548     }
1549 }
1550 
1551 }
1552 }
1553 
1554 int main(int argc, char **argv)
1555 {
1556     using namespace KWayland::Tools;
1557 
1558     parseMapping();
1559 
1560     QCoreApplication app(argc, argv);
1561 
1562     QCommandLineParser parser;
1563     QCommandLineOption xmlFile(QStringList{QStringLiteral("x"), QStringLiteral("xml")},
1564                                QStringLiteral("The wayland protocol to parse."),
1565                                QStringLiteral("FileName"));
1566     QCommandLineOption fileName(QStringList{QStringLiteral("f"), QStringLiteral("file")},
1567                                 QStringLiteral("The base name of files to be generated. E.g. for \"foo\" the files \"foo.h\" and \"foo.cpp\" are generated."
1568                                                "If not provided the base name gets derived from the xml protocol name"),
1569                                 QStringLiteral("FileName"));
1570 
1571     parser.addHelpOption();
1572     parser.addOption(xmlFile);
1573     parser.addOption(fileName);
1574 
1575     parser.process(app);
1576 
1577     Generator generator(&app);
1578     generator.setXmlFileName(parser.value(xmlFile));
1579     generator.setBaseFileName(parser.value(fileName));
1580     generator.start();
1581 
1582     return app.exec();
1583 }
1584 
1585 #include "moc_generator.cpp"