KWayland

generator.cpp
1 /*
2  SPDX-FileCopyrightText: 2015 Martin Gräßlin <[email protected]>
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 <QCoreApplication>
9 #include <QCommandLineParser>
10 #include <QBuffer>
11 #include <QByteArray>
12 #include <QFutureWatcher>
13 #include <QMutexLocker>
14 #include <QDate>
15 #include <QProcess>
16 #include <QtConcurrent>
17 #include <QTextStream>
18 #include <QXmlStreamReader>
19 
20 #include <QDebug>
21 
22 #include <functional>
23 
24 namespace KWayland
25 {
26 namespace Tools
27 {
28 
29 static QMap<QString, QString> s_clientClassNameMapping;
30 
31 static QString toQtInterfaceName(const QString &wlInterface)
32 {
33  auto it = s_clientClassNameMapping.constFind(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 
42 static 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((*it).left(1).toUpper());
53  camelCase.append((*it).mid(1));
54  }
55  return camelCase;
56 }
57 
58 Argument::Argument()
59 {
60 }
61 
62 Argument::Argument(const QXmlStreamAttributes &attributes)
63  : m_name(attributes.value(QStringLiteral("name")).toString())
64  , m_type(parseType(attributes.value(QStringLiteral("type"))))
65  , m_allowNull(attributes.hasAttribute(QStringLiteral("allow-null")))
66  , m_inteface(attributes.value(QStringLiteral("interface")).toString())
67 {
68 }
69 
70 Argument::~Argument() = default;
71 
72 Argument::Type Argument::parseType(const QStringRef &type)
73 {
74  if (type.compare(QLatin1String("new_id")) == 0) {
75  return Type::NewId;
76  }
77  if (type.compare(QLatin1String("destructor")) == 0) {
78  return Type::Destructor;
79  }
80  if (type.compare(QLatin1String("object")) == 0) {
81  return Type::Object;
82  }
83  if (type.compare(QLatin1String("fd")) == 0) {
84  return Type::FileDescriptor;
85  }
86  if (type.compare(QLatin1String("fixed")) == 0) {
87  return Type::Fixed;
88  }
89  if (type.compare(QLatin1String("uint")) == 0) {
90  return Type::Uint;
91  }
92  if (type.compare(QLatin1String("int")) == 0) {
93  return Type::Int;
94  }
95  if (type.compare(QLatin1String("string")) == 0) {
96  return Type::String;
97  }
98 
99  return Type::Unknown;
100 }
101 
102 QString 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(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 
127 QString 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 
152 Request::Request()
153 {
154 }
155 
156 Request::Request(const QString &name)
157  : m_name(name)
158 {
159 }
160 
161 Request::~Request() = default;
162 
163 bool 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 
173 Event::Event()
174 {
175 }
176 
177 Event::Event(const QString &name)
178  : m_name(name)
179 {
180 }
181 
182 Event::~Event() = default;
183 
184 Interface::Interface() = default;
185 
186 Interface::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(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 
199 Interface::~Interface() = default;
200 
201 Generator::Generator(QObject *parent)
202  : QObject(parent)
203 {
204 }
205 
206 Generator::~Generator() = default;
207 
208 void Generator::start()
209 {
210  startAuthorNameProcess();
211  startAuthorEmailProcess();
212 
213  startParseXml();
214 
215  startGenerateHeaderFile();
216  startGenerateCppFile();
217  startGenerateServerHeaderFile();
218  startGenerateServerCppFile();
219 }
220 
221 void Generator::startParseXml()
222 {
223  if (m_xmlFileName.isEmpty()) {
224  return;
225  }
226  QFile xmlFile(m_xmlFileName);
227  xmlFile.open(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(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(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(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 
265 void 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(QLatin1String("protocol")) == 0) {
277  return;
278  }
279  continue;
280  }
281  if (m_xmlReader.qualifiedName().compare(QLatin1String("interface")) == 0) {
282  m_interfaces << parseInterface();
283  }
284  }
285 }
286 
287 Interface 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(QLatin1String("interface")) == 0) {
293  break;
294  }
295  continue;
296  }
297  if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) {
298  interface.addRequest(parseRequest());
299  }
300  if (m_xmlReader.qualifiedName().compare(QLatin1String("event")) == 0) {
301  interface.addEvent(parseEvent());
302  }
303  }
304  return interface;
305 }
306 
307 Request 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(QLatin1String("destructor")) == 0) {
312  request.markAsDestructor();
313  }
314  while (!m_xmlReader.atEnd()) {
315  if (!m_xmlReader.readNextStartElement()) {
316  if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) {
317  break;
318  }
319  continue;
320  }
321  if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) {
322  request.addArgument(Argument(m_xmlReader.attributes()));
323  }
324  }
325  return request;
326 }
327 
328 Event 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(QLatin1String("event")) == 0) {
335  break;
336  }
337  continue;
338  }
339  if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) {
340  event.addArgument(Argument(m_xmlReader.attributes()));
341  }
342  }
343  return event;
344 }
345 
346 void Generator::startGenerateHeaderFile()
347 {
348  QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
349  connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
350  m_finishedCounter++;
351  watcher->setFuture(QtConcurrent::run([this] {
352  QFile file(QStringLiteral("%1.h").arg(m_baseFileName));
353  file.open(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(*it);
364  }
365  generateEndNamespace();
366  generateEndIncludeGuard();
367 
368  m_stream.setLocalData(nullptr);
369  file.close();
370  }));
371 }
372 
373 void Generator::startGenerateCppFile()
374 {
375  QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
376  connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
377  m_finishedCounter++;
378  watcher->setFuture(QtConcurrent::run([this] {
379  QFile file(QStringLiteral("%1.cpp").arg(m_baseFileName));
380  file.open(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(*it);
388  generateClientCpp(*it);
389  generateClientCppRequests(*it);
390  }
391 
392  generateEndNamespace();
393 
394  m_stream.setLocalData(nullptr);
395  file.close();
396  }));
397 }
398 
399 void Generator::startGenerateServerHeaderFile()
400 {
401  QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
402  connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
403  m_finishedCounter++;
404  watcher->setFuture(QtConcurrent::run([this] {
405  QFile file(QStringLiteral("%1_interface.h").arg(m_baseFileName));
406  file.open(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(m_interfaces.constBegin(), m_interfaces.constEnd(), [] (const Interface &i) { return i.isUnstableInterface(); })) {
415  // generate the unstable semantic version
416  auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [] (const Interface &i) { return i.isGlobal(); });
417  if (it != m_interfaces.constEnd()) {
418  const QString templateString = QStringLiteral(
419 "/**\n"
420 " * Enum describing the interface versions the %1 can support.\n"
421 " *\n"
422 " * @since 5.XX\n"
423 " **/\n"
424 "enum class %1Version {\n"
425 " /**\n"
426 " * %2\n"
427 " **/\n"
428 " UnstableV%3\n"
429 "};\n\n");
430  *m_stream.localData() << templateString.arg((*it).kwaylandServerName())
431  .arg((*it).name())
432  .arg((*it).name().mid((*it).name().lastIndexOf(QStringLiteral("_v")) + 2));
433  }
434  }
435  for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
436  generateClass(*it);
437  }
438  generateEndNamespace();
439  generateEndIncludeGuard();
440 
441  m_stream.setLocalData(nullptr);
442  file.close();
443  }));
444 }
445 
446 void Generator::startGenerateServerCppFile()
447 {
448  QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
449  connect(watcher, &QFutureWatcher<void>::finished, this, &Generator::checkEnd);
450  m_finishedCounter++;
451  watcher->setFuture(QtConcurrent::run([this] {
452  QFile file(QStringLiteral("%1_interface.cpp").arg(m_baseFileName));
453  file.open(QIODevice::WriteOnly);
454  m_stream.setLocalData(new QTextStream(&file));
455  m_project.setLocalData(Project::Server);
456  generateCopyrightHeader();
457  generateCppIncludes();
458  generateStartNamespace();
459  for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
460  generatePrivateClass(*it);
461 // generateClientCpp(*it);
462 // generateClientCppRequests(*it);
463  }
464 
465  generateEndNamespace();
466 
467  m_stream.setLocalData(nullptr);
468  file.close();
469  }));
470 }
471 
472 void Generator::checkEnd()
473 {
474  m_finishedCounter--;
475  if (m_finishedCounter == 0) {
477  }
478 }
479 
480 void Generator::startAuthorNameProcess()
481 {
482  QProcess *git = new QProcess(this);
484  QStringLiteral("config"),
485  QStringLiteral("--get"),
486  QStringLiteral("user.name")
487  });
488  git->setProgram(QStringLiteral("git"));
489  connect(git, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
490  [this, git] {
491  QMutexLocker locker(&m_mutex);
492  m_authorName = QString::fromLocal8Bit(git->readAllStandardOutput()).trimmed();
493  git->deleteLater();
494  m_waitCondition.wakeAll();
495  }
496  );
497  git->start();
498 }
499 
500 void Generator::startAuthorEmailProcess()
501 {
502  QProcess *git = new QProcess(this);
504  QStringLiteral("config"),
505  QStringLiteral("--get"),
506  QStringLiteral("user.email")
507  });
508  git->setProgram(QStringLiteral("git"));
509  connect(git, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
510  [this, git] {
511  QMutexLocker locker(&m_mutex);
512  m_authorEmail = QString::fromLocal8Bit(git->readAllStandardOutput()).trimmed();
513  git->deleteLater();
514  m_waitCondition.wakeAll();
515  }
516  );
517  git->start();
518 }
519 
520 void Generator::generateCopyrightHeader()
521 {
522  m_mutex.lock();
523  while (m_authorEmail.isEmpty() || m_authorName.isEmpty()) {
524  m_waitCondition.wait(&m_mutex);
525  }
526  m_mutex.unlock();
527  const QString templateString = QStringLiteral(
528 "/****************************************************************************\n"
529 "Copyright %1 %2 <%3>\n"
530 "\n"
531 "This library is free software; you can redistribute it and/or\n"
532 "modify it under the terms of the GNU Lesser General Public\n"
533 "License as published by the Free Software Foundation; either\n"
534 "version 2.1 of the License, or (at your option) version 3, or any\n"
535 "later version accepted by the membership of KDE e.V. (or its\n"
536 "successor approved by the membership of KDE e.V.), which shall\n"
537 "act as a proxy defined in Section 6 of version 3 of the license.\n"
538 "\n"
539 "This library is distributed in the hope that it will be useful,\n"
540 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
541 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
542 "Lesser General Public License for more details.\n"
543 "\n"
544 "You should have received a copy of the GNU Lesser General Public\n"
545 "License along with this library. If not, see <http://www.gnu.org/licenses/>.\n"
546 "****************************************************************************/\n"
547 );
548  QDate date = QDate::currentDate();
549  *m_stream.localData() << templateString.arg(date.year()).arg(m_authorName).arg(m_authorEmail);
550 }
551 
552 void Generator::generateEndIncludeGuard()
553 {
554  *m_stream.localData() << QStringLiteral("#endif\n");
555 }
556 
557 void Generator::generateStartIncludeGuard()
558 {
559  const QString templateString = QStringLiteral(
560 "#ifndef KWAYLAND_%1_%2_H\n"
561 "#define KWAYLAND_%1_%2_H\n\n");
562 
563  *m_stream.localData() << templateString.arg(projectToName().toUpper()).arg(m_baseFileName.toUpper());
564 }
565 
566 void Generator::generateStartNamespace()
567 {
568  const QString templateString = QStringLiteral(
569 "namespace KWayland\n"
570 "{\n"
571 "namespace %1\n"
572 "{\n\n");
573  *m_stream.localData() << templateString.arg(projectToName());
574 }
575 
576 void Generator::generateEndNamespace()
577 {
578  *m_stream.localData() << QStringLiteral("\n}\n}\n\n");
579 }
580 
581 void Generator::generateHeaderIncludes()
582 {
583  switch (m_project.localData()) {
584  case Project::Client:
585  *m_stream.localData() << QStringLiteral("#include <QObject>\n\n");
586  break;
587  case Project::Server:
588  *m_stream.localData() << QStringLiteral(
589 "#include \"global.h\"\n"
590 "#include \"resource.h\"\n\n");
591  break;
592  default:
593  Q_UNREACHABLE();
594  }
595  *m_stream.localData() << QStringLiteral("#include <KWayland/%1/kwayland%2_export.h>\n\n").arg(projectToName()).arg(projectToName().toLower());
596 }
597 
598 void Generator::generateCppIncludes()
599 {
600  switch (m_project.localData()) {
601  case Project::Client:
602  *m_stream.localData() << QStringLiteral("#include \"%1.h\"\n").arg(m_baseFileName.toLower());
603  *m_stream.localData() << QStringLiteral("#include \"event_queue.h\"\n");
604  *m_stream.localData() << QStringLiteral("#include \"wayland_pointer_p.h\"\n\n");
605  break;
606  case Project::Server:
607  *m_stream.localData() << QStringLiteral("#include \"%1_interface.h\"\n").arg(m_baseFileName.toLower());
608  *m_stream.localData() << QStringLiteral(
609 "#include \"display.h\"\n"
610 "#include \"global_p.h\"\n"
611 "#include \"resource_p.h\"\n\n");
612  break;
613  default:
614  Q_UNREACHABLE();
615  }
616 }
617 
618 void Generator::generateClass(const Interface &interface)
619 {
620  switch (m_project.localData()) {
621  case Project::Client:
622  if (interface.isGlobal()) {
623  generateClientGlobalClass(interface);
624  } else {
625  generateClientResourceClass(interface);
626  }
627  break;
628  case Project::Server:
629  if (interface.isGlobal()) {
630  generateServerGlobalClass(interface);
631  } else {
632  generateServerResourceClass(interface);
633  }
634  break;
635  default:
636  Q_UNREACHABLE();
637  }
638 }
639 
640 void Generator::generateClientGlobalClass(const Interface &interface)
641 {
642  generateClientGlobalClassDoxy(interface);
643  generateClientClassQObjectDerived(interface);
644  generateClientGlobalClassCtor(interface);
645  generateClientClassDtor(interface);
646  generateClientGlobalClassSetup(interface);
647  generateClientClassReleaseDestroy(interface);
648  generateClientClassStart(interface);
649  generateClientClassRequests(interface);
650  generateClientClassCasts(interface);
651  generateClientClassSignals(interface);
652  generateClientGlobalClassEnd(interface);
653 }
654 
655 void Generator::generateClientResourceClass(const Interface &interface)
656 {
657  generateClientClassQObjectDerived(interface);
658  generateClientClassDtor(interface);
659  generateClientResourceClassSetup(interface);
660  generateClientClassReleaseDestroy(interface);
661  generateClientClassRequests(interface);
662  generateClientClassCasts(interface);
663  generateClientResourceClassEnd(interface);
664 }
665 
666 void Generator::generateServerGlobalClass(const Interface &interface)
667 {
668  if (interface.isUnstableInterface()) {
669  generateServerGlobalClassUnstable(interface);
670  return;
671  }
672  const QString templateString = QStringLiteral(
673 "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
674 "{\n"
675 " Q_OBJECT\n"
676 "public:\n"
677 " virtual ~%1();\n"
678 "\n"
679 "private:\n"
680 " explicit %1(Display *display, QObject *parent = nullptr);\n"
681 " friend class Display;\n"
682 " class Private;\n"
683 "};\n"
684 "\n");
685  *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
686 }
687 
688 void Generator::generateServerGlobalClassUnstable(const Interface &interface)
689 {
690  const QString templateString = QStringLiteral(
691 "class KWAYLANDSERVER_EXPORT %1 : public Global\n"
692 "{\n"
693 " Q_OBJECT\n"
694 "public:\n"
695 " virtual ~%1();\n"
696 "\n"
697 " /**\n"
698 " * @returns The interface version used by this %1\n"
699 " **/\n"
700 " %1Version interfaceVersion() const;\n"
701 "\n"
702 "protected:\n"
703 " class Private;\n"
704 " explicit %1(Private *d, QObject *parent = nullptr);\n"
705 "\n"
706 "private:\n"
707 " Private *d_func() const;\n"
708 "};\n"
709 "\n");
710  *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
711 }
712 
713 void Generator::generateServerResourceClass(const Interface &interface)
714 {
715  if (interface.factory()->isUnstableInterface()) {
716  generateServerResourceClassUnstable(interface);
717  return;
718  }
719  const QString templateString = QStringLiteral(
720 "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
721 "{\n"
722 " Q_OBJECT\n"
723 "public:\n"
724 " virtual ~%1();\n"
725 "\n"
726 "private:\n"
727 " explicit %1(%2 *parent, wl_resource *parentResource);\n"
728 " friend class %2;\n"
729 "\n"
730 " class Private;\n"
731 " Private *d_func() const;\n"
732 "};\n"
733 "\n");
734  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
735 }
736 
737 void Generator::generateServerResourceClassUnstable(const Interface &interface)
738 {
739  const QString templateString = QStringLiteral(
740 "class KWAYLANDSERVER_EXPORT %1 : public Resource\n"
741 "{\n"
742 " Q_OBJECT\n"
743 "public:\n"
744 "\n"
745 " virtual ~%1();\n"
746 "\n"
747 " /**\n"
748 " * @returns The interface version used by this %1\n"
749 " **/\n"
750 " %2Version interfaceVersion() const;\n"
751 "\n"
752 "protected:\n"
753 " class Private;\n"
754 " explicit %1(Private *p, QObject *parent = nullptr);\n"
755 "\n"
756 "private:\n"
757 " Private *d_func() const;\n"
758 "};\n"
759 "\n");
760  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
761 }
762 
763 void Generator::generatePrivateClass(const Interface &interface)
764 {
765  switch (m_project.localData()) {
766  case Project::Client:
767  generateClientPrivateClass(interface);
768  break;
769  case Project::Server:
770  if (interface.isGlobal()) {
771  generateServerPrivateGlobalClass(interface);
772  } else {
773  generateServerPrivateResourceClass(interface);
774  }
775  break;
776  default:
777  Q_UNREACHABLE();
778  }
779 }
780 
781 void Generator::generateServerPrivateGlobalClass(const Interface &interface)
782 {
783  QString templateString = QStringLiteral(
784 "class %1::Private : public Global::Private\n"
785 "{\n"
786 "public:\n"
787 " Private(%1 *q, Display *d);\n"
788 "\n"
789 "private:\n"
790 " void bind(wl_client *client, uint32_t version, uint32_t id) override;\n"
791 "\n"
792 " static void unbind(wl_resource *resource);\n"
793 " static Private *cast(wl_resource *r) {\n"
794 " return reinterpret_cast<Private*>(wl_resource_get_user_data(r));\n"
795 " }\n"
796 "\n");
797  *m_stream.localData() << templateString.arg(interface.kwaylandServerName());
798 
799  generateServerPrivateCallbackDefinitions(interface);
800 
801  templateString = QStringLiteral(
802 " %1 *q;\n"
803 " static const struct %2_interface s_interface;\n"
804 " static const quint32 s_version;\n"
805 "};\n"
806 "\n"
807 "const quint32 %1::Private::s_version = %3;\n"
808 "\n");
809  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name()).arg(interface.version());
810  generateServerPrivateInterfaceClass(interface);
811  generateServerPrivateCallbackImpl(interface);
812  generateServerPrivateGlobalCtorBindClass(interface);
813 }
814 
815 void Generator::generateServerPrivateCallbackDefinitions(const Interface &interface)
816 {
817  for (const auto &r: interface.requests()) {
818  if (r.isDestructor() && !interface.isGlobal()) {
819  continue;
820  }
821  *m_stream.localData() << QStringLiteral(" static void %1Callback(wl_client *client, wl_resource *resource").arg(toCamelCase(r.name()));
822  for (const auto &a: r.arguments()) {
823  *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name());
824  }
825  *m_stream.localData() << QStringLiteral(");\n");
826  }
827  *m_stream.localData() << QStringLiteral("\n");
828 }
829 
830 void Generator::generateServerPrivateCallbackImpl(const Interface &interface)
831 {
832  for (const auto &r: interface.requests()) {
833  if (r.isDestructor() && !interface.isGlobal()) {
834  continue;
835  }
836  *m_stream.localData() << QStringLiteral("void %2::Private::%1Callback(wl_client *client, wl_resource *resource").arg(toCamelCase(r.name())).arg(interface.kwaylandServerName());
837  for (const auto &a: r.arguments()) {
838  *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name());
839  }
840  *m_stream.localData() << QStringLiteral(
841 ")\n"
842 "{\n");
843  if (r.isDestructor()) {
844  *m_stream.localData() << QStringLiteral(
845 " Q_UNUSED(client)\n"
846 " wl_resource_destroy(resource);\n");
847  } else {
848  *m_stream.localData() << QStringLiteral(" // TODO: implement\n");
849  }
850  *m_stream.localData() << QStringLiteral(
851 "}\n"
852 "\n");
853  }
854 }
855 
856 void Generator::generateServerPrivateGlobalCtorBindClass(const Interface &interface)
857 {
858  QString templateString = QStringLiteral(
859 "%1::Private::Private(%1 *q, Display *d)\n"
860 " : Global::Private(d, &%2_interface, s_version)\n"
861 " , q(q)\n"
862 "{\n"
863 "}\n"
864 "\n"
865 "void %1::Private::bind(wl_client *client, uint32_t version, uint32_t id)\n"
866 "{\n"
867 " auto c = display->getConnection(client);\n"
868 " wl_resource *resource = c->createResource(&%2_interface, qMin(version, s_version), id);\n"
869 " if (!resource) {\n"
870 " wl_client_post_no_memory(client);\n"
871 " return;\n"
872 " }\n"
873 " wl_resource_set_implementation(resource, &s_interface, this, unbind);\n"
874 " // TODO: should we track?\n"
875 "}\n"
876 "\n"
877 "void %1::Private::unbind(wl_resource *resource)\n"
878 "{\n"
879 " Q_UNUSED(resource)\n"
880 " // TODO: implement?\n"
881 "}\n"
882 "\n");
883  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name());
884 }
885 
886 void Generator::generateServerPrivateResourceClass(const Interface &interface)
887 {
888  QString templateString = QStringLiteral(
889 "class %1::Private : public Resource::Private\n"
890 "{\n"
891 "public:\n"
892 " Private(%1 *q, %2 *c, wl_resource *parentResource);\n"
893 " ~Private();\n"
894 "\n"
895 "private:\n");
896  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName());
897 
898  generateServerPrivateCallbackDefinitions(interface);
899 
900  templateString = QStringLiteral(
901 " %1 *q_func() {\n"
902 " return reinterpret_cast<%1 *>(q);\n"
903 " }\n"
904 "\n"
905 " static const struct %2_interface s_interface;\n"
906 "};\n"
907 "\n");
908  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name());
909 
910  generateServerPrivateInterfaceClass(interface);
911  generateServerPrivateCallbackImpl(interface);
912  generateServerPrivateResourceCtorDtorClass(interface);
913 }
914 
915 void Generator::generateServerPrivateInterfaceClass(const Interface &interface)
916 {
917  *m_stream.localData() << QStringLiteral("#ifndef K_DOXYGEN\n");
918  *m_stream.localData() << QStringLiteral("const struct %2_interface %1::Private::s_interface = {\n").arg(interface.kwaylandServerName()).arg(interface.name());
919  bool first = true;
920  for (auto r: interface.requests()) {
921  if (!first) {
922  *m_stream.localData() << QStringLiteral(",\n");
923  } else {
924  first = false;
925  }
926  if (r.isDestructor() && !interface.isGlobal()) {
927  *m_stream.localData() << QStringLiteral(" resourceDestroyedCallback");
928  } else {
929  *m_stream.localData() << QStringLiteral(" %1Callback").arg(toCamelCase(r.name()));
930  }
931  }
932  *m_stream.localData() << QStringLiteral("\n};\n#endif\n\n");
933 }
934 
935 void Generator::generateServerPrivateResourceCtorDtorClass(const Interface &interface)
936 {
937  QString templateString = QStringLiteral(
938 "%1::Private::Private(%1 *q, %2 *c, wl_resource *parentResource)\n"
939 " : Resource::Private(q, c, parentResource, &%3_interface, &s_interface)\n"
940 "{\n"
941 "}\n"
942 "\n"
943 "%1::Private::~Private()\n"
944 "{\n"
945 " if (resource) {\n"
946 " wl_resource_destroy(resource);\n"
947 " resource = nullptr;\n"
948 " }\n"
949 "}\n");
950  *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()).arg(interface.name());
951 }
952 
953 void Generator::generateClientPrivateClass(const Interface &interface)
954 {
955  if (interface.isGlobal()) {
956  generateClientPrivateGlobalClass(interface);
957  } else {
958  generateClientPrivateResourceClass(interface);
959  }
960 
961  const auto events = interface.events();
962  if (!events.isEmpty()) {
963  *m_stream.localData() << QStringLiteral("\nprivate:\n");
964  // generate the callbacks
965  for (auto event : events) {
966  const QString templateString = QStringLiteral(" static void %1Callback(void *data, %2 *%2");
967  *m_stream.localData() << templateString.arg(event.name()).arg(interface.name());
968  const auto arguments = event.arguments();
969  for (auto argument : arguments) {
970  if (argument.interface().isNull()) {
971  *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name());
972  } else {
973  *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name());
974  }
975  }
976  *m_stream.localData() << ");\n";
977  }
978  *m_stream.localData() << QStringLiteral("\n static const %1_listener s_listener;\n").arg(interface.name());
979  }
980 
981  *m_stream.localData() << QStringLiteral("};\n\n");
982 }
983 
984 void Generator::generateClientPrivateResourceClass(const Interface &interface)
985 {
986  const QString templateString = QStringLiteral(
987 "class %1::Private\n"
988 "{\n"
989 "public:\n"
990 " Private(%1 *q);\n"
991 "\n"
992 " void setup(%2 *arg);\n"
993 "\n"
994 " WaylandPointer<%2, %2_destroy> %3;\n"
995 "\n"
996 "private:\n"
997 " %1 *q;\n");
998 
999  *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1000 }
1001 
1002 void Generator::generateClientPrivateGlobalClass(const Interface &interface)
1003 {
1004  const QString templateString = QStringLiteral(
1005 "class %1::Private\n"
1006 "{\n"
1007 "public:\n"
1008 " Private() = default;\n"
1009 "\n"
1010 " void setup(%2 *arg);\n"
1011 "\n"
1012 " WaylandPointer<%2, %2_destroy> %3;\n"
1013 " EventQueue *queue = nullptr;\n");
1014 
1015  *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1016 }
1017 
1018 void Generator::generateClientCpp(const Interface &interface)
1019 {
1020  // TODO: generate listener and callbacks
1021  const auto events = interface.events();
1022  if (!events.isEmpty()) {
1023  // listener
1024  *m_stream.localData() << QStringLiteral("const %1_listener %2::Private::s_listener = {\n").arg(interface.name()).arg(interface.kwaylandClientName());
1025  bool first = true;
1026  for (auto event : events) {
1027  if (!first) {
1028  *m_stream.localData() << QStringLiteral(",\n");
1029  }
1030  *m_stream.localData() << QStringLiteral(" %1Callback").arg(event.name());
1031  first = false;
1032  }
1033  *m_stream.localData() << QStringLiteral("\n};\n\n");
1034 
1035  // callbacks
1036  for (auto event : events) {
1037  *m_stream.localData() << QStringLiteral("void %1::Private::%2Callback(void *data, %3 *%3").arg(interface.kwaylandClientName()).arg(event.name()).arg(interface.name());
1038 
1039  const auto arguments = event.arguments();
1040  for (auto argument : arguments) {
1041  if (argument.interface().isNull()) {
1042  *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name());
1043  } else {
1044  *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name());
1045  }
1046  }
1047 
1048  *m_stream.localData() << QStringLiteral(
1049 ")\n"
1050 "{\n"
1051 " auto p = reinterpret_cast<%1::Private*>(data);\n"
1052 " Q_ASSERT(p->%2 == %3);\n").arg(interface.kwaylandClientName())
1053  .arg(interface.kwaylandClientName().toLower())
1054  .arg(interface.name());
1055  for (auto argument : arguments) {
1056  *m_stream.localData() << QStringLiteral(" Q_UNUSED(%1)\n").arg(argument.name());
1057  }
1058  *m_stream.localData() << QStringLiteral(" // TODO: implement\n}\n\n");
1059  }
1060  }
1061 
1062  if (interface.isGlobal()) {
1063  // generate ctor without this pointer to Private
1064  const QString templateString = QStringLiteral(
1065 "%1::%1(QObject *parent)\n"
1066 " : QObject(parent)\n"
1067 " , d(new Private)\n"
1068 "{\n"
1069 "}\n");
1070  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1071  } else {
1072  // Private ctor
1073  const QString templateString = QStringLiteral(
1074 "%1::Private::Private(%1 *q)\n"
1075 " : q(q)\n"
1076 "{\n"
1077 "}\n"
1078 "\n"
1079 "%1::%1(QObject *parent)\n"
1080 " : QObject(parent)\n"
1081 " , d(new Private(this))\n"
1082 "{\n"
1083 "}\n");
1084  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1085  }
1086 
1087  // setup call with optional add_listener
1088  const QString setupTemplate = QStringLiteral(
1089 "\n"
1090 "void %1::Private::setup(%3 *arg)\n"
1091 "{\n"
1092 " Q_ASSERT(arg);\n"
1093 " Q_ASSERT(!%2);\n"
1094 " %2.setup(arg);\n");
1095  *m_stream.localData() << setupTemplate.arg(interface.kwaylandClientName())
1096  .arg(interface.kwaylandClientName().toLower())
1097  .arg(interface.name());
1098  if (!interface.events().isEmpty()) {
1099  *m_stream.localData() << QStringLiteral(" %1_add_listener(%2, &s_listener, this);\n").arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1100  }
1101  *m_stream.localData() << QStringLiteral("}\n");
1102 
1103  const QString templateString = QStringLiteral(
1104 "\n"
1105 "%1::~%1()\n"
1106 "{\n"
1107 " release();\n"
1108 "}\n"
1109 "\n"
1110 "void %1::setup(%3 *%2)\n"
1111 "{\n"
1112 " d->setup(%2);\n"
1113 "}\n"
1114 "\n"
1115 "void %1::release()\n"
1116 "{\n"
1117 " d->%2.release();\n"
1118 "}\n"
1119 "\n"
1120 "void %1::destroy()\n"
1121 "{\n"
1122 " d->%2.destroy();\n"
1123 "}\n"
1124 "\n"
1125 "%1::operator %3*() {\n"
1126 " return d->%2;\n"
1127 "}\n"
1128 "\n"
1129 "%1::operator %3*() const {\n"
1130 " return d->%2;\n"
1131 "}\n"
1132 "\n"
1133 "bool %1::isValid() const\n"
1134 "{\n"
1135 " return d->%2.isValid();\n"
1136 "}\n\n");
1137 
1138  *m_stream.localData() << templateString.arg(interface.kwaylandClientName())
1139  .arg(interface.kwaylandClientName().toLower())
1140  .arg(interface.name());
1141  if (interface.isGlobal()) {
1142  const QString templateStringGlobal = QStringLiteral(
1143 "void %1::setEventQueue(EventQueue *queue)\n"
1144 "{\n"
1145 " d->queue = queue;\n"
1146 "}\n"
1147 "\n"
1148 "EventQueue *%1::eventQueue()\n"
1149 "{\n"
1150 " return d->queue;\n"
1151 "}\n\n"
1152  );
1153  *m_stream.localData() << templateStringGlobal.arg(interface.kwaylandClientName());
1154  }
1155 }
1156 
1157 void Generator::generateClientGlobalClassDoxy(const Interface &interface)
1158 {
1159  const QString templateString = QStringLiteral(
1160 "/**\n"
1161 " * @short Wrapper for the %2 interface.\n"
1162 " *\n"
1163 " * This class provides a convenient wrapper for the %2 interface.\n"
1164 " *\n"
1165 " * To use this class one needs to interact with the Registry. There are two\n"
1166 " * possible ways to create the %1 interface:\n"
1167 " * @code\n"
1168 " * %1 *c = registry->create%1(name, version);\n"
1169 " * @endcode\n"
1170 " *\n"
1171 " * This creates the %1 and sets it up directly. As an alternative this\n"
1172 " * can also be done in a more low level way:\n"
1173 " * @code\n"
1174 " * %1 *c = new %1;\n"
1175 " * c->setup(registry->bind%1(name, version));\n"
1176 " * @endcode\n"
1177 " *\n"
1178 " * The %1 can be used as a drop-in replacement for any %2\n"
1179 " * pointer as it provides matching cast operators.\n"
1180 " *\n"
1181 " * @see Registry\n"
1182 " **/\n");
1183  *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name());
1184 }
1185 
1186 void Generator::generateClientClassQObjectDerived(const Interface &interface)
1187 {
1188  const QString templateString = QStringLiteral(
1189 "class KWAYLANDCLIENT_EXPORT %1 : public QObject\n"
1190 "{\n"
1191 " Q_OBJECT\n"
1192 "public:\n");
1193  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1194 }
1195 
1196 void Generator::generateClientGlobalClassCtor(const Interface &interface)
1197 {
1198  const QString templateString = QStringLiteral(
1199 " /**\n"
1200 " * Creates a new %1.\n"
1201 " * Note: after constructing the %1 it is not yet valid and one needs\n"
1202 " * to call setup. In order to get a ready to use %1 prefer using\n"
1203 " * Registry::create%1.\n"
1204 " **/\n"
1205 " explicit %1(QObject *parent = nullptr);\n");
1206  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1207 }
1208 
1209 void Generator::generateClientClassDtor(const Interface &interface)
1210 {
1211  *m_stream.localData() << QStringLiteral(" virtual ~%1();\n\n").arg(interface.kwaylandClientName());
1212 }
1213 
1214 void Generator::generateClientClassReleaseDestroy(const Interface &interface)
1215 {
1216  const QString templateString = QStringLiteral(
1217 " /**\n"
1218 " * @returns @c true if managing a %2.\n"
1219 " **/\n"
1220 " bool isValid() const;\n"
1221 " /**\n"
1222 " * Releases the %2 interface.\n"
1223 " * After the interface has been released the %1 instance is no\n"
1224 " * longer valid and can be setup with another %2 interface.\n"
1225 " **/\n"
1226 " void release();\n"
1227 " /**\n"
1228 " * Destroys the data held by this %1.\n"
1229 " * This method is supposed to be used when the connection to the Wayland\n"
1230 " * server goes away. If the connection is not valid anymore, it's not\n"
1231 " * possible to call release anymore as that calls into the Wayland\n"
1232 " * connection and the call would fail. This method cleans up the data, so\n"
1233 " * that the instance can be deleted or set up to a new %2 interface\n"
1234 " * once there is a new connection available.\n"
1235 " *\n"
1236 " * It is suggested to connect this method to ConnectionThread::connectionDied:\n"
1237 " * @code\n"
1238 " * connect(connection, &ConnectionThread::connectionDied, %3, &%1::destroy);\n"
1239 " * @endcode\n"
1240 " *\n"
1241 " * @see release\n"
1242 " **/\n"
1243 " void destroy();\n"
1244 "\n");
1245  *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1246 }
1247 
1248 void Generator::generateClientGlobalClassSetup(const Interface &interface)
1249 {
1250  const QString templateString = QStringLiteral(
1251 " /**\n"
1252 " * Setup this %1 to manage the @p %3.\n"
1253 " * When using Registry::create%1 there is no need to call this\n"
1254 " * method.\n"
1255 " **/\n"
1256 " void setup(%2 *%3);\n");
1257  *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower());
1258 }
1259 
1260 void Generator::generateClientResourceClassSetup(const Interface &interface)
1261 {
1262  const QString templateString = QStringLiteral(
1263 " /**\n"
1264 " * Setup this %1 to manage the @p %3.\n"
1265 " * When using %4::create%1 there is no need to call this\n"
1266 " * method.\n"
1267 " **/\n"
1268 " void setup(%2 *%3);\n");
1269  *m_stream.localData() << templateString.arg(interface.kwaylandClientName())
1270  .arg(interface.name())
1271  .arg(interface.kwaylandClientName().toLower())
1272  .arg(interface.factory()->kwaylandClientName());
1273 
1274 }
1275 
1276 void Generator::generateClientClassStart(const Interface &interface)
1277 {
1278  const QString templateString = QStringLiteral(
1279 " /**\n"
1280 " * Sets the @p queue to use for creating objects with this %1.\n"
1281 " **/\n"
1282 " void setEventQueue(EventQueue *queue);\n"
1283 " /**\n"
1284 " * @returns The event queue to use for creating objects with this %1.\n"
1285 " **/\n"
1286 " EventQueue *eventQueue();\n\n");
1287  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1288 }
1289 
1290 void Generator::generateClientClassRequests(const Interface &interface)
1291 {
1292  const auto requests = interface.requests();
1293  const QString templateString = QStringLiteral(" void %1(%2);\n\n");
1294  const QString factoryTemplateString = QStringLiteral(" %1 *%2(%3);\n\n");
1295  for (const auto &r: requests) {
1296  if (r.isDestructor()) {
1297  continue;
1298  }
1299  QString arguments;
1300  bool first = true;
1301  QString factored;
1302  for (const auto &a: r.arguments()) {
1303  if (a.type() == Argument::Type::NewId) {
1304  factored = a.interface();
1305  continue;
1306  }
1307  if (!first) {
1308  arguments.append(QStringLiteral(", "));
1309  } else {
1310  first = false;
1311  }
1312  if (a.type() == Argument::Type::Object) {
1313  arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1314  } else {
1315  arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1316  }
1317  }
1318  if (factored.isEmpty()) {
1319  *m_stream.localData() << templateString.arg(toCamelCase((r.name()))).arg(arguments);
1320  } else {
1321  if (!first) {
1322  arguments.append(QStringLiteral(", "));
1323  }
1324  arguments.append(QStringLiteral("QObject *parent = nullptr"));
1325  *m_stream.localData() << factoryTemplateString.arg(toQtInterfaceName(factored)).arg(toCamelCase(r.name())).arg(arguments);
1326  }
1327  }
1328 }
1329 
1330 void Generator::generateClientCppRequests(const Interface &interface)
1331 {
1332  const auto requests = interface.requests();
1333  const QString templateString = QStringLiteral(
1334 "void %1::%2(%3)\n"
1335 "{\n"
1336 " Q_ASSERT(isValid());\n"
1337 " %4_%5(d->%6%7);\n"
1338 "}\n\n");
1339  const QString factoryTemplateString = QStringLiteral(
1340 "%2 *%1::%3(%4)\n"
1341 "{\n"
1342 " Q_ASSERT(isValid());\n"
1343 " auto p = new %2(parent);\n"
1344 " auto w = %5_%6(d->%7%8);\n"
1345 " if (d->queue) {\n"
1346 " d->queue->addProxy(w);\n"
1347 " }\n"
1348 " p->setup(w);\n"
1349 " return p;\n"
1350 "}\n\n"
1351  );
1352  for (const auto &r: requests) {
1353  if (r.isDestructor()) {
1354  continue;
1355  }
1356  QString arguments;
1357  QString requestArguments;
1358  bool first = true;
1359  QString factored;
1360  for (const auto &a: r.arguments()) {
1361  if (a.type() == Argument::Type::NewId) {
1362  factored = a.interface();
1363  continue;
1364  }
1365  if (!first) {
1366  arguments.append(QStringLiteral(", "));
1367  } else {
1368  first = false;
1369  }
1370  if (a.type() == Argument::Type::Object) {
1371  arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1372  requestArguments.append(QStringLiteral(", *%1").arg(toCamelCase(a.name())));
1373  } else {
1374  arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name())));
1375  QString arg = toCamelCase(a.name());
1376  if (a.type() == Argument::Type::Fixed) {
1377  arg = QStringLiteral("wl_fixed_from_double(%1)").arg(arg);
1378  }
1379  requestArguments.append(QStringLiteral(", %1").arg(arg));
1380  }
1381  }
1382  if (factored.isEmpty()) {
1383  *m_stream.localData() << templateString.arg(interface.kwaylandClientName())
1384  .arg(toCamelCase(r.name()))
1385  .arg(arguments)
1386  .arg(interface.name())
1387  .arg(r.name())
1388  .arg(interface.kwaylandClientName().toLower())
1389  .arg(requestArguments);
1390  } else {
1391  if (!first) {
1392  arguments.append(QStringLiteral(", "));
1393  }
1394  arguments.append(QStringLiteral("QObject *parent"));
1395  *m_stream.localData() << factoryTemplateString.arg(interface.kwaylandClientName())
1396  .arg(toQtInterfaceName(factored))
1397  .arg(toCamelCase(r.name()))
1398  .arg(arguments)
1399  .arg(interface.name())
1400  .arg(r.name())
1401  .arg(interface.kwaylandClientName().toLower())
1402  .arg(requestArguments);
1403  }
1404  }
1405 }
1406 
1407 void Generator::generateClientClassCasts(const Interface &interface)
1408 {
1409  const QString templateString = QStringLiteral(
1410 " operator %1*();\n"
1411 " operator %1*() const;\n\n");
1412  *m_stream.localData() << templateString.arg(interface.name());
1413 }
1414 
1415 void Generator::generateClientGlobalClassEnd(const Interface &interface)
1416 {
1417  Q_UNUSED(interface)
1418  *m_stream.localData() << QStringLiteral("private:\n");
1419  generateClientClassDptr(interface);
1420  *m_stream.localData() << QStringLiteral("};\n\n");
1421 }
1422 
1423 void Generator::generateClientClassDptr(const Interface &interface)
1424 {
1425  Q_UNUSED(interface)
1426  *m_stream.localData() << QStringLiteral(
1427 " class Private;\n"
1428 " QScopedPointer<Private> d;\n");
1429 }
1430 
1431 void Generator::generateClientResourceClassEnd(const Interface &interface)
1432 {
1433  *m_stream.localData() << QStringLiteral(
1434 "private:\n"
1435 " friend class %2;\n"
1436 " explicit %1(QObject *parent = nullptr);\n"
1437  ).arg(interface.kwaylandClientName()).arg(interface.factory()->kwaylandClientName());
1438  generateClientClassDptr(interface);
1439  *m_stream.localData() << QStringLiteral("};\n\n");
1440 }
1441 
1442 void Generator::generateWaylandForwardDeclarations()
1443 {
1444  for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
1445  *m_stream.localData() << QStringLiteral("struct %1;\n").arg((*it).name());
1446  }
1447  *m_stream.localData() << "\n";
1448 }
1449 
1450 void Generator::generateNamespaceForwardDeclarations()
1451 {
1452  QSet<QString> referencedObjects;
1453  for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
1454  const auto events = (*it).events();
1455  const auto requests = (*it).requests();
1456  for (const auto &e: events) {
1457  const auto args = e.arguments();
1458  for (const auto &a: args) {
1459  if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) {
1460  continue;
1461  }
1462  referencedObjects << a.interface();
1463  }
1464  }
1465  for (const auto &r: requests) {
1466  const auto args = r.arguments();
1467  for (const auto &a: args) {
1468  if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) {
1469  continue;
1470  }
1471  referencedObjects << a.interface();
1472  }
1473  }
1474  }
1475 
1476  switch (m_project.localData()) {
1477  case Project::Client:
1478  *m_stream.localData() << QStringLiteral("class EventQueue;\n");
1479  for (const auto &o : referencedObjects) {
1480  auto it = s_clientClassNameMapping.constFind(o);
1481  if (it != s_clientClassNameMapping.constEnd()) {
1482  *m_stream.localData() << QStringLiteral("class %1;\n").arg(it.value());
1483  } else {
1484  qWarning() << "Cannot forward declare KWayland class for interface " << o;
1485  }
1486  }
1487  *m_stream.localData() << QStringLiteral("\n");
1488  break;
1489  case Project::Server:
1490  *m_stream.localData() << QStringLiteral("class Display;\n\n");
1491  break;
1492  default:
1493  Q_UNREACHABLE();
1494  }
1495 }
1496 
1497 void Generator::generateClientClassSignals(const Interface &interface)
1498 {
1499  const QString templateString = QStringLiteral(
1500 "Q_SIGNALS:\n"
1501 " /**\n"
1502 " * The corresponding global for this interface on the Registry got removed.\n"
1503 " *\n"
1504 " * This signal gets only emitted if the %1 got created by\n"
1505 " * Registry::create%1\n"
1506 " **/\n"
1507 " void removed();\n\n");
1508  *m_stream.localData() << templateString.arg(interface.kwaylandClientName());
1509 }
1510 
1511 QString Generator::projectToName() const
1512 {
1513  switch (m_project.localData()) {
1514  case Project::Client:
1515  return QStringLiteral("Client");
1516  case Project::Server:
1517  return QStringLiteral("Server");
1518  default:
1519  Q_UNREACHABLE();
1520  }
1521 }
1522 
1523 static void parseMapping()
1524 {
1525  QFile mappingFile(QStringLiteral(MAPPING_FILE));
1526  mappingFile.open(QIODevice::ReadOnly);
1527  QTextStream stream(&mappingFile);
1528  while (!stream.atEnd()) {
1529  QString line = stream.readLine();
1530  if (line.startsWith(QLatin1String("#")) || line.isEmpty()) {
1531  continue;
1532  }
1533  const QStringList parts = line.split(QStringLiteral(";"));
1534  if (parts.count() < 2) {
1535  continue;
1536  }
1537  s_clientClassNameMapping.insert(parts.first(), parts.at(1));
1538  }
1539 }
1540 
1541 }
1542 }
1543 
1544 int main(int argc, char **argv)
1545 {
1546  using namespace KWayland::Tools;
1547 
1548  parseMapping();
1549 
1550  QCoreApplication app(argc, argv);
1551 
1552  QCommandLineParser parser;
1553  QCommandLineOption xmlFile(QStringList{QStringLiteral("x"), QStringLiteral("xml")},
1554  QStringLiteral("The wayland protocol to parse."),
1555  QStringLiteral("FileName"));
1556  QCommandLineOption fileName(QStringList{QStringLiteral("f"), QStringLiteral("file")},
1557  QStringLiteral("The base name of files to be generated. E.g. for \"foo\" the files \"foo.h\" and \"foo.cpp\" are generated."
1558  "If not provided the base name gets derived from the xml protocol name"),
1559  QStringLiteral("FileName"));
1560 
1561  parser.addHelpOption();
1562  parser.addOption(xmlFile);
1563  parser.addOption(fileName);
1564 
1565  parser.process(app);
1566 
1567  Generator generator(&app);
1568  generator.setXmlFileName(parser.value(xmlFile));
1569  generator.setBaseFileName(parser.value(fileName));
1570  generator.start();
1571 
1572  return app.exec();
1573 }
QString & append(QChar ch)
QString toString() const const
const T & at(int i) const const
QMap::const_iterator constFind(const Key &key) const const
QStringRef value(const QString &namespaceUri, const QString &name) const const
void setArguments(const QStringList &arguments)
virtual bool event(QEvent *e)
int count(const T &value) const const
QString fromLocal8Bit(const char *str, int size)
bool isEmpty() const const
QMap::const_iterator constEnd() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void finished(int exitCode)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QCommandLineOption addHelpOption()
void deleteLater()
T & first()
void setFuture(const QFuture< T > &future)
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QString toLower() const const
void setProgram(const QString &program)
void process(const QStringList &arguments)
char * toString(const T &value)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QDate currentDate()
bool addOption(const QCommandLineOption &option)
QMap::iterator insert(const Key &key, const T &value)
QList::const_iterator constEnd() const const
QList::const_iterator constBegin() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int year() const const
QObject * parent() const const
QFuture< T > run(Function function,...)
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QString value(const QString &optionName) const const
QLatin1String KPIMKDAV2_EXPORT protocolName(Protocol protocol)
QByteArray readAllStandardOutput()
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Aug 7 2020 22:48:17 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.