1/*
2 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "generator.h"
7
8#include <QBuffer>
9#include <QByteArray>
10#include <QCommandLineParser>
11#include <QCoreApplication>
12#include <QDate>
13#include <QFile>
14#include <QFutureWatcher>
15#include <QMutexLocker>
16#include <QProcess>
17#include <QStandardPaths>
18#include <QTextStream>
19#include <QtConcurrentRun>
20
21#include <QDebug>
22
23#include <functional>
24
25namespace KWayland
26{
27namespace Tools
28{
29static QMap<QString, QString> s_clientClassNameMapping;
30
31static QString toQtInterfaceName(const QString &wlInterface)
32{
33 auto it = s_clientClassNameMapping.constFind(key: wlInterface);
34 if (it != s_clientClassNameMapping.constEnd()) {
35 return it.value();
36 } else {
37 qWarning() << "Cannot find mapping for " << wlInterface;
38 }
39 return wlInterface;
40}
41
42static QString toCamelCase(const QString &underscoreName)
43{
44 const QStringList parts = underscoreName.split(QStringLiteral("_"));
45 if (parts.count() < 2) {
46 return underscoreName;
47 }
48 auto it = parts.constBegin();
49 QString camelCase = (*it);
50 it++;
51 for (; it != parts.constEnd(); ++it) {
52 camelCase.append(s: (*it).left(n: 1).toUpper());
53 camelCase.append(s: (*it).mid(position: 1));
54 }
55 return camelCase;
56}
57
58Argument::Argument()
59{
60}
61
62Argument::Argument(const QXmlStreamAttributes &attributes)
63 : m_name(attributes.value(QStringLiteral("name")).toString())
64 , m_type(parseType(type: attributes.value(QStringLiteral("type"))))
65 , m_allowNull(attributes.hasAttribute(QStringLiteral("allow-null")))
66 , m_inteface(attributes.value(QStringLiteral("interface")).toString())
67{
68}
69
70Argument::~Argument() = default;
71
72Argument::Type Argument::parseType(const QStringView type)
73{
74 if (type.compare(s: QLatin1String("new_id")) == 0) {
75 return Type::NewId;
76 }
77 if (type.compare(s: QLatin1String("destructor")) == 0) {
78 return Type::Destructor;
79 }
80 if (type.compare(s: QLatin1String("object")) == 0) {
81 return Type::Object;
82 }
83 if (type.compare(s: QLatin1String("fd")) == 0) {
84 return Type::FileDescriptor;
85 }
86 if (type.compare(s: QLatin1String("fixed")) == 0) {
87 return Type::Fixed;
88 }
89 if (type.compare(s: QLatin1String("uint")) == 0) {
90 return Type::Uint;
91 }
92 if (type.compare(s: QLatin1String("int")) == 0) {
93 return Type::Int;
94 }
95 if (type.compare(s: QLatin1String("string")) == 0) {
96 return Type::String;
97 }
98
99 return Type::Unknown;
100}
101
102QString Argument::typeAsQt() const
103{
104 switch (m_type) {
105 case Type::Destructor:
106 return QString();
107 case Type::FileDescriptor:
108 return QStringLiteral("int");
109 case Type::Fixed:
110 return QStringLiteral("qreal");
111 case Type::Int:
112 return QStringLiteral("qint32");
113 case Type::NewId:
114 case Type::Object:
115 return toQtInterfaceName(wlInterface: m_inteface);
116 case Type::String:
117 return QStringLiteral("const QString &");
118 case Type::Uint:
119 return QStringLiteral("quint32");
120 case Type::Unknown:
121 return QString();
122 default:
123 Q_UNREACHABLE();
124 }
125}
126
127QString Argument::typeAsServerWl() const
128{
129 switch (m_type) {
130 case Type::Destructor:
131 return QString();
132 case Type::FileDescriptor:
133 return QStringLiteral("int32_t");
134 case Type::Fixed:
135 return QStringLiteral("wl_fixed");
136 case Type::Int:
137 return QStringLiteral("int32_t");
138 case Type::Object:
139 return QStringLiteral("wl_resource *");
140 case Type::String:
141 return QStringLiteral("const char *");
142 case Type::Uint:
143 case Type::NewId:
144 return QStringLiteral("uint32_t");
145 case Type::Unknown:
146 return QString();
147 default:
148 Q_UNREACHABLE();
149 }
150}
151
152Request::Request()
153{
154}
155
156Request::Request(const QString &name)
157 : m_name(name)
158{
159}
160
161Request::~Request() = default;
162
163bool Request::isFactory() const
164{
165 for (const auto &a : m_arguments) {
166 if (a.type() == Argument::Type::NewId) {
167 return true;
168 }
169 }
170 return false;
171}
172
173Event::Event()
174{
175}
176
177Event::Event(const QString &name)
178 : m_name(name)
179{
180}
181
182Event::~Event() = default;
183
184Interface::Interface() = default;
185
186Interface::Interface(const QXmlStreamAttributes &attributes)
187 : m_name(attributes.value(QStringLiteral("name")).toString())
188 , m_version(attributes.value(QStringLiteral("version")).toUInt())
189 , m_factory(nullptr)
190{
191 auto it = s_clientClassNameMapping.constFind(key: m_name);
192 if (it != s_clientClassNameMapping.constEnd()) {
193 m_clientName = it.value();
194 } else {
195 qWarning() << "Failed to map " << m_name << " to a KWayland name";
196 }
197}
198
199Interface::~Interface() = default;
200
201Generator::Generator(QObject *parent)
202 : QObject(parent)
203{
204}
205
206Generator::~Generator() = default;
207
208void Generator::start()
209{
210 startAuthorNameProcess();
211 startAuthorEmailProcess();
212
213 startParseXml();
214
215 startGenerateHeaderFile();
216 startGenerateCppFile();
217 startGenerateServerHeaderFile();
218 startGenerateServerCppFile();
219}
220
221void Generator::startParseXml()
222{
223 if (m_xmlFileName.isEmpty()) {
224 return;
225 }
226 QFile xmlFile(m_xmlFileName);
227 xmlFile.open(flags: QIODevice::ReadOnly);
228 m_xmlReader.setDevice(&xmlFile);
229 while (!m_xmlReader.atEnd()) {
230 if (!m_xmlReader.readNextStartElement()) {
231 continue;
232 }
233 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("protocol")) == 0) {
234 parseProtocol();
235 }
236 }
237
238 auto findFactory = [this](const QString interfaceName) -> Interface * {
239 for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) {
240 if ((*it).name().compare(s: interfaceName) == 0) {
241 continue;
242 }
243 for (auto r : (*it).requests()) {
244 for (auto a : r.arguments()) {
245 if (a.type() == Argument::Type::NewId && a.interface().compare(s: interfaceName) == 0) {
246 return &(*it);
247 }
248 }
249 }
250 }
251 return nullptr;
252 };
253 for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) {
254 Interface *factory = findFactory((*it).name());
255 if (factory) {
256 qDebug() << (*it).name() << "gets factored by" << factory->kwaylandClientName();
257 (*it).setFactory(factory);
258 } else {
259 qDebug() << (*it).name() << "considered as a global";
260 (*it).markAsGlobal();
261 }
262 }
263}
264
265void Generator::parseProtocol()
266{
267 const auto attributes = m_xmlReader.attributes();
268 const QString protocolName = attributes.value(QStringLiteral("name")).toString();
269
270 if (m_baseFileName.isEmpty()) {
271 m_baseFileName = protocolName.toLower();
272 }
273
274 while (!m_xmlReader.atEnd()) {
275 if (!m_xmlReader.readNextStartElement()) {
276 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("protocol")) == 0) {
277 return;
278 }
279 continue;
280 }
281 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("interface")) == 0) {
282 m_interfaces << parseInterface();
283 }
284 }
285}
286
287Interface Generator::parseInterface()
288{
289 Interface interface(m_xmlReader.attributes());
290 while (!m_xmlReader.atEnd()) {
291 if (!m_xmlReader.readNextStartElement()) {
292 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("interface")) == 0) {
293 break;
294 }
295 continue;
296 }
297 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("request")) == 0) {
298 interface.addRequest(request: parseRequest());
299 }
300 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("event")) == 0) {
301 interface.addEvent(event: parseEvent());
302 }
303 }
304 return interface;
305}
306
307Request Generator::parseRequest()
308{
309 const auto attributes = m_xmlReader.attributes();
310 Request request(attributes.value(QStringLiteral("name")).toString());
311 if (attributes.value(QStringLiteral("type")).toString().compare(other: QLatin1String("destructor")) == 0) {
312 request.markAsDestructor();
313 }
314 while (!m_xmlReader.atEnd()) {
315 if (!m_xmlReader.readNextStartElement()) {
316 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("request")) == 0) {
317 break;
318 }
319 continue;
320 }
321 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("arg")) == 0) {
322 request.addArgument(arg: Argument(m_xmlReader.attributes()));
323 }
324 }
325 return request;
326}
327
328Event Generator::parseEvent()
329{
330 const auto attributes = m_xmlReader.attributes();
331 Event event(attributes.value(QStringLiteral("name")).toString());
332 while (!m_xmlReader.atEnd()) {
333 if (!m_xmlReader.readNextStartElement()) {
334 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("event")) == 0) {
335 break;
336 }
337 continue;
338 }
339 if (m_xmlReader.qualifiedName().compare(s: QLatin1String("arg")) == 0) {
340 event.addArgument(arg: Argument(m_xmlReader.attributes()));
341 }
342 }
343 return event;
344}
345
346void Generator::startGenerateHeaderFile()
347{
348 QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
349 connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd);
350 m_finishedCounter++;
351 watcher->setFuture(QtConcurrent::run(f: [this] {
352 QFile file(QStringLiteral("%1.h").arg(a: m_baseFileName));
353 file.open(flags: QIODevice::WriteOnly);
354 m_stream.setLocalData(new QTextStream(&file));
355 m_project.setLocalData(Project::Client);
356 generateCopyrightHeader();
357 generateStartIncludeGuard();
358 generateHeaderIncludes();
359 generateWaylandForwardDeclarations();
360 generateStartNamespace();
361 generateNamespaceForwardDeclarations();
362 for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
363 generateClass(interface: *it);
364 }
365 generateEndNamespace();
366 generateEndIncludeGuard();
367
368 m_stream.setLocalData(nullptr);
369 file.close();
370 }));
371}
372
373void Generator::startGenerateCppFile()
374{
375 QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
376 connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd);
377 m_finishedCounter++;
378 watcher->setFuture(QtConcurrent::run(f: [this] {
379 QFile file(QStringLiteral("%1.cpp").arg(a: m_baseFileName));
380 file.open(flags: QIODevice::WriteOnly);
381 m_stream.setLocalData(new QTextStream(&file));
382 m_project.setLocalData(Project::Client);
383 generateCopyrightHeader();
384 generateCppIncludes();
385 generateStartNamespace();
386 for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
387 generatePrivateClass(interface: *it);
388 generateClientCpp(interface: *it);
389 generateClientCppRequests(interface: *it);
390 }
391
392 generateEndNamespace();
393
394 m_stream.setLocalData(nullptr);
395 file.close();
396 }));
397}
398
399void Generator::startGenerateServerHeaderFile()
400{
401 QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
402 connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd);
403 m_finishedCounter++;
404 watcher->setFuture(QtConcurrent::run(f: [this] {
405 QFile file(QStringLiteral("%1_interface.h").arg(a: m_baseFileName));
406 file.open(flags: QIODevice::WriteOnly);
407 m_stream.setLocalData(new QTextStream(&file));
408 m_project.setLocalData(Project::Server);
409 generateCopyrightHeader();
410 generateStartIncludeGuard();
411 generateHeaderIncludes();
412 generateStartNamespace();
413 generateNamespaceForwardDeclarations();
414 if (std::any_of(first: m_interfaces.constBegin(), last: m_interfaces.constEnd(), pred: [](const Interface &i) {
415 return i.isUnstableInterface();
416 })) {
417 // generate the unstable semantic version
418 auto it = std::find_if(first: m_interfaces.constBegin(), last: m_interfaces.constEnd(), pred: [](const Interface &i) {
419 return i.isGlobal();
420 });
421 if (it != m_interfaces.constEnd()) {
422 const QString templateString = QStringLiteral(
423 "/**\n"
424 " * Enum describing the interface versions the %1 can support.\n"
425 " *\n"
426 " * @since 5.XX\n"
427 " **/\n"
428 "enum class %1Version {\n"
429 " /**\n"
430 " * %2\n"
431 " **/\n"
432 " UnstableV%3\n"
433 "};\n\n");
434 *m_stream.localData() << templateString.arg(a: (*it).kwaylandServerName())
435 .arg(a: (*it).name())
436 .arg(a: (*it).name().mid(position: (*it).name().lastIndexOf(QStringLiteral("_v")) + 2));
437 }
438 }
439 for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
440 generateClass(interface: *it);
441 }
442 generateEndNamespace();
443 generateEndIncludeGuard();
444
445 m_stream.setLocalData(nullptr);
446 file.close();
447 }));
448}
449
450void Generator::startGenerateServerCppFile()
451{
452 QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
453 connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd);
454 m_finishedCounter++;
455 watcher->setFuture(QtConcurrent::run(f: [this] {
456 QFile file(QStringLiteral("%1_interface.cpp").arg(a: m_baseFileName));
457 file.open(flags: QIODevice::WriteOnly);
458 m_stream.setLocalData(new QTextStream(&file));
459 m_project.setLocalData(Project::Server);
460 generateCopyrightHeader();
461 generateCppIncludes();
462 generateStartNamespace();
463 for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
464 generatePrivateClass(interface: *it);
465 // generateClientCpp(*it);
466 // generateClientCppRequests(*it);
467 }
468
469 generateEndNamespace();
470
471 m_stream.setLocalData(nullptr);
472 file.close();
473 }));
474}
475
476void Generator::checkEnd()
477{
478 m_finishedCounter--;
479 if (m_finishedCounter == 0) {
480 QCoreApplication::quit();
481 }
482}
483
484static QString findGitExec()
485{
486 const QString exec = QStandardPaths::findExecutable(QStringLiteral("git"));
487 if (exec.isEmpty()) {
488 qWarning() << "Could not find git executable in PATH.";
489 }
490 return exec;
491}
492
493void Generator::startAuthorNameProcess()
494{
495 const QString exec = findGitExec();
496 if (exec.isEmpty()) {
497 return;
498 }
499 QProcess *proc = new QProcess(this);
500 proc->setArguments(QStringList{QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.name")});
501 proc->setProgram(exec);
502 connect(sender: proc, signal: static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), context: this, slot: [this, proc] {
503 QMutexLocker locker(&m_mutex);
504 m_authorName = QString::fromLocal8Bit(ba: proc->readAllStandardOutput()).trimmed();
505 proc->deleteLater();
506 m_waitCondition.wakeAll();
507 });
508 proc->start();
509}
510
511void Generator::startAuthorEmailProcess()
512{
513 const QString exec = findGitExec();
514 if (exec.isEmpty()) {
515 return;
516 }
517 QProcess *proc = new QProcess(this);
518 proc->setArguments(QStringList{QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.email")});
519 proc->setProgram(exec);
520 connect(sender: proc, signal: static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), context: this, slot: [this, proc] {
521 QMutexLocker locker(&m_mutex);
522 m_authorEmail = QString::fromLocal8Bit(ba: proc->readAllStandardOutput()).trimmed();
523 proc->deleteLater();
524 m_waitCondition.wakeAll();
525 });
526 proc->start();
527}
528
529void Generator::generateCopyrightHeader()
530{
531 m_mutex.lock();
532 while (m_authorEmail.isEmpty() || m_authorName.isEmpty()) {
533 m_waitCondition.wait(lockedMutex: &m_mutex);
534 }
535 m_mutex.unlock();
536 const QString templateString = QStringLiteral(
537 "/****************************************************************************\n"
538 "Copyright %1 %2 <%3>\n"
539 "\n"
540 "This library is free software; you can redistribute it and/or\n"
541 "modify it under the terms of the GNU Lesser General Public\n"
542 "License as published by the Free Software Foundation; either\n"
543 "version 2.1 of the License, or (at your option) version 3, or any\n"
544 "later version accepted by the membership of KDE e.V. (or its\n"
545 "successor approved by the membership of KDE e.V.), which shall\n"
546 "act as a proxy defined in Section 6 of version 3 of the license.\n"
547 "\n"
548 "This library is distributed in the hope that it will be useful,\n"
549 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
550 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
551 "Lesser General Public License for more details.\n"
552 "\n"
553 "You should have received a copy of the GNU Lesser General Public\n"
554 "License along with this library. If not, see <http://www.gnu.org/licenses/>.\n"
555 "****************************************************************************/\n");
556 QDate date = QDate::currentDate();
557 *m_stream.localData() << templateString.arg(a: date.year()).arg(a: m_authorName).arg(a: m_authorEmail);
558}
559
560void Generator::generateEndIncludeGuard()
561{
562 *m_stream.localData() << QStringLiteral("#endif\n");
563}
564
565void Generator::generateStartIncludeGuard()
566{
567 const QString templateString = QStringLiteral(
568 "#ifndef KWAYLAND_%1_%2_H\n"
569 "#define KWAYLAND_%1_%2_H\n\n");
570
571 *m_stream.localData() << templateString.arg(a: projectToName().toUpper()).arg(a: m_baseFileName.toUpper());
572}
573
574void Generator::generateStartNamespace()
575{
576 const QString templateString = QStringLiteral(
577 "namespace KWayland\n"
578 "{\n"
579 "namespace %1\n"
580 "{\n\n");
581 *m_stream.localData() << templateString.arg(a: projectToName());
582}
583
584void Generator::generateEndNamespace()
585{
586 *m_stream.localData() << QStringLiteral("\n}\n}\n\n");
587}
588
589void Generator::generateHeaderIncludes()
590{
591 switch (m_project.localData()) {
592 case Project::Client:
593 *m_stream.localData() << QStringLiteral("#include <QObject>\n\n");
594 break;
595 case Project::Server:
596 *m_stream.localData() << QStringLiteral(
597 "#include \"global.h\"\n"
598 "#include \"resource.h\"\n\n");
599 break;
600 default:
601 Q_UNREACHABLE();
602 }
603 *m_stream.localData() << QStringLiteral("#include <KWayland/%1/kwayland%2_export.h>\n\n").arg(a: projectToName()).arg(a: projectToName().toLower());
604}
605
606void Generator::generateCppIncludes()
607{
608 switch (m_project.localData()) {
609 case Project::Client:
610 *m_stream.localData() << QStringLiteral("#include \"%1.h\"\n").arg(a: m_baseFileName.toLower());
611 *m_stream.localData() << QStringLiteral("#include \"event_queue.h\"\n");
612 *m_stream.localData() << QStringLiteral("#include \"wayland_pointer_p.h\"\n\n");
613 break;
614 case Project::Server:
615 *m_stream.localData() << QStringLiteral("#include \"%1_interface.h\"\n").arg(a: m_baseFileName.toLower());
616 *m_stream.localData() << QStringLiteral(
617 "#include \"display.h\"\n"
618 "#include \"global_p.h\"\n"
619 "#include \"resource_p.h\"\n\n");
620 break;
621 default:
622 Q_UNREACHABLE();
623 }
624}
625
626void Generator::generateClass(const Interface &interface)
627{
628 switch (m_project.localData()) {
629 case Project::Client:
630 if (interface.isGlobal()) {
631 generateClientGlobalClass(interface);
632 } else {
633 generateClientResourceClass(interface);
634 }
635 break;
636 case Project::Server:
637 if (interface.isGlobal()) {
638 generateServerGlobalClass(interface);
639 } else {
640 generateServerResourceClass(interface);
641 }
642 break;
643 default:
644 Q_UNREACHABLE();
645 }
646}
647
648void Generator::generateClientGlobalClass(const Interface &interface)
649{
650 generateClientGlobalClassDoxy(interface);
651 generateClientClassQObjectDerived(interface);
652 generateClientGlobalClassCtor(interface);
653 generateClientClassDtor(interface);
654 generateClientGlobalClassSetup(interface);
655 generateClientClassReleaseDestroy(interface);
656 generateClientClassStart(interface);
657 generateClientClassRequests(interface);
658 generateClientClassCasts(interface);
659 generateClientClassSignals(interface);
660 generateClientGlobalClassEnd(interface);
661}
662
663void Generator::generateClientResourceClass(const Interface &interface)
664{
665 generateClientClassQObjectDerived(interface);
666 generateClientClassDtor(interface);
667 generateClientResourceClassSetup(interface);
668 generateClientClassReleaseDestroy(interface);
669 generateClientClassRequests(interface);
670 generateClientClassCasts(interface);
671 generateClientResourceClassEnd(interface);
672}
673
674void Generator::generateServerGlobalClass(const Interface &interface)
675{
676 if (interface.isUnstableInterface()) {
677 generateServerGlobalClassUnstable(interface);
678 return;
679 }
680 const QString templateString = QStringLiteral(
681 "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
682 "{\n"
683 " Q_OBJECT\n"
684 "public:\n"
685 " virtual ~%1();\n"
686 "\n"
687 "private:\n"
688 " explicit %1(Display *display, QObject *parent = nullptr);\n"
689 " friend class Display;\n"
690 " class Private;\n"
691 "};\n"
692 "\n");
693 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName());
694}
695
696void Generator::generateServerGlobalClassUnstable(const Interface &interface)
697{
698 const QString templateString = QStringLiteral(
699 "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
700 "{\n"
701 " Q_OBJECT\n"
702 "public:\n"
703 " virtual ~%1();\n"
704 "\n"
705 " /**\n"
706 " * @returns The interface version used by this %1\n"
707 " **/\n"
708 " %1Version interfaceVersion() const;\n"
709 "\n"
710 "protected:\n"
711 " class Private;\n"
712 " explicit %1(Private *d, QObject *parent = nullptr);\n"
713 "\n"
714 "private:\n"
715 " Private *d_func() const;\n"
716 "};\n"
717 "\n");
718 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName());
719}
720
721void Generator::generateServerResourceClass(const Interface &interface)
722{
723 if (interface.factory()->isUnstableInterface()) {
724 generateServerResourceClassUnstable(interface);
725 return;
726 }
727 const QString templateString = QStringLiteral(
728 "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
729 "{\n"
730 " Q_OBJECT\n"
731 "public:\n"
732 " virtual ~%1();\n"
733 "\n"
734 "private:\n"
735 " explicit %1(%2 *parent, wl_resource *parentResource);\n"
736 " friend class %2;\n"
737 "\n"
738 " class Private;\n"
739 " Private *d_func() const;\n"
740 "};\n"
741 "\n");
742 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName());
743}
744
745void Generator::generateServerResourceClassUnstable(const Interface &interface)
746{
747 const QString templateString = QStringLiteral(
748 "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
749 "{\n"
750 " Q_OBJECT\n"
751 "public:\n"
752 "\n"
753 " virtual ~%1();\n"
754 "\n"
755 " /**\n"
756 " * @returns The interface version used by this %1\n"
757 " **/\n"
758 " %2Version interfaceVersion() const;\n"
759 "\n"
760 "protected:\n"
761 " class Private;\n"
762 " explicit %1(Private *p, QObject *parent = nullptr);\n"
763 "\n"
764 "private:\n"
765 " Private *d_func() const;\n"
766 "};\n"
767 "\n");
768 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName());
769}
770
771void Generator::generatePrivateClass(const Interface &interface)
772{
773 switch (m_project.localData()) {
774 case Project::Client:
775 generateClientPrivateClass(interface);
776 break;
777 case Project::Server:
778 if (interface.isGlobal()) {
779 generateServerPrivateGlobalClass(interface);
780 } else {
781 generateServerPrivateResourceClass(interface);
782 }
783 break;
784 default:
785 Q_UNREACHABLE();
786 }
787}
788
789void Generator::generateServerPrivateGlobalClass(const Interface &interface)
790{
791 QString templateString = QStringLiteral(
792 "class %1::Private : public Global::Private\n"
793 "{\n"
794 "public:\n"
795 " Private(%1 *q, Display *d);\n"
796 "\n"
797 "private:\n"
798 " void bind(wl_client *client, uint32_t version, uint32_t id) override;\n"
799 "\n"
800 " static void unbind(wl_resource *resource);\n"
801 " static Private *cast(wl_resource *r) {\n"
802 " return reinterpret_cast<Private*>(wl_resource_get_user_data(r));\n"
803 " }\n"
804 "\n");
805 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName());
806
807 generateServerPrivateCallbackDefinitions(interface);
808
809 templateString = QStringLiteral(
810 " %1 *q;\n"
811 " static const struct %2_interface s_interface;\n"
812 " static const quint32 s_version;\n"
813 "};\n"
814 "\n"
815 "const quint32 %1::Private::s_version = %3;\n"
816 "\n");
817 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name()).arg(a: interface.version());
818 generateServerPrivateInterfaceClass(interface);
819 generateServerPrivateCallbackImpl(interface);
820 generateServerPrivateGlobalCtorBindClass(interface);
821}
822
823void Generator::generateServerPrivateCallbackDefinitions(const Interface &interface)
824{
825 for (const auto &r : interface.requests()) {
826 if (r.isDestructor() && !interface.isGlobal()) {
827 continue;
828 }
829 *m_stream.localData() << QStringLiteral(" static void %1Callback(wl_client *client, wl_resource *resource").arg(a: toCamelCase(underscoreName: r.name()));
830 for (const auto &a : r.arguments()) {
831 *m_stream.localData() << QStringLiteral(", %1 %2").arg(a: a.typeAsServerWl()).arg(a: a.name());
832 }
833 *m_stream.localData() << QStringLiteral(");\n");
834 }
835 *m_stream.localData() << QStringLiteral("\n");
836}
837
838void Generator::generateServerPrivateCallbackImpl(const Interface &interface)
839{
840 for (const auto &r : interface.requests()) {
841 if (r.isDestructor() && !interface.isGlobal()) {
842 continue;
843 }
844 *m_stream.localData() << QStringLiteral("void %2::Private::%1Callback(wl_client *client, wl_resource *resource")
845 .arg(a: toCamelCase(underscoreName: r.name()))
846 .arg(a: interface.kwaylandServerName());
847 for (const auto &a : r.arguments()) {
848 *m_stream.localData() << QStringLiteral(", %1 %2").arg(a: a.typeAsServerWl()).arg(a: a.name());
849 }
850 *m_stream.localData() << QStringLiteral(
851 ")\n"
852 "{\n");
853 if (r.isDestructor()) {
854 *m_stream.localData() << QStringLiteral(
855 " Q_UNUSED(client)\n"
856 " wl_resource_destroy(resource);\n");
857 } else {
858 *m_stream.localData() << QStringLiteral(" // TODO: implement\n");
859 }
860 *m_stream.localData() << QStringLiteral(
861 "}\n"
862 "\n");
863 }
864}
865
866void Generator::generateServerPrivateGlobalCtorBindClass(const Interface &interface)
867{
868 QString templateString = QStringLiteral(
869 "%1::Private::Private(%1 *q, Display *d)\n"
870 " : Global::Private(d, &%2_interface, s_version)\n"
871 " , q(q)\n"
872 "{\n"
873 "}\n"
874 "\n"
875 "void %1::Private::bind(wl_client *client, uint32_t version, uint32_t id)\n"
876 "{\n"
877 " auto c = display->getConnection(client);\n"
878 " wl_resource *resource = c->createResource(&%2_interface, qMin(version, s_version), id);\n"
879 " if (!resource) {\n"
880 " wl_client_post_no_memory(client);\n"
881 " return;\n"
882 " }\n"
883 " wl_resource_set_implementation(resource, &s_interface, this, unbind);\n"
884 " // TODO: should we track?\n"
885 "}\n"
886 "\n"
887 "void %1::Private::unbind(wl_resource *resource)\n"
888 "{\n"
889 " Q_UNUSED(resource)\n"
890 " // TODO: implement?\n"
891 "}\n"
892 "\n");
893 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name());
894}
895
896void Generator::generateServerPrivateResourceClass(const Interface &interface)
897{
898 QString templateString = QStringLiteral(
899 "class %1::Private : public Resource::Private\n"
900 "{\n"
901 "public:\n"
902 " Private(%1 *q, %2 *c, wl_resource *parentResource);\n"
903 " ~Private();\n"
904 "\n"
905 "private:\n");
906 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName());
907
908 generateServerPrivateCallbackDefinitions(interface);
909
910 templateString = QStringLiteral(
911 " %1 *q_func() {\n"
912 " return reinterpret_cast<%1 *>(q);\n"
913 " }\n"
914 "\n"
915 " static const struct %2_interface s_interface;\n"
916 "};\n"
917 "\n");
918 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name());
919
920 generateServerPrivateInterfaceClass(interface);
921 generateServerPrivateCallbackImpl(interface);
922 generateServerPrivateResourceCtorDtorClass(interface);
923}
924
925void Generator::generateServerPrivateInterfaceClass(const Interface &interface)
926{
927 *m_stream.localData() << QStringLiteral("#ifndef K_DOXYGEN\n");
928 *m_stream.localData()
929 << QStringLiteral("const struct %2_interface %1::Private::s_interface = {\n").arg(a: interface.kwaylandServerName()).arg(a: interface.name());
930 bool first = true;
931 for (auto r : interface.requests()) {
932 if (!first) {
933 *m_stream.localData() << QStringLiteral(",\n");
934 } else {
935 first = false;
936 }
937 if (r.isDestructor() && !interface.isGlobal()) {
938 *m_stream.localData() << QStringLiteral(" resourceDestroyedCallback");
939 } else {
940 *m_stream.localData() << QStringLiteral(" %1Callback").arg(a: toCamelCase(underscoreName: r.name()));
941 }
942 }
943 *m_stream.localData() << QStringLiteral("\n};\n#endif\n\n");
944}
945
946void Generator::generateServerPrivateResourceCtorDtorClass(const Interface &interface)
947{
948 QString templateString = QStringLiteral(
949 "%1::Private::Private(%1 *q, %2 *c, wl_resource *parentResource)\n"
950 " : Resource::Private(q, c, parentResource, &%3_interface, &s_interface)\n"
951 "{\n"
952 "}\n"
953 "\n"
954 "%1::Private::~Private()\n"
955 "{\n"
956 " if (resource) {\n"
957 " wl_resource_destroy(resource);\n"
958 " resource = nullptr;\n"
959 " }\n"
960 "}\n");
961 *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName()).arg(a: interface.name());
962}
963
964void Generator::generateClientPrivateClass(const Interface &interface)
965{
966 if (interface.isGlobal()) {
967 generateClientPrivateGlobalClass(interface);
968 } else {
969 generateClientPrivateResourceClass(interface);
970 }
971
972 const auto events = interface.events();
973 if (!events.isEmpty()) {
974 *m_stream.localData() << QStringLiteral("\nprivate:\n");
975 // generate the callbacks
976 for (auto event : events) {
977 const QString templateString = QStringLiteral(" static void %1Callback(void *data, %2 *%2");
978 *m_stream.localData() << templateString.arg(a: event.name()).arg(a: interface.name());
979 const auto arguments = event.arguments();
980 for (auto argument : arguments) {
981 if (argument.interface().isNull()) {
982 *m_stream.localData() << QStringLiteral(", %1 %2").arg(a: argument.typeAsServerWl()).arg(a: argument.name());
983 } else {
984 *m_stream.localData() << QStringLiteral(", %1 *%2").arg(a: argument.interface()).arg(a: argument.name());
985 }
986 }
987 *m_stream.localData() << ");\n";
988 }
989 *m_stream.localData() << QStringLiteral("\n static const %1_listener s_listener;\n").arg(a: interface.name());
990 }
991
992 *m_stream.localData() << QStringLiteral("};\n\n");
993}
994
995void Generator::generateClientPrivateResourceClass(const Interface &interface)
996{
997 const QString templateString = QStringLiteral(
998 "class %1::Private\n"
999 "{\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(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower());
1011}
1012
1013void 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(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower());
1027}
1028
1029void 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(a: interface.name()).arg(a: 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(a: 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(a: interface.kwaylandClientName())
1050 .arg(a: event.name())
1051 .arg(a: 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(a: argument.typeAsServerWl()).arg(a: argument.name());
1057 } else {
1058 *m_stream.localData() << QStringLiteral(", %1 *%2").arg(a: argument.interface()).arg(a: 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(a: interface.kwaylandClientName())
1068 .arg(a: interface.kwaylandClientName().toLower())
1069 .arg(a: interface.name());
1070 for (auto argument : arguments) {
1071 *m_stream.localData() << QStringLiteral(" Q_UNUSED(%1)\n").arg(a: 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(a: 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(a: 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(a: interface.kwaylandClientName()).arg(a: interface.kwaylandClientName().toLower()).arg(a: interface.name());
1111 if (!interface.events().isEmpty()) {
1112 *m_stream.localData()
1113 << QStringLiteral(" %1_add_listener(%2, &s_listener, this);\n").arg(a: interface.name()).arg(a: 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(a: interface.kwaylandClientName()).arg(a: interface.kwaylandClientName().toLower()).arg(a: 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(a: interface.kwaylandClientName());
1165 }
1166}
1167
1168void 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(a: interface.kwaylandClientName()).arg(a: interface.name());
1195}
1196
1197void 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(a: interface.kwaylandClientName());
1205}
1206
1207void 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(a: interface.kwaylandClientName());
1218}
1219
1220void Generator::generateClientClassDtor(const Interface &interface)
1221{
1222 *m_stream.localData() << QStringLiteral(" virtual ~%1();\n\n").arg(a: interface.kwaylandClientName());
1223}
1224
1225void 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(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower());
1257}
1258
1259void 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(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower());
1269}
1270
1271void 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(a: interface.kwaylandClientName())
1281 .arg(a: interface.name())
1282 .arg(a: interface.kwaylandClientName().toLower())
1283 .arg(a: interface.factory()->kwaylandClientName());
1284}
1285
1286void 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(a: interface.kwaylandClientName());
1298}
1299
1300void 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: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name())));
1324 } else {
1325 arguments.append(QStringLiteral("%1 %2").arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name())));
1326 }
1327 }
1328 if (factored.isEmpty()) {
1329 *m_stream.localData() << templateString.arg(a: toCamelCase(underscoreName: (r.name()))).arg(a: arguments);
1330 } else {
1331 if (!first) {
1332 arguments.append(QStringLiteral(", "));
1333 }
1334 arguments.append(QStringLiteral("QObject *parent = nullptr"));
1335 *m_stream.localData() << factoryTemplateString.arg(a: toQtInterfaceName(wlInterface: factored)).arg(a: toCamelCase(underscoreName: r.name())).arg(a: arguments);
1336 }
1337 }
1338}
1339
1340void 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: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name())));
1381 requestArguments.append(QStringLiteral(", *%1").arg(a: toCamelCase(underscoreName: a.name())));
1382 } else {
1383 arguments.append(QStringLiteral("%1 %2").arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name())));
1384 QString arg = toCamelCase(underscoreName: a.name());
1385 if (a.type() == Argument::Type::Fixed) {
1386 arg = QStringLiteral("wl_fixed_from_double(%1)").arg(a: arg);
1387 }
1388 requestArguments.append(QStringLiteral(", %1").arg(a: arg));
1389 }
1390 }
1391 if (factored.isEmpty()) {
1392 *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName())
1393 .arg(a: toCamelCase(underscoreName: r.name()))
1394 .arg(a: arguments)
1395 .arg(a: interface.name())
1396 .arg(a: r.name())
1397 .arg(a: interface.kwaylandClientName().toLower())
1398 .arg(a: requestArguments);
1399 } else {
1400 if (!first) {
1401 arguments.append(QStringLiteral(", "));
1402 }
1403 arguments.append(QStringLiteral("QObject *parent"));
1404 *m_stream.localData() << factoryTemplateString.arg(a: interface.kwaylandClientName())
1405 .arg(a: toQtInterfaceName(wlInterface: factored))
1406 .arg(a: toCamelCase(underscoreName: r.name()))
1407 .arg(a: arguments)
1408 .arg(a: interface.name())
1409 .arg(a: r.name())
1410 .arg(a: interface.kwaylandClientName().toLower())
1411 .arg(a: requestArguments);
1412 }
1413 }
1414}
1415
1416void 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(a: interface.name());
1422}
1423
1424void 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
1432void 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
1440void 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(a: interface.kwaylandClientName())
1447 .arg(a: interface.factory()->kwaylandClientName());
1448 generateClientClassDptr(interface);
1449 *m_stream.localData() << QStringLiteral("};\n\n");
1450}
1451
1452void Generator::generateWaylandForwardDeclarations()
1453{
1454 for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
1455 *m_stream.localData() << QStringLiteral("struct %1;\n").arg(a: (*it).name());
1456 }
1457 *m_stream.localData() << "\n";
1458}
1459
1460void 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(key: o);
1491 if (it != s_clientClassNameMapping.constEnd()) {
1492 *m_stream.localData() << QStringLiteral("class %1;\n").arg(a: 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
1507void 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(a: interface.kwaylandClientName());
1519}
1520
1521QString 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
1533static void parseMapping()
1534{
1535 QFile mappingFile(QStringLiteral(MAPPING_FILE));
1536 mappingFile.open(flags: QIODevice::ReadOnly);
1537 QTextStream stream(&mappingFile);
1538 while (!stream.atEnd()) {
1539 QString line = stream.readLine();
1540 if (line.startsWith(s: 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(key: parts.first(), value: parts.at(i: 1));
1548 }
1549}
1550
1551}
1552}
1553
1554int 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(commandLineOption: xmlFile);
1573 parser.addOption(commandLineOption: fileName);
1574
1575 parser.process(app);
1576
1577 Generator generator(&app);
1578 generator.setXmlFileName(parser.value(option: xmlFile));
1579 generator.setBaseFileName(parser.value(option: fileName));
1580 generator.start();
1581
1582 return app.exec();
1583}
1584
1585#include "moc_generator.cpp"
1586

source code of kwayland/src/tools/generator.cpp