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"